gerbv  2.6A
gerber.c
Go to the documentation of this file.
1 /*
2  * gEDA - GNU Electronic Design Automation
3  * This 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 <stdlib.h>
32 #include <string.h>
33 #include <math.h> /* pow() */
34 #include <locale.h>
35 #include <errno.h>
36 #include <ctype.h>
37 
38 #include "common.h"
39 #include "gerb_image.h"
40 #include "gerber.h"
41 #include "gerb_stats.h"
42 #include "amacro.h"
43 
44 /* include this for macro enums */
45 #include "draw-gdk.h"
46 
47 #undef AMACRO_DEBUG
48 #define dprintf if(DEBUG) printf
49 
50 #define A2I(a,b) (((a & 0xff) << 8) + (b & 0xff))
51 
52 #define MAXL 200
53 
54 /* Local function prototypes */
55 static void parse_G_code(gerb_file_t *fd, gerb_state_t *state,
56  gerbv_image_t *image);
57 static void parse_D_code(gerb_file_t *fd, gerb_state_t *state,
58  gerbv_image_t *image);
59 static int parse_M_code(gerb_file_t *fd, gerbv_image_t *image);
60 static void parse_rs274x(gint levelOfRecursion, gerb_file_t *fd,
61  gerbv_image_t *image, gerb_state_t *state,
62  gerbv_net_t *curr_net, gerbv_stats_t *stats,
63  gchar *directoryPath);
64 static int parse_aperture_definition(gerb_file_t *fd,
65  gerbv_aperture_t *aperture,
66  gerbv_image_t *image, gdouble scale);
67 static void calc_cirseg_sq(struct gerbv_net *net, int cw,
68  double delta_cp_x, double delta_cp_y);
69 static void calc_cirseg_mq(struct gerbv_net *net, int cw,
70  double delta_cp_x, double delta_cp_y);
71 
72 
73 static void gerber_update_any_running_knockout_measurements(gerbv_image_t *image);
74 
75 static void gerber_calculate_final_justify_effects (gerbv_image_t *image);
76 
77 gboolean knockoutMeasure = FALSE;
78 gdouble knockoutLimitXmin, knockoutLimitYmin, knockoutLimitXmax,
79  knockoutLimitYmax;
80 gerbv_layer_t *knockoutLayer = NULL;
81 cairo_matrix_t currentMatrix;
82 
83 
84 /* --------------------------------------------------------- */
86 gerber_create_new_net (gerbv_net_t *currentNet, gerbv_layer_t *layer, gerbv_netstate_t *state){
87  gerbv_net_t *newNet = g_new0 (gerbv_net_t, 1);
88 
89  currentNet->next = newNet;
90  if (layer)
91  newNet->layer = layer;
92  else
93  newNet->layer = currentNet->layer;
94  if (state)
95  newNet->state = state;
96  else
97  newNet->state = currentNet->state;
98  return newNet;
99 }
100 
101 /* --------------------------------------------------------- */
102 gboolean
103 gerber_create_new_aperture (gerbv_image_t *image, int *indexNumber,
104  gerbv_aperture_type_t apertureType, gdouble parameter1, gdouble parameter2){
105  int i;
106 
107  /* search for an available aperture spot */
108  for (i = 0; i <= APERTURE_MAX; i++) {
109  if (image->aperture[i] == NULL) {
110  image->aperture[i] = g_new0 (gerbv_aperture_t, 1);
111  image->aperture[i]->type = apertureType;
112  image->aperture[i]->parameter[0] = parameter1;
113  image->aperture[i]->parameter[1] = parameter2;
114  *indexNumber = i;
115  return TRUE;
116  }
117  }
118  return FALSE;
119 }
120 
121 /* --------------------------------------------------------- */
131 gboolean
132 gerber_parse_file_segment (gint levelOfRecursion, gerbv_image_t *image,
133  gerb_state_t *state, gerbv_net_t *curr_net,
134  gerbv_stats_t *stats, gerb_file_t *fd,
135  gchar *directoryPath) {
136  int read, coord, len, polygonPoints=0;
137  double x_scale = 0.0, y_scale = 0.0;
138  double delta_cp_x = 0.0, delta_cp_y = 0.0;
139  double aperture_sizeX, aperture_sizeY;
140  double scale;
141  gboolean foundEOF = FALSE;
142  gchar *string;
143  gerbv_render_size_t boundingBox={HUGE_VAL,-HUGE_VAL,HUGE_VAL,-HUGE_VAL};
144 
145  while ((read = gerb_fgetc(fd)) != EOF) {
146  /* figure out the scale, since we need to normalize
147  all dimensions to inches */
148  if (state->state->unit == GERBV_UNIT_MM)
149  scale = 25.4;
150  else
151  scale = 1.0;
152  switch ((char)(read & 0xff)) {
153  case 'G':
154  dprintf("... Found G code\n");
155  parse_G_code(fd, state, image);
156  break;
157  case 'D':
158  dprintf("... Found D code\n");
159  parse_D_code(fd, state, image);
160  break;
161  case 'M':
162  dprintf("... Found M code\n");
163  switch(parse_M_code(fd, image)) {
164  case 1 :
165  case 2 :
166  case 3 :
167  foundEOF = TRUE;
168  break;
169  default:
170  gerbv_stats_add_error(stats->error_list,
171  -1,
172  _("Unknown M code found"),
174  } /* switch(parse_M_code) */
175  break;
176  case 'X':
177  dprintf("... Found X code\n");
178  stats->X++;
179  coord = gerb_fgetint(fd, &len);
180  if (image->format && image->format->omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
181 
182  switch ((image->format->x_int + image->format->x_dec) - len) {
183  case 7:
184  coord *= 10;
185  case 6:
186  coord *= 10;
187  case 5:
188  coord *= 10;
189  case 4:
190  coord *= 10;
191  case 3:
192  coord *= 10;
193  case 2:
194  coord *= 10;
195  case 1:
196  coord *= 10;
197  break;
198  default:
199  ;
200  }
201  }
202  if (image->format && (image->format->coordinate==GERBV_COORDINATE_INCREMENTAL))
203  state->curr_x += coord;
204  else
205  state->curr_x = coord;
206  state->changed = 1;
207  break;
208  case 'Y':
209  dprintf("... Found Y code\n");
210  stats->Y++;
211  coord = gerb_fgetint(fd, &len);
212  if (image->format && image->format->omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
213 
214  switch ((image->format->y_int + image->format->y_dec) - len) {
215  case 7:
216  coord *= 10;
217  case 6:
218  coord *= 10;
219  case 5:
220  coord *= 10;
221  case 4:
222  coord *= 10;
223  case 3:
224  coord *= 10;
225  case 2:
226  coord *= 10;
227  case 1:
228  coord *= 10;
229  break;
230  default:
231  ;
232  }
233  }
234  if (image->format && (image->format->coordinate==GERBV_COORDINATE_INCREMENTAL))
235  state->curr_y += coord;
236  else
237  state->curr_y = coord;
238  state->changed = 1;
239  break;
240  case 'I':
241  dprintf("... Found I code\n");
242  stats->I++;
243  coord = gerb_fgetint(fd, &len);
244  if (image->format && image->format->omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
245 
246  switch ((image->format->y_int + image->format->y_dec) - len) {
247  case 7:
248  coord *= 10;
249  case 6:
250  coord *= 10;
251  case 5:
252  coord *= 10;
253  case 4:
254  coord *= 10;
255  case 3:
256  coord *= 10;
257  case 2:
258  coord *= 10;
259  case 1:
260  coord *= 10;
261  break;
262  default:
263  ;
264  }
265  }
266  state->delta_cp_x = coord;
267  state->changed = 1;
268  break;
269  case 'J':
270  dprintf("... Found J code\n");
271  stats->J++;
272  coord = gerb_fgetint(fd, &len);
273  if (image->format && image->format->omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
274 
275  switch ((image->format->y_int + image->format->y_dec) - len) {
276  case 7:
277  coord *= 10;
278  case 6:
279  coord *= 10;
280  case 5:
281  coord *= 10;
282  case 4:
283  coord *= 10;
284  case 3:
285  coord *= 10;
286  case 2:
287  coord *= 10;
288  case 1:
289  coord *= 10;
290  break;
291  default:
292  ;
293  }
294  }
295  state->delta_cp_y = coord;
296  state->changed = 1;
297  break;
298  case '%':
299  dprintf("... Found %% code\n");
300  while (1) {
301  parse_rs274x(levelOfRecursion, fd, image, state, curr_net, stats, directoryPath);
302  /* advance past any whitespace here */
303  int c = gerb_fgetc(fd);
304  while ((c == '\n')||(c == '\r')||(c == ' ')||(c == '\t')||(c == 0))
305  c = gerb_fgetc(fd);
306  if(c == EOF || c == '%')
307  break;
308  // loop again to catch multiple blocks on the same line (separated by * char)
309  fd->ptr--;
310  }
311  break;
312  case '*':
313  dprintf("... Found * code\n");
314  stats->star++;
315  if (state->changed == 0) break;
316  state->changed = 0;
317 
318  /* don't even bother saving the net if the aperture state is GERBV_APERTURE_STATE_OFF and we
319  aren't starting a polygon fill (where we need it to get to the start point) */
320  if ((state->aperture_state == GERBV_APERTURE_STATE_OFF)&&(!state->in_parea_fill)&&
321  (state->interpolation != GERBV_INTERPOLATION_PAREA_START)) {
322  /* save the coordinate so the next net can use it for a start point */
323  state->prev_x = state->curr_x;
324  state->prev_y = state->curr_y;
325  break;
326  }
327  curr_net = gerber_create_new_net (curr_net, state->layer, state->state);
328  /*
329  * Scale to given coordinate format
330  * XXX only "omit leading zeros".
331  */
332  if (image && image->format ){
333  x_scale = pow(10.0, (double)image->format->x_dec);
334  y_scale = pow(10.0, (double)image->format->y_dec);
335  }
336  x_scale *= scale;
337  y_scale *= scale;
338  curr_net->start_x = (double)state->prev_x / x_scale;
339  curr_net->start_y = (double)state->prev_y / y_scale;
340  curr_net->stop_x = (double)state->curr_x / x_scale;
341  curr_net->stop_y = (double)state->curr_y / y_scale;
342  delta_cp_x = (double)state->delta_cp_x / x_scale;
343  delta_cp_y = (double)state->delta_cp_y / y_scale;
344  switch (state->interpolation) {
346  curr_net->cirseg = g_new0 (gerbv_cirseg_t,1);
347  if (state->mq_on)
348  calc_cirseg_mq(curr_net, 1, delta_cp_x, delta_cp_y);
349  else
350  calc_cirseg_sq(curr_net, 1, delta_cp_x, delta_cp_y);
351  break;
353  curr_net->cirseg = g_new0 (gerbv_cirseg_t,1);
354  if (state->mq_on)
355  calc_cirseg_mq(curr_net, 0, delta_cp_x, delta_cp_y);
356  else
357  calc_cirseg_sq(curr_net, 0, delta_cp_x, delta_cp_y);
358  break;
360  /*
361  * To be able to get back and fill in number of polygon corners
362  */
363  state->parea_start_node = curr_net;
364  state->in_parea_fill = 1;
365  polygonPoints = 0;
366  /* reset the bounding box */
367  boundingBox.left = HUGE_VAL;
368  boundingBox.right = -HUGE_VAL;
369  boundingBox.top = -HUGE_VAL;
370  boundingBox.bottom = HUGE_VAL;
371  break;
373  /* save the calculated bounding box to the master node */
374  state->parea_start_node->boundingBox = boundingBox;
375  /* close out the polygon */
376  state->parea_start_node = NULL;
377  state->in_parea_fill = 0;
378  polygonPoints = 0;
379  break;
380  default :
381  break;
382  } /* switch(state->interpolation) */
383 
384  /*
385  * Count number of points in Polygon Area
386  */
387  if (state->in_parea_fill && state->parea_start_node) {
388  /*
389  * "...all lines drawn with D01 are considered edges of the
390  * polygon. D02 closes and fills the polygon."
391  * p.49 rs274xrevd_e.pdf
392  * D02 -> state->aperture_state == GERBV_APERTURE_STATE_OFF
393  */
394 
395  /* UPDATE: only end the polygon during a D02 call if we've already
396  drawn a polygon edge (with D01) */
397 
398  if ((state->aperture_state == GERBV_APERTURE_STATE_OFF &&
399  state->interpolation != GERBV_INTERPOLATION_PAREA_START) && (polygonPoints > 0)) {
401  curr_net = gerber_create_new_net (curr_net, state->layer, state->state);
403  state->parea_start_node->boundingBox = boundingBox;
404  state->parea_start_node = curr_net;
405  polygonPoints = 0;
406  curr_net = gerber_create_new_net (curr_net, state->layer, state->state);
407  curr_net->start_x = (double)state->prev_x / x_scale;
408  curr_net->start_y = (double)state->prev_y / y_scale;
409  curr_net->stop_x = (double)state->curr_x / x_scale;
410  curr_net->stop_y = (double)state->curr_y / y_scale;
411  /* reset the bounding box */
412  boundingBox.left = HUGE_VAL;
413  boundingBox.right = -HUGE_VAL;
414  boundingBox.top = -HUGE_VAL;
415  boundingBox.bottom = HUGE_VAL;
416  }
417  else if (state->interpolation != GERBV_INTERPOLATION_PAREA_START)
418  polygonPoints++;
419 
420  } /* if (state->in_parea_fill && state->parea_start_node) */
421 
422  curr_net->interpolation = state->interpolation;
423 
424  /*
425  * Override circular interpolation if no center was given.
426  * This should be a safe hack, since a good file should always
427  * include I or J. And even if the radius is zero, the endpoint
428  * should be the same as the start point, creating no line
429  */
430  if (((state->interpolation == GERBV_INTERPOLATION_CW_CIRCULAR) ||
431  (state->interpolation == GERBV_INTERPOLATION_CCW_CIRCULAR)) &&
432  ((state->delta_cp_x == 0.0) && (state->delta_cp_y == 0.0)))
434 
435  /*
436  * If we detected the end of Polygon Area Fill we go back to
437  * the interpolation we had before that.
438  * Also if we detected any of the quadrant flags, since some
439  * gerbers don't reset the interpolation (EagleCad again).
440  */
441  if ((state->interpolation == GERBV_INTERPOLATION_PAREA_START) ||
442  (state->interpolation == GERBV_INTERPOLATION_PAREA_END))
443  state->interpolation = state->prev_interpolation;
444 
445  /*
446  * Save layer polarity and unit
447  */
448  curr_net->layer = state->layer;
449 
450  state->delta_cp_x = 0.0;
451  state->delta_cp_y = 0.0;
452  curr_net->aperture = state->curr_aperture;
453  curr_net->aperture_state = state->aperture_state;
454 
455  /*
456  * For next round we save the current position as
457  * the previous position
458  */
459  state->prev_x = state->curr_x;
460  state->prev_y = state->curr_y;
461 
462  /*
463  * If we have an aperture defined at the moment we find
464  * min and max of image with compensation for mm.
465  */
466  if ((curr_net->aperture == 0) && !state->in_parea_fill)
467  break;
468 
469  /* only update the min/max values and aperture stats if we are drawing */
470  if ((curr_net->aperture_state != GERBV_APERTURE_STATE_OFF)&&
472  double repeat_off_X = 0.0, repeat_off_Y = 0.0;
473 
474  /* Update stats with current aperture number if not in polygon */
475  if (!state->in_parea_fill) {
476  dprintf(" In parse_D_code, adding 1 to D_list ...\n");
477  int retcode = gerbv_stats_increment_D_list_count(stats->D_code_list,
478  curr_net->aperture,
479  1,
480  stats->error_list);
481  if (retcode == -1) {
482  string = g_strdup_printf(_("Found undefined D code D%d in file \"%s\""),
483  curr_net->aperture,
484  fd->filename);
485  gerbv_stats_add_error(stats->error_list,
486  -1,
487  string,
489  g_free(string);
490  stats->D_unknown++;
491  }
492  }
493 
494  /*
495  * If step_and_repeat (%SR%) is used, check min_x,max_y etc for
496  * the ends of the step_and_repeat lattice. This goes wrong in
497  * the case of negative dist_X or dist_Y, in which case we
498  * should compare against the startpoints of the lines, not
499  * the stoppoints, but that seems an uncommon case (and the
500  * error isn't very big any way).
501  */
502  repeat_off_X = (state->layer->stepAndRepeat.X - 1) *
503  state->layer->stepAndRepeat.dist_X;
504  repeat_off_Y = (state->layer->stepAndRepeat.Y - 1) *
505  state->layer->stepAndRepeat.dist_Y;
506 
507  cairo_matrix_init (&currentMatrix, 1, 0, 0, 1, 0, 0);
508  /* offset image */
509  cairo_matrix_translate (&currentMatrix, image->info->offsetA,
510  image->info->offsetB);
511  /* do image rotation */
512  cairo_matrix_rotate (&currentMatrix, image->info->imageRotation);
513  /* it's a new layer, so recalculate the new transformation
514  * matrix for it */
515  /* do any rotations */
516  cairo_matrix_rotate (&currentMatrix, state->layer->rotation);
517 
518  /* calculate current layer and state transformation matrices */
519  /* apply scale factor */
520  cairo_matrix_scale (&currentMatrix, state->state->scaleA,
521  state->state->scaleB);
522  /* apply offset */
523  cairo_matrix_translate (&currentMatrix, state->state->offsetA,
524  state->state->offsetB);
525  /* apply mirror */
526  switch (state->state->mirrorState) {
527  case GERBV_MIRROR_STATE_FLIPA:
528  cairo_matrix_scale (&currentMatrix, -1, 1);
529  break;
530  case GERBV_MIRROR_STATE_FLIPB:
531  cairo_matrix_scale (&currentMatrix, 1, -1);
532  break;
533  case GERBV_MIRROR_STATE_FLIPAB:
534  cairo_matrix_scale (&currentMatrix, -1, -1);
535  break;
536  default:
537  break;
538  }
539  /* finally, apply axis select */
540  if (state->state->axisSelect == GERBV_AXIS_SELECT_SWAPAB) {
541  /* we do this by rotating 270 (counterclockwise, then
542  * mirroring the Y axis
543  */
544  cairo_matrix_rotate (&currentMatrix, 3 * M_PI / 2);
545  cairo_matrix_scale (&currentMatrix, 1, -1);
546  }
547  /* if it's a macro, step through all the primitive components
548  and calculate the true bounding box */
549  if ((image->aperture[curr_net->aperture] != NULL) &&
550  (image->aperture[curr_net->aperture]->type == GERBV_APTYPE_MACRO)) {
551  gerbv_simplified_amacro_t *ls = image->aperture[curr_net->aperture]->simplified;
552 
553  while (ls != NULL) {
554  gdouble offsetx = 0, offsety = 0, widthx = 0, widthy = 0;
555  gboolean calculatedAlready = FALSE;
556 
557  if (ls->type == GERBV_APTYPE_MACRO_CIRCLE) {
558  offsetx=ls->parameter[CIRCLE_CENTER_X];
559  offsety=ls->parameter[CIRCLE_CENTER_Y];
560  widthx=widthy=ls->parameter[CIRCLE_DIAMETER];
561  } else if (ls->type == GERBV_APTYPE_MACRO_OUTLINE) {
562  int pointCounter,numberOfPoints;
563  numberOfPoints = (int) ls->parameter[OUTLINE_NUMBER_OF_POINTS];
564 
565  for (pointCounter = 0; pointCounter <= numberOfPoints; pointCounter++) {
566  gerber_update_min_and_max (&boundingBox,
567  curr_net->stop_x +
568  ls->parameter[pointCounter * 2 + OUTLINE_FIRST_X],
569  curr_net->stop_y +
570  ls->parameter[pointCounter * 2 + OUTLINE_FIRST_Y],
571  0,0,0,0);
572  }
573  calculatedAlready = TRUE;
574  } else if (ls->type == GERBV_APTYPE_MACRO_POLYGON) {
575  offsetx = ls->parameter[POLYGON_CENTER_X];
576  offsety = ls->parameter[POLYGON_CENTER_Y];
577  widthx = widthy = ls->parameter[POLYGON_DIAMETER];
578  } else if (ls->type == GERBV_APTYPE_MACRO_MOIRE) {
579  offsetx = ls->parameter[MOIRE_CENTER_X];
580  offsety = ls->parameter[MOIRE_CENTER_Y];
581  widthx = widthy = ls->parameter[MOIRE_OUTSIDE_DIAMETER];
582  } else if (ls->type == GERBV_APTYPE_MACRO_THERMAL) {
583  offsetx = ls->parameter[THERMAL_CENTER_X];
584  offsety = ls->parameter[THERMAL_CENTER_Y];
585  widthx = widthy = ls->parameter[THERMAL_OUTSIDE_DIAMETER];
586  } else if (ls->type == GERBV_APTYPE_MACRO_LINE20) {
587  widthx = widthy = ls->parameter[LINE20_LINE_WIDTH];
588  gerber_update_min_and_max (&boundingBox,
589  curr_net->stop_x +
590  ls->parameter[LINE20_START_X],
591  curr_net->stop_y +
592  ls->parameter[LINE20_START_Y],
593  widthx/2,widthx/2,widthy/2,widthy/2);
594  gerber_update_min_and_max (&boundingBox,
595  curr_net->stop_x +
596  ls->parameter[LINE20_END_X],
597  curr_net->stop_y +
598  ls->parameter[LINE20_END_Y],
599  widthx/2,widthx/2,widthy/2,widthy/2);
600  calculatedAlready = TRUE;
601  } else if (ls->type == GERBV_APTYPE_MACRO_LINE21) {
602  gdouble largestDimension = sqrt (ls->parameter[LINE21_WIDTH]/2 *
603  ls->parameter[LINE21_WIDTH]/2 + ls->parameter[LINE21_HEIGHT/2] *
604  ls->parameter[LINE21_HEIGHT]/2);
605 
606  offsetx = ls->parameter[LINE21_CENTER_X];
607  offsety = ls->parameter[LINE21_CENTER_Y];
608  widthx = widthy=largestDimension;
609  } else if (ls->type == GERBV_APTYPE_MACRO_LINE22) {
610  gdouble largestDimension = sqrt (ls->parameter[LINE22_WIDTH]/2 *
611  ls->parameter[LINE22_WIDTH]/2 + ls->parameter[LINE22_HEIGHT/2] *
612  ls->parameter[LINE22_HEIGHT]/2);
613 
614  offsetx = ls->parameter[LINE22_LOWER_LEFT_X] +
615  ls->parameter[LINE22_WIDTH]/2;
616  offsety = ls->parameter[LINE22_LOWER_LEFT_Y] +
617  ls->parameter[LINE22_HEIGHT]/2;
618  widthx = widthy=largestDimension;
619  }
620 
621  if (!calculatedAlready) {
622  gerber_update_min_and_max (&boundingBox,
623  curr_net->stop_x + offsetx,
624  curr_net->stop_y + offsety,
625  widthx/2,widthx/2,widthy/2,widthy/2);
626  }
627  ls = ls->next;
628  }
629  } else {
630  if (image->aperture[curr_net->aperture] != NULL) {
631  aperture_sizeX = image->aperture[curr_net->aperture]->parameter[0];
632  if ((image->aperture[curr_net->aperture]->type == GERBV_APTYPE_RECTANGLE) || (image->aperture[curr_net->aperture]->type == GERBV_APTYPE_OVAL)) {
633  aperture_sizeY = image->aperture[curr_net->aperture]->parameter[1];
634  }
635  else
636  aperture_sizeY = aperture_sizeX;
637  } else {
638  /* this is usually for polygon fills, where the aperture width
639  is "zero" */
640  aperture_sizeX = aperture_sizeY = 0;
641  }
642  /* if it's an arc path, use a special calc */
643  if ((curr_net->interpolation == GERBV_INTERPOLATION_CW_CIRCULAR) ||
645  /* to calculate the arc bounding box, we chop it into 1 degree steps, calculate
646  the point at each step, and use it to figure out the bounding box */
647  gdouble angleDiff = curr_net->cirseg->angle2 - curr_net->cirseg->angle1;
648  gint i, steps = abs(angleDiff);
649  for (i=0; i<=steps; i++){
650  gdouble tempX = curr_net->cirseg->cp_x + curr_net->cirseg->width / 2.0 *
651  cos ((curr_net->cirseg->angle1 +
652  (angleDiff * i) / steps)*M_PI/180);
653  gdouble tempY = curr_net->cirseg->cp_y + curr_net->cirseg->width / 2.0 *
654  sin ((curr_net->cirseg->angle1 +
655  (angleDiff * i) / steps)*M_PI/180);
656  gerber_update_min_and_max (&boundingBox,
657  tempX, tempY,
658  aperture_sizeX/2,aperture_sizeX/2,
659  aperture_sizeY/2,aperture_sizeY/2);
660  }
661 
662  }
663  else {
664  /* check both the start and stop of the aperture points against
665  a running min/max counter */
666  /* Note: only check start coordinate if this isn't a flash,
667  since the start point may be bogus if it is a flash */
668  if (curr_net->aperture_state != GERBV_APERTURE_STATE_FLASH) {
669  gerber_update_min_and_max (&boundingBox,
670  curr_net->start_x, curr_net->start_y,
671  aperture_sizeX/2,aperture_sizeX/2,
672  aperture_sizeY/2,aperture_sizeY/2);
673  }
674  gerber_update_min_and_max (&boundingBox,
675  curr_net->stop_x, curr_net->stop_y,
676  aperture_sizeX/2,aperture_sizeX/2,
677  aperture_sizeY/2,aperture_sizeY/2);
678  }
679 
680  }
681  /* update the info bounding box with this latest bounding box */
682  /* don't change the bounding box if the polarity is clear */
683  if (state->layer->polarity != GERBV_POLARITY_CLEAR){
684  gerber_update_image_min_max(&boundingBox, repeat_off_X, repeat_off_Y, image);
685  }
686  /* optionally update the knockout measurement box */
687  if (knockoutMeasure) {
688  if (boundingBox.left < knockoutLimitXmin)
689  knockoutLimitXmin = boundingBox.left;
690  if (boundingBox.right+repeat_off_X > knockoutLimitXmax)
691  knockoutLimitXmax = boundingBox.right+repeat_off_X;
692  if (boundingBox.bottom < knockoutLimitYmin)
693  knockoutLimitYmin = boundingBox.bottom;
694  if (boundingBox.top+repeat_off_Y > knockoutLimitYmax)
695  knockoutLimitYmax = boundingBox.top+repeat_off_Y;
696  }
697  /* if we're not in a polygon fill, then update the object bounding box */
698  if (!state->in_parea_fill) {
699  curr_net->boundingBox = boundingBox;
700  /* and reset the bounding box */
701  boundingBox.left = HUGE_VAL;
702  boundingBox.right = -HUGE_VAL;
703  boundingBox.bottom = HUGE_VAL;
704  boundingBox.top = -HUGE_VAL;
705  }
706  }
707  break;
708  case 10 : /* White space */
709  case 13 :
710  case ' ' :
711  case '\t' :
712  case 0 :
713  break;
714  default:
715  stats->unknown++;
716  string = g_strdup_printf(_("Found unknown character (whitespace?) [%d]%c"),
717  read, read);
718  gerbv_stats_add_error(stats->error_list,
719  -1,
720  string,
722  g_free(string);
723  } /* switch((char) (read & 0xff)) */
724  }
725  return foundEOF;
726 }
727 
728 
729 /* ------------------------------------------------------------------ */
737 parse_gerb(gerb_file_t *fd, gchar *directoryPath)
738 {
739  gerb_state_t *state = NULL;
740  gerbv_image_t *image = NULL;
741  gerbv_net_t *curr_net = NULL;
742  gerbv_stats_t *stats;
743  gboolean foundEOF = FALSE;
744  gchar *string;
745 
746  /* added by t.motylewski@bfad.de
747  * many locales redefine "." as "," and so on,
748  * so sscanf and strtod has problems when
749  * reading files using %f format */
750  setlocale(LC_NUMERIC, "C" );
751 
752  /*
753  * Create new state. This is used locally to keep track
754  * of the photoplotter's state as the Gerber is read in.
755  */
756  state = g_new0 (gerb_state_t, 1);
757 
758  /*
759  * Create new image. This will be returned.
760  */
761  image = gerbv_create_image(image, "RS274-X (Gerber) File");
762  if (image == NULL)
763  GERB_FATAL_ERROR(_("malloc image failed"));
764  curr_net = image->netlist;
766  image->gerbv_stats = gerbv_stats_new();
767  if (image->gerbv_stats == NULL)
768  GERB_FATAL_ERROR(_("malloc gerbv_stats failed"));
769  stats = (gerbv_stats_t *) image->gerbv_stats;
770 
771  /* set active layer and netstate to point to first default one created */
772  state->layer = image->layers;
773  state->state = image->states;
774  curr_net->layer = state->layer;
775  curr_net->state = state->state;
776 
777  /*
778  * Start parsing
779  */
780  dprintf("In parse_gerb, starting to parse file...\n");
781  foundEOF = gerber_parse_file_segment (1, image, state, curr_net, stats,
782  fd, directoryPath);
783 
784  if (!foundEOF) {
785  string = g_strdup_printf(_("Missing Gerber EOF code in file \"%s\""), fd->filename);
786  gerbv_stats_add_error(stats->error_list,
787  -1,
788  string,
790  g_free(string);
791  }
792  g_free(state);
793 
794  dprintf(" ... done parsing Gerber file\n");
795  gerber_update_any_running_knockout_measurements (image);
796  gerber_calculate_final_justify_effects(image);
797 
798  return image;
799 } /* parse_gerb */
800 
801 
802 /* ------------------------------------------------------------------- */
806 gboolean
807 gerber_is_rs274x_p(gerb_file_t *fd, gboolean *returnFoundBinary)
808 {
809  char *buf;
810  int len = 0;
811  char *letter;
812  int i;
813  gboolean found_binary = FALSE;
814  gboolean found_ADD = FALSE;
815  gboolean found_D0 = FALSE;
816  gboolean found_D2 = FALSE;
817  gboolean found_M0 = FALSE;
818  gboolean found_M2 = FALSE;
819  gboolean found_star = FALSE;
820  gboolean found_X = FALSE;
821  gboolean found_Y = FALSE;
822 
823  dprintf ("gerber_is_rs274x_p(%p, %p), fd->fd = %p\n", fd, returnFoundBinary, fd->fd);
824  buf = (char *) g_malloc(MAXL);
825  if (buf == NULL)
826  GERB_FATAL_ERROR(_("malloc buf failed while checking for rs274x"));
827 
828  while (fgets(buf, MAXL, fd->fd) != NULL) {
829  dprintf ("buf = \"%s\"\n", buf);
830  len = strlen(buf);
831 
832  /* First look through the file for indications of its type by
833  * checking that file is not binary (non-printing chars and white
834  * spaces)
835  */
836  for (i = 0; i < len; i++) {
837  if (!isprint((int) buf[i]) && (buf[i] != '\r') &&
838  (buf[i] != '\n') && (buf[i] != '\t')) {
839  found_binary = TRUE;
840  dprintf ("found_binary (%d)\n", buf[i]);
841  }
842  }
843  if (g_strstr_len(buf, len, "%ADD")) {
844  found_ADD = TRUE;
845  dprintf ("found_ADD\n");
846  }
847  if (g_strstr_len(buf, len, "D00") || g_strstr_len(buf, len, "D0")) {
848  found_D0 = TRUE;
849  dprintf ("found_D0\n");
850  }
851  if (g_strstr_len(buf, len, "D02") || g_strstr_len(buf, len, "D2")) {
852  found_D2 = TRUE;
853  dprintf ("found_D2\n");
854  }
855  if (g_strstr_len(buf, len, "M00") || g_strstr_len(buf, len, "M0")) {
856  found_M0 = TRUE;
857  dprintf ("found_M0\n");
858  }
859  if (g_strstr_len(buf, len, "M02") || g_strstr_len(buf, len, "M2")) {
860  found_M2 = TRUE;
861  dprintf ("found_M2\n");
862  }
863  if (g_strstr_len(buf, len, "*")) {
864  found_star = TRUE;
865  dprintf ("found_star\n");
866  }
867  /* look for X<number> or Y<number> */
868  if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
869  if (isdigit((int) letter[1])) { /* grab char after X */
870  found_X = TRUE;
871  dprintf ("found_X\n");
872  }
873  }
874  if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
875  if (isdigit((int) letter[1])) { /* grab char after Y */
876  found_Y = TRUE;
877  dprintf ("found_Y\n");
878  }
879  }
880  }
881  rewind(fd->fd);
882  free(buf);
883 
884  *returnFoundBinary = found_binary;
885 
886  /* Now form logical expression determining if the file is RS-274X */
887  if ((found_D0 || found_D2 || found_M0 || found_M2) &&
888  found_ADD && found_star && (found_X || found_Y))
889  return TRUE;
890 
891 
892  return FALSE;
893 
894 } /* gerber_is_rs274x */
895 
896 
897 /* ------------------------------------------------------------------- */
901 gboolean
902 gerber_is_rs274d_p(gerb_file_t *fd)
903 {
904  char *buf;
905  int len = 0;
906  char *letter;
907  int i;
908  gboolean found_binary = FALSE;
909  gboolean found_ADD = FALSE;
910  gboolean found_D0 = FALSE;
911  gboolean found_D2 = FALSE;
912  gboolean found_M0 = FALSE;
913  gboolean found_M2 = FALSE;
914  gboolean found_star = FALSE;
915  gboolean found_X = FALSE;
916  gboolean found_Y = FALSE;
917 
918  buf = malloc(MAXL);
919  if (buf == NULL)
920  GERB_FATAL_ERROR(_("malloc buf failed while checking for rs274d"));
921 
922  while (fgets(buf, MAXL, fd->fd) != NULL) {
923  len = strlen(buf);
924 
925  /* First look through the file for indications of its type */
926 
927  /* check that file is not binary (non-printing chars */
928  for (i = 0; i < len; i++) {
929  if (!isprint( (int) buf[i]) && (buf[i] != '\r') &&
930  (buf[i] != '\n') && (buf[i] != '\t')) {
931  found_binary = TRUE;
932  }
933  }
934 
935  if (g_strstr_len(buf, len, "%ADD")) {
936  found_ADD = TRUE;
937  }
938  if (g_strstr_len(buf, len, "D00") || g_strstr_len(buf, len, "D0")) {
939  found_D0 = TRUE;
940  }
941  if (g_strstr_len(buf, len, "D02") || g_strstr_len(buf, len, "D2")) {
942  found_D2 = TRUE;
943  }
944  if (g_strstr_len(buf, len, "M00") || g_strstr_len(buf, len, "M0")) {
945  found_M0 = TRUE;
946  }
947  if (g_strstr_len(buf, len, "M02") || g_strstr_len(buf, len, "M2")) {
948  found_M2 = TRUE;
949  }
950  if (g_strstr_len(buf, len, "*")) {
951  found_star = TRUE;
952  }
953  /* look for X<number> or Y<number> */
954  if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
955  /* grab char after X */
956  if (isdigit( (int) letter[1])) {
957  found_X = TRUE;
958  }
959  }
960  if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
961  /* grab char after Y */
962  if (isdigit( (int) letter[1])) {
963  found_Y = TRUE;
964  }
965  }
966  }
967  rewind(fd->fd);
968  free(buf);
969 
970  /* Now form logical expression determining if the file is RS-274D */
971  if ((found_D0 || found_D2 || found_M0 || found_M2) &&
972  !found_ADD && found_star && (found_X || found_Y) &&
973  !found_binary)
974  return TRUE;
975 
976  return FALSE;
977 
978 } /* gerber_is_rs274d */
979 
980 
981 /* ------------------------------------------------------------------- */
985 static void
986 parse_G_code(gerb_file_t *fd, gerb_state_t *state, gerbv_image_t *image)
987 {
988  int op_int;
989  gerbv_format_t *format = image->format;
990  gerbv_stats_t *stats = image->gerbv_stats;
991  int c;
992  gchar *string;
993 
994  op_int=gerb_fgetint(fd, NULL);
995 
996  switch(op_int) {
997  case 0: /* Move */
998  /* Is this doing anything really? */
999  stats->G0++;
1000  break;
1001  case 1: /* Linear Interpolation (1X scale) */
1002  state->interpolation = GERBV_INTERPOLATION_LINEARx1;
1003  stats->G1++;
1004  break;
1005  case 2: /* Clockwise Linear Interpolation */
1006  state->interpolation = GERBV_INTERPOLATION_CW_CIRCULAR;
1007  stats->G2++;
1008  break;
1009  case 3: /* Counter Clockwise Linear Interpolation */
1010  state->interpolation = GERBV_INTERPOLATION_CCW_CIRCULAR;
1011  stats->G3++;
1012  break;
1013  case 4: /* Ignore Data Block */
1014  /* Don't do anything, just read 'til * */
1015  /* SDB asks: Should we look for other codes while reading G04 in case
1016  * user forgot to put * at end of comment block? */
1017  c = gerb_fgetc(fd);
1018  while ((c != EOF) && (c != '*')) {
1019  c = gerb_fgetc(fd);
1020  }
1021  stats->G4++;
1022  break;
1023  case 10: /* Linear Interpolation (10X scale) */
1024  state->interpolation = GERBV_INTERPOLATION_x10;
1025  stats->G10++;
1026  break;
1027  case 11: /* Linear Interpolation (0.1X scale) */
1028  state->interpolation = GERBV_INTERPOLATION_LINEARx01;
1029  stats->G11++;
1030  break;
1031  case 12: /* Linear Interpolation (0.01X scale) */
1032  state->interpolation = GERBV_INTERPOLATION_LINEARx001;
1033  stats->G12++;
1034  break;
1035  case 36: /* Turn on Polygon Area Fill */
1036  state->prev_interpolation = state->interpolation;
1037  state->interpolation = GERBV_INTERPOLATION_PAREA_START;
1038  state->changed = 1;
1039  stats->G36++;
1040  break;
1041  case 37: /* Turn off Polygon Area Fill */
1042  state->interpolation = GERBV_INTERPOLATION_PAREA_END;
1043  state->changed = 1;
1044  stats->G37++;
1045  break;
1046  case 54: /* Tool prepare */
1047  /* XXX Maybe uneccesary??? */
1048  if (gerb_fgetc(fd) == 'D') {
1049  int a = gerb_fgetint(fd, NULL);
1050  if ((a >= 0) && (a <= APERTURE_MAX)) {
1051  state->curr_aperture = a;
1052  } else {
1053  string = g_strdup_printf(_("Found aperture D%d out of bounds while parsing G code in file \"%s\""),
1054  a, fd->filename);
1055  gerbv_stats_add_error(stats->error_list,
1056  -1,
1057  string,
1059  g_free(string);
1060  }
1061  } else {
1062  string = g_strdup_printf(_("Found unexpected code after G54 in file \"%s\""), fd->filename);
1063  gerbv_stats_add_error(stats->error_list,
1064  -1,
1065  string,
1067  g_free(string);
1068  /* Must insert error count here */
1069  }
1070  stats->G54++;
1071  break;
1072  case 55: /* Prepare for flash */
1073  stats->G55++;
1074  break;
1075  case 70: /* Specify inches */
1076  state->state = gerbv_image_return_new_netstate (state->state);
1077  state->state->unit = GERBV_UNIT_INCH;
1078  stats->G70++;
1079  break;
1080  case 71: /* Specify millimeters */
1081  state->state = gerbv_image_return_new_netstate (state->state);
1082  state->state->unit = GERBV_UNIT_MM;
1083  stats->G71++;
1084  break;
1085  case 74: /* Disable 360 circular interpolation */
1086  state->mq_on = 0;
1087  stats->G74++;
1088  break;
1089  case 75: /* Enable 360 circular interpolation */
1090  state->mq_on = 1;
1091  stats->G75++;
1092  break;
1093  case 90: /* Specify absolut format */
1094  if (format) format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1095  stats->G90++;
1096  break;
1097  case 91: /* Specify incremental format */
1098  if (format) format->coordinate = GERBV_COORDINATE_INCREMENTAL;
1099  stats->G91++;
1100  break;
1101  default:
1102  string = g_strdup_printf(_("Encountered unknown G code G%d in file \"%s\""), op_int, fd->filename);
1103  gerbv_stats_add_error(stats->error_list,
1104  -1,
1105  string,
1107  g_free(string);
1108  string = g_strdup_printf(_("Ignorning unknown G code G%d"), op_int);
1109  gerbv_stats_add_error(stats->error_list,
1110  -1,
1111  string,
1113  g_free(string);
1114  stats->G_unknown++;
1115  /* Enter error count here */
1116  break;
1117  }
1118 
1119  return;
1120 } /* parse_G_code */
1121 
1122 
1123 /* ------------------------------------------------------------------ */
1127 static void
1128 parse_D_code(gerb_file_t *fd, gerb_state_t *state, gerbv_image_t *image)
1129 {
1130  int a;
1131  gerbv_stats_t *stats = image->gerbv_stats;
1132  gchar *string;
1133 
1134  a = gerb_fgetint(fd, NULL);
1135  dprintf(" In parse_D_code, found D number = %d ...\n", a);
1136  switch(a) {
1137  case 0 : /* Invalid code */
1138  string = g_strdup_printf(_("Found invalid D00 code in file \"%s\""), fd->filename);
1139  gerbv_stats_add_error(stats->error_list,
1140  -1,
1141  string,
1143  g_free(string);
1144  stats->D_error++;
1145  break;
1146  case 1 : /* Exposure on */
1147  state->aperture_state = GERBV_APERTURE_STATE_ON;
1148  state->changed = 1;
1149  stats->D1++;
1150  break;
1151  case 2 : /* Exposure off */
1152  state->aperture_state = GERBV_APERTURE_STATE_OFF;
1153  state->changed = 1;
1154  stats->D2++;
1155  break;
1156  case 3 : /* Flash aperture */
1157  state->aperture_state = GERBV_APERTURE_STATE_FLASH;
1158  state->changed = 1;
1159  stats->D3++;
1160  break;
1161  default: /* Aperture in use */
1162  if ((a >= 0) && (a <= APERTURE_MAX)) {
1163  state->curr_aperture = a;
1164 
1165  } else {
1166  string = g_strdup_printf(_("Found out of bounds aperture D%d in file \"%s\""),
1167  a, fd->filename);
1168  gerbv_stats_add_error(stats->error_list,
1169  -1,
1170  string,
1172  g_free(string);
1173  stats->D_error++;
1174  }
1175  state->changed = 0;
1176  break;
1177  }
1178 
1179  return;
1180 } /* parse_D_code */
1181 
1182 
1183 /* ------------------------------------------------------------------ */
1184 static int
1185 parse_M_code(gerb_file_t *fd, gerbv_image_t *image)
1186 {
1187  int op_int;
1188  gerbv_stats_t *stats = image->gerbv_stats;
1189  gchar *string;
1190 
1191  op_int=gerb_fgetint(fd, NULL);
1192 
1193  switch (op_int) {
1194  case 0: /* Program stop */
1195  stats->M0++;
1196  return 1;
1197  case 1: /* Optional stop */
1198  stats->M1++;
1199  return 2;
1200  case 2: /* End of program */
1201  stats->M2++;
1202  return 3;
1203  default:
1204  string = g_strdup_printf(_("Encountered unknown M code M%d in file \"%s\""),
1205  op_int, fd->filename);
1206  gerbv_stats_add_error(stats->error_list,
1207  -1,
1208  string,
1210  g_free(string);
1211  string = g_strdup_printf(_("Ignorning unknown M code M%d"), op_int);
1212  gerbv_stats_add_error(stats->error_list,
1213  -1,
1214  string,
1216  g_free(string);
1217  stats->M_unknown++;
1218  }
1219  return 0;
1220 } /* parse_M_code */
1221 
1222 
1223 /* ------------------------------------------------------------------ */
1224 static void
1225 parse_rs274x(gint levelOfRecursion, gerb_file_t *fd, gerbv_image_t *image,
1226  gerb_state_t *state, gerbv_net_t *curr_net, gerbv_stats_t *stats,
1227  gchar *directoryPath)
1228 {
1229  int op[2];
1230  char str[3];
1231  int tmp;
1232  gerbv_aperture_t *a = NULL;
1233  gerbv_amacro_t *tmp_amacro;
1234  int ano;
1235  gdouble scale = 1.0;
1236  gchar *string;
1237 
1238  if (state->state->unit == GERBV_UNIT_MM)
1239  scale = 25.4;
1240 
1241  op[0] = gerb_fgetc(fd);
1242  op[1] = gerb_fgetc(fd);
1243 
1244  if ((op[0] == EOF) || (op[1] == EOF)) {
1245  string = g_strdup_printf(_("Unexpected EOF found in file \"%s\""), fd->filename);
1246  gerbv_stats_add_error(stats->error_list,
1247  -1,
1248  string,
1250  g_free(string);
1251  }
1252 
1253  switch (A2I(op[0], op[1])){
1254 
1255  /*
1256  * Directive parameters
1257  */
1258  case A2I('A','S'): /* Axis Select */
1259  op[0] = gerb_fgetc(fd);
1260  op[1] = gerb_fgetc(fd);
1261  state->state = gerbv_image_return_new_netstate (state->state);
1262 
1263  if ((op[0] == EOF) || (op[1] == EOF)) {
1264  string = g_strdup_printf(_("Unexpected EOF found in file \"%s\""), fd->filename);
1265  gerbv_stats_add_error(stats->error_list,
1266  -1,
1267  string,
1269  g_free(string);
1270  }
1271 
1272  if (((op[0] == 'A') && (op[1] == 'Y')) ||
1273  ((op[0] == 'B') && (op[1] == 'X'))) {
1274  state->state->axisSelect = GERBV_AXIS_SELECT_SWAPAB;
1275  } else {
1276  state->state->axisSelect = GERBV_AXIS_SELECT_NOSELECT;
1277  }
1278 
1279  op[0] = gerb_fgetc(fd);
1280  op[1] = gerb_fgetc(fd);
1281 
1282  if ((op[0] == EOF) || (op[1] == EOF)) {
1283  string = g_strdup_printf(_("Unexpected EOF found in file \"%s\""), fd->filename);
1284  gerbv_stats_add_error(stats->error_list,
1285  -1,
1286  string,
1288  g_free(string);
1289  }
1290 
1291  if (((op[0] == 'A') && (op[1] == 'Y')) ||
1292  ((op[0] == 'B') && (op[1] == 'X'))) {
1293  state->state->axisSelect = GERBV_AXIS_SELECT_SWAPAB;
1294  } else {
1295  state->state->axisSelect = GERBV_AXIS_SELECT_NOSELECT;
1296  }
1297  break;
1298 
1299  case A2I('F','S'): /* Format Statement */
1300  image->format = g_new0 (gerbv_format_t,1);
1301 
1302  switch (gerb_fgetc(fd)) {
1303  case 'L':
1304  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1305  break;
1306  case 'T':
1307  image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1308  break;
1309  case 'D':
1310  image->format->omit_zeros = GERBV_OMIT_ZEROS_EXPLICIT;
1311  break;
1312  default:
1313  string = g_strdup_printf(_("EagleCad bug detected: Undefined handling of zeros in format code in file \"%s\""),
1314  fd->filename);
1315  gerbv_stats_add_error(stats->error_list,
1316  -1,
1317  string,
1319  g_free(string);
1320  string = g_strdup_printf(_("Defaulting to omitting leading zeros"));
1321  gerbv_stats_add_error(stats->error_list,
1322  -1,
1323  string,
1325  g_free(string);
1326  gerb_ungetc(fd);
1327  image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1328  }
1329 
1330  switch (gerb_fgetc(fd)) {
1331  case 'A':
1332  image->format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1333  break;
1334  case 'I':
1335  image->format->coordinate = GERBV_COORDINATE_INCREMENTAL;
1336  break;
1337  default:
1338  string = g_strdup_printf(_("Invalid coordinate type defined in format code in file \"%s\""),
1339  fd->filename);
1340  gerbv_stats_add_error(stats->error_list,
1341  -1,
1342  string,
1344  g_free(string);
1345  string = g_strdup_printf(_("Defaulting to absolute coordinates"));
1346  gerbv_stats_add_error(stats->error_list,
1347  -1,
1348  string,
1350  g_free(string);
1351  image->format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1352  }
1353  op[0] = gerb_fgetc(fd);
1354  while((op[0] != '*')&&(op[0] != EOF)) {
1355  switch (op[0]) {
1356  case 'N':
1357  op[0] = (char)gerb_fgetc(fd);
1358  image->format->lim_seqno = op[0] - '0';
1359  break;
1360  case 'G':
1361  op[0] = (char)gerb_fgetc(fd);
1362  image->format->lim_gf = op[0] - '0';
1363  break;
1364  case 'D':
1365  op[0] = (char)gerb_fgetc(fd);
1366  image->format->lim_pf = op[0] - '0';
1367  break;
1368  case 'M':
1369  op[0] = (char)gerb_fgetc(fd);
1370  image->format->lim_mf = op[0] - '0';
1371  break;
1372  case 'X' :
1373  op[0] = gerb_fgetc(fd);
1374  if ((op[0] < '0') || (op[0] > '6')) {
1375  string = g_strdup_printf(_("Illegal format size %c in file \"%s\""),
1376  (char)op[0], fd->filename);
1377  gerbv_stats_add_error(stats->error_list,
1378  -1,
1379  string,
1381  g_free(string);
1382  }
1383  image->format->x_int = op[0] - '0';
1384  op[0] = gerb_fgetc(fd);
1385  if ((op[0] < '0') || (op[0] > '6')) {
1386  string = g_strdup_printf(_("Illegal format size %c in file \"%s\""),
1387  (char)op[0], fd->filename);
1388  gerbv_stats_add_error(stats->error_list,
1389  -1,
1390  string,
1392  g_free(string);
1393  }
1394  image->format->x_dec = op[0] - '0';
1395  break;
1396  case 'Y':
1397  op[0] = gerb_fgetc(fd);
1398  if ((op[0] < '0') || (op[0] > '6')) {
1399  string = g_strdup_printf(_("Illegal format size %c in file \"%s\""),
1400  (char)op[0], fd->filename);
1401  gerbv_stats_add_error(stats->error_list,
1402  -1,
1403  string,
1405  g_free(string);
1406  }
1407  image->format->y_int = op[0] - '0';
1408  op[0] = gerb_fgetc(fd);
1409  if ((op[0] < '0') || (op[0] > '6')) {
1410  string = g_strdup_printf(_("Illegal format size %c in file \"%s\""),
1411  (char)op[0], fd->filename);
1412  gerbv_stats_add_error(stats->error_list,
1413  -1,
1414  string,
1416  g_free(string);
1417  }
1418  image->format->y_dec = op[0] - '0';
1419  break;
1420  default :
1421  string = g_strdup_printf(_("Illegal format statement [%c] in file \"%s\""),
1422  op[0], fd->filename);
1423  gerbv_stats_add_error(stats->error_list,
1424  -1,
1425  string,
1427  g_free(string);
1428  string = g_strdup_printf(_("Ignoring invalid format statement"));
1429  gerbv_stats_add_error(stats->error_list,
1430  -1,
1431  string,
1433  g_free(string);
1434  }
1435  op[0] = gerb_fgetc(fd);
1436  }
1437  break;
1438  case A2I('M','I'): /* Mirror Image */
1439  op[0] = gerb_fgetc(fd);
1440  state->state = gerbv_image_return_new_netstate (state->state);
1441 
1442  while ((op[0] != '*')&&(op[0] != EOF)) {
1443  gint readValue=0;
1444  switch (op[0]) {
1445  case 'A' :
1446  readValue = gerb_fgetint(fd, NULL);
1447  if (readValue == 1) {
1448  if (state->state->mirrorState == GERBV_MIRROR_STATE_FLIPB)
1449  state->state->mirrorState=GERBV_MIRROR_STATE_FLIPAB;
1450  else
1451  state->state->mirrorState=GERBV_MIRROR_STATE_FLIPA;
1452  }
1453  break;
1454  case 'B' :
1455  readValue = gerb_fgetint(fd, NULL);
1456  if (readValue == 1) {
1457  if (state->state->mirrorState == GERBV_MIRROR_STATE_FLIPA)
1458  state->state->mirrorState=GERBV_MIRROR_STATE_FLIPAB;
1459  else
1460  state->state->mirrorState=GERBV_MIRROR_STATE_FLIPB;
1461  }
1462  break;
1463  default :
1464  string = g_strdup_printf(_("Wrong character in mirror: %c"), op[0]);
1465  gerbv_stats_add_error(stats->error_list,
1466  -1,
1467  string,
1469  g_free(string);
1470  }
1471  op[0] = gerb_fgetc(fd);
1472  }
1473  break;
1474  case A2I('M','O'): /* Mode of Units */
1475  op[0] = gerb_fgetc(fd);
1476  op[1] = gerb_fgetc(fd);
1477 
1478  if ((op[0] == EOF) || (op[1] == EOF))
1479  gerbv_stats_add_error(stats->error_list,
1480  -1,
1481  _("Unexpected EOF found"),
1483  switch (A2I(op[0],op[1])) {
1484  case A2I('I','N'):
1485  state->state = gerbv_image_return_new_netstate (state->state);
1486  state->state->unit = GERBV_UNIT_INCH;
1487  break;
1488  case A2I('M','M'):
1489  state->state = gerbv_image_return_new_netstate (state->state);
1490  state->state->unit = GERBV_UNIT_MM;
1491  break;
1492  default:
1493  string = g_strdup_printf(_("Illegal unit: %c%c"), op[0], op[1]);
1494  gerbv_stats_add_error(stats->error_list,
1495  -1,
1496  string,
1498  g_free(string);
1499  }
1500  break;
1501  case A2I('O','F'): /* Offset */
1502  op[0] = gerb_fgetc(fd);
1503 
1504  while ((op[0] != '*')&&(op[0] != EOF)) {
1505  switch (op[0]) {
1506  case 'A' :
1507  state->state->offsetA = gerb_fgetdouble(fd) / scale;
1508  break;
1509  case 'B' :
1510  state->state->offsetB = gerb_fgetdouble(fd) / scale;
1511  break;
1512  default :
1513  string = g_strdup_printf(_("Wrong character in offset: %c"), op[0]);
1514  gerbv_stats_add_error(stats->error_list,
1515  -1,
1516  string,
1518  g_free(string);
1519  }
1520  op[0] = gerb_fgetc(fd);
1521  }
1522  break;
1523  case A2I('I','F'): /* Include file */
1524  {
1525  gchar *includeFilename = gerb_fgetstring(fd, '*');
1526 
1527  if (includeFilename) {
1528  gchar *fullPath;
1529  if (!g_path_is_absolute(includeFilename)) {
1530  fullPath = g_build_filename (directoryPath, includeFilename, NULL);
1531  } else {
1532  fullPath = g_strdup (includeFilename);
1533  }
1534  if (levelOfRecursion < 10) {
1535  gerb_file_t *includefd = NULL;
1536 
1537  includefd = gerb_fopen(fullPath);
1538  if (includefd) {
1539  gerber_parse_file_segment (levelOfRecursion + 1, image, state, curr_net, stats, includefd, directoryPath);
1540  gerb_fclose(includefd);
1541  } else {
1542  string = g_strdup_printf(_("In file \"%s\", included file \"%s\" cannot be found"),
1543  fd->filename, fullPath);
1544  gerbv_stats_add_error(stats->error_list,
1545  -1,
1546  string,
1548  g_free(string);
1549  }
1550  g_free (fullPath);
1551  } else {
1552  string = g_strdup_printf(_("Parser encountered more than 10 levels of include file recursion which is not allowed by the RS-274X spec"));
1553  gerbv_stats_add_error(stats->error_list,
1554  -1,
1555  string,
1557  g_free(string);
1558  }
1559 
1560  }
1561  }
1562  break;
1563  case A2I('I','O'): /* Image offset */
1564  op[0] = gerb_fgetc(fd);
1565 
1566  while ((op[0] != '*')&&(op[0] != EOF)) {
1567  switch (op[0]) {
1568  case 'A' :
1569  image->info->offsetA = gerb_fgetdouble(fd) / scale;
1570  break;
1571  case 'B' :
1572  image->info->offsetB = gerb_fgetdouble(fd) / scale;
1573  break;
1574  default :
1575  string = g_strdup_printf(_("Wrong character in image offset %c in file \"%s\""),
1576  op[0], fd->filename);
1577  gerbv_stats_add_error(stats->error_list,
1578  -1,
1579  string,
1581  g_free(string);
1582  }
1583  op[0] = gerb_fgetc(fd);
1584  }
1585  break;
1586  case A2I('S','F'): /* Scale Factor */
1587  state->state = gerbv_image_return_new_netstate (state->state);
1588  if (gerb_fgetc(fd) == 'A')
1589  state->state->scaleA = gerb_fgetdouble(fd);
1590  else
1591  gerb_ungetc(fd);
1592  if (gerb_fgetc(fd) == 'B')
1593  state->state->scaleB = gerb_fgetdouble(fd);
1594  else
1595  gerb_ungetc(fd);
1596  break;
1597  case A2I('I','C'): /* Input Code */
1598  /* Thanks to Stephen Adam for providing this information. As he writes:
1599  * btw, here's a logic puzzle for you. If you need to
1600  * read the gerber file to see how it's encoded, then
1601  * how can you read it?
1602  */
1603  op[0] = gerb_fgetc(fd);
1604  op[1] = gerb_fgetc(fd);
1605 
1606  if ((op[0] == EOF) || (op[1] == EOF)) {
1607  string = g_strdup_printf(_("Unexpected EOF found in file \"%s\""), fd->filename);
1608  gerbv_stats_add_error(stats->error_list,
1609  -1,
1610  string,
1612  g_free(string);
1613  }
1614  switch (A2I(op[0],op[1])) {
1615  case A2I('A','S'):
1616  image->info->encoding = GERBV_ENCODING_ASCII;
1617  break;
1618  case A2I('E','B'):
1619  image->info->encoding = GERBV_ENCODING_EBCDIC;
1620  break;
1621  case A2I('B','C'):
1622  image->info->encoding = GERBV_ENCODING_BCD;
1623  break;
1624  case A2I('I','S'):
1625  image->info->encoding = GERBV_ENCODING_ISO_ASCII;
1626  break;
1627  case A2I('E','I'):
1628  image->info->encoding = GERBV_ENCODING_EIA;
1629  break;
1630  default:
1631  string = g_strdup_printf(_("Unknown input code (IC): %c%c in file \"%s\""),
1632  op[0], op[1], fd->filename);
1633  gerbv_stats_add_error(stats->error_list,
1634  -1,
1635  string,
1637  g_free(string);
1638  }
1639  break;
1640 
1641  /* Image parameters */
1642  case A2I('I','J'): /* Image Justify */
1643  op[0] = gerb_fgetc(fd);
1644  image->info->imageJustifyTypeA = GERBV_JUSTIFY_LOWERLEFT;
1645  image->info->imageJustifyTypeB = GERBV_JUSTIFY_LOWERLEFT;
1646  image->info->imageJustifyOffsetA = 0.0;
1647  image->info->imageJustifyOffsetB = 0.0;
1648  while ((op[0] != '*')&&(op[0] != EOF)) {
1649  switch (op[0]) {
1650  case 'A' :
1651  op[0] = gerb_fgetc(fd);
1652  if (op[0] == 'C') {
1653  image->info->imageJustifyTypeA = GERBV_JUSTIFY_CENTERJUSTIFY;
1654  } else if (op[0] == 'L') {
1655  image->info->imageJustifyTypeA = GERBV_JUSTIFY_LOWERLEFT;
1656  } else {
1657  gerb_ungetc (fd);
1658  image->info->imageJustifyOffsetA = gerb_fgetdouble(fd) / scale;
1659  }
1660  break;
1661  case 'B' :
1662  op[0] = gerb_fgetc(fd);
1663  if (op[0] == 'C') {
1664  image->info->imageJustifyTypeB = GERBV_JUSTIFY_CENTERJUSTIFY;
1665  } else if (op[0] == 'L') {
1666  image->info->imageJustifyTypeB = GERBV_JUSTIFY_LOWERLEFT;
1667  } else {
1668  gerb_ungetc (fd);
1669  image->info->imageJustifyOffsetB = gerb_fgetdouble(fd) / scale;
1670  }
1671  break;
1672  default :
1673  string = g_strdup_printf(_("Wrong character %c in image justify in file \"%s\""),
1674  op[0], fd->filename);
1675  gerbv_stats_add_error(stats->error_list,
1676  -1,
1677  string,
1679  g_free(string);
1680  }
1681  op[0] = gerb_fgetc(fd);
1682  }
1683  break;
1684  case A2I('I','N'): /* Image Name */
1685  image->info->name = gerb_fgetstring(fd, '*');
1686  break;
1687  case A2I('I','P'): /* Image Polarity */
1688 
1689  for (ano = 0; ano < 3; ano++) {
1690  op[0] = gerb_fgetc(fd);
1691  if (op[0] == EOF) {
1692  string = g_strdup_printf(_("Unexpected EOF while reading image polarity (IP) in file \"%s\""),
1693  fd->filename);
1694  gerbv_stats_add_error(stats->error_list,
1695  -1,
1696  string,
1698  g_free(string);
1699  }
1700  str[ano] = (char)op[0];
1701  }
1702 
1703  if (strncmp(str, "POS", 3) == 0)
1704  image->info->polarity = GERBV_POLARITY_POSITIVE;
1705  else if (strncmp(str, "NEG", 3) == 0)
1706  image->info->polarity = GERBV_POLARITY_NEGATIVE;
1707  else {
1708  string = g_strdup_printf(_("Unknown polarity: %c%c%c"), str[0], str[1], str[2]);
1709  gerbv_stats_add_error(stats->error_list,
1710  -1,
1711  string,
1713  g_free(string);
1714  }
1715  break;
1716  case A2I('I','R'): /* Image Rotation */
1717  tmp = gerb_fgetint(fd, NULL) % 360;
1718  if (tmp == 0)
1719  image->info->imageRotation = 0.0;
1720  else if (tmp == 90)
1721  image->info->imageRotation = M_PI / 2.0;
1722  else if (tmp == 180)
1723  image->info->imageRotation = M_PI;
1724  else if (tmp == 270)
1725  image->info->imageRotation = 3.0 * M_PI / 2.0;
1726  else {
1727  string = g_strdup_printf(_("Image rotation must be 0, 90, 180 or 270 (is actually %d)"), tmp);
1728  gerbv_stats_add_error(stats->error_list,
1729  -1,
1730  string,
1732  g_free(string);
1733  }
1734  break;
1735  case A2I('P','F'): /* Plotter Film */
1736  image->info->plotterFilm = gerb_fgetstring(fd, '*');
1737  break;
1738 
1739  /* Aperture parameters */
1740  case A2I('A','D'): /* Aperture Description */
1741  a = (gerbv_aperture_t *) g_new0 (gerbv_aperture_t,1);
1742 
1743  ano = parse_aperture_definition(fd, a, image, scale);
1744  if (ano == -1) {
1745  /* error with line parse, so just quietly ignore */
1746  }
1747  else if ((ano >= 0) && (ano <= APERTURE_MAX)) {
1748  a->unit = state->state->unit;
1749  image->aperture[ano] = a;
1750  dprintf(" In parse_rs274x, adding new aperture to aperture list ...\n");
1751  gerbv_stats_add_aperture(stats->aperture_list,
1752  -1, ano,
1753  a->type,
1754  a->parameter);
1755  gerbv_stats_add_to_D_list(stats->D_code_list,
1756  ano);
1757  if (ano < APERTURE_MIN) {
1758  string = g_strdup_printf(_("Aperture number out of bounds %d in file \"%s\""),
1759  ano, fd->filename);
1760  gerbv_stats_add_error(stats->error_list,-1, string, GERBV_MESSAGE_ERROR);
1761  }
1762  } else {
1763  string = g_strdup_printf(_("Aperture number out of bounds %d in file \"%s\""),
1764  ano, fd->filename);
1765  gerbv_stats_add_error(stats->error_list,
1766  -1,
1767  string,
1769  g_free(string);
1770  }
1771  /* Add aperture info to stats->aperture_list here */
1772 
1773  break;
1774  case A2I('A','M'): /* Aperture Macro */
1775  tmp_amacro = image->amacro;
1776  image->amacro = parse_aperture_macro(fd);
1777  if (image->amacro) {
1778  image->amacro->next = tmp_amacro;
1779 #ifdef AMACRO_DEBUG
1780  print_program(image->amacro);
1781 #endif
1782  } else {
1783  string = g_strdup_printf(_("Failed to parse aperture macro in file \"%s\""),
1784  fd->filename);
1785  gerbv_stats_add_error(stats->error_list,
1786  -1,
1787  string,
1789  g_free(string);
1790  }
1791  // return, since we want to skip the later back-up loop
1792  return;
1793  /* Layer */
1794  case A2I('L','N'): /* Layer Name */
1795  state->layer = gerbv_image_return_new_layer (state->layer);
1796  state->layer->name = gerb_fgetstring(fd, '*');
1797  break;
1798  case A2I('L','P'): /* Layer Polarity */
1799  state->layer = gerbv_image_return_new_layer (state->layer);
1800  switch (gerb_fgetc(fd)) {
1801  case 'D': /* Dark Polarity (default) */
1802  state->layer->polarity = GERBV_POLARITY_DARK;
1803  break;
1804  case 'C': /* Clear Polarity */
1805  state->layer->polarity = GERBV_POLARITY_CLEAR;
1806  break;
1807  default:
1808  string = g_strdup_printf(_("Unknown Layer Polarity %c in file \"%s\""),
1809  op[0], fd->filename);
1810  gerbv_stats_add_error(stats->error_list,
1811  -1,
1812  string,
1814  g_free(string);
1815  }
1816  break;
1817  case A2I('K','O'): /* Knock Out */
1818  state->layer = gerbv_image_return_new_layer (state->layer);
1819  gerber_update_any_running_knockout_measurements (image);
1820  /* reset any previous knockout measurements */
1821  knockoutMeasure = FALSE;
1822  op[0] = gerb_fgetc(fd);
1823  if (op[0] == '*') { /* Disable previous SR parameters */
1824  state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_NOKNOCKOUT;
1825  break;
1826  } else if (op[0] == 'C') {
1827  state->layer->knockout.polarity = GERBV_POLARITY_CLEAR;
1828  } else if (op[0] == 'D') {
1829  state->layer->knockout.polarity = GERBV_POLARITY_DARK;
1830  } else {
1831  string = g_strdup_printf(_("Knockout must supply a polarity (C, D, or *) in file \"%s\""),
1832  fd->filename);
1833  gerbv_stats_add_error(stats->error_list,
1834  -1,
1835  string,
1837  g_free(string);
1838  }
1839  state->layer->knockout.lowerLeftX = 0.0;
1840  state->layer->knockout.lowerLeftY = 0.0;
1841  state->layer->knockout.width = 0.0;
1842  state->layer->knockout.height = 0.0;
1843  state->layer->knockout.border = 0.0;
1844  state->layer->knockout.firstInstance = TRUE;
1845  op[0] = gerb_fgetc(fd);
1846  while ((op[0] != '*')&&(op[0] != EOF)) {
1847  switch (op[0]) {
1848  case 'X':
1849  state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1850  state->layer->knockout.lowerLeftX = gerb_fgetdouble(fd) / scale;
1851  break;
1852  case 'Y':
1853  state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1854  state->layer->knockout.lowerLeftY = gerb_fgetdouble(fd) / scale;
1855  break;
1856  case 'I':
1857  state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1858  state->layer->knockout.width = gerb_fgetdouble(fd) / scale;
1859  break;
1860  case 'J':
1861  state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1862  state->layer->knockout.height = gerb_fgetdouble(fd) / scale;
1863  break;
1864  case 'K':
1865  state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_BORDER;
1866  state->layer->knockout.border = gerb_fgetdouble(fd) / scale;
1867  /* this is a bordered knockout, so we need to start measuring the
1868  size of a square bordering all future components */
1869  knockoutMeasure = TRUE;
1870  knockoutLimitXmin = HUGE_VAL;
1871  knockoutLimitYmin = HUGE_VAL;
1872  knockoutLimitXmax = -HUGE_VAL;
1873  knockoutLimitYmax = -HUGE_VAL;
1874  knockoutLayer = state->layer;
1875  break;
1876  default:
1877  string = g_strdup_printf(_("Unknown variable in knockout in file \"%s\""),
1878  fd->filename);
1879  gerbv_stats_add_error(stats->error_list,
1880  -1,
1881  string,
1883  g_free(string);
1884  }
1885  op[0] = gerb_fgetc(fd);
1886  }
1887  break;
1888  case A2I('S','R'): /* Step and Repeat */
1889  /* start by generating a new layer (duplicating previous layer settings */
1890  state->layer = gerbv_image_return_new_layer (state->layer);
1891  op[0] = gerb_fgetc(fd);
1892  if (op[0] == '*') { /* Disable previous SR parameters */
1893  state->layer->stepAndRepeat.X = 1;
1894  state->layer->stepAndRepeat.Y = 1;
1895  state->layer->stepAndRepeat.dist_X = 0.0;
1896  state->layer->stepAndRepeat.dist_Y = 0.0;
1897  break;
1898  }
1899  while ((op[0] != '*')&&(op[0] != EOF)) {
1900  switch (op[0]) {
1901  case 'X':
1902  state->layer->stepAndRepeat.X = gerb_fgetint(fd, NULL);
1903  break;
1904  case 'Y':
1905  state->layer->stepAndRepeat.Y = gerb_fgetint(fd, NULL);
1906  break;
1907  case 'I':
1908  state->layer->stepAndRepeat.dist_X = gerb_fgetdouble(fd) / scale;
1909  break;
1910  case 'J':
1911  state->layer->stepAndRepeat.dist_Y = gerb_fgetdouble(fd) / scale;
1912  break;
1913  default:
1914  string = g_strdup_printf(_("Step-and-repeat parameter error in file \"%s\""),
1915  fd->filename);
1916  gerbv_stats_add_error(stats->error_list,
1917  -1,
1918  string,
1920  g_free(string);
1921  }
1922 
1923  /*
1924  * Repeating 0 times in any direction would disable the whole plot, and
1925  * is probably not intended. At least one other tool (viewmate) seems
1926  * to interpret 0-time repeating as repeating just once too.
1927  */
1928  if(state->layer->stepAndRepeat.X == 0)
1929  state->layer->stepAndRepeat.X = 1;
1930  if(state->layer->stepAndRepeat.Y == 0)
1931  state->layer->stepAndRepeat.Y = 1;
1932 
1933  op[0] = gerb_fgetc(fd);
1934  }
1935  break;
1936  /* is this an actual RS274X command?? It isn't explainined in the spec... */
1937  case A2I('R','O'):
1938  state->layer = gerbv_image_return_new_layer (state->layer);
1939 
1940  state->layer->rotation = gerb_fgetdouble(fd) * M_PI / 180;
1941  op[0] = gerb_fgetc(fd);
1942  if (op[0] != '*') {
1943  string = g_strdup_printf(_("Error in layer rotation command in file \"%s\""),
1944  fd->filename);
1945  gerbv_stats_add_error(stats->error_list,
1946  -1,
1947  string,
1949  g_free(string);
1950  }
1951  break;
1952  default:
1953  string = g_strdup_printf(_("Unknown RS-274X extension found %%%c%c%% in file \"%s\""),
1954  op[0], op[1], fd->filename);
1955  gerbv_stats_add_error(stats->error_list,
1956  -1,
1957  string,
1959  g_free(string);
1960  }
1961  // make sure we read until the trailing * character
1962  // first, backspace once in case we already read the trailing *
1963  fd->ptr--;
1964  int c = gerb_fgetc(fd);
1965  while ((c != EOF) && (c != '*'))
1966  c = gerb_fgetc(fd);
1967  return;
1968 } /* parse_rs274x */
1969 
1970 
1971 /*
1972  * Stack declarations and operations to be used by the simple engine that
1973  * executes the parsed aperture macros.
1974  */
1975 typedef struct {
1976  double *stack;
1977  int sp;
1978 } macro_stack_t;
1979 
1980 
1981 static macro_stack_t *
1982 new_stack(unsigned int stack_size)
1983 {
1984  macro_stack_t *s;
1985 
1986  s = (macro_stack_t *) g_new0 (macro_stack_t,1);
1987  s->stack = (double *) g_new0 (double, stack_size);
1988  s->sp = 0;
1989  return s;
1990 } /* new_stack */
1991 
1992 
1993 static void
1994 free_stack(macro_stack_t *s)
1995 {
1996  if (s && s->stack)
1997  free(s->stack);
1998 
1999  if (s)
2000  free(s);
2001 
2002  return;
2003 } /* free_stack */
2004 
2005 
2006 static void
2007 push(macro_stack_t *s, double val)
2008 {
2009  s->stack[s->sp++] = val;
2010  return;
2011 } /* push */
2012 
2013 
2014 static int
2015 pop(macro_stack_t *s, double *value)
2016 {
2017  /* Check if we try to pop an empty stack */
2018  if (s->sp == 0) {
2019  return -1;
2020  }
2021 
2022  *value = s->stack[--s->sp];
2023  return 0;
2024 } /* pop */
2025 
2026 
2027 /* ------------------------------------------------------------------ */
2028 static int
2029 simplify_aperture_macro(gerbv_aperture_t *aperture, gdouble scale)
2030 {
2031  const int extra_stack_size = 10;
2032  macro_stack_t *s;
2033  gerbv_instruction_t *ip;
2034  int handled = 1, nuf_parameters = 0, i, j, clearOperatorUsed = FALSE;
2035  double *lp; /* Local copy of parameters */
2036  double tmp[2] = {0.0, 0.0};
2038  gerbv_simplified_amacro_t *sam;
2039 
2040  if (aperture == NULL)
2041  GERB_FATAL_ERROR(_("aperture NULL in simplify aperture macro"));
2042 
2043  if (aperture->amacro == NULL)
2044  GERB_FATAL_ERROR(_("aperture->amacro NULL in simplify aperture macro"));
2045 
2046  /* Allocate stack for VM */
2047  s = new_stack(aperture->amacro->nuf_push + extra_stack_size);
2048  if (s == NULL)
2049  GERB_FATAL_ERROR(_("malloc stack failed"));
2050 
2051  /* Make a copy of the parameter list that we can rewrite if necessary */
2052  lp = g_new (double,APERTURE_PARAMETERS_MAX);
2053 
2054  memcpy(lp, aperture->parameter, sizeof(double) * APERTURE_PARAMETERS_MAX);
2055 
2056  for(ip = aperture->amacro->program; ip != NULL; ip = ip->next) {
2057  switch(ip->opcode) {
2058  case GERBV_OPCODE_NOP:
2059  break;
2060  case GERBV_OPCODE_PUSH :
2061  push(s, ip->data.fval);
2062  break;
2063  case GERBV_OPCODE_PPUSH :
2064  push(s, lp[ip->data.ival - 1]);
2065  break;
2066  case GERBV_OPCODE_PPOP:
2067  if (pop(s, &tmp[0]) < 0)
2068  GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
2069  lp[ip->data.ival - 1] = tmp[0];
2070  break;
2071  case GERBV_OPCODE_ADD :
2072  if (pop(s, &tmp[0]) < 0)
2073  GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
2074  if (pop(s, &tmp[1]) < 0)
2075  GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
2076  push(s, tmp[1] + tmp[0]);
2077  break;
2078  case GERBV_OPCODE_SUB :
2079  if (pop(s, &tmp[0]) < 0)
2080  GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
2081  if (pop(s, &tmp[1]) < 0)
2082  GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
2083  push(s, tmp[1] - tmp[0]);
2084  break;
2085  case GERBV_OPCODE_MUL :
2086  if (pop(s, &tmp[0]) < 0)
2087  GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
2088  if (pop(s, &tmp[1]) < 0)
2089  GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
2090  push(s, tmp[1] * tmp[0]);
2091  break;
2092  case GERBV_OPCODE_DIV :
2093  if (pop(s, &tmp[0]) < 0)
2094  GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
2095  if (pop(s, &tmp[1]) < 0)
2096  GERB_FATAL_ERROR(_("Tried to pop an empty stack"));
2097  push(s, tmp[1] / tmp[0]);
2098  break;
2099  case GERBV_OPCODE_PRIM :
2100  /*
2101  * This handles the exposure thing in the aperture macro
2102  * The exposure is always the first element on stack independent
2103  * of aperture macro.
2104  */
2105  switch(ip->data.ival) {
2106  case 1:
2107  dprintf(" Aperture macro circle [1] (");
2109  nuf_parameters = 4;
2110  break;
2111  case 3:
2112  break;
2113  case 4 :
2114  dprintf(" Aperture macro outline [4] (");
2116  /*
2117  * Number of parameters are:
2118  * - number of points defined in entry 1 of the stack +
2119  * start point. Times two since it is both X and Y.
2120  * - Then three more; exposure, nuf points and rotation.
2121  */
2122  nuf_parameters = ((int)s->stack[1] + 1) * 2 + 3;
2123  break;
2124  case 5 :
2125  dprintf(" Aperture macro polygon [5] (");
2127  nuf_parameters = 6;
2128  break;
2129  case 6 :
2130  dprintf(" Aperture macro moiré [6] (");
2131  type = GERBV_APTYPE_MACRO_MOIRE;
2132  nuf_parameters = 9;
2133  break;
2134  case 7 :
2135  dprintf(" Aperture macro thermal [7] (");
2137  nuf_parameters = 6;
2138  break;
2139  case 2 :
2140  case 20 :
2141  dprintf(" Aperture macro line 20/2 (");
2143  nuf_parameters = 7;
2144  break;
2145  case 21 :
2146  dprintf(" Aperture macro line 21 (");
2148  nuf_parameters = 6;
2149  break;
2150  case 22 :
2151  dprintf(" Aperture macro line 22 (");
2153  nuf_parameters = 6;
2154  break;
2155  default :
2156  handled = 0;
2157  }
2158 
2159  if (type != GERBV_APTYPE_NONE) {
2160  if (nuf_parameters > APERTURE_PARAMETERS_MAX) {
2161  GERB_COMPILE_ERROR(_("Number of parameters to aperture macro (%d) "
2162  "are more than gerbv is able to store (%d)"),
2163  nuf_parameters, APERTURE_PARAMETERS_MAX);
2164  nuf_parameters = APERTURE_PARAMETERS_MAX;
2165  }
2166 
2167  /*
2168  * Create struct for simplified aperture macro and
2169  * start filling in the blanks.
2170  */
2171  sam = g_new (gerbv_simplified_amacro_t, 1);
2172  sam->type = type;
2173  sam->next = NULL;
2174  memset(sam->parameter, 0,
2175  sizeof(double) * APERTURE_PARAMETERS_MAX);
2176  memcpy(sam->parameter, s->stack,
2177  sizeof(double) * nuf_parameters);
2178 
2179  /* convert any mm values to inches */
2180  switch (type) {
2182  if (fabs(sam->parameter[0]) < 0.001)
2183  clearOperatorUsed = TRUE;
2184  sam->parameter[1]/=scale;
2185  sam->parameter[2]/=scale;
2186  sam->parameter[3]/=scale;
2187  break;
2189  if (fabs(sam->parameter[0]) < 0.001)
2190  clearOperatorUsed = TRUE;
2191  for (j=2; j<nuf_parameters-1; j++){
2192  sam->parameter[j]/=scale;
2193  }
2194  break;
2196  if (fabs(sam->parameter[0]) < 0.001)
2197  clearOperatorUsed = TRUE;
2198  sam->parameter[2]/=scale;
2199  sam->parameter[3]/=scale;
2200  sam->parameter[4]/=scale;
2201  break;
2203  sam->parameter[0]/=scale;
2204  sam->parameter[1]/=scale;
2205  sam->parameter[2]/=scale;
2206  sam->parameter[3]/=scale;
2207  sam->parameter[4]/=scale;
2208  sam->parameter[6]/=scale;
2209  sam->parameter[7]/=scale;
2210  break;
2212  sam->parameter[0]/=scale;
2213  sam->parameter[1]/=scale;
2214  sam->parameter[2]/=scale;
2215  sam->parameter[3]/=scale;
2216  sam->parameter[4]/=scale;
2217  break;
2219  if (fabs(sam->parameter[0]) < 0.001)
2220  clearOperatorUsed = TRUE;
2221  sam->parameter[1]/=scale;
2222  sam->parameter[2]/=scale;
2223  sam->parameter[3]/=scale;
2224  sam->parameter[4]/=scale;
2225  sam->parameter[5]/=scale;
2226  break;
2229  if (fabs(sam->parameter[0]) < 0.001)
2230  clearOperatorUsed = TRUE;
2231  sam->parameter[1]/=scale;
2232  sam->parameter[2]/=scale;
2233  sam->parameter[3]/=scale;
2234  sam->parameter[4]/=scale;
2235  break;
2236  default:
2237  break;
2238  }
2239  /*
2240  * Add this simplified aperture macro to the end of the list
2241  * of simplified aperture macros. If first entry, put it
2242  * in the top.
2243  */
2244  if (aperture->simplified == NULL) {
2245  aperture->simplified = sam;
2246  } else {
2247  gerbv_simplified_amacro_t *tmp_sam;
2248  tmp_sam = aperture->simplified;
2249  while (tmp_sam->next != NULL) {
2250  tmp_sam = tmp_sam->next;
2251  }
2252  tmp_sam->next = sam;
2253  }
2254 
2255 #ifdef DEBUG
2256  for (i = 0; i < nuf_parameters; i++) {
2257  dprintf("%f, ", s->stack[i]);
2258  }
2259 #endif /* DEBUG */
2260  dprintf(")\n");
2261  }
2262 
2263  /*
2264  * Here we reset the stack pointer. It's not general correct
2265  * correct to do this, but since I know how the compiler works
2266  * I can do this. The correct way to do this should be to
2267  * subtract number of used elements in each primitive operation.
2268  */
2269  s->sp = 0;
2270  break;
2271  default :
2272  break;
2273  }
2274  }
2275  free_stack(s);
2276  g_free (lp);
2277 
2278  /* store a flag to let the renderer know if it should expect any "clear"
2279  primatives */
2280  aperture->parameter[0]= (gdouble) clearOperatorUsed;
2281  return handled;
2282 } /* simplify_aperture_macro */
2283 
2284 
2285 /* ------------------------------------------------------------------ */
2286 static int
2287 parse_aperture_definition(gerb_file_t *fd, gerbv_aperture_t *aperture,
2288  gerbv_image_t *image, gdouble scale)
2289 {
2290  int ano, i;
2291  char *ad;
2292  char *token;
2293  gerbv_amacro_t *curr_amacro;
2294  gerbv_amacro_t *amacro = image->amacro;
2295  gerbv_stats_t *stats = image->gerbv_stats;
2296  gdouble tempHolder;
2297  gchar *string;
2298 
2299  if (gerb_fgetc(fd) != 'D') {
2300  string = g_strdup_printf(_("Found AD code with no following 'D' in file \"%s\""),
2301  fd->filename);
2302  gerbv_stats_add_error(stats->error_list,
2303  -1,
2304  string,
2306  g_free(string);
2307  return -1;
2308  }
2309 
2310  /*
2311  * Get aperture no
2312  */
2313  ano = gerb_fgetint(fd, NULL);
2314 
2315  /*
2316  * Read in the whole aperture defintion and tokenize it
2317  */
2318  ad = gerb_fgetstring(fd, '*');
2319  token = strtok(ad, ",");
2320 
2321  if (token == NULL) {
2322  string = g_strdup_printf(_("Invalid aperture definition in file \"%s\""),
2323  fd->filename);
2324  gerbv_stats_add_error(stats->error_list,
2325  -1,
2326  string,
2328  g_free(string);
2329  return -1;
2330  }
2331  if (strlen(token) == 1) {
2332  switch (token[0]) {
2333  case 'C':
2334  aperture->type = GERBV_APTYPE_CIRCLE;
2335  break;
2336  case 'R' :
2337  aperture->type = GERBV_APTYPE_RECTANGLE;
2338  break;
2339  case 'O' :
2340  aperture->type = GERBV_APTYPE_OVAL;
2341  break;
2342  case 'P' :
2343  aperture->type = GERBV_APTYPE_POLYGON;
2344  break;
2345  }
2346  /* Here a should a T be defined, but I don't know what it represents */
2347  } else {
2348  aperture->type = GERBV_APTYPE_MACRO;
2349  /*
2350  * In aperture definition, point to the aperture macro
2351  * used in the defintion
2352  */
2353  curr_amacro = amacro;
2354  while (curr_amacro) {
2355  if ((strlen(curr_amacro->name) == strlen(token)) &&
2356  (strcmp(curr_amacro->name, token) == 0)) {
2357  aperture->amacro = curr_amacro;
2358  break;
2359  }
2360  curr_amacro = curr_amacro->next;
2361  }
2362  }
2363 
2364  /*
2365  * Parse all parameters
2366  */
2367  for (token = strtok(NULL, "X"), i = 0; token != NULL;
2368  token = strtok(NULL, "X"), i++) {
2369  if (i == APERTURE_PARAMETERS_MAX) {
2370  string = g_strdup_printf(_("Maximum number of allowed parameters exceeded in aperture %d in file \"%s\""),
2371  ano, fd->filename);
2372  gerbv_stats_add_error(stats->error_list,
2373  -1,
2374  string,
2376  g_free(string);
2377  break;
2378  }
2379  errno = 0;
2380 
2381  tempHolder = strtod(token, NULL);
2382  /* convert any MM values to inches */
2383  /* don't scale polygon angles or side numbers, or macro parmaeters */
2384  if (!(((aperture->type == GERBV_APTYPE_POLYGON) && ((i==1) || (i==2)))||
2385  (aperture->type == GERBV_APTYPE_MACRO))) {
2386  tempHolder /= scale;
2387  }
2388 
2389  aperture->parameter[i] = tempHolder;
2390  if (errno) {
2391  string = g_strdup_printf(_("Failed to read all parameters exceeded in aperture %d"), ano);
2392  gerbv_stats_add_error(stats->error_list,
2393  -1,
2394  string,
2396  g_free(string);
2397  aperture->parameter[i] = 0.0;
2398  }
2399  }
2400 
2401  aperture->nuf_parameters = i;
2402 
2403  gerb_ungetc(fd);
2404 
2405  if (aperture->type == GERBV_APTYPE_MACRO) {
2406  dprintf("Simplifying aperture %d using aperture macro \"%s\"\n", ano,
2407  aperture->amacro->name);
2408  simplify_aperture_macro(aperture, scale);
2409  dprintf("Done simplifying\n");
2410  }
2411 
2412  g_free(ad);
2413 
2414  return ano;
2415 } /* parse_aperture_definition */
2416 
2417 
2418 /* ------------------------------------------------------------------ */
2419 static void
2420 calc_cirseg_sq(struct gerbv_net *net, int cw,
2421  double delta_cp_x, double delta_cp_y)
2422 {
2423  double d1x, d1y, d2x, d2y;
2424  double alfa, beta;
2425  int quadrant = 0;
2426 
2427 
2428  /*
2429  * Quadrant detection (based on ccw, converted below if cw)
2430  * Y ^
2431  * /!\
2432  * !
2433  * ---->X
2434  */
2435  if (net->start_x > net->stop_x)
2436  /* 1st and 2nd quadrant */
2437  if (net->start_y < net->stop_y)
2438  quadrant = 1;
2439  else
2440  quadrant = 2;
2441  else
2442  /* 3rd and 4th quadrant */
2443  if (net->start_y > net->stop_y)
2444  quadrant = 3;
2445  else
2446  quadrant = 4;
2447 
2448  /*
2449  * If clockwise, rotate quadrant
2450  */
2451  if (cw) {
2452  switch (quadrant) {
2453  case 1 :
2454  quadrant = 3;
2455  break;
2456  case 2 :
2457  quadrant = 4;
2458  break;
2459  case 3 :
2460  quadrant = 1;
2461  break;
2462  case 4 :
2463  quadrant = 2;
2464  break;
2465  default :
2466  GERB_COMPILE_ERROR(_("Unknow quadrant value while converting to cw"));
2467  }
2468  }
2469 
2470  /*
2471  * Calculate arc center point
2472  */
2473  switch (quadrant) {
2474  case 1 :
2475  net->cirseg->cp_x = net->start_x - delta_cp_x;
2476  net->cirseg->cp_y = net->start_y - delta_cp_y;
2477  break;
2478  case 2 :
2479  net->cirseg->cp_x = net->start_x + delta_cp_x;
2480  net->cirseg->cp_y = net->start_y - delta_cp_y;
2481  break;
2482  case 3 :
2483  net->cirseg->cp_x = net->start_x + delta_cp_x;
2484  net->cirseg->cp_y = net->start_y + delta_cp_y;
2485  break;
2486  case 4 :
2487  net->cirseg->cp_x = net->start_x - delta_cp_x;
2488  net->cirseg->cp_y = net->start_y + delta_cp_y;
2489  break;
2490  default :
2491  GERB_COMPILE_ERROR(_("Strange quadrant: %d"), quadrant);
2492  }
2493 
2494  /*
2495  * Some good values
2496  */
2497  d1x = fabs(net->start_x - net->cirseg->cp_x);
2498  d1y = fabs(net->start_y - net->cirseg->cp_y);
2499  d2x = fabs(net->stop_x - net->cirseg->cp_x);
2500  d2y = fabs(net->stop_y - net->cirseg->cp_y);
2501 
2502  alfa = atan2(d1y, d1x);
2503  beta = atan2(d2y, d2x);
2504 
2505  /*
2506  * Avoid divide by zero when sin(0) = 0 and cos(90) = 0
2507  */
2508  net->cirseg->width = alfa < beta ?
2509  2 * (d1x / cos(alfa)) : 2 * (d2x / cos(beta));
2510  net->cirseg->height = alfa > beta ?
2511  2 * (d1y / sin(alfa)) : 2 * (d2y / sin(beta));
2512 
2513  if (alfa < 0.000001 && beta < 0.000001) {
2514  net->cirseg->height = 0;
2515  }
2516 
2517 #define RAD2DEG(a) (a * 180 / M_PI)
2518 
2519  switch (quadrant) {
2520  case 1 :
2521  net->cirseg->angle1 = RAD2DEG(alfa);
2522  net->cirseg->angle2 = RAD2DEG(beta);
2523  break;
2524  case 2 :
2525  net->cirseg->angle1 = 180.0 - RAD2DEG(alfa);
2526  net->cirseg->angle2 = 180.0 - RAD2DEG(beta);
2527  break;
2528  case 3 :
2529  net->cirseg->angle1 = 180.0 + RAD2DEG(alfa);
2530  net->cirseg->angle2 = 180.0 + RAD2DEG(beta);
2531  break;
2532  case 4 :
2533  net->cirseg->angle1 = 360.0 - RAD2DEG(alfa);
2534  net->cirseg->angle2 = 360.0 - RAD2DEG(beta);
2535  break;
2536  default :
2537  GERB_COMPILE_ERROR(_("Strange quadrant: %d"), quadrant);
2538  }
2539 
2540  if (net->cirseg->width < 0.0)
2541  GERB_COMPILE_WARNING(_("Negative width [%f] in quadrant %d [%f][%f]"),
2542  net->cirseg->width, quadrant, alfa, beta);
2543 
2544  if (net->cirseg->height < 0.0)
2545  GERB_COMPILE_WARNING(_("Negative height [%f] in quadrant %d [%f][%f]"),
2546  net->cirseg->height, quadrant, RAD2DEG(alfa), RAD2DEG(beta));
2547 
2548  return;
2549 
2550 } /* calc_cirseg_sq */
2551 
2552 
2553 /* ------------------------------------------------------------------ */
2554 static void
2555 calc_cirseg_mq(struct gerbv_net *net, int cw,
2556  double delta_cp_x, double delta_cp_y)
2557 {
2558  double d1x, d1y, d2x, d2y;
2559  double alfa, beta;
2560 
2561  net->cirseg->cp_x = net->start_x + delta_cp_x;
2562  net->cirseg->cp_y = net->start_y + delta_cp_y;
2563 
2564  /*
2565  * Some good values
2566  */
2567  d1x = net->start_x - net->cirseg->cp_x;
2568  d1y = net->start_y - net->cirseg->cp_y;
2569  d2x = net->stop_x - net->cirseg->cp_x;
2570  d2y = net->stop_y - net->cirseg->cp_y;
2571 
2572  alfa = atan2(d1y, d1x);
2573  beta = atan2(d2y, d2x);
2574 
2575  net->cirseg->width = sqrt(delta_cp_x*delta_cp_x + delta_cp_y*delta_cp_y);
2576  net->cirseg->width *= 2.0;
2577  net->cirseg->height = net->cirseg->width;
2578 
2579  net->cirseg->angle1 = RAD2DEG(alfa);
2580  net->cirseg->angle2 = RAD2DEG(beta);
2581 
2582  /*
2583  * Make sure it's always positive angles
2584  */
2585  if (net->cirseg->angle1 < 0.0) {
2586  net->cirseg->angle1 += 360.0;
2587  net->cirseg->angle2 += 360.0;
2588  }
2589 
2590  if (net->cirseg->angle2 < 0.0)
2591  net->cirseg->angle2 += 360.0;
2592 
2593  if(net->cirseg->angle2 == 0.0)
2594  net->cirseg->angle2 = 360.0;
2595 
2596  /*
2597  * This is a sanity check for angles after the nature of atan2.
2598  * If cw we must make sure angle1-angle2 are always positive,
2599  * If ccw we must make sure angle2-angle1 are always negative.
2600  * We should really return one angle and the difference as GTK
2601  * uses them. But what the heck, it works for me.
2602  */
2603  if (cw) {
2604  if (net->cirseg->angle1 <= net->cirseg->angle2)
2605  net->cirseg->angle2 -= 360.0;
2606  } else {
2607  if (net->cirseg->angle1 >= net->cirseg->angle2)
2608  net->cirseg->angle2 += 360.0;
2609  }
2610 
2611  return;
2612 } /* calc_cirseg_mq */
2613 
2614 
2615 static void
2616 gerber_update_any_running_knockout_measurements (gerbv_image_t *image)
2617 {
2618  if (knockoutMeasure) {
2619  knockoutLayer->knockout.lowerLeftX = knockoutLimitXmin;
2620  knockoutLayer->knockout.lowerLeftY = knockoutLimitYmin;
2621  knockoutLayer->knockout.width = knockoutLimitXmax - knockoutLimitXmin;
2622  knockoutLayer->knockout.height = knockoutLimitYmax - knockoutLimitYmin;
2623  knockoutMeasure = FALSE;
2624  }
2625 }
2626 
2627 
2628 static void
2629 gerber_calculate_final_justify_effects(gerbv_image_t *image)
2630 {
2631  gdouble translateA = 0.0, translateB = 0.0;
2632 
2633  if (image->info->imageJustifyTypeA != GERBV_JUSTIFY_NOJUSTIFY) {
2634  if (image->info->imageJustifyTypeA == GERBV_JUSTIFY_CENTERJUSTIFY)
2635  translateA = (image->info->max_x - image->info->min_x) / 2.0;
2636  else
2637  translateA = -image->info->min_x;
2638  }
2639  if (image->info->imageJustifyTypeB != GERBV_JUSTIFY_NOJUSTIFY) {
2640  if (image->info->imageJustifyTypeB == GERBV_JUSTIFY_CENTERJUSTIFY)
2641  translateB = (image->info->max_y - image->info->min_y) / 2.0;
2642  else
2643  translateB = -image->info->min_y;
2644  }
2645 
2646  /* update the min/max values so the autoscale function can correctly
2647  centered a justified image */
2648  image->info->min_x += translateA+ image->info->imageJustifyOffsetA;
2649  image->info->max_x += translateA+ image->info->imageJustifyOffsetA;
2650  image->info->min_y += translateB+ image->info->imageJustifyOffsetB;
2651  image->info->max_y += translateB+ image->info->imageJustifyOffsetB;
2652 
2653  /* store the absolute offset for the justify so we can quickly offset
2654  the rendered picture during drawing */
2655  image->info->imageJustifyOffsetActualA = translateA +
2656  image->info->imageJustifyOffsetA;
2657  image->info->imageJustifyOffsetActualB = translateB +
2658  image->info->imageJustifyOffsetB;
2659 } /* gerber_calculate_final_justify_effects */
2660 
2661 
2662 void gerber_update_image_min_max (gerbv_render_size_t *boundingBox, double repeat_off_X,
2663  double repeat_off_Y, gerbv_image_t* image) {
2664  if (boundingBox->left < image->info->min_x)
2665  image->info->min_x = boundingBox->left;
2666  if (boundingBox->right+repeat_off_X > image->info->max_x)
2667  image->info->max_x = boundingBox->right+repeat_off_X;
2668  if (boundingBox->bottom < image->info->min_y)
2669  image->info->min_y = boundingBox->bottom;
2670  if (boundingBox->top+repeat_off_Y > image->info->max_y)
2671  image->info->max_y = boundingBox->top+repeat_off_Y;
2672 }
2673 
2674 void
2675 gerber_update_min_and_max(gerbv_render_size_t *boundingBox,
2676  gdouble x, gdouble y, gdouble apertureSizeX1,
2677  gdouble apertureSizeX2,gdouble apertureSizeY1,
2678  gdouble apertureSizeY2)
2679 {
2680  gdouble ourX1 = x - apertureSizeX1, ourY1 = y - apertureSizeY1;
2681  gdouble ourX2 = x + apertureSizeX2, ourY2 = y + apertureSizeY2;
2682 
2683  /* transform the point to the final rendered position, accounting
2684  for any scaling, offsets, mirroring, etc */
2685  /* NOTE: we need to already add/subtract in the aperture size since
2686  the final rendering may be scaled */
2687  cairo_matrix_transform_point (&currentMatrix, &ourX1, &ourY1);
2688  cairo_matrix_transform_point (&currentMatrix, &ourX2, &ourY2);
2689 
2690  /* check both points against the min/max, since depending on the rotation,
2691  mirroring, etc, either point could possibly be a min or max */
2692  if(boundingBox->left > ourX1)
2693  boundingBox->left = ourX1;
2694  if(boundingBox->left > ourX2)
2695  boundingBox->left = ourX2;
2696  if(boundingBox->right < ourX1)
2697  boundingBox->right = ourX1;
2698  if(boundingBox->right < ourX2)
2699  boundingBox->right = ourX2;
2700  if(boundingBox->bottom > ourY1)
2701  boundingBox->bottom = ourY1;
2702  if(boundingBox->bottom > ourY2)
2703  boundingBox->bottom = ourY2;
2704  if(boundingBox->top < ourY1)
2705  boundingBox->top = ourY1;
2706  if(boundingBox->top < ourY2)
2707  boundingBox->top = ourY2;
2708 } /* gerber_update_min_and_max */
2709