* change all gerb_* namespace to gerbv_* namespace, clarifying the libgerbv API.
[geda-gerbv.git] / src / draw.c
blob1024b9f9b56b8631ceafbd8418f560997b8c0de0
1 /*
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)
7 * $Id$
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
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <math.h> /* ceil(), atan2() */
32 #ifdef HAVE_STRING_H
33 #include <string.h>
34 #endif
36 #include <gtk/gtk.h>
37 #include "gerbv.h"
38 #include "draw.h"
39 #include "draw-gdk.h"
40 #include <cairo.h>
42 #define dprintf if(DEBUG) printf
43 static void
44 draw_check_if_object_is_in_selected_area (cairo_t *cairoTarget, gboolean isStroke,
45 gerbv_selection_info_t *selectionInfo, gerbv_image_t *image, struct gerbv_net *net){
46 gdouble corner1X,corner1Y,corner2X,corner2Y;
48 corner1X = selectionInfo->lowerLeftX;
49 corner1Y = selectionInfo->lowerLeftY;
50 corner2X = selectionInfo->upperRightX;
51 corner2Y = selectionInfo->upperRightY;
53 /* calculate the coordinate of the user's click in the current
54 transformation matrix */
55 cairo_device_to_user (cairoTarget, &corner1X, &corner1Y);
56 cairo_device_to_user (cairoTarget, &corner2X, &corner2Y);
57 if (selectionInfo->type == POINT_CLICK) {
58 /* use the cairo in_fill routine to see if the point is within the
59 drawn area */
60 if ((isStroke && cairo_in_stroke (cairoTarget, corner1X, corner1Y)) ||
61 (!isStroke && cairo_in_fill (cairoTarget, corner1X, corner1Y))) {
62 /* add the net to the selection array */
63 gerbv_selection_item_t sItem = {image, net};
64 g_array_append_val (selectionInfo->selectedNodeArray, sItem);
67 else if (selectionInfo->type == DRAG_BOX) {
68 gdouble x1,x2,y1,y2;
69 gdouble minX,minY,maxX,maxY;
71 /* we can't assume the "lowerleft" corner is actually in the lower left,
72 since the cairo transformation matrix may be mirrored,etc */
73 minX = MIN(corner1X,corner2X);
74 maxX = MAX(corner1X,corner2X);
75 minY = MIN(corner1Y,corner2Y);
76 maxY = MAX(corner1Y,corner2Y);
77 if (isStroke)
78 cairo_stroke_extents (cairoTarget, &x1, &y1, &x2, &y2);
79 else
80 cairo_fill_extents (cairoTarget, &x1, &y1, &x2, &y2);
82 if ((minX < x1) && (minY < y1) && (maxX > x2) && (maxY > y2)) {
83 /* add the net to the selection array */
84 gerbv_selection_item_t sItem = {image, net};
85 g_array_append_val (selectionInfo->selectedNodeArray, sItem);
88 /* clear the path, since we didn't actually draw it and cairo
89 doesn't reset it after the previous calls */
90 cairo_new_path (cairoTarget);
93 static void
94 draw_fill (cairo_t *cairoTarget, gchar drawMode, gerbv_selection_info_t *selectionInfo,
95 gerbv_image_t *image, struct gerbv_net *net){
96 if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
97 cairo_fill (cairoTarget);
98 else
99 draw_check_if_object_is_in_selected_area (cairoTarget, FALSE,
100 selectionInfo, image, net);
103 static void
104 draw_stroke (cairo_t *cairoTarget, gchar drawMode, gerbv_selection_info_t *selectionInfo,
105 gerbv_image_t *image, struct gerbv_net *net){
106 if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
107 cairo_stroke (cairoTarget);
108 else
109 draw_check_if_object_is_in_selected_area (cairoTarget, TRUE,
110 selectionInfo, image, net);
114 * Draws a circle _centered_ at x,y with diameter dia
116 static void
117 gerbv_draw_circle(cairo_t *cairoTarget, gdouble diameter)
119 cairo_arc (cairoTarget, 0.0, 0.0, diameter/2.0, 0, 2.0*M_PI);
120 return;
121 } /* gerbv_draw_circle */
125 * Draws a rectangle _centered_ at x,y with sides x_side, y_side
127 static void
128 gerbv_draw_rectangle(cairo_t *cairoTarget, gdouble width, gdouble height)
130 cairo_rectangle (cairoTarget, - width / 2.0, - height / 2.0, width, height);
131 return;
132 } /* gerbv_draw_rectangle */
136 * Draws an oblong _centered_ at x,y with x axis x_axis and y axis y_axis
138 static void
139 gerbv_draw_oblong(cairo_t *cairoTarget, gdouble width, gdouble height)
142 cairo_new_path (cairoTarget);
143 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
144 cairo_set_line_width (cairoTarget, height);
145 cairo_move_to (cairoTarget, -width/2.0 + height/2.0, 0);
146 cairo_line_to (cairoTarget, width/2.0 - height/2.0, 0);
147 draw_stroke (cairoTarget, drawMode, selectionInfo);
149 /* cairo doesn't have a function to draw ovals, so we must
150 * draw an arc and stretch it by scaling different x and y values
152 cairo_save (cairoTarget);
153 cairo_scale (cairoTarget, width, height);
154 gerbv_draw_circle (cairoTarget, 1);
155 cairo_restore (cairoTarget);
156 return;
157 } /* gerbv_draw_oval */
160 static void
161 gerbv_draw_polygon(cairo_t *cairoTarget, gdouble outsideDiameter,
162 gdouble numberOfSides, gdouble degreesOfRotation)
164 int i, numberOfSidesInteger = (int) numberOfSides;
166 cairo_rotate(cairoTarget, degreesOfRotation * M_PI/180);
167 cairo_move_to(cairoTarget, outsideDiameter / 2.0, 0);
168 /* skip first point, since we've moved there already */
169 /* include last point, since we may be drawing an aperture hole next
170 and cairo may not correctly close the path itself */
171 for (i = 1; i <= (int)numberOfSidesInteger; i++){
172 gdouble angle = (double) i / numberOfSidesInteger * M_PI * 2.0;
173 cairo_line_to (cairoTarget, cos(angle) * outsideDiameter / 2.0,
174 sin(angle) * outsideDiameter / 2.0);
176 return;
177 } /* gerbv_draw_polygon */
180 static void
181 gerbv_draw_aperature_hole(cairo_t *cairoTarget, gdouble dimensionX, gdouble dimensionY)
183 if (dimensionX) {
184 if (dimensionY) {
185 gerbv_draw_rectangle (cairoTarget, dimensionX, dimensionY);
186 } else {
187 gerbv_draw_circle (cairoTarget, dimensionX);
190 return;
191 } /* gerbv_draw_aperature_hole */
193 gboolean
194 draw_update_macro_exposure (cairo_t *cairoTarget, cairo_operator_t clearOperator,
195 cairo_operator_t darkOperator, gdouble exposureSetting){
197 if (exposureSetting == 0.0) {
198 cairo_set_operator (cairoTarget, clearOperator);
200 else if (exposureSetting == 1.0) {
201 cairo_set_operator (cairoTarget, darkOperator);
203 else if (exposureSetting == 2.0) {
204 /* reverse current exposure setting */
205 cairo_operator_t currentOperator = cairo_get_operator (cairoTarget);
206 if (currentOperator == clearOperator) {
207 cairo_set_operator (cairoTarget, darkOperator);
209 else {
210 cairo_set_operator (cairoTarget, clearOperator);
213 return TRUE;
218 gerbv_draw_amacro(cairo_t *cairoTarget, cairo_operator_t clearOperator,
219 cairo_operator_t darkOperator, gerbv_simplified_amacro_t *s,
220 gint usesClearPrimative, gchar drawMode, gerbv_selection_info_t *selectionInfo,
221 gerbv_image_t *image, struct gerbv_net *net)
223 int handled = 1;
224 gerbv_simplified_amacro_t *ls = s;
226 dprintf("Drawing simplified aperture macros:\n");
227 if (usesClearPrimative)
228 cairo_push_group (cairoTarget);
229 while (ls != NULL) {
231 * This handles the exposure thing in the aperture macro
232 * The exposure is always the first element on stack independent
233 * of aperture macro.
235 cairo_save (cairoTarget);
236 cairo_new_path(cairoTarget);
237 cairo_operator_t oldOperator = cairo_get_operator (cairoTarget);
239 if (ls->type == MACRO_CIRCLE) {
241 if (draw_update_macro_exposure (cairoTarget, clearOperator,
242 darkOperator, ls->parameter[CIRCLE_EXPOSURE])){
243 cairo_translate (cairoTarget, ls->parameter[CIRCLE_CENTER_X],
244 ls->parameter[CIRCLE_CENTER_Y]);
246 gerbv_draw_circle (cairoTarget, ls->parameter[CIRCLE_DIAMETER]);
247 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
249 } else if (ls->type == MACRO_OUTLINE) {
250 int pointCounter,numberOfPoints;
251 /* Number of points parameter seems to not include the start point,
252 * so we add one to include the start point.
254 numberOfPoints = (int) ls->parameter[OUTLINE_NUMBER_OF_POINTS] + 1;
256 if (draw_update_macro_exposure (cairoTarget, clearOperator,
257 darkOperator, ls->parameter[OUTLINE_EXPOSURE])){
258 cairo_rotate (cairoTarget, ls->parameter[(numberOfPoints - 1) * 2 + OUTLINE_ROTATION] * M_PI/180.0);
259 cairo_move_to (cairoTarget, ls->parameter[OUTLINE_FIRST_X], ls->parameter[OUTLINE_FIRST_Y]);
261 for (pointCounter=0; pointCounter < numberOfPoints; pointCounter++) {
262 cairo_line_to (cairoTarget, ls->parameter[pointCounter * 2 + OUTLINE_FIRST_X],
263 ls->parameter[pointCounter * 2 + OUTLINE_FIRST_Y]);
265 /* although the gerber specs allow for an open outline,
266 I interpret it to mean the outline should be closed by the
267 rendering softare automatically, since there is no dimension
268 for line thickness.
270 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
272 } else if (ls->type == MACRO_POLYGON) {
273 if (draw_update_macro_exposure (cairoTarget, clearOperator,
274 darkOperator, ls->parameter[POLYGON_EXPOSURE])){
275 cairo_translate (cairoTarget, ls->parameter[POLYGON_CENTER_X],
276 ls->parameter[POLYGON_CENTER_Y]);
277 gerbv_draw_polygon(cairoTarget, ls->parameter[POLYGON_DIAMETER],
278 ls->parameter[POLYGON_NUMBER_OF_POINTS], ls->parameter[POLYGON_ROTATION]);
279 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
281 } else if (ls->type == MACRO_MOIRE) {
282 gdouble diameter, gap;
283 int circleIndex;
285 cairo_translate (cairoTarget, ls->parameter[MOIRE_CENTER_X],
286 ls->parameter[MOIRE_CENTER_Y]);
287 cairo_rotate (cairoTarget, ls->parameter[MOIRE_ROTATION] * M_PI/180);
288 diameter = ls->parameter[MOIRE_OUTSIDE_DIAMETER] - ls->parameter[MOIRE_CIRCLE_THICKNESS];
289 gap = ls->parameter[MOIRE_GAP_WIDTH] + ls->parameter[MOIRE_CIRCLE_THICKNESS];
290 cairo_set_line_width (cairoTarget, ls->parameter[MOIRE_CIRCLE_THICKNESS]);
292 for (circleIndex = 0; circleIndex < (int)ls->parameter[MOIRE_NUMBER_OF_CIRCLES]; circleIndex++) {
293 gdouble currentDiameter = (diameter - gap * (float) circleIndex);
294 gerbv_draw_circle (cairoTarget, currentDiameter);
295 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
298 gdouble crosshairRadius = (ls->parameter[MOIRE_CROSSHAIR_LENGTH] / 2.0);
300 cairo_set_line_width (cairoTarget, ls->parameter[MOIRE_CROSSHAIR_THICKNESS]);
301 cairo_move_to (cairoTarget, -crosshairRadius, 0);
302 cairo_line_to (cairoTarget, crosshairRadius, 0);
303 cairo_move_to (cairoTarget, 0, -crosshairRadius);
304 cairo_line_to (cairoTarget, 0, crosshairRadius);
305 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
306 } else if (ls->type == MACRO_THERMAL) {
307 gint i;
308 gdouble startAngle1, startAngle2, endAngle1, endAngle2;
310 cairo_translate (cairoTarget, ls->parameter[THERMAL_CENTER_X],
311 ls->parameter[THERMAL_CENTER_Y]);
312 cairo_rotate (cairoTarget, ls->parameter[THERMAL_ROTATION] * M_PI/180.0);
313 startAngle1 = atan (ls->parameter[THERMAL_CROSSHAIR_THICKNESS]/ls->parameter[THERMAL_INSIDE_DIAMETER]);
314 endAngle1 = M_PI/2 - startAngle1;
315 endAngle2 = atan (ls->parameter[THERMAL_CROSSHAIR_THICKNESS]/ls->parameter[THERMAL_OUTSIDE_DIAMETER]);
316 startAngle2 = M_PI/2 - endAngle2;
317 for (i = 0; i < 4; i++) {
318 cairo_arc (cairoTarget, 0, 0, ls->parameter[THERMAL_INSIDE_DIAMETER]/2.0, startAngle1, endAngle1);
319 cairo_rel_line_to (cairoTarget, 0, ls->parameter[THERMAL_CROSSHAIR_THICKNESS]);
320 cairo_arc_negative (cairoTarget, 0, 0, ls->parameter[THERMAL_OUTSIDE_DIAMETER]/2.0,
321 startAngle2, endAngle2);
322 cairo_rel_line_to (cairoTarget, -ls->parameter[THERMAL_CROSSHAIR_THICKNESS],0);
323 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
324 cairo_rotate (cairoTarget, 90 * M_PI/180);
326 } else if (ls->type == MACRO_LINE20) {
327 if (draw_update_macro_exposure (cairoTarget, clearOperator,
328 darkOperator, ls->parameter[LINE20_EXPOSURE])){
329 cairo_set_line_width (cairoTarget, ls->parameter[LINE20_LINE_WIDTH]);
330 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_BUTT);
331 cairo_rotate (cairoTarget, ls->parameter[LINE20_ROTATION] * M_PI/180.0);
332 cairo_move_to (cairoTarget, ls->parameter[LINE20_START_X], ls->parameter[LINE20_START_Y]);
333 cairo_line_to (cairoTarget, ls->parameter[LINE20_END_X], ls->parameter[LINE20_END_Y]);
334 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
336 } else if (ls->type == MACRO_LINE21) {
337 gdouble halfWidth, halfHeight;
339 if (draw_update_macro_exposure (cairoTarget, clearOperator,
340 darkOperator, ls->parameter[LINE22_EXPOSURE])){
341 halfWidth = ls->parameter[LINE21_WIDTH] / 2.0;
342 halfHeight = ls->parameter[LINE21_HEIGHT] / 2.0;
343 cairo_translate (cairoTarget, ls->parameter[LINE21_CENTER_X], ls->parameter[LINE21_CENTER_Y]);
344 cairo_rotate (cairoTarget, ls->parameter[LINE21_ROTATION] * M_PI/180.0);
345 cairo_rectangle (cairoTarget, -halfWidth, -halfHeight,
346 ls->parameter[LINE21_WIDTH], ls->parameter[LINE21_HEIGHT]);
347 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
349 } else if (ls->type == MACRO_LINE22) {
350 gdouble halfWidth, halfHeight;
352 if (draw_update_macro_exposure (cairoTarget, clearOperator,
353 darkOperator, ls->parameter[LINE22_EXPOSURE])){
354 halfWidth = ls->parameter[LINE22_WIDTH] / 2.0;
355 halfHeight = ls->parameter[LINE22_HEIGHT] / 2.0;
356 cairo_translate (cairoTarget, ls->parameter[LINE22_LOWER_LEFT_X],
357 ls->parameter[LINE22_LOWER_LEFT_Y]);
358 cairo_rotate (cairoTarget, ls->parameter[LINE22_ROTATION] * M_PI/180.0);
359 cairo_rectangle (cairoTarget, 0, 0,
360 ls->parameter[LINE22_WIDTH], ls->parameter[LINE22_HEIGHT]);
361 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
363 } else {
364 handled = 0;
366 cairo_set_operator (cairoTarget, oldOperator);
367 cairo_restore (cairoTarget);
368 ls = ls->next;
370 if (usesClearPrimative) {
371 cairo_pop_group_to_source (cairoTarget);
372 cairo_paint (cairoTarget);
374 return handled;
375 } /* gerbv_draw_amacro */
378 void
379 draw_apply_netstate_transformation (cairo_t *cairoTarget, gerbv_netstate_t *state)
381 /* apply scale factor */
382 cairo_scale (cairoTarget, state->scaleA, state->scaleB);
383 /* apply offset */
384 cairo_translate (cairoTarget, state->offsetA, state->offsetB);
385 /* apply mirror */
386 switch (state->mirrorState) {
387 case FLIPA:
388 cairo_scale (cairoTarget, -1, 1);
389 break;
390 case FLIPB:
391 cairo_scale (cairoTarget, 1, -1);
392 break;
393 case FLIPAB:
394 cairo_scale (cairoTarget, -1, -1);
395 break;
396 default:
397 break;
399 /* finally, apply axis select */
400 if (state->axisSelect == SWAPAB) {
401 /* we do this by rotating 270 (counterclockwise, then mirroring
402 the Y axis */
403 cairo_rotate (cairoTarget, 3 * M_PI / 2);
404 cairo_scale (cairoTarget, 1, -1);
409 draw_image_to_cairo_target (cairo_t *cairoTarget, gerbv_image_t *image,
410 gboolean invertLayer, gdouble pixelWidth,
411 gchar drawMode, gerbv_selection_info_t *selectionInfo)
413 struct gerbv_net *net, *polygonStartNet=NULL;
414 double x1, y1, x2, y2, cp_x=0, cp_y=0;
415 int in_parea_fill = 0,haveDrawnFirstFillPoint = 0;
416 gdouble p1, p2, p3, p4, p5, dx, dy;
417 gerbv_netstate_t *oldState;
418 gerbv_layer_t *oldLayer;
419 int repeat_X=1, repeat_Y=1;
420 double repeat_dist_X = 0, repeat_dist_Y = 0;
421 int repeat_i, repeat_j;
422 cairo_operator_t drawOperatorClear, drawOperatorDark;
423 gboolean invertPolarity = FALSE;
425 /* do initial justify */
426 cairo_translate (cairoTarget, image->info->imageJustifyOffsetActualA,
427 image->info->imageJustifyOffsetActualB);
429 /* set the fill rule so aperture holes are cleared correctly */
430 cairo_set_fill_rule (cairoTarget, CAIRO_FILL_RULE_EVEN_ODD);
431 /* offset image */
432 cairo_translate (cairoTarget, image->info->offsetA, image->info->offsetB);
433 /* do image rotation */
434 cairo_rotate (cairoTarget, image->info->imageRotation);
435 /* load in polarity operators depending on the image polarity */
436 invertPolarity = invertLayer;
437 if (image->info->polarity == NEGATIVE)
438 invertPolarity = !invertPolarity;
439 if (invertPolarity) {
440 drawOperatorClear = CAIRO_OPERATOR_OVER;
441 drawOperatorDark = CAIRO_OPERATOR_CLEAR;
442 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_OVER);
443 cairo_paint (cairoTarget);
444 cairo_set_operator (cairoTarget, CAIRO_OPERATOR_CLEAR);
446 else {
447 drawOperatorClear = CAIRO_OPERATOR_CLEAR;
448 drawOperatorDark = CAIRO_OPERATOR_OVER;
450 /* next, push two cairo states to simulate the first layer and netstate
451 translations (these will be popped when another layer or netstate is
452 started */
454 cairo_save (cairoTarget);
455 cairo_save (cairoTarget);
456 /* store the current layer and netstate so we know when they change */
457 oldLayer = image->layers;
458 oldState = image->states;
460 for (net = image->netlist->next ; net != NULL; net = net->next) {
462 /* check if this is a new layer */
463 if (net->layer != oldLayer){
464 /* it's a new layer, so recalculate the new transformation matrix
465 for it */
466 cairo_restore (cairoTarget);
467 cairo_restore (cairoTarget);
468 cairo_save (cairoTarget);
469 /* do any rotations */
470 cairo_rotate (cairoTarget, net->layer->rotation);
471 /* handle the layer polarity */
472 if ((net->layer->polarity == CLEAR)) {
473 cairo_set_operator (cairoTarget, drawOperatorClear);
475 else {
476 cairo_set_operator (cairoTarget, drawOperatorDark);
478 /* check for changes to step and repeat */
479 repeat_X = net->layer->stepAndRepeat.X;
480 repeat_Y = net->layer->stepAndRepeat.Y;
481 repeat_dist_X = net->layer->stepAndRepeat.dist_X;
482 repeat_dist_Y = net->layer->stepAndRepeat.dist_Y;
483 /* draw any knockout areas */
484 if (net->layer->knockout.firstInstance == TRUE) {
485 cairo_operator_t oldOperator = cairo_get_operator (cairoTarget);
486 if (net->layer->knockout.polarity == CLEAR) {
487 cairo_set_operator (cairoTarget, drawOperatorClear);
489 else {
490 cairo_set_operator (cairoTarget, drawOperatorDark);
492 cairo_new_path (cairoTarget);
493 cairo_rectangle (cairoTarget, net->layer->knockout.lowerLeftX - net->layer->knockout.border,
494 net->layer->knockout.lowerLeftY - net->layer->knockout.border,
495 net->layer->knockout.width + (net->layer->knockout.border*2),
496 net->layer->knockout.height + (net->layer->knockout.border*2));
497 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
498 cairo_set_operator (cairoTarget, oldOperator);
500 /* finally, reapply old netstate transformation */
501 cairo_save (cairoTarget);
502 draw_apply_netstate_transformation (cairoTarget, net->state);
503 oldLayer = net->layer;
505 /* check if this is a new netstate */
506 if (net->state != oldState){
507 /* pop the transformation matrix back to the "pre-state" state and
508 resave it */
509 cairo_restore (cairoTarget);
510 cairo_save (cairoTarget);
511 /* it's a new state, so recalculate the new transformation matrix
512 for it */
513 draw_apply_netstate_transformation (cairoTarget, net->state);
514 oldState = net->state;
516 /* if we are only drawing from the selection buffer, search if this net is
517 in the buffer */
518 if (drawMode == DRAW_SELECTIONS) {
519 /* this flag makes sure we don't draw any unintentional polygons...
520 if we've successfully entered a polygon (the first net matches, and
521 we don't want to check the nets inside the polygon) then
522 polygonStartNet will be set */
523 if (!polygonStartNet) {
524 int i;
525 gboolean foundNet = FALSE;
527 for (i=0; i<selectionInfo->selectedNodeArray->len; i++){
528 gerbv_selection_item_t sItem = g_array_index (selectionInfo->selectedNodeArray,
529 gerbv_selection_item_t, i);
530 if (sItem.net == net)
531 foundNet = TRUE;
533 if (!foundNet)
534 continue;
538 for(repeat_i = 0; repeat_i < repeat_X; repeat_i++) {
539 for(repeat_j = 0; repeat_j < repeat_Y; repeat_j++) {
540 double sr_x = repeat_i * repeat_dist_X;
541 double sr_y = repeat_j * repeat_dist_Y;
543 x1 = net->start_x + sr_x;
544 y1 = net->start_y + sr_y;
545 x2 = net->stop_x + sr_x;
546 y2 = net->stop_y + sr_y;
548 /* translate circular x,y data as well */
549 if (net->cirseg) {
550 cp_x = net->cirseg->cp_x + sr_x;
551 cp_y = net->cirseg->cp_y + sr_y;
554 /* render any labels attached to this net */
555 /* NOTE: this is currently only used on PNP files, so we may
556 make some assumptions here... */
557 if (net->label) {
558 cairo_set_font_size (cairoTarget, 0.05);
559 cairo_save (cairoTarget);
561 cairo_move_to (cairoTarget, x1, y1);
562 cairo_scale (cairoTarget, 1, -1);
563 cairo_show_text (cairoTarget, net->label->str);
564 cairo_restore (cairoTarget);
567 * Polygon Area Fill routines
569 switch (net->interpolation) {
570 case PAREA_START :
571 in_parea_fill = 1;
572 haveDrawnFirstFillPoint = FALSE;
573 /* save the first net in the polygon as the "ID" net pointer
574 in case we are saving this net to the selection array */
575 polygonStartNet = net;
576 cairo_new_path(cairoTarget);
577 continue;
578 case PAREA_END :
579 cairo_close_path(cairoTarget);
580 /* turn off anti-aliasing for polygons, since it shows seams
581 with adjacent polygons (usually on PCB ground planes) */
582 cairo_antialias_t oldAlias = cairo_get_antialias (cairoTarget);
583 cairo_set_antialias (cairoTarget, CAIRO_ANTIALIAS_NONE);
584 draw_fill (cairoTarget, drawMode, selectionInfo, image, polygonStartNet);
585 cairo_set_antialias (cairoTarget, oldAlias);
586 in_parea_fill = 0;
587 polygonStartNet = NULL;
588 continue;
589 /* make sure we completely skip over any deleted nodes */
590 case DELETED:
591 continue;
592 default :
593 break;
595 if (in_parea_fill) {
596 if (!haveDrawnFirstFillPoint) {
597 cairo_move_to (cairoTarget, x2,y2);
598 haveDrawnFirstFillPoint=TRUE;
599 continue;
601 switch (net->interpolation) {
602 case LINEARx10 :
603 case LINEARx01 :
604 case LINEARx001 :
605 case LINEARx1 :
606 cairo_line_to (cairoTarget, x2,y2);
607 break;
608 case CW_CIRCULAR :
609 case CCW_CIRCULAR :
610 if (net->cirseg->angle2 > net->cirseg->angle1) {
611 cairo_arc (cairoTarget, cp_x, cp_y, net->cirseg->width/2.0,
612 net->cirseg->angle1 * M_PI/180,net->cirseg->angle2 * M_PI/180);
614 else {
615 cairo_arc_negative (cairoTarget, cp_x, cp_y, net->cirseg->width/2.0,
616 net->cirseg->angle1 * M_PI/180,net->cirseg->angle2 * M_PI/180);
618 break;
619 default:
620 break;
622 continue;
626 * If aperture state is off we allow use of undefined apertures.
627 * This happens when gerber files starts, but hasn't decided on
628 * which aperture to use.
630 if (image->aperture[net->aperture] == NULL) {
631 if (net->aperture_state != OFF)
632 GERB_MESSAGE("Aperture [%d] is not defined\n", net->aperture);
633 continue;
635 switch (net->aperture_state) {
636 case ON :
637 /* if the aperture width is truly 0, then render as a 1 pixel width
638 line. 0 diameter apertures are used by some programs to draw labels,
639 etc, and they are rendered by other programs as 1 pixel wide */
640 /* NOTE: also, make sure all lines are at least 1 pixel wide, so they
641 always show up at low zoom levels */
642 if (image->aperture[net->aperture]->parameter[0] > pixelWidth)
643 cairo_set_line_width (cairoTarget, image->aperture[net->aperture]->parameter[0]);
644 else
645 cairo_set_line_width (cairoTarget, pixelWidth);
646 switch (net->interpolation) {
647 case LINEARx10 :
648 case LINEARx01 :
649 case LINEARx001 :
650 case LINEARx1 :
651 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
652 switch (image->aperture[net->aperture]->type) {
653 case CIRCLE :
654 cairo_move_to (cairoTarget, x1,y1);
655 cairo_line_to (cairoTarget, x2,y2);
656 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
657 break;
658 case RECTANGLE :
659 dx = (image->aperture[net->aperture]->parameter[0]/ 2);
660 dy = (image->aperture[net->aperture]->parameter[1]/ 2);
661 if(x1 > x2)
662 dx = -dx;
663 if(y1 > y2)
664 dy = -dy;
665 cairo_new_path(cairoTarget);
666 cairo_move_to (cairoTarget, x1 - dx, y1 - dy);
667 cairo_line_to (cairoTarget, x1 - dx, y1 + dy);
668 cairo_line_to (cairoTarget, x2 - dx, y2 + dy);
669 cairo_line_to (cairoTarget, x2 + dx, y2 + dy);
670 cairo_line_to (cairoTarget, x2 + dx, y2 - dy);
671 cairo_line_to (cairoTarget, x1 + dx, y1 - dy);
672 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
673 break;
674 /* for now, just render ovals or polygons like a circle */
675 case OVAL :
676 case POLYGON :
677 cairo_move_to (cairoTarget, x1,y1);
678 cairo_line_to (cairoTarget, x2,y2);
679 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
680 break;
681 /* macros can only be flashed, so ignore any that might be here */
682 default :
683 break;
685 break;
686 case CW_CIRCULAR :
687 case CCW_CIRCULAR :
688 /* cairo doesn't have a function to draw oval arcs, so we must
689 * draw an arc and stretch it by scaling different x and y values
691 cairo_new_path(cairoTarget);
692 if (image->aperture[net->aperture]->type == RECTANGLE) {
693 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_SQUARE);
695 else {
696 cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
698 cairo_save (cairoTarget);
699 cairo_translate(cairoTarget, cp_x, cp_y);
700 cairo_scale (cairoTarget, net->cirseg->width, net->cirseg->height);
701 if (net->cirseg->angle2 > net->cirseg->angle1) {
702 cairo_arc (cairoTarget, 0.0, 0.0, 0.5, net->cirseg->angle1 * M_PI/180,
703 net->cirseg->angle2 * M_PI/180);
705 else {
706 cairo_arc_negative (cairoTarget, 0.0, 0.0, 0.5, net->cirseg->angle1 * M_PI/180,
707 net->cirseg->angle2 * M_PI/180);
709 cairo_restore (cairoTarget);
710 draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
711 break;
712 default :
713 break;
715 break;
716 case OFF :
717 break;
718 case FLASH :
719 p1 = image->aperture[net->aperture]->parameter[0];
720 p2 = image->aperture[net->aperture]->parameter[1];
721 p3 = image->aperture[net->aperture]->parameter[2];
722 p4 = image->aperture[net->aperture]->parameter[3];
723 p5 = image->aperture[net->aperture]->parameter[4];
725 cairo_save (cairoTarget);
726 cairo_translate (cairoTarget, x2, y2);
728 switch (image->aperture[net->aperture]->type) {
729 case CIRCLE :
730 gerbv_draw_circle(cairoTarget, p1);
731 gerbv_draw_aperature_hole (cairoTarget, p2, p3);
732 break;
733 case RECTANGLE :
734 gerbv_draw_rectangle(cairoTarget, p1, p2);
735 gerbv_draw_aperature_hole (cairoTarget, p3, p4);
736 break;
737 case OVAL :
738 gerbv_draw_oblong(cairoTarget, p1, p2);
739 gerbv_draw_aperature_hole (cairoTarget, p3, p4);
740 break;
741 case POLYGON :
742 gerbv_draw_polygon(cairoTarget, p1, p2, p3);
743 gerbv_draw_aperature_hole (cairoTarget, p4, p5);
744 break;
745 case MACRO :
746 gerbv_draw_amacro(cairoTarget, drawOperatorClear, drawOperatorDark,
747 image->aperture[net->aperture]->simplified,
748 (int) image->aperture[net->aperture]->parameter[0],
749 drawMode, selectionInfo, image, net);
750 break;
751 default :
752 GERB_MESSAGE("Unknown aperture type\n");
753 return 0;
755 /* and finally fill the path */
756 draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
757 cairo_restore (cairoTarget);
758 break;
759 default:
760 GERB_MESSAGE("Unknown aperture state\n");
761 return 0;
767 /* restore the initial two state saves (one for layer, one for netstate)*/
768 cairo_restore (cairoTarget);
769 cairo_restore (cairoTarget);
771 return 1;