2 * gEDA - GNU Electronic Design Automation
3 * This file is a part of gerbv.
5 * Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
25 \brief Cairo rendering functions and the related selection calculating functions
35 #include <math.h> /* ceil(), atan2() */
47 #define dprintf if(DEBUG) printf
50 draw_cairo_line_to (cairo_t
*cairoTarget
, gdouble x
, gdouble y
, gboolean adjustByHalf
, gboolean pixelOutput
){
51 gdouble x1
= x
, y1
= y
;
53 cairo_user_to_device (cairoTarget
, &x1
, &y1
);
60 cairo_device_to_user (cairoTarget
, &x1
, &y1
);
62 cairo_line_to (cairoTarget
, x1
, y1
);
66 draw_cairo_move_to (cairo_t
*cairoTarget
, gdouble x
, gdouble y
, gboolean oddWidth
, gboolean pixelOutput
){
67 gdouble x1
= x
, y1
= y
;
69 cairo_user_to_device (cairoTarget
, &x1
, &y1
);
76 cairo_device_to_user (cairoTarget
, &x1
, &y1
);
78 cairo_move_to (cairoTarget
, x1
, y1
);
82 draw_cairo_translate_adjust (cairo_t
*cairoTarget
, gdouble x
, gdouble y
, gboolean pixelOutput
){
83 gdouble x1
= x
, y1
= y
;
85 cairo_user_to_device (cairoTarget
, &x1
, &y1
);
88 cairo_device_to_user (cairoTarget
, &x1
, &y1
);
90 cairo_translate (cairoTarget
, x1
, y1
);
94 draw_net_in_selection_buffer (gerbv_net_t
*net
, gerbv_selection_info_t
*selectionInfo
) {
97 for (i
=0; i
<selectionInfo
->selectedNodeArray
->len
; i
++){
98 gerbv_selection_item_t sItem
= g_array_index (selectionInfo
->selectedNodeArray
,
99 gerbv_selection_item_t
, i
);
100 if (sItem
.net
== net
)
107 draw_check_if_object_is_in_selected_area (cairo_t
*cairoTarget
, gboolean isStroke
,
108 gerbv_selection_info_t
*selectionInfo
, gerbv_image_t
*image
, struct gerbv_net
*net
){
109 gdouble corner1X
,corner1Y
,corner2X
,corner2Y
;
111 corner1X
= selectionInfo
->lowerLeftX
;
112 corner1Y
= selectionInfo
->lowerLeftY
;
113 corner2X
= selectionInfo
->upperRightX
;
114 corner2Y
= selectionInfo
->upperRightY
;
116 /* calculate the coordinate of the user's click in the current
117 transformation matrix */
118 cairo_device_to_user (cairoTarget
, &corner1X
, &corner1Y
);
119 cairo_device_to_user (cairoTarget
, &corner2X
, &corner2Y
);
120 if (selectionInfo
->type
== GERBV_SELECTION_POINT_CLICK
) {
121 /* use the cairo in_fill routine to see if the point is within the
123 if ((isStroke
&& cairo_in_stroke (cairoTarget
, corner1X
, corner1Y
)) ||
124 (!isStroke
&& cairo_in_fill (cairoTarget
, corner1X
, corner1Y
))) {
125 /* add the net to the selection array */
126 if (!draw_net_in_selection_buffer(net
, selectionInfo
)) {
127 gerbv_selection_item_t sItem
= {image
, net
};
128 g_array_append_val (selectionInfo
->selectedNodeArray
, sItem
);
132 else if (selectionInfo
->type
== GERBV_SELECTION_DRAG_BOX
) {
134 gdouble minX
,minY
,maxX
,maxY
;
136 /* we can't assume the "lowerleft" corner is actually in the lower left,
137 since the cairo transformation matrix may be mirrored,etc */
138 minX
= MIN(corner1X
,corner2X
);
139 maxX
= MAX(corner1X
,corner2X
);
140 minY
= MIN(corner1Y
,corner2Y
);
141 maxY
= MAX(corner1Y
,corner2Y
);
143 cairo_stroke_extents (cairoTarget
, &x1
, &y1
, &x2
, &y2
);
145 cairo_fill_extents (cairoTarget
, &x1
, &y1
, &x2
, &y2
);
147 if ((minX
< x1
) && (minY
< y1
) && (maxX
> x2
) && (maxY
> y2
)) {
148 /* add the net to the selection array */
149 if (!draw_net_in_selection_buffer(net
, selectionInfo
)) {
150 gerbv_selection_item_t sItem
= {image
, net
};
151 g_array_append_val (selectionInfo
->selectedNodeArray
, sItem
);
155 /* clear the path, since we didn't actually draw it and cairo
156 doesn't reset it after the previous calls */
157 cairo_new_path (cairoTarget
);
161 draw_fill (cairo_t
*cairoTarget
, gchar drawMode
, gerbv_selection_info_t
*selectionInfo
,
162 gerbv_image_t
*image
, struct gerbv_net
*net
){
163 if ((drawMode
== DRAW_IMAGE
) || (drawMode
== DRAW_SELECTIONS
))
164 cairo_fill (cairoTarget
);
166 draw_check_if_object_is_in_selected_area (cairoTarget
, FALSE
,
167 selectionInfo
, image
, net
);
171 draw_stroke (cairo_t
*cairoTarget
, gchar drawMode
, gerbv_selection_info_t
*selectionInfo
,
172 gerbv_image_t
*image
, struct gerbv_net
*net
){
173 if ((drawMode
== DRAW_IMAGE
) || (drawMode
== DRAW_SELECTIONS
))
174 cairo_stroke (cairoTarget
);
176 draw_check_if_object_is_in_selected_area (cairoTarget
, TRUE
,
177 selectionInfo
, image
, net
);
181 * Draws a circle _centered_ at x,y with diameter dia
184 gerbv_draw_circle(cairo_t
*cairoTarget
, gdouble diameter
)
186 cairo_arc (cairoTarget
, 0.0, 0.0, diameter
/2.0, 0, 2.0*M_PI
);
188 } /* gerbv_draw_circle */
192 * Draws a rectangle _centered_ at x,y with sides x_side, y_side
195 gerbv_draw_rectangle(cairo_t
*cairoTarget
, gdouble width1
, gdouble height1
, gboolean pixelOutput
)
197 gdouble width
= width1
, height
= height1
;
199 cairo_user_to_device_distance (cairoTarget
, &width
, &height
);
200 width
= round(width
);
201 height
= round(height
);
202 width
-= ((int)width
% 2);
203 height
-= ((int)height
% 2);
204 cairo_device_to_user_distance (cairoTarget
, &width
, &height
);
206 cairo_rectangle (cairoTarget
, - width
/ 2.0, - height
/ 2.0, width
, height
);
208 } /* gerbv_draw_rectangle */
212 * Draws an oblong _centered_ at x,y with x axis x_axis and y axis y_axis
215 gerbv_draw_oblong(cairo_t
*cairoTarget
, gdouble width
, gdouble height
)
217 /* --- This stuff produces a line + rounded ends --- */
218 gdouble circleDiameter
, strokeDistance
;
220 cairo_new_path (cairoTarget
);
221 if (width
< height
) {
222 circleDiameter
= width
;
223 strokeDistance
= (height
- width
)/2.0;
224 cairo_arc (cairoTarget
, 0.0, strokeDistance
, circleDiameter
/2.0, 0, -M_PI
);
225 cairo_line_to (cairoTarget
, -circleDiameter
/2.0, -strokeDistance
);
226 cairo_arc (cairoTarget
, 0.0, -strokeDistance
, circleDiameter
/2.0, -M_PI
, 0);
227 cairo_line_to (cairoTarget
, circleDiameter
/2.0, strokeDistance
);
230 circleDiameter
= height
;
231 strokeDistance
= (width
- height
)/2.0;
232 cairo_arc (cairoTarget
, -strokeDistance
, 0.0, circleDiameter
/2.0, M_PI
/2.0, -M_PI
/2.0);
233 cairo_line_to (cairoTarget
, strokeDistance
, -circleDiameter
/2.0);
234 cairo_arc (cairoTarget
, strokeDistance
, 0.0, circleDiameter
/2.0, -M_PI
/2.0, M_PI
/2.0);
235 cairo_line_to (cairoTarget
, -strokeDistance
, circleDiameter
/2.0);
237 /* --- This stuff produces an oval pad --- */
238 /* cairo doesn't have a function to draw ovals, so we must
239 * draw an arc and stretch it by scaling different x and y values
240 cairo_save (cairoTarget);
241 cairo_scale (cairoTarget, width, height);
242 gerbv_draw_circle (cairoTarget, 1);
243 cairo_restore (cairoTarget);
246 } /* gerbv_draw_oblong */
250 gerbv_draw_polygon(cairo_t
*cairoTarget
, gdouble outsideDiameter
,
251 gdouble numberOfSides
, gdouble degreesOfRotation
)
253 int i
, numberOfSidesInteger
= (int) numberOfSides
;
255 cairo_rotate(cairoTarget
, degreesOfRotation
* M_PI
/180);
256 cairo_move_to(cairoTarget
, outsideDiameter
/ 2.0, 0);
257 /* skip first point, since we've moved there already */
258 /* include last point, since we may be drawing an aperture hole next
259 and cairo may not correctly close the path itself */
260 for (i
= 1; i
<= (int)numberOfSidesInteger
; i
++){
261 gdouble angle
= (double) i
/ numberOfSidesInteger
* M_PI
* 2.0;
262 cairo_line_to (cairoTarget
, cos(angle
) * outsideDiameter
/ 2.0,
263 sin(angle
) * outsideDiameter
/ 2.0);
266 } /* gerbv_draw_polygon */
270 gerbv_draw_aperature_hole(cairo_t
*cairoTarget
, gdouble dimensionX
, gdouble dimensionY
, gboolean pixelOutput
)
274 gerbv_draw_rectangle (cairoTarget
, dimensionX
, dimensionY
, pixelOutput
);
276 gerbv_draw_circle (cairoTarget
, dimensionX
);
280 } /* gerbv_draw_aperature_hole */
283 draw_update_macro_exposure (cairo_t
*cairoTarget
, cairo_operator_t clearOperator
,
284 cairo_operator_t darkOperator
, gdouble exposureSetting
){
286 if (exposureSetting
== 0.0) {
287 cairo_set_operator (cairoTarget
, clearOperator
);
289 else if (exposureSetting
== 1.0) {
290 cairo_set_operator (cairoTarget
, darkOperator
);
292 else if (exposureSetting
== 2.0) {
293 /* reverse current exposure setting */
294 cairo_operator_t currentOperator
= cairo_get_operator (cairoTarget
);
295 if (currentOperator
== clearOperator
) {
296 cairo_set_operator (cairoTarget
, darkOperator
);
299 cairo_set_operator (cairoTarget
, clearOperator
);
307 gerbv_draw_amacro(cairo_t
*cairoTarget
, cairo_operator_t clearOperator
,
308 cairo_operator_t darkOperator
, gerbv_simplified_amacro_t
*s
,
309 gint usesClearPrimative
, gdouble pixelWidth
, gchar drawMode
,
310 gerbv_selection_info_t
*selectionInfo
,
311 gerbv_image_t
*image
, struct gerbv_net
*net
)
314 gerbv_simplified_amacro_t
*ls
= s
;
316 dprintf("Drawing simplified aperture macros:\n");
317 if (usesClearPrimative
)
318 cairo_push_group (cairoTarget
);
321 * This handles the exposure thing in the aperture macro
322 * The exposure is always the first element on stack independent
325 cairo_save (cairoTarget
);
326 cairo_new_path(cairoTarget
);
327 cairo_operator_t oldOperator
= cairo_get_operator (cairoTarget
);
329 if (ls
->type
== GERBV_APTYPE_MACRO_CIRCLE
) {
331 if (draw_update_macro_exposure (cairoTarget
, clearOperator
,
332 darkOperator
, ls
->parameter
[CIRCLE_EXPOSURE
])){
333 cairo_translate (cairoTarget
, ls
->parameter
[CIRCLE_CENTER_X
],
334 ls
->parameter
[CIRCLE_CENTER_Y
]);
336 gerbv_draw_circle (cairoTarget
, ls
->parameter
[CIRCLE_DIAMETER
]);
337 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
339 } else if (ls
->type
== GERBV_APTYPE_MACRO_OUTLINE
) {
340 int pointCounter
,numberOfPoints
;
341 /* Number of points parameter seems to not include the start point,
342 * so we add one to include the start point.
344 numberOfPoints
= (int) ls
->parameter
[OUTLINE_NUMBER_OF_POINTS
] + 1;
346 if (draw_update_macro_exposure (cairoTarget
, clearOperator
,
347 darkOperator
, ls
->parameter
[OUTLINE_EXPOSURE
])){
348 cairo_rotate (cairoTarget
, ls
->parameter
[(numberOfPoints
- 1) * 2 + OUTLINE_ROTATION
] * M_PI
/180.0);
349 cairo_move_to (cairoTarget
, ls
->parameter
[OUTLINE_FIRST_X
], ls
->parameter
[OUTLINE_FIRST_Y
]);
351 for (pointCounter
=0; pointCounter
< numberOfPoints
; pointCounter
++) {
352 cairo_line_to (cairoTarget
, ls
->parameter
[pointCounter
* 2 + OUTLINE_FIRST_X
],
353 ls
->parameter
[pointCounter
* 2 + OUTLINE_FIRST_Y
]);
355 /* although the gerber specs allow for an open outline,
356 I interpret it to mean the outline should be closed by the
357 rendering softare automatically, since there is no dimension
360 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
362 } else if (ls
->type
== GERBV_APTYPE_MACRO_POLYGON
) {
363 if (draw_update_macro_exposure (cairoTarget
, clearOperator
,
364 darkOperator
, ls
->parameter
[POLYGON_EXPOSURE
])){
365 cairo_translate (cairoTarget
, ls
->parameter
[POLYGON_CENTER_X
],
366 ls
->parameter
[POLYGON_CENTER_Y
]);
367 gerbv_draw_polygon(cairoTarget
, ls
->parameter
[POLYGON_DIAMETER
],
368 ls
->parameter
[POLYGON_NUMBER_OF_POINTS
], ls
->parameter
[POLYGON_ROTATION
]);
369 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
371 } else if (ls
->type
== GERBV_APTYPE_MACRO_MOIRE
) {
372 gdouble diameter
, gap
;
375 cairo_translate (cairoTarget
, ls
->parameter
[MOIRE_CENTER_X
],
376 ls
->parameter
[MOIRE_CENTER_Y
]);
377 cairo_rotate (cairoTarget
, ls
->parameter
[MOIRE_ROTATION
] * M_PI
/180);
378 diameter
= ls
->parameter
[MOIRE_OUTSIDE_DIAMETER
] - ls
->parameter
[MOIRE_CIRCLE_THICKNESS
];
379 gap
= ls
->parameter
[MOIRE_GAP_WIDTH
] + ls
->parameter
[MOIRE_CIRCLE_THICKNESS
];
380 cairo_set_line_width (cairoTarget
, ls
->parameter
[MOIRE_CIRCLE_THICKNESS
]);
382 for (circleIndex
= 0; circleIndex
< (int)ls
->parameter
[MOIRE_NUMBER_OF_CIRCLES
]; circleIndex
++) {
383 gdouble currentDiameter
= (diameter
- gap
* (float) circleIndex
);
384 gerbv_draw_circle (cairoTarget
, currentDiameter
);
385 draw_stroke (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
388 gdouble crosshairRadius
= (ls
->parameter
[MOIRE_CROSSHAIR_LENGTH
] / 2.0);
390 cairo_set_line_width (cairoTarget
, ls
->parameter
[MOIRE_CROSSHAIR_THICKNESS
]);
391 cairo_move_to (cairoTarget
, -crosshairRadius
, 0);
392 cairo_line_to (cairoTarget
, crosshairRadius
, 0);
393 cairo_move_to (cairoTarget
, 0, -crosshairRadius
);
394 cairo_line_to (cairoTarget
, 0, crosshairRadius
);
395 draw_stroke (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
396 } else if (ls
->type
== GERBV_APTYPE_MACRO_THERMAL
) {
398 gdouble startAngle1
, startAngle2
, endAngle1
, endAngle2
;
400 cairo_translate (cairoTarget
, ls
->parameter
[THERMAL_CENTER_X
],
401 ls
->parameter
[THERMAL_CENTER_Y
]);
402 cairo_rotate (cairoTarget
, ls
->parameter
[THERMAL_ROTATION
] * M_PI
/180.0);
403 startAngle1
= atan (ls
->parameter
[THERMAL_CROSSHAIR_THICKNESS
]/ls
->parameter
[THERMAL_INSIDE_DIAMETER
]);
404 endAngle1
= M_PI
/2 - startAngle1
;
405 endAngle2
= atan (ls
->parameter
[THERMAL_CROSSHAIR_THICKNESS
]/ls
->parameter
[THERMAL_OUTSIDE_DIAMETER
]);
406 startAngle2
= M_PI
/2 - endAngle2
;
407 for (i
= 0; i
< 4; i
++) {
408 cairo_arc (cairoTarget
, 0, 0, ls
->parameter
[THERMAL_INSIDE_DIAMETER
]/2.0, startAngle1
, endAngle1
);
409 cairo_rel_line_to (cairoTarget
, 0, ls
->parameter
[THERMAL_CROSSHAIR_THICKNESS
]);
410 cairo_arc_negative (cairoTarget
, 0, 0, ls
->parameter
[THERMAL_OUTSIDE_DIAMETER
]/2.0,
411 startAngle2
, endAngle2
);
412 cairo_rel_line_to (cairoTarget
, -ls
->parameter
[THERMAL_CROSSHAIR_THICKNESS
],0);
413 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
414 cairo_rotate (cairoTarget
, 90 * M_PI
/180);
416 } else if (ls
->type
== GERBV_APTYPE_MACRO_LINE20
) {
417 if (draw_update_macro_exposure (cairoTarget
, clearOperator
,
418 darkOperator
, ls
->parameter
[LINE20_EXPOSURE
])){
419 gdouble cParameter
= ls
->parameter
[LINE20_LINE_WIDTH
];
420 if (cParameter
< pixelWidth
)
421 cParameter
= pixelWidth
;
423 cairo_set_line_width (cairoTarget
, cParameter
);
424 cairo_set_line_cap (cairoTarget
, CAIRO_LINE_CAP_BUTT
);
425 cairo_rotate (cairoTarget
, ls
->parameter
[LINE20_ROTATION
] * M_PI
/180.0);
426 cairo_move_to (cairoTarget
, ls
->parameter
[LINE20_START_X
], ls
->parameter
[LINE20_START_Y
]);
427 cairo_line_to (cairoTarget
, ls
->parameter
[LINE20_END_X
], ls
->parameter
[LINE20_END_Y
]);
428 draw_stroke (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
430 } else if (ls
->type
== GERBV_APTYPE_MACRO_LINE21
) {
431 gdouble halfWidth
, halfHeight
;
433 if (draw_update_macro_exposure (cairoTarget
, clearOperator
,
434 darkOperator
, ls
->parameter
[LINE22_EXPOSURE
])){
435 halfWidth
= ls
->parameter
[LINE21_WIDTH
] / 2.0;
436 halfHeight
= ls
->parameter
[LINE21_HEIGHT
] / 2.0;
437 if (halfWidth
< pixelWidth
)
438 halfWidth
= pixelWidth
;
439 if (halfHeight
< pixelWidth
)
440 halfHeight
= pixelWidth
;
441 cairo_translate (cairoTarget
, ls
->parameter
[LINE21_CENTER_X
], ls
->parameter
[LINE21_CENTER_Y
]);
442 cairo_rotate (cairoTarget
, ls
->parameter
[LINE21_ROTATION
] * M_PI
/180.0);
443 cairo_rectangle (cairoTarget
, -halfWidth
, -halfHeight
,
444 ls
->parameter
[LINE21_WIDTH
], ls
->parameter
[LINE21_HEIGHT
]);
445 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
447 } else if (ls
->type
== GERBV_APTYPE_MACRO_LINE22
) {
448 gdouble halfWidth
, halfHeight
;
450 if (draw_update_macro_exposure (cairoTarget
, clearOperator
,
451 darkOperator
, ls
->parameter
[LINE22_EXPOSURE
])){
452 halfWidth
= ls
->parameter
[LINE22_WIDTH
] / 2.0;
453 halfHeight
= ls
->parameter
[LINE22_HEIGHT
] / 2.0;
454 if (halfWidth
< pixelWidth
)
455 halfWidth
= pixelWidth
;
456 if (halfHeight
< pixelWidth
)
457 halfHeight
= pixelWidth
;
458 cairo_translate (cairoTarget
, ls
->parameter
[LINE22_LOWER_LEFT_X
],
459 ls
->parameter
[LINE22_LOWER_LEFT_Y
]);
460 cairo_rotate (cairoTarget
, ls
->parameter
[LINE22_ROTATION
] * M_PI
/180.0);
461 cairo_rectangle (cairoTarget
, 0, 0,
462 ls
->parameter
[LINE22_WIDTH
], ls
->parameter
[LINE22_HEIGHT
]);
463 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
468 cairo_set_operator (cairoTarget
, oldOperator
);
469 cairo_restore (cairoTarget
);
472 if (usesClearPrimative
) {
473 cairo_pop_group_to_source (cairoTarget
);
474 cairo_paint (cairoTarget
);
477 } /* gerbv_draw_amacro */
481 draw_apply_netstate_transformation (cairo_t
*cairoTarget
, gerbv_netstate_t
*state
)
483 /* apply scale factor */
484 cairo_scale (cairoTarget
, state
->scaleA
, state
->scaleB
);
486 cairo_translate (cairoTarget
, state
->offsetA
, state
->offsetB
);
488 switch (state
->mirrorState
) {
489 case GERBV_MIRROR_STATE_FLIPA
:
490 cairo_scale (cairoTarget
, -1, 1);
492 case GERBV_MIRROR_STATE_FLIPB
:
493 cairo_scale (cairoTarget
, 1, -1);
495 case GERBV_MIRROR_STATE_FLIPAB
:
496 cairo_scale (cairoTarget
, -1, -1);
501 /* finally, apply axis select */
502 if (state
->axisSelect
== GERBV_AXIS_SELECT_SWAPAB
) {
503 /* we do this by rotating 270 (counterclockwise, then mirroring
505 cairo_rotate (cairoTarget
, 3 * M_PI
/ 2);
506 cairo_scale (cairoTarget
, 1, -1);
511 draw_render_polygon_object (gerbv_net_t
*oldNet
, cairo_t
*cairoTarget
, gdouble sr_x
, gdouble sr_y
,
512 gerbv_image_t
*image
, gchar drawMode
, gerbv_selection_info_t
*selectionInfo
, gboolean pixelOutput
){
513 gerbv_net_t
*currentNet
, *polygonStartNet
;
514 int haveDrawnFirstFillPoint
= 0;
515 gdouble x1
,y1
,x2
,y2
,cp_x
=0,cp_y
=0;
517 haveDrawnFirstFillPoint
= FALSE
;
518 /* save the first net in the polygon as the "ID" net pointer
519 in case we are saving this net to the selection array */
520 polygonStartNet
= oldNet
;
521 cairo_new_path(cairoTarget
);
523 for (currentNet
= oldNet
->next
; currentNet
!=NULL
; currentNet
= currentNet
->next
){
524 x1
= currentNet
->start_x
+ sr_x
;
525 y1
= currentNet
->start_y
+ sr_y
;
526 x2
= currentNet
->stop_x
+ sr_x
;
527 y2
= currentNet
->stop_y
+ sr_y
;
529 /* translate circular x,y data as well */
530 if (currentNet
->cirseg
) {
531 cp_x
= currentNet
->cirseg
->cp_x
+ sr_x
;
532 cp_y
= currentNet
->cirseg
->cp_y
+ sr_y
;
534 if (!haveDrawnFirstFillPoint
) {
535 draw_cairo_move_to (cairoTarget
, x2
, y2
, FALSE
, pixelOutput
);
536 haveDrawnFirstFillPoint
=TRUE
;
539 switch (currentNet
->interpolation
) {
540 case GERBV_INTERPOLATION_x10
:
541 case GERBV_INTERPOLATION_LINEARx01
:
542 case GERBV_INTERPOLATION_LINEARx001
:
543 case GERBV_INTERPOLATION_LINEARx1
:
544 draw_cairo_line_to (cairoTarget
, x2
, y2
, FALSE
, pixelOutput
);
546 case GERBV_INTERPOLATION_CW_CIRCULAR
:
547 case GERBV_INTERPOLATION_CCW_CIRCULAR
:
548 if (currentNet
->cirseg
->angle2
> currentNet
->cirseg
->angle1
) {
549 cairo_arc (cairoTarget
, cp_x
, cp_y
, currentNet
->cirseg
->width
/2.0,
550 currentNet
->cirseg
->angle1
* M_PI
/180,currentNet
->cirseg
->angle2
* M_PI
/180);
553 cairo_arc_negative (cairoTarget
, cp_x
, cp_y
, currentNet
->cirseg
->width
/2.0,
554 currentNet
->cirseg
->angle1
* M_PI
/180,currentNet
->cirseg
->angle2
* M_PI
/180);
557 case GERBV_INTERPOLATION_PAREA_END
:
558 cairo_close_path(cairoTarget
);
559 /* turn off anti-aliasing for polygons, since it shows seams
560 with adjacent polygons (usually on PCB ground planes) */
561 cairo_antialias_t oldAlias
= cairo_get_antialias (cairoTarget
);
562 cairo_set_antialias (cairoTarget
, CAIRO_ANTIALIAS_NONE
);
563 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, polygonStartNet
);
564 cairo_set_antialias (cairoTarget
, oldAlias
);
575 draw_image_to_cairo_target (cairo_t
*cairoTarget
, gerbv_image_t
*image
,
577 gchar drawMode
, gerbv_selection_info_t
*selectionInfo
,
578 gerbv_render_info_t
*renderInfo
, gboolean allowOptimization
,
579 gerbv_user_transformation_t transform
, gboolean pixelOutput
){
580 struct gerbv_net
*net
, *polygonStartNet
=NULL
;
581 double x1
, y1
, x2
, y2
, cp_x
=0, cp_y
=0;
582 gdouble p1
, p2
, p3
, p4
, p5
, dx
, dy
, lineWidth
;
583 gerbv_netstate_t
*oldState
;
584 gerbv_layer_t
*oldLayer
;
585 int repeat_X
=1, repeat_Y
=1;
586 double repeat_dist_X
= 0, repeat_dist_Y
= 0;
587 int repeat_i
, repeat_j
;
588 cairo_operator_t drawOperatorClear
, drawOperatorDark
;
589 gboolean invertPolarity
= FALSE
, oddWidth
= FALSE
;
590 gdouble minX
=0, minY
=0, maxX
=0, maxY
=0;
591 gdouble criticalRadius
;
592 gdouble scaleX
= transform
.scaleX
;
593 gdouble scaleY
= transform
.scaleY
;
594 gboolean limitLineWidth
= TRUE
;
595 gboolean displayPixel
= TRUE
;
596 // if we are scaling the image at all, ignore the line width checks since scaled up
597 // lines can still be visible
598 if ((scaleX
!= 1)||(scaleY
!= 1)){
599 limitLineWidth
= FALSE
;
602 if (transform
.mirrorAroundX
)
604 if (transform
.mirrorAroundY
)
606 cairo_translate (cairoTarget
, transform
.translateX
, transform
.translateY
);
607 cairo_scale (cairoTarget
, scaleX
, scaleY
);
608 cairo_rotate (cairoTarget
, transform
.rotation
);
610 gboolean useOptimizations
= allowOptimization
;
611 // if the user is using any transformations for this layer, then don't bother using rendering
613 if ((fabs(transform
.translateX
) > 0.00001) ||
614 (fabs(transform
.translateY
) > 0.00001) ||
615 (fabs(transform
.scaleX
- 1) > 0.00001) ||
616 (fabs(transform
.scaleY
- 1) > 0.00001) ||
617 (fabs(transform
.rotation
) > 0.00001) ||
618 transform
.mirrorAroundX
|| transform
.mirrorAroundY
)
619 useOptimizations
= FALSE
;
621 if (useOptimizations
&& pixelOutput
) {
622 minX
= renderInfo
->lowerLeftX
;
623 minY
= renderInfo
->lowerLeftY
;
624 maxX
= renderInfo
->lowerLeftX
+ (renderInfo
->displayWidth
/
625 renderInfo
->scaleFactorX
);
626 maxY
= renderInfo
->lowerLeftY
+ (renderInfo
->displayHeight
/
627 renderInfo
->scaleFactorY
);
630 /* do initial justify */
631 cairo_translate (cairoTarget
, image
->info
->imageJustifyOffsetActualA
,
632 image
->info
->imageJustifyOffsetActualB
);
634 /* set the fill rule so aperture holes are cleared correctly */
635 cairo_set_fill_rule (cairoTarget
, CAIRO_FILL_RULE_EVEN_ODD
);
637 cairo_translate (cairoTarget
, image
->info
->offsetA
, image
->info
->offsetB
);
638 /* do image rotation */
639 cairo_rotate (cairoTarget
, image
->info
->imageRotation
);
640 /* load in polarity operators depending on the image polarity */
641 invertPolarity
= transform
.inverted
;
642 if (image
->info
->polarity
== GERBV_POLARITY_NEGATIVE
)
643 invertPolarity
= !invertPolarity
;
644 if (drawMode
== DRAW_SELECTIONS
)
645 invertPolarity
= FALSE
;
647 if (invertPolarity
) {
648 drawOperatorClear
= CAIRO_OPERATOR_OVER
;
649 drawOperatorDark
= CAIRO_OPERATOR_CLEAR
;
650 cairo_set_operator (cairoTarget
, CAIRO_OPERATOR_OVER
);
651 cairo_paint (cairoTarget
);
652 cairo_set_operator (cairoTarget
, CAIRO_OPERATOR_CLEAR
);
655 drawOperatorClear
= CAIRO_OPERATOR_CLEAR
;
656 drawOperatorDark
= CAIRO_OPERATOR_OVER
;
658 /* next, push two cairo states to simulate the first layer and netstate
659 translations (these will be popped when another layer or netstate is
662 cairo_save (cairoTarget
);
663 cairo_save (cairoTarget
);
664 /* store the current layer and netstate so we know when they change */
665 oldLayer
= image
->layers
;
666 oldState
= image
->states
;
668 for (net
= image
->netlist
->next
; net
!= NULL
; net
= gerbv_image_return_next_renderable_object(net
)) {
670 /* check if this is a new layer */
671 if (net
->layer
!= oldLayer
){
672 /* it's a new layer, so recalculate the new transformation matrix
674 cairo_restore (cairoTarget
);
675 cairo_restore (cairoTarget
);
676 cairo_save (cairoTarget
);
677 /* do any rotations */
678 cairo_rotate (cairoTarget
, net
->layer
->rotation
);
679 /* handle the layer polarity */
680 if ((net
->layer
->polarity
== GERBV_POLARITY_CLEAR
)^invertPolarity
) {
681 cairo_set_operator (cairoTarget
, CAIRO_OPERATOR_CLEAR
);
682 drawOperatorClear
= CAIRO_OPERATOR_OVER
;
683 drawOperatorDark
= CAIRO_OPERATOR_CLEAR
;
686 cairo_set_operator (cairoTarget
, CAIRO_OPERATOR_OVER
);
687 drawOperatorClear
= CAIRO_OPERATOR_CLEAR
;
688 drawOperatorDark
= CAIRO_OPERATOR_OVER
;
690 /* check for changes to step and repeat */
691 repeat_X
= net
->layer
->stepAndRepeat
.X
;
692 repeat_Y
= net
->layer
->stepAndRepeat
.Y
;
693 repeat_dist_X
= net
->layer
->stepAndRepeat
.dist_X
;
694 repeat_dist_Y
= net
->layer
->stepAndRepeat
.dist_Y
;
695 /* draw any knockout areas */
696 if (net
->layer
->knockout
.firstInstance
== TRUE
) {
697 cairo_operator_t oldOperator
= cairo_get_operator (cairoTarget
);
698 if (net
->layer
->knockout
.polarity
== GERBV_POLARITY_CLEAR
) {
699 cairo_set_operator (cairoTarget
, drawOperatorClear
);
702 cairo_set_operator (cairoTarget
, drawOperatorDark
);
704 cairo_new_path (cairoTarget
);
705 cairo_rectangle (cairoTarget
, net
->layer
->knockout
.lowerLeftX
- net
->layer
->knockout
.border
,
706 net
->layer
->knockout
.lowerLeftY
- net
->layer
->knockout
.border
,
707 net
->layer
->knockout
.width
+ (net
->layer
->knockout
.border
*2),
708 net
->layer
->knockout
.height
+ (net
->layer
->knockout
.border
*2));
709 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
710 cairo_set_operator (cairoTarget
, oldOperator
);
712 /* finally, reapply old netstate transformation */
713 cairo_save (cairoTarget
);
714 draw_apply_netstate_transformation (cairoTarget
, net
->state
);
715 oldLayer
= net
->layer
;
717 /* check if this is a new netstate */
718 if (net
->state
!= oldState
){
719 /* pop the transformation matrix back to the "pre-state" state and
721 cairo_restore (cairoTarget
);
722 cairo_save (cairoTarget
);
723 /* it's a new state, so recalculate the new transformation matrix
725 draw_apply_netstate_transformation (cairoTarget
, net
->state
);
726 oldState
= net
->state
;
728 /* if we are only drawing from the selection buffer, search if this net is
730 if (drawMode
== DRAW_SELECTIONS
) {
731 /* this flag makes sure we don't draw any unintentional polygons...
732 if we've successfully entered a polygon (the first net matches, and
733 we don't want to check the nets inside the polygon) then
734 polygonStartNet will be set */
735 if (!polygonStartNet
) {
736 if (!draw_net_in_selection_buffer(net
, selectionInfo
))
740 for(repeat_i
= 0; repeat_i
< repeat_X
; repeat_i
++) {
741 for(repeat_j
= 0; repeat_j
< repeat_Y
; repeat_j
++) {
742 double sr_x
= repeat_i
* repeat_dist_X
;
743 double sr_y
= repeat_j
* repeat_dist_Y
;
746 if ((useOptimizations
&& pixelOutput
) &&
747 ((net
->boundingBox
.right
+sr_x
< minX
)
748 || (net
->boundingBox
.left
+sr_x
> maxX
)
749 || (net
->boundingBox
.top
+sr_y
< minY
)
750 || (net
->boundingBox
.bottom
+sr_y
> maxY
))) {
754 x1
= net
->start_x
+ sr_x
;
755 y1
= net
->start_y
+ sr_y
;
756 x2
= net
->stop_x
+ sr_x
;
757 y2
= net
->stop_y
+ sr_y
;
759 /* translate circular x,y data as well */
761 cp_x
= net
->cirseg
->cp_x
+ sr_x
;
762 cp_y
= net
->cirseg
->cp_y
+ sr_y
;
765 /* render any labels attached to this net */
766 /* NOTE: this is currently only used on PNP files, so we may
767 make some assumptions here... */
769 cairo_set_font_size (cairoTarget
, 0.05);
770 cairo_save (cairoTarget
);
772 cairo_move_to (cairoTarget
, x1
, y1
);
773 cairo_scale (cairoTarget
, 1, -1);
774 cairo_show_text (cairoTarget
, net
->label
->str
);
775 cairo_restore (cairoTarget
);
778 * Polygon Area Fill routines
780 switch (net
->interpolation
) {
781 case GERBV_INTERPOLATION_PAREA_START
:
782 draw_render_polygon_object (net
, cairoTarget
, sr_x
, sr_y
, image
,
783 drawMode
, selectionInfo
, pixelOutput
);
785 case GERBV_INTERPOLATION_DELETED
:
792 * If aperture state is off we allow use of undefined apertures.
793 * This happens when gerber files starts, but hasn't decided on
794 * which aperture to use.
796 if (image
->aperture
[net
->aperture
] == NULL
) {
797 /* Commenting this out since it gets emitted every time you click on the screen
798 if (net->aperture_state != GERBV_APERTURE_STATE_OFF)
799 GERB_MESSAGE("Aperture D%d is not defined\n", net->aperture);
803 switch (net
->aperture_state
) {
804 case GERBV_APERTURE_STATE_ON
:
805 /* if the aperture width is truly 0, then render as a 1 pixel width
806 line. 0 diameter apertures are used by some programs to draw labels,
807 etc, and they are rendered by other programs as 1 pixel wide */
808 /* NOTE: also, make sure all lines are at least 1 pixel wide, so they
809 always show up at low zoom levels */
811 if (limitLineWidth
&&((image
->aperture
[net
->aperture
]->parameter
[0] < pixelWidth
)&&
813 criticalRadius
= pixelWidth
/2.0;
815 criticalRadius
= image
->aperture
[net
->aperture
]->parameter
[0]/2.0;
816 lineWidth
= criticalRadius
*2.0;
817 // convert to a pixel integer
818 cairo_user_to_device_distance (cairoTarget
, &lineWidth
, &x1
);
820 lineWidth
= round(lineWidth
);
821 if ((int)lineWidth
% 2) {
828 cairo_device_to_user_distance (cairoTarget
, &lineWidth
, &x1
);
829 cairo_set_line_width (cairoTarget
, lineWidth
);
830 switch (net
->interpolation
) {
831 case GERBV_INTERPOLATION_x10
:
832 case GERBV_INTERPOLATION_LINEARx01
:
833 case GERBV_INTERPOLATION_LINEARx001
:
834 case GERBV_INTERPOLATION_LINEARx1
:
835 cairo_set_line_cap (cairoTarget
, CAIRO_LINE_CAP_ROUND
);
836 // weed out any lines that are obviously not going to render on the
839 switch (image
->aperture
[net
->aperture
]->type
) {
840 case GERBV_APTYPE_CIRCLE
:
841 draw_cairo_move_to (cairoTarget
, x1
, y1
, oddWidth
, pixelOutput
);
842 draw_cairo_line_to (cairoTarget
, x2
, y2
, oddWidth
, pixelOutput
);
843 draw_stroke (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
845 case GERBV_APTYPE_RECTANGLE
:
846 dx
= (image
->aperture
[net
->aperture
]->parameter
[0]/ 2);
847 dy
= (image
->aperture
[net
->aperture
]->parameter
[1]/ 2);
852 cairo_new_path(cairoTarget
);
853 draw_cairo_move_to (cairoTarget
, x1
- dx
, y1
- dy
, FALSE
, pixelOutput
);
854 draw_cairo_line_to (cairoTarget
, x1
- dx
, y1
+ dy
, FALSE
, pixelOutput
);
855 draw_cairo_line_to (cairoTarget
, x2
- dx
, y2
+ dy
, FALSE
, pixelOutput
);
856 draw_cairo_line_to (cairoTarget
, x2
+ dx
, y2
+ dy
, FALSE
, pixelOutput
);
857 draw_cairo_line_to (cairoTarget
, x2
+ dx
, y2
- dy
, FALSE
, pixelOutput
);
858 draw_cairo_line_to (cairoTarget
, x1
+ dx
, y1
- dy
, FALSE
, pixelOutput
);
859 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
861 /* for now, just render ovals or polygons like a circle */
862 case GERBV_APTYPE_OVAL
:
863 case GERBV_APTYPE_POLYGON
:
864 draw_cairo_move_to (cairoTarget
, x1
,y1
, oddWidth
, pixelOutput
);
865 draw_cairo_line_to (cairoTarget
, x2
,y2
, oddWidth
, pixelOutput
);
866 draw_stroke (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
868 /* macros can only be flashed, so ignore any that might be here */
873 case GERBV_INTERPOLATION_CW_CIRCULAR
:
874 case GERBV_INTERPOLATION_CCW_CIRCULAR
:
875 /* cairo doesn't have a function to draw oval arcs, so we must
876 * draw an arc and stretch it by scaling different x and y values
878 cairo_new_path(cairoTarget
);
879 if (image
->aperture
[net
->aperture
]->type
== GERBV_APTYPE_RECTANGLE
) {
880 cairo_set_line_cap (cairoTarget
, CAIRO_LINE_CAP_SQUARE
);
883 cairo_set_line_cap (cairoTarget
, CAIRO_LINE_CAP_ROUND
);
885 cairo_save (cairoTarget
);
886 cairo_translate(cairoTarget
, cp_x
, cp_y
);
887 cairo_scale (cairoTarget
, net
->cirseg
->width
, net
->cirseg
->height
);
888 if (net
->cirseg
->angle2
> net
->cirseg
->angle1
) {
889 cairo_arc (cairoTarget
, 0.0, 0.0, 0.5, net
->cirseg
->angle1
* M_PI
/180,
890 net
->cirseg
->angle2
* M_PI
/180);
893 cairo_arc_negative (cairoTarget
, 0.0, 0.0, 0.5, net
->cirseg
->angle1
* M_PI
/180,
894 net
->cirseg
->angle2
* M_PI
/180);
896 cairo_restore (cairoTarget
);
897 draw_stroke (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
903 case GERBV_APERTURE_STATE_OFF
:
905 case GERBV_APERTURE_STATE_FLASH
:
906 p1
= image
->aperture
[net
->aperture
]->parameter
[0];
907 p2
= image
->aperture
[net
->aperture
]->parameter
[1];
908 p3
= image
->aperture
[net
->aperture
]->parameter
[2];
909 p4
= image
->aperture
[net
->aperture
]->parameter
[3];
910 p5
= image
->aperture
[net
->aperture
]->parameter
[4];
912 cairo_save (cairoTarget
);
913 draw_cairo_translate_adjust(cairoTarget
, x2
, y2
, pixelOutput
);
915 switch (image
->aperture
[net
->aperture
]->type
) {
916 case GERBV_APTYPE_CIRCLE
:
917 gerbv_draw_circle(cairoTarget
, p1
);
918 gerbv_draw_aperature_hole (cairoTarget
, p2
, p3
, pixelOutput
);
920 case GERBV_APTYPE_RECTANGLE
:
921 // some CAD programs use very thin flashed rectangles to compose
922 // logos/images, so we must make sure those display here
923 displayPixel
= pixelOutput
;
924 if (limitLineWidth
&&((p1
< pixelWidth
)&&
927 displayPixel
= FALSE
;
929 if (limitLineWidth
&&((p2
< pixelWidth
)&&
932 displayPixel
= FALSE
;
934 gerbv_draw_rectangle(cairoTarget
, p1
, p2
, displayPixel
);
935 gerbv_draw_aperature_hole (cairoTarget
, p3
, p4
, displayPixel
);
937 case GERBV_APTYPE_OVAL
:
938 gerbv_draw_oblong(cairoTarget
, p1
, p2
);
939 gerbv_draw_aperature_hole (cairoTarget
, p3
, p4
, pixelOutput
);
941 case GERBV_APTYPE_POLYGON
:
942 gerbv_draw_polygon(cairoTarget
, p1
, p2
, p3
);
943 gerbv_draw_aperature_hole (cairoTarget
, p4
, p5
, pixelOutput
);
945 case GERBV_APTYPE_MACRO
:
946 gerbv_draw_amacro(cairoTarget
, drawOperatorClear
, drawOperatorDark
,
947 image
->aperture
[net
->aperture
]->simplified
,
948 (int) image
->aperture
[net
->aperture
]->parameter
[0], pixelWidth
,
949 drawMode
, selectionInfo
, image
, net
);
952 GERB_MESSAGE("Unknown aperture type\n");
955 /* and finally fill the path */
956 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
957 cairo_restore (cairoTarget
);
960 GERB_MESSAGE("Unknown aperture state\n");
967 /* restore the initial two state saves (one for layer, one for netstate)*/
968 cairo_restore (cairoTarget
);
969 cairo_restore (cairoTarget
);