recalculate the bounding boxes when drawing primatives via the libgerbv API
[geda-gerbv.git] / src / gerber.c
blobac63a8a6c7ed71e7468960bbb9ea83cc67f6749d
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);
79 static void gerber_update_any_running_knockout_measurements(gerbv_image_t *image);
81 static void gerber_calculate_final_justify_effects (gerbv_image_t *image);
83 gboolean knockoutMeasure = FALSE;
84 gdouble knockoutLimitXmin, knockoutLimitYmin, knockoutLimitXmax,
85 knockoutLimitYmax;
86 gerbv_layer_t *knockoutLayer = NULL;
87 cairo_matrix_t currentMatrix;
90 /* --------------------------------------------------------- */
91 gerbv_net_t *
92 gerber_create_new_net (gerbv_net_t *currentNet, gerbv_layer_t *layer, gerbv_netstate_t *state){
93 gerbv_net_t *newNet = g_new0 (gerbv_net_t, 1);
95 currentNet->next = newNet;
96 if (layer)
97 newNet->layer = layer;
98 else
99 newNet->layer = currentNet->layer;
100 if (state)
101 newNet->state = state;
102 else
103 newNet->state = currentNet->state;
104 return newNet;
107 /* --------------------------------------------------------- */
108 gboolean
109 gerber_create_new_aperture (gerbv_image_t *image, int *indexNumber,
110 gerbv_aperture_type_t apertureType, gdouble parameter1, gdouble parameter2){
111 int i;
113 /* search for an available aperture spot */
114 for (i = 0; i <= APERTURE_MAX; i++) {
115 if (image->aperture[i] == NULL) {
116 image->aperture[i] = g_new0 (gerbv_aperture_t, 1);
117 image->aperture[i]->type = apertureType;
118 image->aperture[i]->parameter[0] = parameter1;
119 image->aperture[i]->parameter[1] = parameter2;
120 *indexNumber = i;
121 return TRUE;
124 return FALSE;
127 /* --------------------------------------------------------- */
128 /*! This function reads the Gerber file char by char, looking
129 * for various Gerber codes (e.g. G, D, etc). Once it reads
130 * a code, it then dispatches control to one or another
131 * bits of code which parse the individual code.
132 * It also updates the state struct, which holds info about
133 * the current state of the hypothetical photoplotter
134 * (i.e. updates whether the aperture is on or off, updates
135 * any other parameters, like units, offsets, apertures, etc.)
137 gboolean
138 gerber_parse_file_segment (gint levelOfRecursion, gerbv_image_t *image,
139 gerb_state_t *state, gerbv_net_t *curr_net,
140 gerbv_stats_t *stats, gerb_file_t *fd,
141 gchar *directoryPath) {
142 int read, coord, len, polygonPoints=0;
143 double x_scale = 0.0, y_scale = 0.0;
144 double delta_cp_x = 0.0, delta_cp_y = 0.0;
145 double aperture_sizeX, aperture_sizeY;
146 double scale;
147 gboolean foundEOF = FALSE;
148 gchar *string;
149 gerbv_render_size_t boundingBox={HUGE_VAL,-HUGE_VAL,HUGE_VAL,-HUGE_VAL};
151 while ((read = gerb_fgetc(fd)) != EOF) {
152 /* figure out the scale, since we need to normalize
153 all dimensions to inches */
154 if (state->state->unit == GERBV_UNIT_MM)
155 scale = 25.4;
156 else
157 scale = 1.0;
158 switch ((char)(read & 0xff)) {
159 case 'G':
160 dprintf("... Found G code\n");
161 parse_G_code(fd, state, image);
162 break;
163 case 'D':
164 dprintf("... Found D code\n");
165 parse_D_code(fd, state, image);
166 break;
167 case 'M':
168 dprintf("... Found M code\n");
169 switch(parse_M_code(fd, image)) {
170 case 1 :
171 case 2 :
172 case 3 :
173 foundEOF = TRUE;
174 break;
175 default:
176 gerbv_stats_add_error(stats->error_list,
178 "Unknown M code found.\n",
179 GERBV_MESSAGE_ERROR);
180 } /* switch(parse_M_code) */
181 break;
182 case 'X':
183 dprintf("... Found X code\n");
184 stats->X++;
185 coord = gerb_fgetint(fd, &len);
186 if (image->format && image->format->omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
188 switch ((image->format->x_int + image->format->x_dec) - len) {
189 case 7:
190 coord *= 10;
191 case 6:
192 coord *= 10;
193 case 5:
194 coord *= 10;
195 case 4:
196 coord *= 10;
197 case 3:
198 coord *= 10;
199 case 2:
200 coord *= 10;
201 case 1:
202 coord *= 10;
203 break;
204 default:
208 if (image->format && (image->format->coordinate==GERBV_COORDINATE_INCREMENTAL))
209 state->curr_x += coord;
210 else
211 state->curr_x = coord;
212 state->changed = 1;
213 break;
214 case 'Y':
215 dprintf("... Found Y code\n");
216 stats->Y++;
217 coord = gerb_fgetint(fd, &len);
218 if (image->format && image->format->omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
220 switch ((image->format->y_int + image->format->y_dec) - len) {
221 case 7:
222 coord *= 10;
223 case 6:
224 coord *= 10;
225 case 5:
226 coord *= 10;
227 case 4:
228 coord *= 10;
229 case 3:
230 coord *= 10;
231 case 2:
232 coord *= 10;
233 case 1:
234 coord *= 10;
235 break;
236 default:
240 if (image->format && (image->format->coordinate==GERBV_COORDINATE_INCREMENTAL))
241 state->curr_y += coord;
242 else
243 state->curr_y = coord;
244 state->changed = 1;
245 break;
246 case 'I':
247 dprintf("... Found I code\n");
248 stats->I++;
249 coord = gerb_fgetint(fd, &len);
250 if (image->format && image->format->omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
252 switch ((image->format->y_int + image->format->y_dec) - len) {
253 case 7:
254 coord *= 10;
255 case 6:
256 coord *= 10;
257 case 5:
258 coord *= 10;
259 case 4:
260 coord *= 10;
261 case 3:
262 coord *= 10;
263 case 2:
264 coord *= 10;
265 case 1:
266 coord *= 10;
267 break;
268 default:
272 state->delta_cp_x = coord;
273 state->changed = 1;
274 break;
275 case 'J':
276 dprintf("... Found J code\n");
277 stats->J++;
278 coord = gerb_fgetint(fd, &len);
279 if (image->format && image->format->omit_zeros == GERBV_OMIT_ZEROS_TRAILING) {
281 switch ((image->format->y_int + image->format->y_dec) - len) {
282 case 7:
283 coord *= 10;
284 case 6:
285 coord *= 10;
286 case 5:
287 coord *= 10;
288 case 4:
289 coord *= 10;
290 case 3:
291 coord *= 10;
292 case 2:
293 coord *= 10;
294 case 1:
295 coord *= 10;
296 break;
297 default:
301 state->delta_cp_y = coord;
302 state->changed = 1;
303 break;
304 case '%':
305 dprintf("... Found %% code\n");
306 while (1) {
307 parse_rs274x(levelOfRecursion, fd, image, state, curr_net, stats, directoryPath);
308 /* advance past any whitespace here */
309 int c = gerb_fgetc(fd);
310 while ((c == '\n')||(c == '\r')||(c == ' ')||(c == '\t')||(c == 0))
311 c = gerb_fgetc(fd);
312 if(c == EOF || c == '%')
313 break;
314 // loop again to catch multiple blocks on the same line (separated by * char)
315 fd->ptr--;
317 break;
318 case '*':
319 dprintf("... Found * code\n");
320 stats->star++;
321 if (state->changed == 0) break;
322 state->changed = 0;
324 /* don't even bother saving the net if the aperture state is GERBV_APERTURE_STATE_OFF and we
325 aren't starting a polygon fill (where we need it to get to the start point) */
326 if ((state->aperture_state == GERBV_APERTURE_STATE_OFF)&&(!state->in_parea_fill)&&
327 (state->interpolation != GERBV_INTERPOLATION_PAREA_START)) {
328 /* save the coordinate so the next net can use it for a start point */
329 state->prev_x = state->curr_x;
330 state->prev_y = state->curr_y;
331 break;
333 curr_net = gerber_create_new_net (curr_net, state->layer, state->state);
335 * Scale to given coordinate format
336 * XXX only "omit leading zeros".
338 if (image && image->format ){
339 x_scale = pow(10.0, (double)image->format->x_dec);
340 y_scale = pow(10.0, (double)image->format->y_dec);
342 x_scale *= scale;
343 y_scale *= scale;
344 curr_net->start_x = (double)state->prev_x / x_scale;
345 curr_net->start_y = (double)state->prev_y / y_scale;
346 curr_net->stop_x = (double)state->curr_x / x_scale;
347 curr_net->stop_y = (double)state->curr_y / y_scale;
348 delta_cp_x = (double)state->delta_cp_x / x_scale;
349 delta_cp_y = (double)state->delta_cp_y / y_scale;
350 switch (state->interpolation) {
351 case GERBV_INTERPOLATION_CW_CIRCULAR :
352 curr_net->cirseg = g_new0 (gerbv_cirseg_t,1);
353 if (state->mq_on)
354 calc_cirseg_mq(curr_net, 1, delta_cp_x, delta_cp_y);
355 else
356 calc_cirseg_sq(curr_net, 1, delta_cp_x, delta_cp_y);
357 break;
358 case GERBV_INTERPOLATION_CCW_CIRCULAR :
359 curr_net->cirseg = g_new0 (gerbv_cirseg_t,1);
360 if (state->mq_on)
361 calc_cirseg_mq(curr_net, 0, delta_cp_x, delta_cp_y);
362 else
363 calc_cirseg_sq(curr_net, 0, delta_cp_x, delta_cp_y);
364 break;
365 case GERBV_INTERPOLATION_PAREA_START :
367 * To be able to get back and fill in number of polygon corners
369 state->parea_start_node = curr_net;
370 state->in_parea_fill = 1;
371 polygonPoints = 0;
372 /* reset the bounding box */
373 boundingBox.left = HUGE_VAL;
374 boundingBox.right = -HUGE_VAL;
375 boundingBox.top = -HUGE_VAL;
376 boundingBox.bottom = HUGE_VAL;
377 break;
378 case GERBV_INTERPOLATION_PAREA_END :
379 /* save the calculated bounding box to the master node */
380 state->parea_start_node->boundingBox = boundingBox;
381 /* close out the polygon */
382 state->parea_start_node = NULL;
383 state->in_parea_fill = 0;
384 polygonPoints = 0;
385 break;
386 default :
387 break;
388 } /* switch(state->interpolation) */
391 * Count number of points in Polygon Area
393 if (state->in_parea_fill && state->parea_start_node) {
395 * "...all lines drawn with D01 are considered edges of the
396 * polygon. D02 closes and fills the polygon."
397 * p.49 rs274xrevd_e.pdf
398 * D02 -> state->aperture_state == GERBV_APERTURE_STATE_OFF
401 /* UPDATE: only end the polygon during a D02 call if we've already
402 drawn a polygon edge (with D01) */
404 if ((state->aperture_state == GERBV_APERTURE_STATE_OFF &&
405 state->interpolation != GERBV_INTERPOLATION_PAREA_START) && (polygonPoints > 0)) {
406 curr_net->interpolation = GERBV_INTERPOLATION_PAREA_END;
407 curr_net = gerber_create_new_net (curr_net, state->layer, state->state);
408 curr_net->interpolation = GERBV_INTERPOLATION_PAREA_START;
409 state->parea_start_node->boundingBox = boundingBox;
410 state->parea_start_node = curr_net;
411 polygonPoints = 0;
412 curr_net = gerber_create_new_net (curr_net, state->layer, state->state);
413 curr_net->start_x = (double)state->prev_x / x_scale;
414 curr_net->start_y = (double)state->prev_y / y_scale;
415 curr_net->stop_x = (double)state->curr_x / x_scale;
416 curr_net->stop_y = (double)state->curr_y / y_scale;
417 /* reset the bounding box */
418 boundingBox.left = HUGE_VAL;
419 boundingBox.right = -HUGE_VAL;
420 boundingBox.top = -HUGE_VAL;
421 boundingBox.bottom = HUGE_VAL;
423 else if (state->interpolation != GERBV_INTERPOLATION_PAREA_START)
424 polygonPoints++;
426 } /* if (state->in_parea_fill && state->parea_start_node) */
428 curr_net->interpolation = state->interpolation;
431 * Override circular interpolation if no center was given.
432 * This should be a safe hack, since a good file should always
433 * include I or J. And even if the radius is zero, the endpoint
434 * should be the same as the start point, creating no line
436 if (((state->interpolation == GERBV_INTERPOLATION_CW_CIRCULAR) ||
437 (state->interpolation == GERBV_INTERPOLATION_CCW_CIRCULAR)) &&
438 ((state->delta_cp_x == 0.0) && (state->delta_cp_y == 0.0)))
439 curr_net->interpolation = GERBV_INTERPOLATION_LINEARx1;
442 * If we detected the end of Polygon Area Fill we go back to
443 * the interpolation we had before that.
444 * Also if we detected any of the quadrant flags, since some
445 * gerbers don't reset the interpolation (EagleCad again).
447 if ((state->interpolation == GERBV_INTERPOLATION_PAREA_START) ||
448 (state->interpolation == GERBV_INTERPOLATION_PAREA_END))
449 state->interpolation = state->prev_interpolation;
452 * Save layer polarity and unit
454 curr_net->layer = state->layer;
456 state->delta_cp_x = 0.0;
457 state->delta_cp_y = 0.0;
458 curr_net->aperture = state->curr_aperture;
459 curr_net->aperture_state = state->aperture_state;
462 * For next round we save the current position as
463 * the previous position
465 state->prev_x = state->curr_x;
466 state->prev_y = state->curr_y;
469 * If we have an aperture defined at the moment we find
470 * min and max of image with compensation for mm.
472 if ((curr_net->aperture == 0) && !state->in_parea_fill)
473 break;
475 /* only update the min/max values and aperture stats if we are drawing */
476 if ((curr_net->aperture_state != GERBV_APERTURE_STATE_OFF)&&
477 (curr_net->interpolation != GERBV_INTERPOLATION_PAREA_START)){
478 double repeat_off_X = 0.0, repeat_off_Y = 0.0;
480 /* Update stats with current aperture number if not in polygon */
481 if (!state->in_parea_fill) {
482 dprintf(" In parse_D_code, adding 1 to D_list ...\n");
483 int retcode = gerbv_stats_increment_D_list_count(stats->D_code_list,
484 curr_net->aperture,
486 stats->error_list);
487 if (retcode == -1) {
488 string = g_strdup_printf("Found undefined D code D%d in file \n%s\n",
489 curr_net->aperture,
490 fd->filename);
491 gerbv_stats_add_error(stats->error_list,
493 string,
494 GERBV_MESSAGE_ERROR);
495 g_free(string);
496 stats->D_unknown++;
501 * If step_and_repeat (%SR%) is used, check min_x,max_y etc for
502 * the ends of the step_and_repeat lattice. This goes wrong in
503 * the case of negative dist_X or dist_Y, in which case we
504 * should compare against the startpoints of the lines, not
505 * the stoppoints, but that seems an uncommon case (and the
506 * error isn't very big any way).
508 repeat_off_X = (state->layer->stepAndRepeat.X - 1) *
509 state->layer->stepAndRepeat.dist_X;
510 repeat_off_Y = (state->layer->stepAndRepeat.Y - 1) *
511 state->layer->stepAndRepeat.dist_Y;
513 cairo_matrix_init (&currentMatrix, 1, 0, 0, 1, 0, 0);
514 /* offset image */
515 cairo_matrix_translate (&currentMatrix, image->info->offsetA,
516 image->info->offsetB);
517 /* do image rotation */
518 cairo_matrix_rotate (&currentMatrix, image->info->imageRotation);
519 /* it's a new layer, so recalculate the new transformation
520 * matrix for it */
521 /* do any rotations */
522 cairo_matrix_rotate (&currentMatrix, state->layer->rotation);
524 /* calculate current layer and state transformation matrices */
525 /* apply scale factor */
526 cairo_matrix_scale (&currentMatrix, state->state->scaleA,
527 state->state->scaleB);
528 /* apply offset */
529 cairo_matrix_translate (&currentMatrix, state->state->offsetA,
530 state->state->offsetB);
531 /* apply mirror */
532 switch (state->state->mirrorState) {
533 case GERBV_MIRROR_STATE_FLIPA:
534 cairo_matrix_scale (&currentMatrix, -1, 1);
535 break;
536 case GERBV_MIRROR_STATE_FLIPB:
537 cairo_matrix_scale (&currentMatrix, 1, -1);
538 break;
539 case GERBV_MIRROR_STATE_FLIPAB:
540 cairo_matrix_scale (&currentMatrix, -1, -1);
541 break;
542 default:
543 break;
545 /* finally, apply axis select */
546 if (state->state->axisSelect == GERBV_AXIS_SELECT_SWAPAB) {
547 /* we do this by rotating 270 (counterclockwise, then
548 * mirroring the Y axis
550 cairo_matrix_rotate (&currentMatrix, 3 * M_PI / 2);
551 cairo_matrix_scale (&currentMatrix, 1, -1);
553 /* if it's a macro, step through all the primitive components
554 and calculate the true bounding box */
555 if ((image->aperture[curr_net->aperture] != NULL) &&
556 (image->aperture[curr_net->aperture]->type == GERBV_APTYPE_MACRO)) {
557 gerbv_simplified_amacro_t *ls = image->aperture[curr_net->aperture]->simplified;
559 while (ls != NULL) {
560 gdouble offsetx = 0, offsety = 0, widthx = 0, widthy = 0;
561 gboolean calculatedAlready = FALSE;
563 if (ls->type == GERBV_APTYPE_MACRO_CIRCLE) {
564 offsetx=ls->parameter[CIRCLE_CENTER_X];
565 offsety=ls->parameter[CIRCLE_CENTER_Y];
566 widthx=widthy=ls->parameter[CIRCLE_DIAMETER];
567 } else if (ls->type == GERBV_APTYPE_MACRO_OUTLINE) {
568 int pointCounter,numberOfPoints;
569 numberOfPoints = (int) ls->parameter[OUTLINE_NUMBER_OF_POINTS];
571 for (pointCounter = 0; pointCounter <= numberOfPoints; pointCounter++) {
572 gerber_update_min_and_max (&boundingBox,
573 curr_net->stop_x +
574 ls->parameter[pointCounter * 2 + OUTLINE_FIRST_X],
575 curr_net->stop_y +
576 ls->parameter[pointCounter * 2 + OUTLINE_FIRST_Y],
577 0,0,0,0);
579 calculatedAlready = TRUE;
580 } else if (ls->type == GERBV_APTYPE_MACRO_POLYGON) {
581 offsetx = ls->parameter[POLYGON_CENTER_X];
582 offsety = ls->parameter[POLYGON_CENTER_Y];
583 widthx = widthy = ls->parameter[POLYGON_DIAMETER];
584 } else if (ls->type == GERBV_APTYPE_MACRO_MOIRE) {
585 offsetx = ls->parameter[MOIRE_CENTER_X];
586 offsety = ls->parameter[MOIRE_CENTER_Y];
587 widthx = widthy = ls->parameter[MOIRE_OUTSIDE_DIAMETER];
588 } else if (ls->type == GERBV_APTYPE_MACRO_THERMAL) {
589 offsetx = ls->parameter[THERMAL_CENTER_X];
590 offsety = ls->parameter[THERMAL_CENTER_Y];
591 widthx = widthy = ls->parameter[THERMAL_OUTSIDE_DIAMETER];
592 } else if (ls->type == GERBV_APTYPE_MACRO_LINE20) {
593 widthx = widthy = ls->parameter[LINE20_LINE_WIDTH];
594 gerber_update_min_and_max (&boundingBox,
595 curr_net->stop_x +
596 ls->parameter[LINE20_START_X],
597 curr_net->stop_y +
598 ls->parameter[LINE20_START_Y],
599 widthx/2,widthx/2,widthy/2,widthy/2);
600 gerber_update_min_and_max (&boundingBox,
601 curr_net->stop_x +
602 ls->parameter[LINE20_END_X],
603 curr_net->stop_y +
604 ls->parameter[LINE20_END_Y],
605 widthx/2,widthx/2,widthy/2,widthy/2);
606 calculatedAlready = TRUE;
607 } else if (ls->type == GERBV_APTYPE_MACRO_LINE21) {
608 gdouble largestDimension = sqrt (ls->parameter[LINE21_WIDTH]/2 *
609 ls->parameter[LINE21_WIDTH]/2 + ls->parameter[LINE21_HEIGHT/2] *
610 ls->parameter[LINE21_HEIGHT]/2);
612 offsetx = ls->parameter[LINE21_CENTER_X];
613 offsety = ls->parameter[LINE21_CENTER_Y];
614 widthx = widthy=largestDimension;
615 } else if (ls->type == GERBV_APTYPE_MACRO_LINE22) {
616 gdouble largestDimension = sqrt (ls->parameter[LINE22_WIDTH]/2 *
617 ls->parameter[LINE22_WIDTH]/2 + ls->parameter[LINE22_HEIGHT/2] *
618 ls->parameter[LINE22_HEIGHT]/2);
620 offsetx = ls->parameter[LINE22_LOWER_LEFT_X] +
621 ls->parameter[LINE22_WIDTH]/2;
622 offsety = ls->parameter[LINE22_LOWER_LEFT_Y] +
623 ls->parameter[LINE22_HEIGHT]/2;
624 widthx = widthy=largestDimension;
627 if (!calculatedAlready) {
628 gerber_update_min_and_max (&boundingBox,
629 curr_net->stop_x + offsetx,
630 curr_net->stop_y + offsety,
631 widthx/2,widthx/2,widthy/2,widthy/2);
633 ls = ls->next;
635 } else {
636 if (image->aperture[curr_net->aperture] != NULL) {
637 aperture_sizeX = image->aperture[curr_net->aperture]->parameter[0];
638 if ((image->aperture[curr_net->aperture]->type == GERBV_APTYPE_RECTANGLE) || (image->aperture[curr_net->aperture]->type == GERBV_APTYPE_OVAL)) {
639 aperture_sizeY = image->aperture[curr_net->aperture]->parameter[1];
641 else
642 aperture_sizeY = aperture_sizeX;
643 } else {
644 /* this is usually for polygon fills, where the aperture width
645 is "zero" */
646 aperture_sizeX = aperture_sizeY = 0;
648 /* if it's an arc path, use a special calc */
649 if ((curr_net->interpolation == GERBV_INTERPOLATION_CW_CIRCULAR) ||
650 (curr_net->interpolation == GERBV_INTERPOLATION_CCW_CIRCULAR)) {
651 /* to calculate the arc bounding box, we chop it into 1 degree steps, calculate
652 the point at each step, and use it to figure out the bounding box */
653 gdouble angleDiff = curr_net->cirseg->angle2 - curr_net->cirseg->angle1;
654 gint i, steps = abs(angleDiff);
655 for (i=0; i<=steps; i++){
656 gdouble tempX = curr_net->cirseg->cp_x + curr_net->cirseg->width / 2.0 *
657 cos ((curr_net->cirseg->angle1 +
658 (angleDiff * i) / steps)*M_PI/180);
659 gdouble tempY = curr_net->cirseg->cp_y + curr_net->cirseg->width / 2.0 *
660 sin ((curr_net->cirseg->angle1 +
661 (angleDiff * i) / steps)*M_PI/180);
662 gerber_update_min_and_max (&boundingBox,
663 tempX, tempY,
664 aperture_sizeX/2,aperture_sizeX/2,
665 aperture_sizeY/2,aperture_sizeY/2);
669 else {
670 /* check both the start and stop of the aperture points against
671 a running min/max counter */
672 /* Note: only check start coordinate if this isn't a flash,
673 since the start point may be bogus if it is a flash */
674 if (curr_net->aperture_state != GERBV_APERTURE_STATE_FLASH) {
675 gerber_update_min_and_max (&boundingBox,
676 curr_net->start_x, curr_net->start_y,
677 aperture_sizeX/2,aperture_sizeX/2,
678 aperture_sizeY/2,aperture_sizeY/2);
680 gerber_update_min_and_max (&boundingBox,
681 curr_net->stop_x, curr_net->stop_y,
682 aperture_sizeX/2,aperture_sizeX/2,
683 aperture_sizeY/2,aperture_sizeY/2);
687 /* update the info bounding box with this latest bounding box */
688 gerber_update_image_min_max(&boundingBox, repeat_off_X, repeat_off_Y, image);
690 /* optionally update the knockout measurement box */
691 if (knockoutMeasure) {
692 if (boundingBox.left < knockoutLimitXmin)
693 knockoutLimitXmin = boundingBox.left;
694 if (boundingBox.right+repeat_off_X > knockoutLimitXmax)
695 knockoutLimitXmax = boundingBox.right+repeat_off_X;
696 if (boundingBox.bottom < knockoutLimitYmin)
697 knockoutLimitYmin = boundingBox.bottom;
698 if (boundingBox.top+repeat_off_Y > knockoutLimitYmax)
699 knockoutLimitYmax = boundingBox.top+repeat_off_Y;
701 /* if we're not in a polygon fill, then update the object bounding box */
702 if (!state->in_parea_fill) {
703 curr_net->boundingBox = boundingBox;
704 /* and reset the bounding box */
705 boundingBox.left = HUGE_VAL;
706 boundingBox.right = -HUGE_VAL;
707 boundingBox.bottom = HUGE_VAL;
708 boundingBox.top = -HUGE_VAL;
711 break;
712 case 10 : /* White space */
713 case 13 :
714 case ' ' :
715 case '\t' :
716 case 0 :
717 break;
718 default:
719 stats->unknown++;
720 string = g_strdup_printf("Found unknown character (whitespace?) [%d]%c\n",
721 read, read);
722 gerbv_stats_add_error(stats->error_list,
724 string,
725 GERBV_MESSAGE_ERROR);
726 g_free(string);
727 } /* switch((char) (read & 0xff)) */
729 return foundEOF;
733 /* ------------------------------------------------------------------ */
734 /*! This is a wrapper which gets called from top level. It
735 * does some initialization and pre-processing, and
736 * then calls gerber_parse_file_segment
737 * which processes the actual file. Then it does final
738 * modifications to the image created.
740 gerbv_image_t *
741 parse_gerb(gerb_file_t *fd, gchar *directoryPath)
743 gerb_state_t *state = NULL;
744 gerbv_image_t *image = NULL;
745 gerbv_net_t *curr_net = NULL;
746 gerbv_stats_t *stats;
747 gboolean foundEOF = FALSE;
748 gchar *string;
750 /* added by t.motylewski@bfad.de
751 * many locales redefine "." as "," and so on,
752 * so sscanf and strtod has problems when
753 * reading files using %f format */
754 setlocale(LC_NUMERIC, "C" );
757 * Create new state. This is used locally to keep track
758 * of the photoplotter's state as the Gerber is read in.
760 state = g_new0 (gerb_state_t, 1);
763 * Create new image. This will be returned.
765 image = gerbv_create_image(image, "RS274-X (Gerber) File");
766 if (image == NULL)
767 GERB_FATAL_ERROR("malloc image failed\n");
768 curr_net = image->netlist;
769 image->layertype = GERBV_LAYERTYPE_RS274X;
770 image->gerbv_stats = gerbv_stats_new();
771 if (image->gerbv_stats == NULL)
772 GERB_FATAL_ERROR("malloc gerbv_stats failed\n");
773 stats = (gerbv_stats_t *) image->gerbv_stats;
775 /* set active layer and netstate to point to first default one created */
776 state->layer = image->layers;
777 state->state = image->states;
778 curr_net->layer = state->layer;
779 curr_net->state = state->state;
782 * Start parsing
784 dprintf("In parse_gerb, starting to parse file...\n");
785 foundEOF = gerber_parse_file_segment (1, image, state, curr_net, stats,
786 fd, directoryPath);
788 if (!foundEOF) {
789 string = g_strdup_printf("File %s is missing Gerber EOF code.\n", fd->filename);
790 gerbv_stats_add_error(stats->error_list,
792 string,
793 GERBV_MESSAGE_ERROR);
794 g_free(string);
796 g_free(state);
798 dprintf(" ... done parsing Gerber file\n");
799 gerber_update_any_running_knockout_measurements (image);
800 gerber_calculate_final_justify_effects(image);
802 return image;
803 } /* parse_gerb */
806 /* ------------------------------------------------------------------- */
807 /*! Checks for signs that this is a RS-274X file
808 * Returns TRUE if it is, FALSE if not.
810 gboolean
811 gerber_is_rs274x_p(gerb_file_t *fd, gboolean *returnFoundBinary)
813 char *buf;
814 int len = 0;
815 char *letter;
816 int i;
817 gboolean found_binary = FALSE;
818 gboolean found_ADD = FALSE;
819 gboolean found_D0 = FALSE;
820 gboolean found_D2 = FALSE;
821 gboolean found_M0 = FALSE;
822 gboolean found_M2 = FALSE;
823 gboolean found_star = FALSE;
824 gboolean found_X = FALSE;
825 gboolean found_Y = FALSE;
827 dprintf ("gerber_is_rs274x_p(%p, %p), fd->fd = %p\n", fd, returnFoundBinary, fd->fd);
828 buf = (char *) g_malloc(MAXL);
829 if (buf == NULL)
830 GERB_FATAL_ERROR("malloc buf failed while checking for rs274x.\n");
832 while (fgets(buf, MAXL, fd->fd) != NULL) {
833 dprintf ("buf = \"%s\"\n", buf);
834 len = strlen(buf);
836 /* First look through the file for indications of its type by
837 * checking that file is not binary (non-printing chars and white
838 * spaces)
840 for (i = 0; i < len; i++) {
841 if (!isprint((int) buf[i]) && (buf[i] != '\r') &&
842 (buf[i] != '\n') && (buf[i] != '\t')) {
843 found_binary = TRUE;
844 dprintf ("found_binary (%d)\n", buf[i]);
847 if (g_strstr_len(buf, len, "%ADD")) {
848 found_ADD = TRUE;
849 dprintf ("found_ADD\n");
851 if (g_strstr_len(buf, len, "D00") || g_strstr_len(buf, len, "D0")) {
852 found_D0 = TRUE;
853 dprintf ("found_D0\n");
855 if (g_strstr_len(buf, len, "D02") || g_strstr_len(buf, len, "D2")) {
856 found_D2 = TRUE;
857 dprintf ("found_D2\n");
859 if (g_strstr_len(buf, len, "M00") || g_strstr_len(buf, len, "M0")) {
860 found_M0 = TRUE;
861 dprintf ("found_M0\n");
863 if (g_strstr_len(buf, len, "M02") || g_strstr_len(buf, len, "M2")) {
864 found_M2 = TRUE;
865 dprintf ("found_M2\n");
867 if (g_strstr_len(buf, len, "*")) {
868 found_star = TRUE;
869 dprintf ("found_star\n");
871 /* look for X<number> or Y<number> */
872 if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
873 if (isdigit((int) letter[1])) { /* grab char after X */
874 found_X = TRUE;
875 dprintf ("found_X\n");
878 if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
879 if (isdigit((int) letter[1])) { /* grab char after Y */
880 found_Y = TRUE;
881 dprintf ("found_Y\n");
885 rewind(fd->fd);
886 free(buf);
888 *returnFoundBinary = found_binary;
890 /* Now form logical expression determining if the file is RS-274X */
891 if ((found_D0 || found_D2 || found_M0 || found_M2) &&
892 found_ADD && found_star && (found_X || found_Y))
893 return TRUE;
896 return FALSE;
898 } /* gerber_is_rs274x */
901 /* ------------------------------------------------------------------- */
902 /*! Checks for signs that this is a RS-274D file
903 * Returns TRUE if it is, FALSE if not.
905 gboolean
906 gerber_is_rs274d_p(gerb_file_t *fd)
908 char *buf;
909 int len = 0;
910 char *letter;
911 int i;
912 gboolean found_binary = FALSE;
913 gboolean found_ADD = FALSE;
914 gboolean found_D0 = FALSE;
915 gboolean found_D2 = FALSE;
916 gboolean found_M0 = FALSE;
917 gboolean found_M2 = FALSE;
918 gboolean found_star = FALSE;
919 gboolean found_X = FALSE;
920 gboolean found_Y = FALSE;
922 buf = malloc(MAXL);
923 if (buf == NULL)
924 GERB_FATAL_ERROR("malloc buf failed while checking for rs274d.\n");
926 while (fgets(buf, MAXL, fd->fd) != NULL) {
927 len = strlen(buf);
929 /* First look through the file for indications of its type */
931 /* check that file is not binary (non-printing chars */
932 for (i = 0; i < len; i++) {
933 if (!isprint( (int) buf[i]) && (buf[i] != '\r') &&
934 (buf[i] != '\n') && (buf[i] != '\t')) {
935 found_binary = TRUE;
939 if (g_strstr_len(buf, len, "%ADD")) {
940 found_ADD = TRUE;
942 if (g_strstr_len(buf, len, "D00") || g_strstr_len(buf, len, "D0")) {
943 found_D0 = TRUE;
945 if (g_strstr_len(buf, len, "D02") || g_strstr_len(buf, len, "D2")) {
946 found_D2 = TRUE;
948 if (g_strstr_len(buf, len, "M00") || g_strstr_len(buf, len, "M0")) {
949 found_M0 = TRUE;
951 if (g_strstr_len(buf, len, "M02") || g_strstr_len(buf, len, "M2")) {
952 found_M2 = TRUE;
954 if (g_strstr_len(buf, len, "*")) {
955 found_star = TRUE;
957 /* look for X<number> or Y<number> */
958 if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
959 /* grab char after X */
960 if (isdigit( (int) letter[1])) {
961 found_X = TRUE;
964 if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
965 /* grab char after Y */
966 if (isdigit( (int) letter[1])) {
967 found_Y = TRUE;
971 rewind(fd->fd);
972 free(buf);
974 /* Now form logical expression determining if the file is RS-274D */
975 if ((found_D0 || found_D2 || found_M0 || found_M2) &&
976 !found_ADD && found_star && (found_X || found_Y) &&
977 !found_binary)
978 return TRUE;
980 return FALSE;
982 } /* gerber_is_rs274d */
985 /* ------------------------------------------------------------------- */
986 /*! This function reads a G number and updates the current
987 * state. It also updates the G stats counters
989 static void
990 parse_G_code(gerb_file_t *fd, gerb_state_t *state, gerbv_image_t *image)
992 int op_int;
993 gerbv_format_t *format = image->format;
994 gerbv_stats_t *stats = image->gerbv_stats;
995 int c;
996 gchar *string;
998 op_int=gerb_fgetint(fd, NULL);
1000 switch(op_int) {
1001 case 0: /* Move */
1002 /* Is this doing anything really? */
1003 stats->G0++;
1004 break;
1005 case 1: /* Linear Interpolation (1X scale) */
1006 state->interpolation = GERBV_INTERPOLATION_LINEARx1;
1007 stats->G1++;
1008 break;
1009 case 2: /* Clockwise Linear Interpolation */
1010 state->interpolation = GERBV_INTERPOLATION_CW_CIRCULAR;
1011 stats->G2++;
1012 break;
1013 case 3: /* Counter Clockwise Linear Interpolation */
1014 state->interpolation = GERBV_INTERPOLATION_CCW_CIRCULAR;
1015 stats->G3++;
1016 break;
1017 case 4: /* Ignore Data Block */
1018 /* Don't do anything, just read 'til * */
1019 /* SDB asks: Should we look for other codes while reading G04 in case
1020 * user forgot to put * at end of comment block? */
1021 c = gerb_fgetc(fd);
1022 while ((c != EOF) && (c != '*')) {
1023 c = gerb_fgetc(fd);
1025 stats->G4++;
1026 break;
1027 case 10: /* Linear Interpolation (10X scale) */
1028 state->interpolation = GERBV_INTERPOLATION_x10;
1029 stats->G10++;
1030 break;
1031 case 11: /* Linear Interpolation (0.1X scale) */
1032 state->interpolation = GERBV_INTERPOLATION_LINEARx01;
1033 stats->G11++;
1034 break;
1035 case 12: /* Linear Interpolation (0.01X scale) */
1036 state->interpolation = GERBV_INTERPOLATION_LINEARx001;
1037 stats->G12++;
1038 break;
1039 case 36: /* Turn on Polygon Area Fill */
1040 state->prev_interpolation = state->interpolation;
1041 state->interpolation = GERBV_INTERPOLATION_PAREA_START;
1042 state->changed = 1;
1043 stats->G36++;
1044 break;
1045 case 37: /* Turn off Polygon Area Fill */
1046 state->interpolation = GERBV_INTERPOLATION_PAREA_END;
1047 state->changed = 1;
1048 stats->G37++;
1049 break;
1050 case 54: /* Tool prepare */
1051 /* XXX Maybe uneccesary??? */
1052 if (gerb_fgetc(fd) == 'D') {
1053 int a = gerb_fgetint(fd, NULL);
1054 if ((a >= 0) && (a <= APERTURE_MAX)) {
1055 state->curr_aperture = a;
1056 } else {
1057 string = g_strdup_printf("Found aperture D%d out of bounds while parsing G code in file \n%s\n",
1058 a, fd->filename);
1059 gerbv_stats_add_error(stats->error_list,
1061 string,
1062 GERBV_MESSAGE_ERROR);
1063 g_free(string);
1065 } else {
1066 string = g_strdup_printf("Found unexpected code after G54 in file \n%s\n", fd->filename);
1067 gerbv_stats_add_error(stats->error_list,
1069 string,
1070 GERBV_MESSAGE_ERROR);
1071 g_free(string);
1072 /* Must insert error count here */
1074 stats->G54++;
1075 break;
1076 case 55: /* Prepare for flash */
1077 stats->G55++;
1078 break;
1079 case 70: /* Specify inches */
1080 state->state = gerbv_image_return_new_netstate (state->state);
1081 state->state->unit = GERBV_UNIT_INCH;
1082 stats->G70++;
1083 break;
1084 case 71: /* Specify millimeters */
1085 state->state = gerbv_image_return_new_netstate (state->state);
1086 state->state->unit = GERBV_UNIT_MM;
1087 stats->G71++;
1088 break;
1089 case 74: /* Disable 360 circular interpolation */
1090 state->mq_on = 0;
1091 stats->G74++;
1092 break;
1093 case 75: /* Enable 360 circular interpolation */
1094 state->mq_on = 1;
1095 stats->G75++;
1096 break;
1097 case 90: /* Specify absolut format */
1098 if (format) format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1099 stats->G90++;
1100 break;
1101 case 91: /* Specify incremental format */
1102 if (format) format->coordinate = GERBV_COORDINATE_INCREMENTAL;
1103 stats->G91++;
1104 break;
1105 default:
1106 string = g_strdup_printf("Encountered unknown G code G%d in file \n%s\n", op_int, fd->filename);
1107 gerbv_stats_add_error(stats->error_list,
1109 string,
1110 GERBV_MESSAGE_ERROR);
1111 g_free(string);
1112 string = g_strdup_printf("Ignorning unknown G code G%d\n", op_int);
1113 gerbv_stats_add_error(stats->error_list,
1115 string,
1116 GERBV_MESSAGE_WARNING);
1117 g_free(string);
1118 stats->G_unknown++;
1119 /* Enter error count here */
1120 break;
1123 return;
1124 } /* parse_G_code */
1127 /* ------------------------------------------------------------------ */
1128 /*! This function reads the numeric value of a D code and updates the
1129 * state. It also updates the D stats counters
1131 static void
1132 parse_D_code(gerb_file_t *fd, gerb_state_t *state, gerbv_image_t *image)
1134 int a;
1135 gerbv_stats_t *stats = image->gerbv_stats;
1136 gchar *string;
1138 a = gerb_fgetint(fd, NULL);
1139 dprintf(" In parse_D_code, found D number = %d ... \n", a);
1140 switch(a) {
1141 case 0 : /* Invalid code */
1142 string = g_strdup_printf("Found invalid D00 code in file \n%s.\n", fd->filename);
1143 gerbv_stats_add_error(stats->error_list,
1145 string,
1146 GERBV_MESSAGE_ERROR);
1147 g_free(string);
1148 stats->D_error++;
1149 break;
1150 case 1 : /* Exposure on */
1151 state->aperture_state = GERBV_APERTURE_STATE_ON;
1152 state->changed = 1;
1153 stats->D1++;
1154 break;
1155 case 2 : /* Exposure off */
1156 state->aperture_state = GERBV_APERTURE_STATE_OFF;
1157 state->changed = 1;
1158 stats->D2++;
1159 break;
1160 case 3 : /* Flash aperture */
1161 state->aperture_state = GERBV_APERTURE_STATE_FLASH;
1162 state->changed = 1;
1163 stats->D3++;
1164 break;
1165 default: /* Aperture in use */
1166 if ((a >= 0) && (a <= APERTURE_MAX)) {
1167 state->curr_aperture = a;
1169 } else {
1170 string = g_strdup_printf("Found out of bounds aperture D%d in file \n%s\n",
1171 a, fd->filename);
1172 gerbv_stats_add_error(stats->error_list,
1174 string,
1175 GERBV_MESSAGE_ERROR);
1176 g_free(string);
1177 stats->D_error++;
1179 state->changed = 0;
1180 break;
1183 return;
1184 } /* parse_D_code */
1187 /* ------------------------------------------------------------------ */
1188 static int
1189 parse_M_code(gerb_file_t *fd, gerbv_image_t *image)
1191 int op_int;
1192 gerbv_stats_t *stats = image->gerbv_stats;
1193 gchar *string;
1195 op_int=gerb_fgetint(fd, NULL);
1197 switch (op_int) {
1198 case 0: /* Program stop */
1199 stats->M0++;
1200 return 1;
1201 case 1: /* Optional stop */
1202 stats->M1++;
1203 return 2;
1204 case 2: /* End of program */
1205 stats->M2++;
1206 return 3;
1207 default:
1208 string = g_strdup_printf("Encountered unknown M code M%d in file \n%s\n",
1209 op_int, fd->filename);
1210 gerbv_stats_add_error(stats->error_list,
1212 string,
1213 GERBV_MESSAGE_ERROR);
1214 g_free(string);
1215 string = g_strdup_printf("Ignorning unknown M code M%d\n", op_int);
1216 gerbv_stats_add_error(stats->error_list,
1218 string,
1219 GERBV_MESSAGE_WARNING);
1220 g_free(string);
1221 stats->M_unknown++;
1223 return 0;
1224 } /* parse_M_code */
1227 /* ------------------------------------------------------------------ */
1228 static void
1229 parse_rs274x(gint levelOfRecursion, gerb_file_t *fd, gerbv_image_t *image,
1230 gerb_state_t *state, gerbv_net_t *curr_net, gerbv_stats_t *stats,
1231 gchar *directoryPath)
1233 int op[2];
1234 char str[3];
1235 int tmp;
1236 gerbv_aperture_t *a = NULL;
1237 gerbv_amacro_t *tmp_amacro;
1238 int ano;
1239 gdouble scale = 1.0;
1240 gchar *string;
1242 if (state->state->unit == GERBV_UNIT_MM)
1243 scale = 25.4;
1245 op[0] = gerb_fgetc(fd);
1246 op[1] = gerb_fgetc(fd);
1248 if ((op[0] == EOF) || (op[1] == EOF)) {
1249 string = g_strdup_printf("Unexpected EOF found in file \n%s\n", fd->filename);
1250 gerbv_stats_add_error(stats->error_list,
1252 string,
1253 GERBV_MESSAGE_ERROR);
1254 g_free(string);
1257 switch (A2I(op[0], op[1])){
1260 * Directive parameters
1262 case A2I('A','S'): /* Axis Select */
1263 op[0] = gerb_fgetc(fd);
1264 op[1] = gerb_fgetc(fd);
1265 state->state = gerbv_image_return_new_netstate (state->state);
1267 if ((op[0] == EOF) || (op[1] == EOF)) {
1268 string = g_strdup_printf("Unexpected EOF found in file \n%s\n", fd->filename);
1269 gerbv_stats_add_error(stats->error_list,
1271 string,
1272 GERBV_MESSAGE_ERROR);
1273 g_free(string);
1276 if (((op[0] == 'A') && (op[1] == 'Y')) ||
1277 ((op[0] == 'B') && (op[1] == 'X'))) {
1278 state->state->axisSelect = GERBV_AXIS_SELECT_SWAPAB;
1279 } else {
1280 state->state->axisSelect = GERBV_AXIS_SELECT_NOSELECT;
1283 op[0] = gerb_fgetc(fd);
1284 op[1] = gerb_fgetc(fd);
1286 if ((op[0] == EOF) || (op[1] == EOF)) {
1287 string = g_strdup_printf("Unexpected EOF found in file \n%s\n", fd->filename);
1288 gerbv_stats_add_error(stats->error_list,
1290 string,
1291 GERBV_MESSAGE_ERROR);
1292 g_free(string);
1295 if (((op[0] == 'A') && (op[1] == 'Y')) ||
1296 ((op[0] == 'B') && (op[1] == 'X'))) {
1297 state->state->axisSelect = GERBV_AXIS_SELECT_SWAPAB;
1298 } else {
1299 state->state->axisSelect = GERBV_AXIS_SELECT_NOSELECT;
1301 break;
1303 case A2I('F','S'): /* Format Statement */
1304 image->format = g_new0 (gerbv_format_t,1);
1306 switch (gerb_fgetc(fd)) {
1307 case 'L':
1308 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1309 break;
1310 case 'T':
1311 image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1312 break;
1313 case 'D':
1314 image->format->omit_zeros = GERBV_OMIT_ZEROS_EXPLICIT;
1315 break;
1316 default:
1317 string = g_strdup_printf("EagleCad bug detected: Undefined handling of zeros in format code in file \n%s\n",
1318 fd->filename);
1319 gerbv_stats_add_error(stats->error_list,
1321 string,
1322 GERBV_MESSAGE_ERROR);
1323 g_free(string);
1324 string = g_strdup_printf("Defaulting to omitting leading zeros.\n");
1325 gerbv_stats_add_error(stats->error_list,
1327 string,
1328 GERBV_MESSAGE_WARNING);
1329 g_free(string);
1330 gerb_ungetc(fd);
1331 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1334 switch (gerb_fgetc(fd)) {
1335 case 'A':
1336 image->format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1337 break;
1338 case 'I':
1339 image->format->coordinate = GERBV_COORDINATE_INCREMENTAL;
1340 break;
1341 default:
1342 string = g_strdup_printf("Invalid coordinate type defined in format code in file \n%s\n",
1343 fd->filename);
1344 gerbv_stats_add_error(stats->error_list,
1346 string,
1347 GERBV_MESSAGE_ERROR);
1348 g_free(string);
1349 string = g_strdup_printf("Defaulting to absolute coordinates.\n");
1350 gerbv_stats_add_error(stats->error_list,
1352 string,
1353 GERBV_MESSAGE_WARNING);
1354 g_free(string);
1355 image->format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1357 op[0] = gerb_fgetc(fd);
1358 while((op[0] != '*')&&(op[0] != EOF)) {
1359 switch (op[0]) {
1360 case 'N':
1361 op[0] = (char)gerb_fgetc(fd);
1362 image->format->lim_seqno = op[0] - '0';
1363 break;
1364 case 'G':
1365 op[0] = (char)gerb_fgetc(fd);
1366 image->format->lim_gf = op[0] - '0';
1367 break;
1368 case 'D':
1369 op[0] = (char)gerb_fgetc(fd);
1370 image->format->lim_pf = op[0] - '0';
1371 break;
1372 case 'M':
1373 op[0] = (char)gerb_fgetc(fd);
1374 image->format->lim_mf = op[0] - '0';
1375 break;
1376 case 'X' :
1377 op[0] = gerb_fgetc(fd);
1378 if ((op[0] < '0') || (op[0] > '6')) {
1379 string = g_strdup_printf("Illegal format size %c in file \n%s\n",
1380 (char)op[0], fd->filename);
1381 gerbv_stats_add_error(stats->error_list,
1383 string,
1384 GERBV_MESSAGE_ERROR);
1385 g_free(string);
1387 image->format->x_int = op[0] - '0';
1388 op[0] = gerb_fgetc(fd);
1389 if ((op[0] < '0') || (op[0] > '6')) {
1390 string = g_strdup_printf("Illegal format size %c in file \n%s\n",
1391 (char)op[0], fd->filename);
1392 gerbv_stats_add_error(stats->error_list,
1394 string,
1395 GERBV_MESSAGE_ERROR);
1396 g_free(string);
1398 image->format->x_dec = op[0] - '0';
1399 break;
1400 case 'Y':
1401 op[0] = gerb_fgetc(fd);
1402 if ((op[0] < '0') || (op[0] > '6')) {
1403 string = g_strdup_printf("Illegal format size %c in file \n%s\n",
1404 (char)op[0], fd->filename);
1405 gerbv_stats_add_error(stats->error_list,
1407 string,
1408 GERBV_MESSAGE_ERROR);
1409 g_free(string);
1411 image->format->y_int = op[0] - '0';
1412 op[0] = gerb_fgetc(fd);
1413 if ((op[0] < '0') || (op[0] > '6')) {
1414 string = g_strdup_printf("Illegal format size %c in file \n%s\n",
1415 (char)op[0], fd->filename);
1416 gerbv_stats_add_error(stats->error_list,
1418 string,
1419 GERBV_MESSAGE_ERROR);
1420 g_free(string);
1422 image->format->y_dec = op[0] - '0';
1423 break;
1424 default :
1425 string = g_strdup_printf("Illegal format statement [%c] in file \n%s\n",
1426 op[0], fd->filename);
1427 gerbv_stats_add_error(stats->error_list,
1429 string,
1430 GERBV_MESSAGE_ERROR);
1431 g_free(string);
1432 string = g_strdup_printf("Ignoring invalid format statement.\n");
1433 gerbv_stats_add_error(stats->error_list,
1435 string,
1436 GERBV_MESSAGE_WARNING);
1437 g_free(string);
1439 op[0] = gerb_fgetc(fd);
1441 break;
1442 case A2I('M','I'): /* Mirror Image */
1443 op[0] = gerb_fgetc(fd);
1444 state->state = gerbv_image_return_new_netstate (state->state);
1446 while ((op[0] != '*')&&(op[0] != EOF)) {
1447 gint readValue=0;
1448 switch (op[0]) {
1449 case 'A' :
1450 readValue = gerb_fgetint(fd, NULL);
1451 if (readValue == 1) {
1452 if (state->state->mirrorState == GERBV_MIRROR_STATE_FLIPB)
1453 state->state->mirrorState=GERBV_MIRROR_STATE_FLIPAB;
1454 else
1455 state->state->mirrorState=GERBV_MIRROR_STATE_FLIPA;
1457 break;
1458 case 'B' :
1459 readValue = gerb_fgetint(fd, NULL);
1460 if (readValue == 1) {
1461 if (state->state->mirrorState == GERBV_MIRROR_STATE_FLIPA)
1462 state->state->mirrorState=GERBV_MIRROR_STATE_FLIPAB;
1463 else
1464 state->state->mirrorState=GERBV_MIRROR_STATE_FLIPB;
1466 break;
1467 default :
1468 string = g_strdup_printf("Wrong character in mirror:%c\n", op[0]);
1469 gerbv_stats_add_error(stats->error_list,
1471 string,
1472 GERBV_MESSAGE_ERROR);
1473 g_free(string);
1475 op[0] = gerb_fgetc(fd);
1477 break;
1478 case A2I('M','O'): /* Mode of Units */
1479 op[0] = gerb_fgetc(fd);
1480 op[1] = gerb_fgetc(fd);
1482 if ((op[0] == EOF) || (op[1] == EOF))
1483 gerbv_stats_add_error(stats->error_list,
1485 "Unexpected EOF found.\n",
1486 GERBV_MESSAGE_ERROR);
1487 switch (A2I(op[0],op[1])) {
1488 case A2I('I','N'):
1489 state->state = gerbv_image_return_new_netstate (state->state);
1490 state->state->unit = GERBV_UNIT_INCH;
1491 break;
1492 case A2I('M','M'):
1493 state->state = gerbv_image_return_new_netstate (state->state);
1494 state->state->unit = GERBV_UNIT_MM;
1495 break;
1496 default:
1497 string = g_strdup_printf("Illegal unit:%c%c\n", op[0], op[1]);
1498 gerbv_stats_add_error(stats->error_list,
1500 string,
1501 GERBV_MESSAGE_ERROR);
1502 g_free(string);
1504 break;
1505 case A2I('O','F'): /* Offset */
1506 op[0] = gerb_fgetc(fd);
1508 while ((op[0] != '*')&&(op[0] != EOF)) {
1509 switch (op[0]) {
1510 case 'A' :
1511 state->state->offsetA = gerb_fgetdouble(fd) / scale;
1512 break;
1513 case 'B' :
1514 state->state->offsetB = gerb_fgetdouble(fd) / scale;
1515 break;
1516 default :
1517 string = g_strdup_printf("Wrong character in offset:%c\n", op[0]);
1518 gerbv_stats_add_error(stats->error_list,
1520 string,
1521 GERBV_MESSAGE_ERROR);
1522 g_free(string);
1524 op[0] = gerb_fgetc(fd);
1526 break;
1527 case A2I('I','F'): /* Include file */
1529 gchar *includeFilename = gerb_fgetstring(fd, '*');
1531 if (includeFilename) {
1532 gchar *fullPath;
1533 if (!g_path_is_absolute(includeFilename)) {
1534 fullPath = g_build_filename (directoryPath, includeFilename, NULL);
1535 } else {
1536 fullPath = g_strdup (includeFilename);
1538 if (levelOfRecursion < 10) {
1539 gerb_file_t *includefd = NULL;
1541 includefd = gerb_fopen(fullPath);
1542 if (includefd) {
1543 gerber_parse_file_segment (levelOfRecursion + 1, image, state, curr_net, stats, includefd, directoryPath);
1544 gerb_fclose(includefd);
1545 } else {
1546 string = g_strdup_printf("In file %s,\nIncluded file %s cannot be found\n",
1547 fd->filename, fullPath);
1548 gerbv_stats_add_error(stats->error_list,
1550 string,
1551 GERBV_MESSAGE_ERROR);
1552 g_free(string);
1554 g_free (fullPath);
1555 } else {
1556 string = g_strdup_printf("Parser encountered more than 10 levels of include file recursion which is not allowed by the RS-274X spec\n");
1557 gerbv_stats_add_error(stats->error_list,
1559 string,
1560 GERBV_MESSAGE_ERROR);
1561 g_free(string);
1566 break;
1567 case A2I('I','O'): /* Image offset */
1568 op[0] = gerb_fgetc(fd);
1570 while ((op[0] != '*')&&(op[0] != EOF)) {
1571 switch (op[0]) {
1572 case 'A' :
1573 image->info->offsetA = gerb_fgetdouble(fd) / scale;
1574 break;
1575 case 'B' :
1576 image->info->offsetB = gerb_fgetdouble(fd) / scale;
1577 break;
1578 default :
1579 string = g_strdup_printf("In file %s,\nwrong character in image offset %c\n",
1580 fd->filename, op[0]);
1581 gerbv_stats_add_error(stats->error_list,
1583 string,
1584 GERBV_MESSAGE_ERROR);
1585 g_free(string);
1587 op[0] = gerb_fgetc(fd);
1589 break;
1590 case A2I('S','F'): /* Scale Factor */
1591 if (gerb_fgetc(fd) == 'A')
1592 state->state->scaleA = gerb_fgetdouble(fd);
1593 else
1594 gerb_ungetc(fd);
1595 if (gerb_fgetc(fd) == 'B')
1596 state->state->scaleB = gerb_fgetdouble(fd);
1597 else
1598 gerb_ungetc(fd);
1599 break;
1600 case A2I('I','C'): /* Input Code */
1601 /* Thanks to Stephen Adam for providing this information. As he writes:
1602 * btw, here's a logic puzzle for you. If you need to
1603 * read the gerber file to see how it's encoded, then
1604 * how can you read it?
1606 op[0] = gerb_fgetc(fd);
1607 op[1] = gerb_fgetc(fd);
1609 if ((op[0] == EOF) || (op[1] == EOF)) {
1610 string = g_strdup_printf("Unexpected EOF found in file \n%s\n", fd->filename);
1611 gerbv_stats_add_error(stats->error_list,
1613 string,
1614 GERBV_MESSAGE_ERROR);
1615 g_free(string);
1617 switch (A2I(op[0],op[1])) {
1618 case A2I('A','S'):
1619 image->info->encoding = GERBV_ENCODING_ASCII;
1620 break;
1621 case A2I('E','B'):
1622 image->info->encoding = GERBV_ENCODING_EBCDIC;
1623 break;
1624 case A2I('B','C'):
1625 image->info->encoding = GERBV_ENCODING_BCD;
1626 break;
1627 case A2I('I','S'):
1628 image->info->encoding = GERBV_ENCODING_ISO_ASCII;
1629 break;
1630 case A2I('E','I'):
1631 image->info->encoding = GERBV_ENCODING_EIA;
1632 break;
1633 default:
1634 string = g_strdup_printf("In file %s, \nunknown input code (IC): %c%c\n",
1635 fd->filename, op[0], op[1]);
1636 gerbv_stats_add_error(stats->error_list,
1638 string,
1639 GERBV_MESSAGE_ERROR);
1640 g_free(string);
1642 break;
1644 /* Image parameters */
1645 case A2I('I','J'): /* Image Justify */
1646 op[0] = gerb_fgetc(fd);
1647 image->info->imageJustifyTypeA = GERBV_JUSTIFY_LOWERLEFT;
1648 image->info->imageJustifyTypeB = GERBV_JUSTIFY_LOWERLEFT;
1649 image->info->imageJustifyOffsetA = 0.0;
1650 image->info->imageJustifyOffsetB = 0.0;
1651 while ((op[0] != '*')&&(op[0] != EOF)) {
1652 switch (op[0]) {
1653 case 'A' :
1654 op[0] = gerb_fgetc(fd);
1655 if (op[0] == 'C') {
1656 image->info->imageJustifyTypeA = GERBV_JUSTIFY_CENTERJUSTIFY;
1657 } else if (op[0] == 'L') {
1658 image->info->imageJustifyTypeA = GERBV_JUSTIFY_LOWERLEFT;
1659 } else {
1660 gerb_ungetc (fd);
1661 image->info->imageJustifyOffsetA = gerb_fgetdouble(fd) / scale;
1663 break;
1664 case 'B' :
1665 op[0] = gerb_fgetc(fd);
1666 if (op[0] == 'C') {
1667 image->info->imageJustifyTypeB = GERBV_JUSTIFY_CENTERJUSTIFY;
1668 } else if (op[0] == 'L') {
1669 image->info->imageJustifyTypeB = GERBV_JUSTIFY_LOWERLEFT;
1670 } else {
1671 gerb_ungetc (fd);
1672 image->info->imageJustifyOffsetB = gerb_fgetdouble(fd) / scale;
1674 break;
1675 default :
1676 string = g_strdup_printf("In file %s,\nwrong character in image justify:%c\n",
1677 fd->filename, op[0]);
1678 gerbv_stats_add_error(stats->error_list,
1680 string,
1681 GERBV_MESSAGE_ERROR);
1682 g_free(string);
1684 op[0] = gerb_fgetc(fd);
1686 break;
1687 case A2I('I','N'): /* Image Name */
1688 image->info->name = gerb_fgetstring(fd, '*');
1689 break;
1690 case A2I('I','P'): /* Image Polarity */
1692 for (ano = 0; ano < 3; ano++) {
1693 op[0] = gerb_fgetc(fd);
1694 if (op[0] == EOF) {
1695 string = g_strdup_printf("In file %s,\nunexpected EOF while reading image polarity (IP)\n",
1696 fd->filename);
1697 gerbv_stats_add_error(stats->error_list,
1699 string,
1700 GERBV_MESSAGE_ERROR);
1701 g_free(string);
1703 str[ano] = (char)op[0];
1706 if (strncmp(str, "POS", 3) == 0)
1707 image->info->polarity = GERBV_POLARITY_POSITIVE;
1708 else if (strncmp(str, "NEG", 3) == 0)
1709 image->info->polarity = GERBV_POLARITY_NEGATIVE;
1710 else {
1711 string = g_strdup_printf("Unknown polarity : %c%c%c\n", str[0], str[1], str[2]);
1712 gerbv_stats_add_error(stats->error_list,
1714 string,
1715 GERBV_MESSAGE_ERROR);
1716 g_free(string);
1718 break;
1719 case A2I('I','R'): /* Image Rotation */
1720 tmp = gerb_fgetint(fd, NULL) % 360;
1721 if (tmp == 0)
1722 image->info->imageRotation = 0.0;
1723 else if (tmp == 90)
1724 image->info->imageRotation = M_PI / 2.0;
1725 else if (tmp == 180)
1726 image->info->imageRotation = M_PI;
1727 else if (tmp == 270)
1728 image->info->imageRotation = 3.0 * M_PI / 2.0;
1729 else {
1730 string = g_strdup_printf("Image rotation must be 0, 90, 180 or 270 (is actually %d)\n", tmp);
1731 gerbv_stats_add_error(stats->error_list,
1733 string,
1734 GERBV_MESSAGE_ERROR);
1735 g_free(string);
1737 break;
1738 case A2I('P','F'): /* Plotter Film */
1739 image->info->plotterFilm = gerb_fgetstring(fd, '*');
1740 break;
1742 /* Aperture parameters */
1743 case A2I('A','D'): /* Aperture Description */
1744 a = (gerbv_aperture_t *) g_new0 (gerbv_aperture_t,1);
1746 ano = parse_aperture_definition(fd, a, image, scale);
1747 if (ano == -1) {
1748 /* error with line parse, so just quietly ignore */
1750 else if ((ano >= 0) && (ano <= APERTURE_MAX)) {
1751 a->unit = state->state->unit;
1752 image->aperture[ano] = a;
1753 dprintf(" In parse_rs274x, adding new aperture to aperture list ...\n");
1754 gerbv_stats_add_aperture(stats->aperture_list,
1755 -1, ano,
1756 a->type,
1757 a->parameter);
1758 gerbv_stats_add_to_D_list(stats->D_code_list,
1759 ano);
1760 if (ano < APERTURE_MIN) {
1761 string = g_strdup_printf("In file %s,\naperture number out of bounds : %d\n",
1762 fd->filename, ano);
1763 gerbv_stats_add_error(stats->error_list,-1, string, GERBV_MESSAGE_ERROR);
1765 } else {
1766 string = g_strdup_printf("In file %s,\naperture number out of bounds : %d\n",
1767 fd->filename, ano);
1768 gerbv_stats_add_error(stats->error_list,
1770 string,
1771 GERBV_MESSAGE_ERROR);
1772 g_free(string);
1774 /* Add aperture info to stats->aperture_list here */
1776 break;
1777 case A2I('A','M'): /* Aperture Macro */
1778 tmp_amacro = image->amacro;
1779 image->amacro = parse_aperture_macro(fd);
1780 if (image->amacro) {
1781 image->amacro->next = tmp_amacro;
1782 #ifdef AMACRO_DEBUG
1783 print_program(image->amacro);
1784 #endif
1785 } else {
1786 string = g_strdup_printf("In file %s, \nfailed to parse aperture macro\n",
1787 fd->filename);
1788 gerbv_stats_add_error(stats->error_list,
1790 string,
1791 GERBV_MESSAGE_ERROR);
1792 g_free(string);
1794 // return, since we want to skip the later back-up loop
1795 return;
1796 /* Layer */
1797 case A2I('L','N'): /* Layer Name */
1798 state->layer = gerbv_image_return_new_layer (state->layer);
1799 state->layer->name = gerb_fgetstring(fd, '*');
1800 break;
1801 case A2I('L','P'): /* Layer Polarity */
1802 state->layer = gerbv_image_return_new_layer (state->layer);
1803 switch (gerb_fgetc(fd)) {
1804 case 'D': /* Dark Polarity (default) */
1805 state->layer->polarity = GERBV_POLARITY_DARK;
1806 break;
1807 case 'C': /* Clear Polarity */
1808 state->layer->polarity = GERBV_POLARITY_CLEAR;
1809 break;
1810 default:
1811 string = g_strdup_printf("In file %s,\nunknown Layer Polarity: %c\n",
1812 fd->filename, op[0]);
1813 gerbv_stats_add_error(stats->error_list,
1815 string,
1816 GERBV_MESSAGE_ERROR);
1817 g_free(string);
1819 break;
1820 case A2I('K','O'): /* Knock Out */
1821 state->layer = gerbv_image_return_new_layer (state->layer);
1822 gerber_update_any_running_knockout_measurements (image);
1823 /* reset any previous knockout measurements */
1824 knockoutMeasure = FALSE;
1825 op[0] = gerb_fgetc(fd);
1826 if (op[0] == '*') { /* Disable previous SR parameters */
1827 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_NOKNOCKOUT;
1828 break;
1829 } else if (op[0] == 'C') {
1830 state->layer->knockout.polarity = GERBV_POLARITY_CLEAR;
1831 } else if (op[0] == 'D') {
1832 state->layer->knockout.polarity = GERBV_POLARITY_DARK;
1833 } else {
1834 string = g_strdup_printf("In file %s,\nknockout must supply a polarity (C, D, or *)\n",
1835 fd->filename);
1836 gerbv_stats_add_error(stats->error_list,
1838 string,
1839 GERBV_MESSAGE_ERROR);
1840 g_free(string);
1842 state->layer->knockout.lowerLeftX = 0.0;
1843 state->layer->knockout.lowerLeftY = 0.0;
1844 state->layer->knockout.width = 0.0;
1845 state->layer->knockout.height = 0.0;
1846 state->layer->knockout.border = 0.0;
1847 state->layer->knockout.firstInstance = TRUE;
1848 op[0] = gerb_fgetc(fd);
1849 while ((op[0] != '*')&&(op[0] != EOF)) {
1850 switch (op[0]) {
1851 case 'X':
1852 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1853 state->layer->knockout.lowerLeftX = gerb_fgetdouble(fd) / scale;
1854 break;
1855 case 'Y':
1856 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1857 state->layer->knockout.lowerLeftY = gerb_fgetdouble(fd) / scale;
1858 break;
1859 case 'I':
1860 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1861 state->layer->knockout.width = gerb_fgetdouble(fd) / scale;
1862 break;
1863 case 'J':
1864 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1865 state->layer->knockout.height = gerb_fgetdouble(fd) / scale;
1866 break;
1867 case 'K':
1868 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_BORDER;
1869 state->layer->knockout.border = gerb_fgetdouble(fd) / scale;
1870 /* this is a bordered knockout, so we need to start measuring the
1871 size of a square bordering all future components */
1872 knockoutMeasure = TRUE;
1873 knockoutLimitXmin = HUGE_VAL;
1874 knockoutLimitYmin = HUGE_VAL;
1875 knockoutLimitXmax = -HUGE_VAL;
1876 knockoutLimitYmax = -HUGE_VAL;
1877 knockoutLayer = state->layer;
1878 break;
1879 default:
1880 string = g_strdup_printf("In file %s, \nunknown variable in knockout",
1881 fd->filename);
1882 gerbv_stats_add_error(stats->error_list,
1884 string,
1885 GERBV_MESSAGE_ERROR);
1886 g_free(string);
1888 op[0] = gerb_fgetc(fd);
1890 break;
1891 case A2I('S','R'): /* Step and Repeat */
1892 /* start by generating a new layer (duplicating previous layer settings */
1893 state->layer = gerbv_image_return_new_layer (state->layer);
1894 op[0] = gerb_fgetc(fd);
1895 if (op[0] == '*') { /* Disable previous SR parameters */
1896 state->layer->stepAndRepeat.X = 1;
1897 state->layer->stepAndRepeat.Y = 1;
1898 state->layer->stepAndRepeat.dist_X = 0.0;
1899 state->layer->stepAndRepeat.dist_Y = 0.0;
1900 break;
1902 while ((op[0] != '*')&&(op[0] != EOF)) {
1903 switch (op[0]) {
1904 case 'X':
1905 state->layer->stepAndRepeat.X = gerb_fgetint(fd, NULL);
1906 break;
1907 case 'Y':
1908 state->layer->stepAndRepeat.Y = gerb_fgetint(fd, NULL);
1909 break;
1910 case 'I':
1911 state->layer->stepAndRepeat.dist_X = gerb_fgetdouble(fd) / scale;
1912 break;
1913 case 'J':
1914 state->layer->stepAndRepeat.dist_Y = gerb_fgetdouble(fd) / scale;
1915 break;
1916 default:
1917 string = g_strdup_printf("In file %s,\nstep-and-repeat parameter error\n",
1918 fd->filename);
1919 gerbv_stats_add_error(stats->error_list,
1921 string,
1922 GERBV_MESSAGE_ERROR);
1923 g_free(string);
1927 * Repeating 0 times in any direction would disable the whole plot, and
1928 * is probably not intended. At least one other tool (viewmate) seems
1929 * to interpret 0-time repeating as repeating just once too.
1931 if(state->layer->stepAndRepeat.X == 0)
1932 state->layer->stepAndRepeat.X = 1;
1933 if(state->layer->stepAndRepeat.Y == 0)
1934 state->layer->stepAndRepeat.Y = 1;
1936 op[0] = gerb_fgetc(fd);
1938 break;
1939 /* is this an actual RS274X command?? It isn't explainined in the spec... */
1940 case A2I('R','O'):
1941 state->layer = gerbv_image_return_new_layer (state->layer);
1943 state->layer->rotation = gerb_fgetdouble(fd) * M_PI / 180;
1944 op[0] = gerb_fgetc(fd);
1945 if (op[0] != '*') {
1946 string = g_strdup_printf("In file %s,\nerror in layer rotation command\n",
1947 fd->filename);
1948 gerbv_stats_add_error(stats->error_list,
1950 string,
1951 GERBV_MESSAGE_ERROR);
1952 g_free(string);
1954 break;
1955 default:
1956 string = g_strdup_printf("In file %s,\nunknown RS-274X extension found %%%c%c%%\n",
1957 fd->filename, op[0], op[1]);
1958 gerbv_stats_add_error(stats->error_list,
1960 string,
1961 GERBV_MESSAGE_ERROR);
1962 g_free(string);
1964 // make sure we read until the trailing * character
1965 // first, backspace once in case we already read the trailing *
1966 fd->ptr--;
1967 int c = gerb_fgetc(fd);
1968 while ((c != EOF) && (c != '*'))
1969 c = gerb_fgetc(fd);
1970 return;
1971 } /* parse_rs274x */
1975 * Stack declarations and operations to be used by the simple engine that
1976 * executes the parsed aperture macros.
1978 typedef struct {
1979 double *stack;
1980 int sp;
1981 } macro_stack_t;
1984 static macro_stack_t *
1985 new_stack(unsigned int stack_size)
1987 macro_stack_t *s;
1989 s = (macro_stack_t *) g_new0 (macro_stack_t,1);
1990 s->stack = (double *) g_new0 (double, stack_size);
1991 s->sp = 0;
1992 return s;
1993 } /* new_stack */
1996 static void
1997 free_stack(macro_stack_t *s)
1999 if (s && s->stack)
2000 free(s->stack);
2002 if (s)
2003 free(s);
2005 return;
2006 } /* free_stack */
2009 static void
2010 push(macro_stack_t *s, double val)
2012 s->stack[s->sp++] = val;
2013 return;
2014 } /* push */
2017 static int
2018 pop(macro_stack_t *s, double *value)
2020 /* Check if we try to pop an empty stack */
2021 if (s->sp == 0) {
2022 return -1;
2025 *value = s->stack[--s->sp];
2026 return 0;
2027 } /* pop */
2030 /* ------------------------------------------------------------------ */
2031 static int
2032 simplify_aperture_macro(gerbv_aperture_t *aperture, gdouble scale)
2034 const int extra_stack_size = 10;
2035 macro_stack_t *s;
2036 gerbv_instruction_t *ip;
2037 int handled = 1, nuf_parameters = 0, i, j, clearOperatorUsed = FALSE;
2038 double *lp; /* Local copy of parameters */
2039 double tmp[2] = {0.0, 0.0};
2040 gerbv_aperture_type_t type = GERBV_APTYPE_NONE;
2041 gerbv_simplified_amacro_t *sam;
2043 if (aperture == NULL)
2044 GERB_FATAL_ERROR("aperture NULL in simplify aperture macro\n");
2046 if (aperture->amacro == NULL)
2047 GERB_FATAL_ERROR("aperture->amacro NULL in simplify aperture macro\n");
2049 /* Allocate stack for VM */
2050 s = new_stack(aperture->amacro->nuf_push + extra_stack_size);
2051 if (s == NULL)
2052 GERB_FATAL_ERROR("malloc stack failed\n");
2054 /* Make a copy of the parameter list that we can rewrite if necessary */
2055 lp = g_new (double,APERTURE_PARAMETERS_MAX);
2057 memcpy(lp, aperture->parameter, sizeof(double) * APERTURE_PARAMETERS_MAX);
2059 for(ip = aperture->amacro->program; ip != NULL; ip = ip->next) {
2060 switch(ip->opcode) {
2061 case GERBV_OPCODE_NOP:
2062 break;
2063 case GERBV_OPCODE_PUSH :
2064 push(s, ip->data.fval);
2065 break;
2066 case GERBV_OPCODE_PPUSH :
2067 push(s, lp[ip->data.ival - 1]);
2068 break;
2069 case GERBV_OPCODE_PPOP:
2070 if (pop(s, &tmp[0]) < 0)
2071 GERB_FATAL_ERROR("Tried to pop an empty stack");
2072 lp[ip->data.ival - 1] = tmp[0];
2073 break;
2074 case GERBV_OPCODE_ADD :
2075 if (pop(s, &tmp[0]) < 0)
2076 GERB_FATAL_ERROR("Tried to pop an empty stack");
2077 if (pop(s, &tmp[1]) < 0)
2078 GERB_FATAL_ERROR("Tried to pop an empty stack");
2079 push(s, tmp[1] + tmp[0]);
2080 break;
2081 case GERBV_OPCODE_SUB :
2082 if (pop(s, &tmp[0]) < 0)
2083 GERB_FATAL_ERROR("Tried to pop an empty stack");
2084 if (pop(s, &tmp[1]) < 0)
2085 GERB_FATAL_ERROR("Tried to pop an empty stack");
2086 push(s, tmp[1] - tmp[0]);
2087 break;
2088 case GERBV_OPCODE_MUL :
2089 if (pop(s, &tmp[0]) < 0)
2090 GERB_FATAL_ERROR("Tried to pop an empty stack");
2091 if (pop(s, &tmp[1]) < 0)
2092 GERB_FATAL_ERROR("Tried to pop an empty stack");
2093 push(s, tmp[1] * tmp[0]);
2094 break;
2095 case GERBV_OPCODE_DIV :
2096 if (pop(s, &tmp[0]) < 0)
2097 GERB_FATAL_ERROR("Tried to pop an empty stack");
2098 if (pop(s, &tmp[1]) < 0)
2099 GERB_FATAL_ERROR("Tried to pop an empty stack");
2100 push(s, tmp[1] / tmp[0]);
2101 break;
2102 case GERBV_OPCODE_PRIM :
2104 * This handles the exposure thing in the aperture macro
2105 * The exposure is always the first element on stack independent
2106 * of aperture macro.
2108 switch(ip->data.ival) {
2109 case 1:
2110 dprintf(" Aperture macro circle [1] (");
2111 type = GERBV_APTYPE_MACRO_CIRCLE;
2112 nuf_parameters = 4;
2113 break;
2114 case 3:
2115 break;
2116 case 4 :
2117 dprintf(" Aperture macro outline [4] (");
2118 type = GERBV_APTYPE_MACRO_OUTLINE;
2120 * Number of parameters are:
2121 * - number of points defined in entry 1 of the stack +
2122 * start point. Times two since it is both X and Y.
2123 * - Then three more; exposure, nuf points and rotation.
2125 nuf_parameters = ((int)s->stack[1] + 1) * 2 + 3;
2126 break;
2127 case 5 :
2128 dprintf(" Aperture macro polygon [5] (");
2129 type = GERBV_APTYPE_MACRO_POLYGON;
2130 nuf_parameters = 6;
2131 break;
2132 case 6 :
2133 dprintf(" Aperture macro moiré [6] (");
2134 type = GERBV_APTYPE_MACRO_MOIRE;
2135 nuf_parameters = 9;
2136 break;
2137 case 7 :
2138 dprintf(" Aperture macro thermal [7] (");
2139 type = GERBV_APTYPE_MACRO_THERMAL;
2140 nuf_parameters = 6;
2141 break;
2142 case 2 :
2143 case 20 :
2144 dprintf(" Aperture macro line 20/2 (");
2145 type = GERBV_APTYPE_MACRO_LINE20;
2146 nuf_parameters = 7;
2147 break;
2148 case 21 :
2149 dprintf(" Aperture macro line 21 (");
2150 type = GERBV_APTYPE_MACRO_LINE21;
2151 nuf_parameters = 6;
2152 break;
2153 case 22 :
2154 dprintf(" Aperture macro line 22 (");
2155 type = GERBV_APTYPE_MACRO_LINE22;
2156 nuf_parameters = 6;
2157 break;
2158 default :
2159 handled = 0;
2162 if (type != GERBV_APTYPE_NONE) {
2163 if (nuf_parameters > APERTURE_PARAMETERS_MAX) {
2164 GERB_COMPILE_ERROR("Number of parameters to aperture macro are more than gerbv is able to store\n");
2168 * Create struct for simplified aperture macro and
2169 * start filling in the blanks.
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);
2179 /* convert any mm values to inches */
2180 switch (type) {
2181 case GERBV_APTYPE_MACRO_CIRCLE:
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;
2188 case GERBV_APTYPE_MACRO_OUTLINE:
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;
2194 break;
2195 case GERBV_APTYPE_MACRO_POLYGON:
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;
2202 case GERBV_APTYPE_MACRO_MOIRE:
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;
2211 case GERBV_APTYPE_MACRO_THERMAL:
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;
2218 case GERBV_APTYPE_MACRO_LINE20:
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;
2227 case GERBV_APTYPE_MACRO_LINE21:
2228 case GERBV_APTYPE_MACRO_LINE22:
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;
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.
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;
2252 tmp_sam->next = sam;
2255 #ifdef DEBUG
2256 for (i = 0; i < nuf_parameters; i++) {
2257 dprintf("%f, ", s->stack[i]);
2259 #endif /* DEBUG */
2260 dprintf(")\n");
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.
2269 s->sp = 0;
2270 break;
2271 default :
2272 break;
2275 free_stack(s);
2276 g_free (lp);
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 */
2285 /* ------------------------------------------------------------------ */
2286 static int
2287 parse_aperture_definition(gerb_file_t *fd, gerbv_aperture_t *aperture,
2288 gerbv_image_t *image, gdouble scale)
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;
2299 if (gerb_fgetc(fd) != 'D') {
2300 string = g_strdup_printf("Found AD code with no following 'D' in file \n%s\n",
2301 fd->filename);
2302 gerbv_stats_add_error(stats->error_list,
2304 string,
2305 GERBV_MESSAGE_ERROR);
2306 g_free(string);
2307 return -1;
2311 * Get aperture no
2313 ano = gerb_fgetint(fd, NULL);
2316 * Read in the whole aperture defintion and tokenize it
2318 ad = gerb_fgetstring(fd, '*');
2319 token = strtok(ad, ",");
2321 if (token == NULL) {
2322 string = g_strdup_printf("Invalid aperture definition in file \n%s\n",
2323 fd->filename);
2324 gerbv_stats_add_error(stats->error_list,
2326 string,
2327 GERBV_MESSAGE_ERROR);
2328 g_free(string);
2329 return -1;
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;
2346 /* Here a should a T be defined, but I don't know what it represents */
2347 } else {
2348 aperture->type = GERBV_APTYPE_MACRO;
2350 * In aperture definition, point to the aperture macro
2351 * used in the defintion
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;
2360 curr_amacro = curr_amacro->next;
2365 * Parse all parameters
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("In file %s,\nmaximum number of allowed parameters exceeded in aperture %d\n",
2371 fd->filename, ano);
2372 gerbv_stats_add_error(stats->error_list,
2374 string,
2375 GERBV_MESSAGE_ERROR);
2376 g_free(string);
2377 break;
2379 errno = 0;
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;
2389 aperture->parameter[i] = tempHolder;
2390 if (errno) {
2391 string = g_strdup_printf("Failed to read all parameters exceeded in aperture %d\n", ano);
2392 gerbv_stats_add_error(stats->error_list,
2394 string,
2395 GERBV_MESSAGE_WARNING);
2396 g_free(string);
2397 aperture->parameter[i] = 0.0;
2401 aperture->nuf_parameters = i;
2403 gerb_ungetc(fd);
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");
2412 g_free(ad);
2414 return ano;
2415 } /* parse_aperture_definition */
2418 /* ------------------------------------------------------------------ */
2419 static void
2420 calc_cirseg_sq(struct gerbv_net *net, int cw,
2421 double delta_cp_x, double delta_cp_y)
2423 double d1x, d1y, d2x, d2y;
2424 double alfa, beta;
2425 int quadrant = 0;
2429 * Quadrant detection (based on ccw, converted below if cw)
2430 * Y ^
2431 * /!\
2433 * ---->X
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;
2449 * If clockwise, rotate quadrant
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\n");
2471 * Calculate arc center point
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\n", quadrant);
2495 * Some good values
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);
2502 alfa = atan2(d1y, d1x);
2503 beta = atan2(d2y, d2x);
2506 * Avoid divide by zero when sin(0) = 0 and cos(90) = 0
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));
2513 if (alfa < 0.000001 && beta < 0.000001) {
2514 net->cirseg->height = 0;
2517 #define RAD2DEG(a) (a * 180 / M_PI)
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\n", quadrant);
2540 if (net->cirseg->width < 0.0)
2541 GERB_COMPILE_WARNING("Negative width [%f] in quadrant %d [%f][%f]\n",
2542 net->cirseg->width, quadrant, alfa, beta);
2544 if (net->cirseg->height < 0.0)
2545 GERB_COMPILE_WARNING("Negative height [%f] in quadrant %d [%f][%f]\n",
2546 net->cirseg->height, quadrant, RAD2DEG(alfa), RAD2DEG(beta));
2548 return;
2550 } /* calc_cirseg_sq */
2553 /* ------------------------------------------------------------------ */
2554 static void
2555 calc_cirseg_mq(struct gerbv_net *net, int cw,
2556 double delta_cp_x, double delta_cp_y)
2558 double d1x, d1y, d2x, d2y;
2559 double alfa, beta;
2561 net->cirseg->cp_x = net->start_x + delta_cp_x;
2562 net->cirseg->cp_y = net->start_y + delta_cp_y;
2565 * Some good values
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;
2572 alfa = atan2(d1y, d1x);
2573 beta = atan2(d2y, d2x);
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;
2579 net->cirseg->angle1 = RAD2DEG(alfa);
2580 net->cirseg->angle2 = RAD2DEG(beta);
2583 * Make sure it's always positive angles
2585 if (net->cirseg->angle1 < 0.0) {
2586 net->cirseg->angle1 += 360.0;
2587 net->cirseg->angle2 += 360.0;
2590 if (net->cirseg->angle2 < 0.0)
2591 net->cirseg->angle2 += 360.0;
2593 if(net->cirseg->angle2 == 0.0)
2594 net->cirseg->angle2 = 360.0;
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.
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;
2611 return;
2612 } /* calc_cirseg_mq */
2615 static void
2616 gerber_update_any_running_knockout_measurements (gerbv_image_t *image)
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;
2628 static void
2629 gerber_calculate_final_justify_effects(gerbv_image_t *image)
2631 gdouble translateA = 0.0, translateB = 0.0;
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;
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;
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;
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 */
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;
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)
2680 gdouble ourX1 = x - apertureSizeX1, ourY1 = y - apertureSizeY1;
2681 gdouble ourX2 = x + apertureSizeX2, ourY2 = y + apertureSizeY2;
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);
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 */