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