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
51 draw_cairo_line_to (cairo_t
*cairoTarget
, gdouble x
, gdouble y
, gboolean adjustByHalf
){
52 gdouble x1
= x
, y1
= y
;
54 cairo_user_to_device (cairoTarget
, &x1
, &y1
);
61 cairo_device_to_user (cairoTarget
, &x1
, &y1
);
63 cairo_line_to (cairoTarget
, x1
, y1
);
67 draw_cairo_move_to (cairo_t
*cairoTarget
, gdouble x
, gdouble y
, gboolean oddWidth
){
68 gdouble x1
= x
, y1
= y
;
70 cairo_user_to_device (cairoTarget
, &x1
, &y1
);
77 cairo_device_to_user (cairoTarget
, &x1
, &y1
);
79 cairo_move_to (cairoTarget
, x1
, y1
);
83 draw_cairo_translate_adjust (cairo_t
*cairoTarget
, gdouble x
, gdouble y
){
84 gdouble x1
= x
, y1
= y
;
86 cairo_user_to_device (cairoTarget
, &x1
, &y1
);
89 cairo_device_to_user (cairoTarget
, &x1
, &y1
);
91 cairo_translate (cairoTarget
, x1
, y1
);
95 draw_net_in_selection_buffer (gerbv_net_t
*net
, gerbv_selection_info_t
*selectionInfo
) {
98 for (i
=0; i
<selectionInfo
->selectedNodeArray
->len
; i
++){
99 gerbv_selection_item_t sItem
= g_array_index (selectionInfo
->selectedNodeArray
,
100 gerbv_selection_item_t
, i
);
101 if (sItem
.net
== net
)
108 draw_check_if_object_is_in_selected_area (cairo_t
*cairoTarget
, gboolean isStroke
,
109 gerbv_selection_info_t
*selectionInfo
, gerbv_image_t
*image
, struct gerbv_net
*net
){
110 gdouble corner1X
,corner1Y
,corner2X
,corner2Y
;
112 corner1X
= selectionInfo
->lowerLeftX
;
113 corner1Y
= selectionInfo
->lowerLeftY
;
114 corner2X
= selectionInfo
->upperRightX
;
115 corner2Y
= selectionInfo
->upperRightY
;
117 /* calculate the coordinate of the user's click in the current
118 transformation matrix */
119 cairo_device_to_user (cairoTarget
, &corner1X
, &corner1Y
);
120 cairo_device_to_user (cairoTarget
, &corner2X
, &corner2Y
);
121 if (selectionInfo
->type
== GERBV_SELECTION_POINT_CLICK
) {
122 /* use the cairo in_fill routine to see if the point is within the
124 if ((isStroke
&& cairo_in_stroke (cairoTarget
, corner1X
, corner1Y
)) ||
125 (!isStroke
&& cairo_in_fill (cairoTarget
, corner1X
, corner1Y
))) {
126 /* add the net to the selection array */
127 if (!draw_net_in_selection_buffer(net
, selectionInfo
)) {
128 gerbv_selection_item_t sItem
= {image
, net
};
129 g_array_append_val (selectionInfo
->selectedNodeArray
, sItem
);
133 else if (selectionInfo
->type
== GERBV_SELECTION_DRAG_BOX
) {
135 gdouble minX
,minY
,maxX
,maxY
;
137 /* we can't assume the "lowerleft" corner is actually in the lower left,
138 since the cairo transformation matrix may be mirrored,etc */
139 minX
= MIN(corner1X
,corner2X
);
140 maxX
= MAX(corner1X
,corner2X
);
141 minY
= MIN(corner1Y
,corner2Y
);
142 maxY
= MAX(corner1Y
,corner2Y
);
144 cairo_stroke_extents (cairoTarget
, &x1
, &y1
, &x2
, &y2
);
146 cairo_fill_extents (cairoTarget
, &x1
, &y1
, &x2
, &y2
);
148 if ((minX
< x1
) && (minY
< y1
) && (maxX
> x2
) && (maxY
> y2
)) {
149 /* add the net to the selection array */
150 if (!draw_net_in_selection_buffer(net
, selectionInfo
)) {
151 gerbv_selection_item_t sItem
= {image
, net
};
152 g_array_append_val (selectionInfo
->selectedNodeArray
, sItem
);
156 /* clear the path, since we didn't actually draw it and cairo
157 doesn't reset it after the previous calls */
158 cairo_new_path (cairoTarget
);
162 draw_fill (cairo_t
*cairoTarget
, gchar drawMode
, gerbv_selection_info_t
*selectionInfo
,
163 gerbv_image_t
*image
, struct gerbv_net
*net
){
164 if ((drawMode
== DRAW_IMAGE
) || (drawMode
== DRAW_SELECTIONS
))
165 cairo_fill (cairoTarget
);
167 draw_check_if_object_is_in_selected_area (cairoTarget
, FALSE
,
168 selectionInfo
, image
, net
);
172 draw_stroke (cairo_t
*cairoTarget
, gchar drawMode
, gerbv_selection_info_t
*selectionInfo
,
173 gerbv_image_t
*image
, struct gerbv_net
*net
){
174 if ((drawMode
== DRAW_IMAGE
) || (drawMode
== DRAW_SELECTIONS
))
175 cairo_stroke (cairoTarget
);
177 draw_check_if_object_is_in_selected_area (cairoTarget
, TRUE
,
178 selectionInfo
, image
, net
);
182 * Draws a circle _centered_ at x,y with diameter dia
185 gerbv_draw_circle(cairo_t
*cairoTarget
, gdouble diameter
)
187 cairo_arc (cairoTarget
, 0.0, 0.0, diameter
/2.0, 0, 2.0*M_PI
);
189 } /* gerbv_draw_circle */
193 * Draws a rectangle _centered_ at x,y with sides x_side, y_side
196 gerbv_draw_rectangle(cairo_t
*cairoTarget
, gdouble width1
, gdouble height1
)
198 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
)
274 gerbv_draw_rectangle (cairoTarget
, dimensionX
, dimensionY
);
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
){
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
);
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
);
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 limitPixelSize
){
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
;
595 if (transform
.mirrorAroundX
)
597 if (transform
.mirrorAroundY
)
599 cairo_translate (cairoTarget
, transform
.translateX
, transform
.translateY
);
600 cairo_scale (cairoTarget
, scaleX
, scaleY
);
601 cairo_rotate (cairoTarget
, transform
.rotation
);
603 gboolean useOptimizations
= allowOptimization
;
604 // if the user is using any transformations for this layer, then don't bother using rendering
606 if ((fabs(transform
.translateX
) > 0.00001) ||
607 (fabs(transform
.translateY
) > 0.00001) ||
608 (fabs(transform
.scaleX
- 1) > 0.00001) ||
609 (fabs(transform
.scaleY
- 1) > 0.00001) ||
610 (fabs(transform
.rotation
) > 0.00001) ||
611 transform
.mirrorAroundX
|| transform
.mirrorAroundY
)
612 useOptimizations
= FALSE
;
614 if (useOptimizations
) {
615 minX
= renderInfo
->lowerLeftX
;
616 minY
= renderInfo
->lowerLeftY
;
617 maxX
= renderInfo
->lowerLeftX
+ (renderInfo
->displayWidth
/
618 renderInfo
->scaleFactorX
);
619 maxY
= renderInfo
->lowerLeftY
+ (renderInfo
->displayHeight
/
620 renderInfo
->scaleFactorY
);
623 /* do initial justify */
624 cairo_translate (cairoTarget
, image
->info
->imageJustifyOffsetActualA
,
625 image
->info
->imageJustifyOffsetActualB
);
627 /* set the fill rule so aperture holes are cleared correctly */
628 cairo_set_fill_rule (cairoTarget
, CAIRO_FILL_RULE_EVEN_ODD
);
630 cairo_translate (cairoTarget
, image
->info
->offsetA
, image
->info
->offsetB
);
631 /* do image rotation */
632 cairo_rotate (cairoTarget
, image
->info
->imageRotation
);
633 /* load in polarity operators depending on the image polarity */
634 invertPolarity
= transform
.inverted
;
635 if (image
->info
->polarity
== GERBV_POLARITY_NEGATIVE
)
636 invertPolarity
= !invertPolarity
;
637 if (drawMode
== DRAW_SELECTIONS
)
638 invertPolarity
= FALSE
;
640 if (invertPolarity
) {
641 drawOperatorClear
= CAIRO_OPERATOR_OVER
;
642 drawOperatorDark
= CAIRO_OPERATOR_CLEAR
;
643 cairo_set_operator (cairoTarget
, CAIRO_OPERATOR_OVER
);
644 cairo_paint (cairoTarget
);
645 cairo_set_operator (cairoTarget
, CAIRO_OPERATOR_CLEAR
);
648 drawOperatorClear
= CAIRO_OPERATOR_CLEAR
;
649 drawOperatorDark
= CAIRO_OPERATOR_OVER
;
651 /* next, push two cairo states to simulate the first layer and netstate
652 translations (these will be popped when another layer or netstate is
655 cairo_save (cairoTarget
);
656 cairo_save (cairoTarget
);
657 /* store the current layer and netstate so we know when they change */
658 oldLayer
= image
->layers
;
659 oldState
= image
->states
;
661 for (net
= image
->netlist
->next
; net
!= NULL
; net
= gerbv_image_return_next_renderable_object(net
)) {
663 /* check if this is a new layer */
664 if (net
->layer
!= oldLayer
){
665 /* it's a new layer, so recalculate the new transformation matrix
667 cairo_restore (cairoTarget
);
668 cairo_restore (cairoTarget
);
669 cairo_save (cairoTarget
);
670 /* do any rotations */
671 cairo_rotate (cairoTarget
, net
->layer
->rotation
);
672 /* handle the layer polarity */
673 if ((net
->layer
->polarity
== GERBV_POLARITY_CLEAR
)^invertPolarity
) {
674 cairo_set_operator (cairoTarget
, CAIRO_OPERATOR_CLEAR
);
675 drawOperatorClear
= CAIRO_OPERATOR_OVER
;
676 drawOperatorDark
= CAIRO_OPERATOR_CLEAR
;
679 cairo_set_operator (cairoTarget
, CAIRO_OPERATOR_OVER
);
680 drawOperatorClear
= CAIRO_OPERATOR_CLEAR
;
681 drawOperatorDark
= CAIRO_OPERATOR_OVER
;
683 /* check for changes to step and repeat */
684 repeat_X
= net
->layer
->stepAndRepeat
.X
;
685 repeat_Y
= net
->layer
->stepAndRepeat
.Y
;
686 repeat_dist_X
= net
->layer
->stepAndRepeat
.dist_X
;
687 repeat_dist_Y
= net
->layer
->stepAndRepeat
.dist_Y
;
688 /* draw any knockout areas */
689 if (net
->layer
->knockout
.firstInstance
== TRUE
) {
690 cairo_operator_t oldOperator
= cairo_get_operator (cairoTarget
);
691 if (net
->layer
->knockout
.polarity
== GERBV_POLARITY_CLEAR
) {
692 cairo_set_operator (cairoTarget
, drawOperatorClear
);
695 cairo_set_operator (cairoTarget
, drawOperatorDark
);
697 cairo_new_path (cairoTarget
);
698 cairo_rectangle (cairoTarget
, net
->layer
->knockout
.lowerLeftX
- net
->layer
->knockout
.border
,
699 net
->layer
->knockout
.lowerLeftY
- net
->layer
->knockout
.border
,
700 net
->layer
->knockout
.width
+ (net
->layer
->knockout
.border
*2),
701 net
->layer
->knockout
.height
+ (net
->layer
->knockout
.border
*2));
702 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
703 cairo_set_operator (cairoTarget
, oldOperator
);
705 /* finally, reapply old netstate transformation */
706 cairo_save (cairoTarget
);
707 draw_apply_netstate_transformation (cairoTarget
, net
->state
);
708 oldLayer
= net
->layer
;
710 /* check if this is a new netstate */
711 if (net
->state
!= oldState
){
712 /* pop the transformation matrix back to the "pre-state" state and
714 cairo_restore (cairoTarget
);
715 cairo_save (cairoTarget
);
716 /* it's a new state, so recalculate the new transformation matrix
718 draw_apply_netstate_transformation (cairoTarget
, net
->state
);
719 oldState
= net
->state
;
721 /* if we are only drawing from the selection buffer, search if this net is
723 if (drawMode
== DRAW_SELECTIONS
) {
724 /* this flag makes sure we don't draw any unintentional polygons...
725 if we've successfully entered a polygon (the first net matches, and
726 we don't want to check the nets inside the polygon) then
727 polygonStartNet will be set */
728 if (!polygonStartNet
) {
729 if (!draw_net_in_selection_buffer(net
, selectionInfo
))
733 for(repeat_i
= 0; repeat_i
< repeat_X
; repeat_i
++) {
734 for(repeat_j
= 0; repeat_j
< repeat_Y
; repeat_j
++) {
735 double sr_x
= repeat_i
* repeat_dist_X
;
736 double sr_y
= repeat_j
* repeat_dist_Y
;
739 if ((useOptimizations
) &&
740 ((net
->boundingBox
.right
+sr_x
< minX
)
741 || (net
->boundingBox
.left
+sr_x
> maxX
)
742 || (net
->boundingBox
.top
+sr_y
< minY
)
743 || (net
->boundingBox
.bottom
+sr_y
> maxY
))) {
747 x1
= net
->start_x
+ sr_x
;
748 y1
= net
->start_y
+ sr_y
;
749 x2
= net
->stop_x
+ sr_x
;
750 y2
= net
->stop_y
+ sr_y
;
752 /* translate circular x,y data as well */
754 cp_x
= net
->cirseg
->cp_x
+ sr_x
;
755 cp_y
= net
->cirseg
->cp_y
+ sr_y
;
758 /* render any labels attached to this net */
759 /* NOTE: this is currently only used on PNP files, so we may
760 make some assumptions here... */
762 cairo_set_font_size (cairoTarget
, 0.05);
763 cairo_save (cairoTarget
);
765 cairo_move_to (cairoTarget
, x1
, y1
);
766 cairo_scale (cairoTarget
, 1, -1);
767 cairo_show_text (cairoTarget
, net
->label
->str
);
768 cairo_restore (cairoTarget
);
771 * Polygon Area Fill routines
773 switch (net
->interpolation
) {
774 case GERBV_INTERPOLATION_PAREA_START
:
775 draw_render_polygon_object (net
, cairoTarget
, sr_x
, sr_y
, image
,
776 drawMode
, selectionInfo
);
778 case GERBV_INTERPOLATION_DELETED
:
785 * If aperture state is off we allow use of undefined apertures.
786 * This happens when gerber files starts, but hasn't decided on
787 * which aperture to use.
789 if (image
->aperture
[net
->aperture
] == NULL
) {
790 /* Commenting this out since it gets emitted every time you click on the screen
791 if (net->aperture_state != GERBV_APERTURE_STATE_OFF)
792 GERB_MESSAGE("Aperture D%d is not defined\n", net->aperture);
796 switch (net
->aperture_state
) {
797 case GERBV_APERTURE_STATE_ON
:
798 /* if the aperture width is truly 0, then render as a 1 pixel width
799 line. 0 diameter apertures are used by some programs to draw labels,
800 etc, and they are rendered by other programs as 1 pixel wide */
801 /* NOTE: also, make sure all lines are at least 1 pixel wide, so they
802 always show up at low zoom levels */
804 if ((image
->aperture
[net
->aperture
]->parameter
[0] < pixelWidth
)&&
806 criticalRadius
= pixelWidth
/2.0;
808 else if (image->aperture[net->aperture]->parameter[0] == 0)
809 criticalRadius = pixelWidth/2.0;
810 else if (random() < (RAND_MAX / 10))
811 criticalRadius = pixelWidth/2.0;
816 criticalRadius
= image
->aperture
[net
->aperture
]->parameter
[0]/2.0;
817 lineWidth
= criticalRadius
*2.0;
818 // convert to a pixel integer
819 cairo_user_to_device_distance (cairoTarget
, &lineWidth
, &x1
);
820 lineWidth
= round(lineWidth
);
821 if ((int)lineWidth
% 2) {
827 cairo_device_to_user_distance (cairoTarget
, &lineWidth
, &x1
);
828 cairo_set_line_width (cairoTarget
, lineWidth
);
829 switch (net
->interpolation
) {
830 case GERBV_INTERPOLATION_x10
:
831 case GERBV_INTERPOLATION_LINEARx01
:
832 case GERBV_INTERPOLATION_LINEARx001
:
833 case GERBV_INTERPOLATION_LINEARx1
:
834 cairo_set_line_cap (cairoTarget
, CAIRO_LINE_CAP_ROUND
);
835 // weed out any lines that are obviously not going to render on the
838 switch (image
->aperture
[net
->aperture
]->type
) {
839 case GERBV_APTYPE_CIRCLE
:
840 draw_cairo_move_to (cairoTarget
, x1
, y1
, oddWidth
);
841 draw_cairo_line_to (cairoTarget
, x2
, y2
, oddWidth
);
842 draw_stroke (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
844 case GERBV_APTYPE_RECTANGLE
:
845 dx
= (image
->aperture
[net
->aperture
]->parameter
[0]/ 2);
846 dy
= (image
->aperture
[net
->aperture
]->parameter
[1]/ 2);
851 cairo_new_path(cairoTarget
);
852 draw_cairo_move_to (cairoTarget
, x1
- dx
, y1
- dy
, FALSE
);
853 draw_cairo_line_to (cairoTarget
, x1
- dx
, y1
+ dy
, FALSE
);
854 draw_cairo_line_to (cairoTarget
, x2
- dx
, y2
+ dy
, FALSE
);
855 draw_cairo_line_to (cairoTarget
, x2
+ dx
, y2
+ dy
, FALSE
);
856 draw_cairo_line_to (cairoTarget
, x2
+ dx
, y2
- dy
, FALSE
);
857 draw_cairo_line_to (cairoTarget
, x1
+ dx
, y1
- dy
, FALSE
);
858 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
860 /* for now, just render ovals or polygons like a circle */
861 case GERBV_APTYPE_OVAL
:
862 case GERBV_APTYPE_POLYGON
:
863 draw_cairo_move_to (cairoTarget
, x1
,y1
, oddWidth
);
864 draw_cairo_line_to (cairoTarget
, x2
,y2
, oddWidth
);
865 draw_stroke (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
867 /* macros can only be flashed, so ignore any that might be here */
872 case GERBV_INTERPOLATION_CW_CIRCULAR
:
873 case GERBV_INTERPOLATION_CCW_CIRCULAR
:
874 /* cairo doesn't have a function to draw oval arcs, so we must
875 * draw an arc and stretch it by scaling different x and y values
877 cairo_new_path(cairoTarget
);
878 if (image
->aperture
[net
->aperture
]->type
== GERBV_APTYPE_RECTANGLE
) {
879 cairo_set_line_cap (cairoTarget
, CAIRO_LINE_CAP_SQUARE
);
882 cairo_set_line_cap (cairoTarget
, CAIRO_LINE_CAP_ROUND
);
884 cairo_save (cairoTarget
);
885 cairo_translate(cairoTarget
, cp_x
, cp_y
);
886 cairo_scale (cairoTarget
, net
->cirseg
->width
, net
->cirseg
->height
);
887 if (net
->cirseg
->angle2
> net
->cirseg
->angle1
) {
888 cairo_arc (cairoTarget
, 0.0, 0.0, 0.5, net
->cirseg
->angle1
* M_PI
/180,
889 net
->cirseg
->angle2
* M_PI
/180);
892 cairo_arc_negative (cairoTarget
, 0.0, 0.0, 0.5, net
->cirseg
->angle1
* M_PI
/180,
893 net
->cirseg
->angle2
* M_PI
/180);
895 cairo_restore (cairoTarget
);
896 draw_stroke (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
902 case GERBV_APERTURE_STATE_OFF
:
904 case GERBV_APERTURE_STATE_FLASH
:
905 p1
= image
->aperture
[net
->aperture
]->parameter
[0];
906 p2
= image
->aperture
[net
->aperture
]->parameter
[1];
907 p3
= image
->aperture
[net
->aperture
]->parameter
[2];
908 p4
= image
->aperture
[net
->aperture
]->parameter
[3];
909 p5
= image
->aperture
[net
->aperture
]->parameter
[4];
911 cairo_save (cairoTarget
);
912 draw_cairo_translate_adjust(cairoTarget
, x2
, y2
);
914 switch (image
->aperture
[net
->aperture
]->type
) {
915 case GERBV_APTYPE_CIRCLE
:
916 gerbv_draw_circle(cairoTarget
, p1
);
917 gerbv_draw_aperature_hole (cairoTarget
, p2
, p3
);
919 case GERBV_APTYPE_RECTANGLE
:
920 gerbv_draw_rectangle(cairoTarget
, p1
, p2
);
921 gerbv_draw_aperature_hole (cairoTarget
, p3
, p4
);
923 case GERBV_APTYPE_OVAL
:
924 gerbv_draw_oblong(cairoTarget
, p1
, p2
);
925 gerbv_draw_aperature_hole (cairoTarget
, p3
, p4
);
927 case GERBV_APTYPE_POLYGON
:
928 gerbv_draw_polygon(cairoTarget
, p1
, p2
, p3
);
929 gerbv_draw_aperature_hole (cairoTarget
, p4
, p5
);
931 case GERBV_APTYPE_MACRO
:
932 gerbv_draw_amacro(cairoTarget
, drawOperatorClear
, drawOperatorDark
,
933 image
->aperture
[net
->aperture
]->simplified
,
934 (int) image
->aperture
[net
->aperture
]->parameter
[0], pixelWidth
,
935 drawMode
, selectionInfo
, image
, net
);
938 GERB_MESSAGE("Unknown aperture type\n");
941 /* and finally fill the path */
942 draw_fill (cairoTarget
, drawMode
, selectionInfo
, image
, net
);
943 cairo_restore (cairoTarget
);
946 GERB_MESSAGE("Unknown aperture state\n");
953 /* restore the initial two state saves (one for layer, one for netstate)*/
954 cairo_restore (cairoTarget
);
955 cairo_restore (cairoTarget
);