gerbv  2.6A
draw.c
Go to the documentation of this file.
1 /*
2  * gEDA - GNU Electronic Design Automation
3  * This file is a part of gerbv.
4  *
5  * Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
6  *
7  * $Id$
8  *
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.
13  *
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.
18  *
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
22  */
23 
29 #include "gerbv.h"
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <math.h> /* ceil(), atan2() */
34 
35 #ifdef HAVE_STRING_H
36 # include <string.h>
37 #endif
38 
39 #include "draw.h"
40 #include "draw-gdk.h"
41 #include "common.h"
42 
43 #define dprintf if(DEBUG) printf
44 
45 void
46 draw_cairo_line_to (cairo_t *cairoTarget, gdouble x, gdouble y, gboolean adjustByHalf, gboolean pixelOutput){
47  gdouble x1 = x, y1 = y;
48  if (pixelOutput) {
49  cairo_user_to_device (cairoTarget, &x1, &y1);
50  x1 = round(x1);
51  y1 = round(y1);
52  if (adjustByHalf) {
53  x1 += 0.5;
54  y1 += 0.5;
55  }
56  cairo_device_to_user (cairoTarget, &x1, &y1);
57  }
58  cairo_line_to (cairoTarget, x1, y1);
59 }
60 
61 void
62 draw_cairo_move_to (cairo_t *cairoTarget, gdouble x, gdouble y, gboolean oddWidth, gboolean pixelOutput){
63  gdouble x1 = x, y1 = y;
64  if (pixelOutput) {
65  cairo_user_to_device (cairoTarget, &x1, &y1);
66  x1 = round(x1);
67  y1 = round(y1);
68  if (oddWidth) {
69  x1 += 0.5;
70  y1 += 0.5;
71  }
72  cairo_device_to_user (cairoTarget, &x1, &y1);
73  }
74  cairo_move_to (cairoTarget, x1, y1);
75 }
76 
77 void
78 draw_cairo_translate_adjust (cairo_t *cairoTarget, gdouble x, gdouble y, gboolean pixelOutput){
79  gdouble x1 = x, y1 = y;
80  if (pixelOutput) {
81  cairo_user_to_device (cairoTarget, &x1, &y1);
82  x1 = round(x1);
83  y1 = round(y1);
84  cairo_device_to_user (cairoTarget, &x1, &y1);
85  }
86  cairo_translate (cairoTarget, x1, y1);
87 }
88 
89 gboolean
90 draw_net_in_selection_buffer (gerbv_net_t *net, gerbv_selection_info_t *selectionInfo) {
91  int i;
92 
93  for (i=0; i<selectionInfo->selectedNodeArray->len; i++){
94  gerbv_selection_item_t sItem = g_array_index (selectionInfo->selectedNodeArray,
95  gerbv_selection_item_t, i);
96  if (sItem.net == net)
97  return TRUE;
98  }
99  return FALSE;
100 }
101 
102 static void
103 draw_check_if_object_is_in_selected_area (cairo_t *cairoTarget, gboolean isStroke,
104  gerbv_selection_info_t *selectionInfo, gerbv_image_t *image, struct gerbv_net *net){
105  gdouble corner1X,corner1Y,corner2X,corner2Y;
106 
107  corner1X = selectionInfo->lowerLeftX;
108  corner1Y = selectionInfo->lowerLeftY;
109  corner2X = selectionInfo->upperRightX;
110  corner2Y = selectionInfo->upperRightY;
111 
112  /* calculate the coordinate of the user's click in the current
113  transformation matrix */
114  cairo_device_to_user (cairoTarget, &corner1X, &corner1Y);
115  cairo_device_to_user (cairoTarget, &corner2X, &corner2Y);
116  if (selectionInfo->type == GERBV_SELECTION_POINT_CLICK) {
117  /* use the cairo in_fill routine to see if the point is within the
118  drawn area */
119  if ((isStroke && cairo_in_stroke (cairoTarget, corner1X, corner1Y)) ||
120  (!isStroke && cairo_in_fill (cairoTarget, corner1X, corner1Y))) {
121  /* add the net to the selection array */
122  if (!draw_net_in_selection_buffer(net, selectionInfo)) {
123  gerbv_selection_item_t sItem = {image, net};
124  g_array_append_val (selectionInfo->selectedNodeArray, sItem);
125  }
126  }
127  }
128  else if (selectionInfo->type == GERBV_SELECTION_DRAG_BOX) {
129  gdouble x1,x2,y1,y2;
130  gdouble minX,minY,maxX,maxY;
131 
132  /* we can't assume the "lowerleft" corner is actually in the lower left,
133  since the cairo transformation matrix may be mirrored,etc */
134  minX = MIN(corner1X,corner2X);
135  maxX = MAX(corner1X,corner2X);
136  minY = MIN(corner1Y,corner2Y);
137  maxY = MAX(corner1Y,corner2Y);
138  if (isStroke)
139  cairo_stroke_extents (cairoTarget, &x1, &y1, &x2, &y2);
140  else
141  cairo_fill_extents (cairoTarget, &x1, &y1, &x2, &y2);
142 
143  if ((minX < x1) && (minY < y1) && (maxX > x2) && (maxY > y2)) {
144  /* add the net to the selection array */
145  if (!draw_net_in_selection_buffer(net, selectionInfo)) {
146  gerbv_selection_item_t sItem = {image, net};
147  g_array_append_val (selectionInfo->selectedNodeArray, sItem);
148  }
149  }
150  }
151  /* clear the path, since we didn't actually draw it and cairo
152  doesn't reset it after the previous calls */
153  cairo_new_path (cairoTarget);
154 }
155 
156 static void
157 draw_fill (cairo_t *cairoTarget, gchar drawMode, gerbv_selection_info_t *selectionInfo,
158  gerbv_image_t *image, struct gerbv_net *net){
159  if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
160  cairo_fill (cairoTarget);
161  else
162  draw_check_if_object_is_in_selected_area (cairoTarget, FALSE,
163  selectionInfo, image, net);
164 }
165 
166 static void
167 draw_stroke (cairo_t *cairoTarget, gchar drawMode, gerbv_selection_info_t *selectionInfo,
168  gerbv_image_t *image, struct gerbv_net *net){
169  if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
170  cairo_stroke (cairoTarget);
171  else
172  draw_check_if_object_is_in_selected_area (cairoTarget, TRUE,
173  selectionInfo, image, net);
174 }
175 
176 /*
177  * Draws a circle _centered_ at x,y with diameter dia
178  */
179 static void
180 gerbv_draw_circle(cairo_t *cairoTarget, gdouble diameter)
181 {
182  cairo_arc (cairoTarget, 0.0, 0.0, diameter/2.0, 0, 2.0*M_PI);
183  return;
184 } /* gerbv_draw_circle */
185 
186 
187 /*
188  * Draws a rectangle _centered_ at x,y with sides x_side, y_side
189  */
190 static void
191 gerbv_draw_rectangle(cairo_t *cairoTarget, gdouble width1, gdouble height1, gboolean pixelOutput)
192 {
193  gdouble width = width1, height = height1;
194  if (pixelOutput) {
195  cairo_user_to_device_distance (cairoTarget, &width, &height);
196  width = round(width);
197  height = round(height);
198  width -= (int)width % 2;
199  height -= (int)height % 2;
200  cairo_device_to_user_distance (cairoTarget, &width, &height);
201  }
202  cairo_rectangle (cairoTarget, - width / 2.0, - height / 2.0, width, height);
203  return;
204 } /* gerbv_draw_rectangle */
205 
206 
207 /*
208  * Draws an oblong _centered_ at x,y with x axis x_axis and y axis y_axis
209  */
210 static void
211 gerbv_draw_oblong(cairo_t *cairoTarget, gdouble width, gdouble height)
212 {
213  /* --- This stuff produces a line + rounded ends --- */
214  gdouble circleDiameter, strokeDistance;
215 
216  cairo_new_path (cairoTarget);
217  if (width < height) {
218  circleDiameter = width;
219  strokeDistance = (height - width)/2.0;
220  cairo_arc (cairoTarget, 0.0, strokeDistance, circleDiameter/2.0, 0, -M_PI);
221  cairo_line_to (cairoTarget, -circleDiameter/2.0, -strokeDistance);
222  cairo_arc (cairoTarget, 0.0, -strokeDistance, circleDiameter/2.0, -M_PI, 0);
223  cairo_line_to (cairoTarget, circleDiameter/2.0, strokeDistance);
224  }
225  else {
226  circleDiameter = height;
227  strokeDistance = (width - height)/2.0;
228  cairo_arc (cairoTarget, -strokeDistance, 0.0, circleDiameter/2.0, M_PI/2.0, -M_PI/2.0);
229  cairo_line_to (cairoTarget, strokeDistance, -circleDiameter/2.0);
230  cairo_arc (cairoTarget, strokeDistance, 0.0, circleDiameter/2.0, -M_PI/2.0, M_PI/2.0);
231  cairo_line_to (cairoTarget, -strokeDistance, circleDiameter/2.0);
232  }
233  /* --- This stuff produces an oval pad --- */
234  /* cairo doesn't have a function to draw ovals, so we must
235  * draw an arc and stretch it by scaling different x and y values
236  cairo_save (cairoTarget);
237  cairo_scale (cairoTarget, width, height);
238  gerbv_draw_circle (cairoTarget, 1);
239  cairo_restore (cairoTarget);
240  */
241  return;
242 } /* gerbv_draw_oblong */
243 
244 
245 static void
246 gerbv_draw_polygon(cairo_t *cairoTarget, gdouble outsideDiameter,
247  gdouble numberOfSides, gdouble degreesOfRotation)
248 {
249  int i, numberOfSidesInteger = (int) numberOfSides;
250 
251  cairo_rotate(cairoTarget, degreesOfRotation * M_PI/180);
252  cairo_move_to(cairoTarget, outsideDiameter / 2.0, 0);
253  /* skip first point, since we've moved there already */
254  /* include last point, since we may be drawing an aperture hole next
255  and cairo may not correctly close the path itself */
256  for (i = 1; i <= (int)numberOfSidesInteger; i++){
257  gdouble angle = (double) i / numberOfSidesInteger * M_PI * 2.0;
258  cairo_line_to (cairoTarget, cos(angle) * outsideDiameter / 2.0,
259  sin(angle) * outsideDiameter / 2.0);
260  }
261  return;
262 } /* gerbv_draw_polygon */
263 
264 
265 static void
266 gerbv_draw_aperature_hole(cairo_t *cairoTarget, gdouble dimensionX, gdouble dimensionY, gboolean pixelOutput)
267 {
268  if (dimensionX) {
269  if (dimensionY) {
270  gerbv_draw_rectangle (cairoTarget, dimensionX, dimensionY, pixelOutput);
271  } else {
272  gerbv_draw_circle (cairoTarget, dimensionX);
273  }
274  }
275  return;
276 } /* gerbv_draw_aperature_hole */
277 
278 gboolean
279 draw_update_macro_exposure (cairo_t *cairoTarget, cairo_operator_t clearOperator,
280  cairo_operator_t darkOperator, gdouble exposureSetting){
281 
282  if (exposureSetting == 0.0) {
283  cairo_set_operator (cairoTarget, clearOperator);
284  }
285  else if (exposureSetting == 1.0) {
286  cairo_set_operator (cairoTarget, darkOperator);
287  }
288  else if (exposureSetting == 2.0) {
289  /* reverse current exposure setting */
290  cairo_operator_t currentOperator = cairo_get_operator (cairoTarget);
291  if (currentOperator == clearOperator) {
292  cairo_set_operator (cairoTarget, darkOperator);
293  }
294  else {
295  cairo_set_operator (cairoTarget, clearOperator);
296  }
297  }
298  return TRUE;
299 }
300 
301 
302 int
303 gerbv_draw_amacro(cairo_t *cairoTarget, cairo_operator_t clearOperator,
304  cairo_operator_t darkOperator, gerbv_simplified_amacro_t *s,
305  gint usesClearPrimative, gdouble pixelWidth, gchar drawMode,
306  gerbv_selection_info_t *selectionInfo,
307  gerbv_image_t *image, struct gerbv_net *net)
308 {
309  int handled = 1;
310  gerbv_simplified_amacro_t *ls = s;
311 
312  dprintf(_("Drawing simplified aperture macros:\n"));
313  if (usesClearPrimative)
314  cairo_push_group (cairoTarget);
315  while (ls != NULL) {
316  /*
317  * This handles the exposure thing in the aperture macro
318  * The exposure is always the first element on stack independent
319  * of aperture macro.
320  */
321  cairo_save (cairoTarget);
322  cairo_new_path(cairoTarget);
323  cairo_operator_t oldOperator = cairo_get_operator (cairoTarget);
324 
325  if (ls->type == GERBV_APTYPE_MACRO_CIRCLE) {
326 
327  if (draw_update_macro_exposure (cairoTarget, clearOperator,
328  darkOperator, ls->parameter[CIRCLE_EXPOSURE])){
329  cairo_translate (cairoTarget, ls->parameter[CIRCLE_CENTER_X],
330  ls->parameter[CIRCLE_CENTER_Y]);
331 
332  gerbv_draw_circle (cairoTarget, ls->parameter[CIRCLE_DIAMETER]);
333  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
334  }
335  } else if (ls->type == GERBV_APTYPE_MACRO_OUTLINE) {
336  int pointCounter,numberOfPoints;
337  /* Number of points parameter seems to not include the start point,
338  * so we add one to include the start point.
339  */
340  numberOfPoints = (int) ls->parameter[OUTLINE_NUMBER_OF_POINTS] + 1;
341 
342  if (draw_update_macro_exposure (cairoTarget, clearOperator,
343  darkOperator, ls->parameter[OUTLINE_EXPOSURE])){
344  cairo_rotate (cairoTarget, ls->parameter[(numberOfPoints - 1) * 2 + OUTLINE_ROTATION] * M_PI/180.0);
345  cairo_move_to (cairoTarget, ls->parameter[OUTLINE_FIRST_X], ls->parameter[OUTLINE_FIRST_Y]);
346 
347  for (pointCounter=0; pointCounter < numberOfPoints; pointCounter++) {
348  cairo_line_to (cairoTarget, ls->parameter[pointCounter * 2 + OUTLINE_FIRST_X],
349  ls->parameter[pointCounter * 2 + OUTLINE_FIRST_Y]);
350  }
351  /* although the gerber specs allow for an open outline,
352  I interpret it to mean the outline should be closed by the
353  rendering softare automatically, since there is no dimension
354  for line thickness.
355  */
356  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
357  }
358  } else if (ls->type == GERBV_APTYPE_MACRO_POLYGON) {
359  if (draw_update_macro_exposure (cairoTarget, clearOperator,
360  darkOperator, ls->parameter[POLYGON_EXPOSURE])){
361  cairo_translate (cairoTarget, ls->parameter[POLYGON_CENTER_X],
362  ls->parameter[POLYGON_CENTER_Y]);
363  gerbv_draw_polygon(cairoTarget, ls->parameter[POLYGON_DIAMETER],
364  ls->parameter[POLYGON_NUMBER_OF_POINTS], ls->parameter[POLYGON_ROTATION]);
365  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
366  }
367  } else if (ls->type == GERBV_APTYPE_MACRO_MOIRE) {
368  gdouble diameter, diameterDifference;
369  int circleIndex;
370 
371  cairo_translate (cairoTarget, ls->parameter[MOIRE_CENTER_X],
372  ls->parameter[MOIRE_CENTER_Y]);
373  cairo_rotate (cairoTarget, ls->parameter[MOIRE_ROTATION] * M_PI/180);
374  diameter = ls->parameter[MOIRE_OUTSIDE_DIAMETER] - ls->parameter[MOIRE_CIRCLE_THICKNESS];
375  diameterDifference = 2*(ls->parameter[MOIRE_GAP_WIDTH] +
376  ls->parameter[MOIRE_CIRCLE_THICKNESS]);
377  cairo_set_line_width (cairoTarget, ls->parameter[MOIRE_CIRCLE_THICKNESS]);
378 
379  for (circleIndex = 0; circleIndex < (int)ls->parameter[MOIRE_NUMBER_OF_CIRCLES]; circleIndex++) {
380  gdouble currentDiameter = diameter - diameterDifference * (float) circleIndex;
381  if (currentDiameter >= 0){
382  gerbv_draw_circle (cairoTarget, currentDiameter);
383  draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
384  }
385  }
386 
387  gdouble crosshairRadius = ls->parameter[MOIRE_CROSSHAIR_LENGTH] / 2.0;
388 
389  cairo_set_line_width (cairoTarget, ls->parameter[MOIRE_CROSSHAIR_THICKNESS]);
390  cairo_move_to (cairoTarget, -crosshairRadius, 0);
391  cairo_line_to (cairoTarget, crosshairRadius, 0);
392  cairo_move_to (cairoTarget, 0, -crosshairRadius);
393  cairo_line_to (cairoTarget, 0, crosshairRadius);
394  draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
395  } else if (ls->type == GERBV_APTYPE_MACRO_THERMAL) {
396  gint i;
397  gdouble startAngle1, startAngle2, endAngle1, endAngle2;
398 
399  cairo_translate (cairoTarget, ls->parameter[THERMAL_CENTER_X],
400  ls->parameter[THERMAL_CENTER_Y]);
401  cairo_rotate (cairoTarget, ls->parameter[THERMAL_ROTATION] * M_PI/180.0);
402  startAngle1 = atan (ls->parameter[THERMAL_CROSSHAIR_THICKNESS]/ls->parameter[THERMAL_INSIDE_DIAMETER]);
403  endAngle1 = M_PI/2 - startAngle1;
404  endAngle2 = atan (ls->parameter[THERMAL_CROSSHAIR_THICKNESS]/ls->parameter[THERMAL_OUTSIDE_DIAMETER]);
405  startAngle2 = M_PI/2 - endAngle2;
406  for (i = 0; i < 4; i++) {
407  cairo_arc (cairoTarget, 0, 0, ls->parameter[THERMAL_INSIDE_DIAMETER]/2.0, startAngle1, endAngle1);
408  cairo_rel_line_to (cairoTarget, 0, ls->parameter[THERMAL_CROSSHAIR_THICKNESS]);
409  cairo_arc_negative (cairoTarget, 0, 0, ls->parameter[THERMAL_OUTSIDE_DIAMETER]/2.0,
410  startAngle2, endAngle2);
411  cairo_rel_line_to (cairoTarget, -ls->parameter[THERMAL_CROSSHAIR_THICKNESS],0);
412  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
413  cairo_rotate (cairoTarget, 90 * M_PI/180);
414  }
415  } else if (ls->type == GERBV_APTYPE_MACRO_LINE20) {
416  if (draw_update_macro_exposure (cairoTarget, clearOperator,
417  darkOperator, ls->parameter[LINE20_EXPOSURE])){
418  gdouble cParameter = ls->parameter[LINE20_LINE_WIDTH];
419  if (cParameter < pixelWidth)
420  cParameter = pixelWidth;
421 
422  cairo_set_line_width (cairoTarget, cParameter);
423  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_BUTT);
424  cairo_rotate (cairoTarget, ls->parameter[LINE20_ROTATION] * M_PI/180.0);
425  cairo_move_to (cairoTarget, ls->parameter[LINE20_START_X], ls->parameter[LINE20_START_Y]);
426  cairo_line_to (cairoTarget, ls->parameter[LINE20_END_X], ls->parameter[LINE20_END_Y]);
427  draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
428  }
429  } else if (ls->type == GERBV_APTYPE_MACRO_LINE21) {
430  gdouble halfWidth, halfHeight;
431 
432  if (draw_update_macro_exposure (cairoTarget, clearOperator,
433  darkOperator, ls->parameter[LINE22_EXPOSURE])){
434  halfWidth = ls->parameter[LINE21_WIDTH] / 2.0;
435  halfHeight = ls->parameter[LINE21_HEIGHT] / 2.0;
436  if (halfWidth < pixelWidth)
437  halfWidth = pixelWidth;
438  if (halfHeight < pixelWidth)
439  halfHeight = pixelWidth;
440  cairo_translate (cairoTarget, ls->parameter[LINE21_CENTER_X], ls->parameter[LINE21_CENTER_Y]);
441  cairo_rotate (cairoTarget, ls->parameter[LINE21_ROTATION] * M_PI/180.0);
442  cairo_rectangle (cairoTarget, -halfWidth, -halfHeight,
443  ls->parameter[LINE21_WIDTH], ls->parameter[LINE21_HEIGHT]);
444  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
445  }
446  } else if (ls->type == GERBV_APTYPE_MACRO_LINE22) {
447  gdouble halfWidth, halfHeight;
448 
449  if (draw_update_macro_exposure (cairoTarget, clearOperator,
450  darkOperator, ls->parameter[LINE22_EXPOSURE])){
451  halfWidth = ls->parameter[LINE22_WIDTH] / 2.0;
452  halfHeight = ls->parameter[LINE22_HEIGHT] / 2.0;
453  if (halfWidth < pixelWidth)
454  halfWidth = pixelWidth;
455  if (halfHeight < pixelWidth)
456  halfHeight = pixelWidth;
457  cairo_translate (cairoTarget, ls->parameter[LINE22_LOWER_LEFT_X],
458  ls->parameter[LINE22_LOWER_LEFT_Y]);
459  cairo_rotate (cairoTarget, ls->parameter[LINE22_ROTATION] * M_PI/180.0);
460  cairo_rectangle (cairoTarget, 0, 0,
461  ls->parameter[LINE22_WIDTH], ls->parameter[LINE22_HEIGHT]);
462  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
463  }
464  } else {
465  handled = 0;
466  }
467  cairo_set_operator (cairoTarget, oldOperator);
468  cairo_restore (cairoTarget);
469  ls = ls->next;
470  }
471  if (usesClearPrimative) {
472  cairo_pop_group_to_source (cairoTarget);
473  cairo_paint (cairoTarget);
474  }
475  return handled;
476 } /* gerbv_draw_amacro */
477 
478 
479 void
480 draw_apply_netstate_transformation (cairo_t *cairoTarget, gerbv_netstate_t *state)
481 {
482  /* apply scale factor */
483  cairo_scale (cairoTarget, state->scaleA, state->scaleB);
484  /* apply offset */
485  cairo_translate (cairoTarget, state->offsetA, state->offsetB);
486  /* apply mirror */
487  switch (state->mirrorState) {
488  case GERBV_MIRROR_STATE_FLIPA:
489  cairo_scale (cairoTarget, -1, 1);
490  break;
491  case GERBV_MIRROR_STATE_FLIPB:
492  cairo_scale (cairoTarget, 1, -1);
493  break;
494  case GERBV_MIRROR_STATE_FLIPAB:
495  cairo_scale (cairoTarget, -1, -1);
496  break;
497  default:
498  break;
499  }
500  /* finally, apply axis select */
501  if (state->axisSelect == GERBV_AXIS_SELECT_SWAPAB) {
502  /* we do this by rotating 270 (counterclockwise, then mirroring
503  the Y axis */
504  cairo_rotate (cairoTarget, 3 * M_PI / 2);
505  cairo_scale (cairoTarget, 1, -1);
506  }
507 }
508 
509 void
510 draw_render_polygon_object (gerbv_net_t *oldNet, cairo_t *cairoTarget, gdouble sr_x, gdouble sr_y,
511  gerbv_image_t *image, gchar drawMode, gerbv_selection_info_t *selectionInfo, gboolean pixelOutput){
512  gerbv_net_t *currentNet, *polygonStartNet;
513  int haveDrawnFirstFillPoint = 0;
514  gdouble x2,y2,cp_x=0,cp_y=0;
515 
516  haveDrawnFirstFillPoint = FALSE;
517  /* save the first net in the polygon as the "ID" net pointer
518  in case we are saving this net to the selection array */
519  polygonStartNet = oldNet;
520  cairo_new_path(cairoTarget);
521 
522  for (currentNet = oldNet->next; currentNet!=NULL; currentNet = currentNet->next){
523  x2 = currentNet->stop_x + sr_x;
524  y2 = currentNet->stop_y + sr_y;
525 
526  /* translate circular x,y data as well */
527  if (currentNet->cirseg) {
528  cp_x = currentNet->cirseg->cp_x + sr_x;
529  cp_y = currentNet->cirseg->cp_y + sr_y;
530  }
531  if (!haveDrawnFirstFillPoint) {
532  draw_cairo_move_to (cairoTarget, x2, y2, FALSE, pixelOutput);
533  haveDrawnFirstFillPoint=TRUE;
534  continue;
535  }
536  switch (currentNet->interpolation) {
541  draw_cairo_line_to (cairoTarget, x2, y2, FALSE, pixelOutput);
542  break;
545  if (currentNet->cirseg->angle2 > currentNet->cirseg->angle1) {
546  cairo_arc (cairoTarget, cp_x, cp_y, currentNet->cirseg->width/2.0,
547  currentNet->cirseg->angle1 * M_PI/180,currentNet->cirseg->angle2 * M_PI/180);
548  }
549  else {
550  cairo_arc_negative (cairoTarget, cp_x, cp_y, currentNet->cirseg->width/2.0,
551  currentNet->cirseg->angle1 * M_PI/180,currentNet->cirseg->angle2 * M_PI/180);
552  }
553  break;
555  cairo_close_path(cairoTarget);
556  /* turn off anti-aliasing for polygons, since it shows seams
557  with adjacent polygons (usually on PCB ground planes) */
558  cairo_antialias_t oldAlias = cairo_get_antialias (cairoTarget);
559  cairo_set_antialias (cairoTarget, CAIRO_ANTIALIAS_NONE);
560  draw_fill (cairoTarget, drawMode, selectionInfo, image, polygonStartNet);
561  cairo_set_antialias (cairoTarget, oldAlias);
562  return;
563  default :
564  break;
565  }
566  }
567 }
568 
569 
570 
571 int
572 draw_image_to_cairo_target (cairo_t *cairoTarget, gerbv_image_t *image,
573  gdouble pixelWidth,
574  gchar drawMode, gerbv_selection_info_t *selectionInfo,
575  gerbv_render_info_t *renderInfo, gboolean allowOptimization,
576  gerbv_user_transformation_t transform, gboolean pixelOutput){
577  struct gerbv_net *net, *polygonStartNet=NULL;
578  double x1, y1, x2, y2, cp_x=0, cp_y=0;
579  gdouble p0, p1, p2, p3, p4, dx, dy, lineWidth;
580  gerbv_netstate_t *oldState;
581  gerbv_layer_t *oldLayer;
582  cairo_operator_t drawOperatorClear, drawOperatorDark;
583  gboolean invertPolarity = FALSE, oddWidth = FALSE;
584  gdouble minX=0, minY=0, maxX=0, maxY=0;
585  gdouble criticalRadius;
586  gdouble scaleX = transform.scaleX;
587  gdouble scaleY = transform.scaleY;
588  gboolean limitLineWidth = TRUE;
589  gboolean displayPixel = TRUE;
590  // if we are scaling the image at all, ignore the line width checks since scaled up
591  // lines can still be visible
592  if ((scaleX != 1)||(scaleY != 1)){
593  limitLineWidth = FALSE;
594  }
595 
596  if (transform.mirrorAroundX)
597  scaleY *= -1;
598  if (transform.mirrorAroundY)
599  scaleX *= -1;
600  cairo_translate (cairoTarget, transform.translateX, transform.translateY);
601  cairo_scale (cairoTarget, scaleX, scaleY);
602  cairo_rotate (cairoTarget, transform.rotation);
603 
604  gboolean useOptimizations = allowOptimization;
605  // if the user is using any transformations for this layer, then don't bother using rendering
606  // optimizations
607  if ((fabs(transform.translateX) > 0.00001) ||
608  (fabs(transform.translateY) > 0.00001) ||
609  (fabs(transform.scaleX - 1) > 0.00001) ||
610  (fabs(transform.scaleY - 1) > 0.00001) ||
611  (fabs(transform.rotation) > 0.00001) ||
612  transform.mirrorAroundX || transform.mirrorAroundY)
613  useOptimizations = FALSE;
614 
615  if (useOptimizations && pixelOutput) {
616  minX = renderInfo->lowerLeftX;
617  minY = renderInfo->lowerLeftY;
618  maxX = renderInfo->lowerLeftX + (renderInfo->displayWidth /
619  renderInfo->scaleFactorX);
620  maxY = renderInfo->lowerLeftY + (renderInfo->displayHeight /
621  renderInfo->scaleFactorY);
622  }
623 
624  /* do initial justify */
625  cairo_translate (cairoTarget, image->info->imageJustifyOffsetActualA,
626  image->info->imageJustifyOffsetActualB);
627 
628  /* set the fill rule so aperture holes are cleared correctly */
629  cairo_set_fill_rule (cairoTarget, CAIRO_FILL_RULE_EVEN_ODD);
630  /* offset image */
631  cairo_translate (cairoTarget, image->info->offsetA, image->info->offsetB);
632  /* do image rotation */
633  cairo_rotate (cairoTarget, image->info->imageRotation);
634  /* load in polarity operators depending on the image polarity */
635  invertPolarity = transform.inverted;
636  if (image->info->polarity == GERBV_POLARITY_NEGATIVE)
637  invertPolarity = !invertPolarity;
638  if (drawMode == DRAW_SELECTIONS)
639  invertPolarity = FALSE;
640 
641  if (invertPolarity) {
642  drawOperatorClear = CAIRO_OPERATOR_OVER;
643  drawOperatorDark = CAIRO_OPERATOR_CLEAR;
644  cairo_set_operator (cairoTarget, CAIRO_OPERATOR_OVER);
645  cairo_paint (cairoTarget);
646  cairo_set_operator (cairoTarget, CAIRO_OPERATOR_CLEAR);
647  }
648  else {
649  drawOperatorClear = CAIRO_OPERATOR_CLEAR;
650  drawOperatorDark = CAIRO_OPERATOR_OVER;
651  }
652  /* next, push two cairo states to simulate the first layer and netstate
653  translations (these will be popped when another layer or netstate is
654  started */
655 
656  cairo_save (cairoTarget);
657  cairo_save (cairoTarget);
658  /* store the current layer and netstate so we know when they change */
659  oldLayer = image->layers;
660  oldState = image->states;
661 
662  for (net = image->netlist->next ; net != NULL; net = gerbv_image_return_next_renderable_object(net)) {
663 
664  /* check if this is a new layer */
665  if (net->layer != oldLayer){
666  /* it's a new layer, so recalculate the new transformation matrix
667  for it */
668  cairo_restore (cairoTarget);
669  cairo_restore (cairoTarget);
670  cairo_save (cairoTarget);
671  /* do any rotations */
672  cairo_rotate (cairoTarget, net->layer->rotation);
673  /* handle the layer polarity */
674  if ((net->layer->polarity == GERBV_POLARITY_CLEAR)^invertPolarity) {
675  cairo_set_operator (cairoTarget, CAIRO_OPERATOR_CLEAR);
676  drawOperatorClear = CAIRO_OPERATOR_OVER;
677  drawOperatorDark = CAIRO_OPERATOR_CLEAR;
678  }
679  else {
680  cairo_set_operator (cairoTarget, CAIRO_OPERATOR_OVER);
681  drawOperatorClear = CAIRO_OPERATOR_CLEAR;
682  drawOperatorDark = CAIRO_OPERATOR_OVER;
683  }
684  /* draw any knockout areas */
685  if (net->layer->knockout.firstInstance == TRUE) {
686  cairo_operator_t oldOperator = cairo_get_operator (cairoTarget);
687  if (net->layer->knockout.polarity == GERBV_POLARITY_CLEAR) {
688  cairo_set_operator (cairoTarget, drawOperatorClear);
689  }
690  else {
691  cairo_set_operator (cairoTarget, drawOperatorDark);
692  }
693  cairo_new_path (cairoTarget);
694  cairo_rectangle (cairoTarget, net->layer->knockout.lowerLeftX - net->layer->knockout.border,
695  net->layer->knockout.lowerLeftY - net->layer->knockout.border,
696  net->layer->knockout.width + (net->layer->knockout.border*2),
697  net->layer->knockout.height + (net->layer->knockout.border*2));
698  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
699  cairo_set_operator (cairoTarget, oldOperator);
700  }
701  /* finally, reapply old netstate transformation */
702  cairo_save (cairoTarget);
703  draw_apply_netstate_transformation (cairoTarget, net->state);
704  oldLayer = net->layer;
705  }
706  /* check if this is a new netstate */
707  if (net->state != oldState){
708  /* pop the transformation matrix back to the "pre-state" state and
709  resave it */
710  cairo_restore (cairoTarget);
711  cairo_save (cairoTarget);
712  /* it's a new state, so recalculate the new transformation matrix
713  for it */
714  draw_apply_netstate_transformation (cairoTarget, net->state);
715  oldState = net->state;
716  }
717  /* if we are only drawing from the selection buffer, search if this net is
718  in the buffer */
719  if (drawMode == DRAW_SELECTIONS) {
720  /* this flag makes sure we don't draw any unintentional polygons...
721  if we've successfully entered a polygon (the first net matches, and
722  we don't want to check the nets inside the polygon) then
723  polygonStartNet will be set */
724  if (!polygonStartNet) {
725  if (!draw_net_in_selection_buffer(net, selectionInfo))
726  continue;
727  }
728  }
729 
730  /* step and repeat */
731  int ix, iy;
732  for (ix = 0; ix < net->layer->stepAndRepeat.X; ix++) {
733  for (iy = 0; iy < net->layer->stepAndRepeat.Y; iy++) {
734  double sr_x = ix * net->layer->stepAndRepeat.dist_X;
735  double sr_y = iy * net->layer->stepAndRepeat.dist_Y;
736 
737  if ((useOptimizations && pixelOutput) &&
738  ((net->boundingBox.right+sr_x < minX)
739  || (net->boundingBox.left+sr_x > maxX)
740  || (net->boundingBox.top+sr_y < minY)
741  || (net->boundingBox.bottom+sr_y > maxY))) {
742  continue;
743  }
744 
745  x1 = net->start_x + sr_x;
746  y1 = net->start_y + sr_y;
747  x2 = net->stop_x + sr_x;
748  y2 = net->stop_y + sr_y;
749 
750  /* translate circular x,y data as well */
751  if (net->cirseg) {
752  cp_x = net->cirseg->cp_x + sr_x;
753  cp_y = net->cirseg->cp_y + sr_y;
754  }
755 
756  /* render any labels attached to this net */
757  /* NOTE: this is currently only used on PNP files, so we may
758  make some assumptions here... */
759  if (net->label) {
760  cairo_set_font_size (cairoTarget, 0.05);
761  cairo_save (cairoTarget);
762 
763  cairo_move_to (cairoTarget, x1, y1);
764  cairo_scale (cairoTarget, 1, -1);
765  cairo_show_text (cairoTarget, net->label->str);
766  cairo_restore (cairoTarget);
767  }
768  /*
769  * Polygon Area Fill routines
770  */
771  switch (net->interpolation) {
773  draw_render_polygon_object (net, cairoTarget, sr_x, sr_y, image,
774  drawMode, selectionInfo, pixelOutput);
775  continue;
777  continue;
778  default :
779  break;
780  }
781 
782  /*
783  * If aperture state is off we allow use of undefined apertures.
784  * This happens when gerber files starts, but hasn't decided on
785  * which aperture to use.
786  */
787  if (image->aperture[net->aperture] == NULL) {
788  /* Commenting this out since it gets emitted every time you click on the screen
789  if (net->aperture_state != GERBV_APERTURE_STATE_OFF)
790  GERB_MESSAGE("Aperture D%d is not defined", net->aperture);
791  */
792  continue;
793  }
794  switch (net->aperture_state) {
796  /* if the aperture width is truly 0, then render as a 1 pixel width
797  line. 0 diameter apertures are used by some programs to draw labels,
798  etc, and they are rendered by other programs as 1 pixel wide */
799  /* NOTE: also, make sure all lines are at least 1 pixel wide, so they
800  always show up at low zoom levels */
801 
802  if (limitLineWidth&&((image->aperture[net->aperture]->parameter[0] < pixelWidth)&&
803  (pixelOutput)))
804  criticalRadius = pixelWidth/2.0;
805  else
806  criticalRadius = image->aperture[net->aperture]->parameter[0]/2.0;
807  lineWidth = criticalRadius*2.0;
808  // convert to a pixel integer
809  cairo_user_to_device_distance (cairoTarget, &lineWidth, &x1);
810  if (pixelOutput) {
811  lineWidth = round(lineWidth);
812  if ((int)lineWidth % 2) {
813  oddWidth = TRUE;
814  }
815  else {
816  oddWidth = FALSE;
817  }
818  }
819  cairo_device_to_user_distance (cairoTarget, &lineWidth, &x1);
820  cairo_set_line_width (cairoTarget, lineWidth);
821  switch (net->interpolation) {
826  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
827  // weed out any lines that are obviously not going to render on the
828  // visible screen
829 
830  switch (image->aperture[net->aperture]->type) {
831  case GERBV_APTYPE_CIRCLE :
832  draw_cairo_move_to (cairoTarget, x1, y1, oddWidth, pixelOutput);
833  draw_cairo_line_to (cairoTarget, x2, y2, oddWidth, pixelOutput);
834  draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
835  break;
836  case GERBV_APTYPE_RECTANGLE :
837  dx = image->aperture[net->aperture]->parameter[0]/2;
838  dy = image->aperture[net->aperture]->parameter[1]/2;
839  if(x1 > x2)
840  dx = -dx;
841  if(y1 > y2)
842  dy = -dy;
843  cairo_new_path(cairoTarget);
844  draw_cairo_move_to (cairoTarget, x1 - dx, y1 - dy, FALSE, pixelOutput);
845  draw_cairo_line_to (cairoTarget, x1 - dx, y1 + dy, FALSE, pixelOutput);
846  draw_cairo_line_to (cairoTarget, x2 - dx, y2 + dy, FALSE, pixelOutput);
847  draw_cairo_line_to (cairoTarget, x2 + dx, y2 + dy, FALSE, pixelOutput);
848  draw_cairo_line_to (cairoTarget, x2 + dx, y2 - dy, FALSE, pixelOutput);
849  draw_cairo_line_to (cairoTarget, x1 + dx, y1 - dy, FALSE, pixelOutput);
850  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
851  break;
852  /* for now, just render ovals or polygons like a circle */
853  case GERBV_APTYPE_OVAL :
854  case GERBV_APTYPE_POLYGON :
855  draw_cairo_move_to (cairoTarget, x1,y1, oddWidth, pixelOutput);
856  draw_cairo_line_to (cairoTarget, x2,y2, oddWidth, pixelOutput);
857  draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
858  break;
859  /* macros can only be flashed, so ignore any that might be here */
860  default :
861  break;
862  }
863  break;
866  /* cairo doesn't have a function to draw oval arcs, so we must
867  * draw an arc and stretch it by scaling different x and y values
868  */
869  cairo_new_path(cairoTarget);
870  if (image->aperture[net->aperture]->type == GERBV_APTYPE_RECTANGLE) {
871  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_SQUARE);
872  }
873  else {
874  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
875  }
876  cairo_save (cairoTarget);
877  cairo_translate(cairoTarget, cp_x, cp_y);
878  cairo_scale (cairoTarget, net->cirseg->width, net->cirseg->height);
879  if (net->cirseg->angle2 > net->cirseg->angle1) {
880  cairo_arc (cairoTarget, 0.0, 0.0, 0.5, net->cirseg->angle1 * M_PI/180,
881  net->cirseg->angle2 * M_PI/180);
882  }
883  else {
884  cairo_arc_negative (cairoTarget, 0.0, 0.0, 0.5, net->cirseg->angle1 * M_PI/180,
885  net->cirseg->angle2 * M_PI/180);
886  }
887  cairo_restore (cairoTarget);
888  draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
889  break;
890  default :
891  break;
892  }
893  break;
895  break;
897  p0 = image->aperture[net->aperture]->parameter[0];
898  p1 = image->aperture[net->aperture]->parameter[1];
899  p2 = image->aperture[net->aperture]->parameter[2];
900  p3 = image->aperture[net->aperture]->parameter[3];
901  p4 = image->aperture[net->aperture]->parameter[4];
902 
903  cairo_save (cairoTarget);
904  draw_cairo_translate_adjust(cairoTarget, x2, y2, pixelOutput);
905 
906  switch (image->aperture[net->aperture]->type) {
907  case GERBV_APTYPE_CIRCLE :
908  gerbv_draw_circle(cairoTarget, p0);
909  gerbv_draw_aperature_hole (cairoTarget, p1, p2, pixelOutput);
910  break;
912  // some CAD programs use very thin flashed rectangles to compose
913  // logos/images, so we must make sure those display here
914  displayPixel = pixelOutput;
915  if (limitLineWidth && (p0 < pixelWidth) && pixelOutput) {
916  p0 = pixelWidth;
917  displayPixel = FALSE;
918  }
919  if (limitLineWidth && (p1 < pixelWidth) && pixelOutput) {
920  p1 = pixelWidth;
921  displayPixel = FALSE;
922  }
923  gerbv_draw_rectangle(cairoTarget, p0, p1, displayPixel);
924  gerbv_draw_aperature_hole (cairoTarget, p2, p3, displayPixel);
925  break;
926  case GERBV_APTYPE_OVAL :
927  gerbv_draw_oblong(cairoTarget, p0, p1);
928  gerbv_draw_aperature_hole (cairoTarget, p2, p3, pixelOutput);
929  break;
930  case GERBV_APTYPE_POLYGON :
931  gerbv_draw_polygon(cairoTarget, p0, p1, p2);
932  gerbv_draw_aperature_hole (cairoTarget, p3, p4, pixelOutput);
933  break;
934  case GERBV_APTYPE_MACRO :
935  gerbv_draw_amacro(cairoTarget, drawOperatorClear, drawOperatorDark,
936  image->aperture[net->aperture]->simplified,
937  (gint) image->aperture[net->aperture]->parameter[0], pixelWidth,
938  drawMode, selectionInfo, image, net);
939  break;
940  default :
941  GERB_MESSAGE(_("Unknown aperture type"));
942  return 0;
943  }
944  /* and finally fill the path */
945  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
946  cairo_restore (cairoTarget);
947  break;
948  default:
949  GERB_MESSAGE(_("Unknown aperture state"));
950  return 0;
951  }
952  }
953  }
954  }
955 
956  /* restore the initial two state saves (one for layer, one for netstate)*/
957  cairo_restore (cairoTarget);
958  cairo_restore (cairoTarget);
959 
960  return 1;
961 }
962 
963