don't update min/max bounding box when drawing with CLEAR polarity
[geda-gerbv.git] / src / gerber.c
blobd4a2aac8c1366a02ee8887a9e8c3c908e3289485
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 /* don't change the bounding box if the polarity is clear */
689 if (state->layer->polarity != GERBV_POLARITY_CLEAR){
690 gerber_update_image_min_max(&boundingBox, repeat_off_X, repeat_off_Y, image);
692 /* optionally update the knockout measurement box */
693 if (knockoutMeasure) {
694 if (boundingBox.left < knockoutLimitXmin)
695 knockoutLimitXmin = boundingBox.left;
696 if (boundingBox.right+repeat_off_X > knockoutLimitXmax)
697 knockoutLimitXmax = boundingBox.right+repeat_off_X;
698 if (boundingBox.bottom < knockoutLimitYmin)
699 knockoutLimitYmin = boundingBox.bottom;
700 if (boundingBox.top+repeat_off_Y > knockoutLimitYmax)
701 knockoutLimitYmax = boundingBox.top+repeat_off_Y;
703 /* if we're not in a polygon fill, then update the object bounding box */
704 if (!state->in_parea_fill) {
705 curr_net->boundingBox = boundingBox;
706 /* and reset the bounding box */
707 boundingBox.left = HUGE_VAL;
708 boundingBox.right = -HUGE_VAL;
709 boundingBox.bottom = HUGE_VAL;
710 boundingBox.top = -HUGE_VAL;
713 break;
714 case 10 : /* White space */
715 case 13 :
716 case ' ' :
717 case '\t' :
718 case 0 :
719 break;
720 default:
721 stats->unknown++;
722 string = g_strdup_printf("Found unknown character (whitespace?) [%d]%c\n",
723 read, read);
724 gerbv_stats_add_error(stats->error_list,
726 string,
727 GERBV_MESSAGE_ERROR);
728 g_free(string);
729 } /* switch((char) (read & 0xff)) */
731 return foundEOF;
735 /* ------------------------------------------------------------------ */
736 /*! This is a wrapper which gets called from top level. It
737 * does some initialization and pre-processing, and
738 * then calls gerber_parse_file_segment
739 * which processes the actual file. Then it does final
740 * modifications to the image created.
742 gerbv_image_t *
743 parse_gerb(gerb_file_t *fd, gchar *directoryPath)
745 gerb_state_t *state = NULL;
746 gerbv_image_t *image = NULL;
747 gerbv_net_t *curr_net = NULL;
748 gerbv_stats_t *stats;
749 gboolean foundEOF = FALSE;
750 gchar *string;
752 /* added by t.motylewski@bfad.de
753 * many locales redefine "." as "," and so on,
754 * so sscanf and strtod has problems when
755 * reading files using %f format */
756 setlocale(LC_NUMERIC, "C" );
759 * Create new state. This is used locally to keep track
760 * of the photoplotter's state as the Gerber is read in.
762 state = g_new0 (gerb_state_t, 1);
765 * Create new image. This will be returned.
767 image = gerbv_create_image(image, "RS274-X (Gerber) File");
768 if (image == NULL)
769 GERB_FATAL_ERROR("malloc image failed\n");
770 curr_net = image->netlist;
771 image->layertype = GERBV_LAYERTYPE_RS274X;
772 image->gerbv_stats = gerbv_stats_new();
773 if (image->gerbv_stats == NULL)
774 GERB_FATAL_ERROR("malloc gerbv_stats failed\n");
775 stats = (gerbv_stats_t *) image->gerbv_stats;
777 /* set active layer and netstate to point to first default one created */
778 state->layer = image->layers;
779 state->state = image->states;
780 curr_net->layer = state->layer;
781 curr_net->state = state->state;
784 * Start parsing
786 dprintf("In parse_gerb, starting to parse file...\n");
787 foundEOF = gerber_parse_file_segment (1, image, state, curr_net, stats,
788 fd, directoryPath);
790 if (!foundEOF) {
791 string = g_strdup_printf("File %s is missing Gerber EOF code.\n", fd->filename);
792 gerbv_stats_add_error(stats->error_list,
794 string,
795 GERBV_MESSAGE_ERROR);
796 g_free(string);
798 g_free(state);
800 dprintf(" ... done parsing Gerber file\n");
801 gerber_update_any_running_knockout_measurements (image);
802 gerber_calculate_final_justify_effects(image);
804 return image;
805 } /* parse_gerb */
808 /* ------------------------------------------------------------------- */
809 /*! Checks for signs that this is a RS-274X file
810 * Returns TRUE if it is, FALSE if not.
812 gboolean
813 gerber_is_rs274x_p(gerb_file_t *fd, gboolean *returnFoundBinary)
815 char *buf;
816 int len = 0;
817 char *letter;
818 int i;
819 gboolean found_binary = FALSE;
820 gboolean found_ADD = FALSE;
821 gboolean found_D0 = FALSE;
822 gboolean found_D2 = FALSE;
823 gboolean found_M0 = FALSE;
824 gboolean found_M2 = FALSE;
825 gboolean found_star = FALSE;
826 gboolean found_X = FALSE;
827 gboolean found_Y = FALSE;
829 dprintf ("gerber_is_rs274x_p(%p, %p), fd->fd = %p\n", fd, returnFoundBinary, fd->fd);
830 buf = (char *) g_malloc(MAXL);
831 if (buf == NULL)
832 GERB_FATAL_ERROR("malloc buf failed while checking for rs274x.\n");
834 while (fgets(buf, MAXL, fd->fd) != NULL) {
835 dprintf ("buf = \"%s\"\n", buf);
836 len = strlen(buf);
838 /* First look through the file for indications of its type by
839 * checking that file is not binary (non-printing chars and white
840 * spaces)
842 for (i = 0; i < len; i++) {
843 if (!isprint((int) buf[i]) && (buf[i] != '\r') &&
844 (buf[i] != '\n') && (buf[i] != '\t')) {
845 found_binary = TRUE;
846 dprintf ("found_binary (%d)\n", buf[i]);
849 if (g_strstr_len(buf, len, "%ADD")) {
850 found_ADD = TRUE;
851 dprintf ("found_ADD\n");
853 if (g_strstr_len(buf, len, "D00") || g_strstr_len(buf, len, "D0")) {
854 found_D0 = TRUE;
855 dprintf ("found_D0\n");
857 if (g_strstr_len(buf, len, "D02") || g_strstr_len(buf, len, "D2")) {
858 found_D2 = TRUE;
859 dprintf ("found_D2\n");
861 if (g_strstr_len(buf, len, "M00") || g_strstr_len(buf, len, "M0")) {
862 found_M0 = TRUE;
863 dprintf ("found_M0\n");
865 if (g_strstr_len(buf, len, "M02") || g_strstr_len(buf, len, "M2")) {
866 found_M2 = TRUE;
867 dprintf ("found_M2\n");
869 if (g_strstr_len(buf, len, "*")) {
870 found_star = TRUE;
871 dprintf ("found_star\n");
873 /* look for X<number> or Y<number> */
874 if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
875 if (isdigit((int) letter[1])) { /* grab char after X */
876 found_X = TRUE;
877 dprintf ("found_X\n");
880 if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
881 if (isdigit((int) letter[1])) { /* grab char after Y */
882 found_Y = TRUE;
883 dprintf ("found_Y\n");
887 rewind(fd->fd);
888 free(buf);
890 *returnFoundBinary = found_binary;
892 /* Now form logical expression determining if the file is RS-274X */
893 if ((found_D0 || found_D2 || found_M0 || found_M2) &&
894 found_ADD && found_star && (found_X || found_Y))
895 return TRUE;
898 return FALSE;
900 } /* gerber_is_rs274x */
903 /* ------------------------------------------------------------------- */
904 /*! Checks for signs that this is a RS-274D file
905 * Returns TRUE if it is, FALSE if not.
907 gboolean
908 gerber_is_rs274d_p(gerb_file_t *fd)
910 char *buf;
911 int len = 0;
912 char *letter;
913 int i;
914 gboolean found_binary = FALSE;
915 gboolean found_ADD = FALSE;
916 gboolean found_D0 = FALSE;
917 gboolean found_D2 = FALSE;
918 gboolean found_M0 = FALSE;
919 gboolean found_M2 = FALSE;
920 gboolean found_star = FALSE;
921 gboolean found_X = FALSE;
922 gboolean found_Y = FALSE;
924 buf = malloc(MAXL);
925 if (buf == NULL)
926 GERB_FATAL_ERROR("malloc buf failed while checking for rs274d.\n");
928 while (fgets(buf, MAXL, fd->fd) != NULL) {
929 len = strlen(buf);
931 /* First look through the file for indications of its type */
933 /* check that file is not binary (non-printing chars */
934 for (i = 0; i < len; i++) {
935 if (!isprint( (int) buf[i]) && (buf[i] != '\r') &&
936 (buf[i] != '\n') && (buf[i] != '\t')) {
937 found_binary = TRUE;
941 if (g_strstr_len(buf, len, "%ADD")) {
942 found_ADD = TRUE;
944 if (g_strstr_len(buf, len, "D00") || g_strstr_len(buf, len, "D0")) {
945 found_D0 = TRUE;
947 if (g_strstr_len(buf, len, "D02") || g_strstr_len(buf, len, "D2")) {
948 found_D2 = TRUE;
950 if (g_strstr_len(buf, len, "M00") || g_strstr_len(buf, len, "M0")) {
951 found_M0 = TRUE;
953 if (g_strstr_len(buf, len, "M02") || g_strstr_len(buf, len, "M2")) {
954 found_M2 = TRUE;
956 if (g_strstr_len(buf, len, "*")) {
957 found_star = TRUE;
959 /* look for X<number> or Y<number> */
960 if ((letter = g_strstr_len(buf, len, "X")) != NULL) {
961 /* grab char after X */
962 if (isdigit( (int) letter[1])) {
963 found_X = TRUE;
966 if ((letter = g_strstr_len(buf, len, "Y")) != NULL) {
967 /* grab char after Y */
968 if (isdigit( (int) letter[1])) {
969 found_Y = TRUE;
973 rewind(fd->fd);
974 free(buf);
976 /* Now form logical expression determining if the file is RS-274D */
977 if ((found_D0 || found_D2 || found_M0 || found_M2) &&
978 !found_ADD && found_star && (found_X || found_Y) &&
979 !found_binary)
980 return TRUE;
982 return FALSE;
984 } /* gerber_is_rs274d */
987 /* ------------------------------------------------------------------- */
988 /*! This function reads a G number and updates the current
989 * state. It also updates the G stats counters
991 static void
992 parse_G_code(gerb_file_t *fd, gerb_state_t *state, gerbv_image_t *image)
994 int op_int;
995 gerbv_format_t *format = image->format;
996 gerbv_stats_t *stats = image->gerbv_stats;
997 int c;
998 gchar *string;
1000 op_int=gerb_fgetint(fd, NULL);
1002 switch(op_int) {
1003 case 0: /* Move */
1004 /* Is this doing anything really? */
1005 stats->G0++;
1006 break;
1007 case 1: /* Linear Interpolation (1X scale) */
1008 state->interpolation = GERBV_INTERPOLATION_LINEARx1;
1009 stats->G1++;
1010 break;
1011 case 2: /* Clockwise Linear Interpolation */
1012 state->interpolation = GERBV_INTERPOLATION_CW_CIRCULAR;
1013 stats->G2++;
1014 break;
1015 case 3: /* Counter Clockwise Linear Interpolation */
1016 state->interpolation = GERBV_INTERPOLATION_CCW_CIRCULAR;
1017 stats->G3++;
1018 break;
1019 case 4: /* Ignore Data Block */
1020 /* Don't do anything, just read 'til * */
1021 /* SDB asks: Should we look for other codes while reading G04 in case
1022 * user forgot to put * at end of comment block? */
1023 c = gerb_fgetc(fd);
1024 while ((c != EOF) && (c != '*')) {
1025 c = gerb_fgetc(fd);
1027 stats->G4++;
1028 break;
1029 case 10: /* Linear Interpolation (10X scale) */
1030 state->interpolation = GERBV_INTERPOLATION_x10;
1031 stats->G10++;
1032 break;
1033 case 11: /* Linear Interpolation (0.1X scale) */
1034 state->interpolation = GERBV_INTERPOLATION_LINEARx01;
1035 stats->G11++;
1036 break;
1037 case 12: /* Linear Interpolation (0.01X scale) */
1038 state->interpolation = GERBV_INTERPOLATION_LINEARx001;
1039 stats->G12++;
1040 break;
1041 case 36: /* Turn on Polygon Area Fill */
1042 state->prev_interpolation = state->interpolation;
1043 state->interpolation = GERBV_INTERPOLATION_PAREA_START;
1044 state->changed = 1;
1045 stats->G36++;
1046 break;
1047 case 37: /* Turn off Polygon Area Fill */
1048 state->interpolation = GERBV_INTERPOLATION_PAREA_END;
1049 state->changed = 1;
1050 stats->G37++;
1051 break;
1052 case 54: /* Tool prepare */
1053 /* XXX Maybe uneccesary??? */
1054 if (gerb_fgetc(fd) == 'D') {
1055 int a = gerb_fgetint(fd, NULL);
1056 if ((a >= 0) && (a <= APERTURE_MAX)) {
1057 state->curr_aperture = a;
1058 } else {
1059 string = g_strdup_printf("Found aperture D%d out of bounds while parsing G code in file \n%s\n",
1060 a, fd->filename);
1061 gerbv_stats_add_error(stats->error_list,
1063 string,
1064 GERBV_MESSAGE_ERROR);
1065 g_free(string);
1067 } else {
1068 string = g_strdup_printf("Found unexpected code after G54 in file \n%s\n", fd->filename);
1069 gerbv_stats_add_error(stats->error_list,
1071 string,
1072 GERBV_MESSAGE_ERROR);
1073 g_free(string);
1074 /* Must insert error count here */
1076 stats->G54++;
1077 break;
1078 case 55: /* Prepare for flash */
1079 stats->G55++;
1080 break;
1081 case 70: /* Specify inches */
1082 state->state = gerbv_image_return_new_netstate (state->state);
1083 state->state->unit = GERBV_UNIT_INCH;
1084 stats->G70++;
1085 break;
1086 case 71: /* Specify millimeters */
1087 state->state = gerbv_image_return_new_netstate (state->state);
1088 state->state->unit = GERBV_UNIT_MM;
1089 stats->G71++;
1090 break;
1091 case 74: /* Disable 360 circular interpolation */
1092 state->mq_on = 0;
1093 stats->G74++;
1094 break;
1095 case 75: /* Enable 360 circular interpolation */
1096 state->mq_on = 1;
1097 stats->G75++;
1098 break;
1099 case 90: /* Specify absolut format */
1100 if (format) format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1101 stats->G90++;
1102 break;
1103 case 91: /* Specify incremental format */
1104 if (format) format->coordinate = GERBV_COORDINATE_INCREMENTAL;
1105 stats->G91++;
1106 break;
1107 default:
1108 string = g_strdup_printf("Encountered unknown G code G%d in file \n%s\n", op_int, fd->filename);
1109 gerbv_stats_add_error(stats->error_list,
1111 string,
1112 GERBV_MESSAGE_ERROR);
1113 g_free(string);
1114 string = g_strdup_printf("Ignorning unknown G code G%d\n", op_int);
1115 gerbv_stats_add_error(stats->error_list,
1117 string,
1118 GERBV_MESSAGE_WARNING);
1119 g_free(string);
1120 stats->G_unknown++;
1121 /* Enter error count here */
1122 break;
1125 return;
1126 } /* parse_G_code */
1129 /* ------------------------------------------------------------------ */
1130 /*! This function reads the numeric value of a D code and updates the
1131 * state. It also updates the D stats counters
1133 static void
1134 parse_D_code(gerb_file_t *fd, gerb_state_t *state, gerbv_image_t *image)
1136 int a;
1137 gerbv_stats_t *stats = image->gerbv_stats;
1138 gchar *string;
1140 a = gerb_fgetint(fd, NULL);
1141 dprintf(" In parse_D_code, found D number = %d ... \n", a);
1142 switch(a) {
1143 case 0 : /* Invalid code */
1144 string = g_strdup_printf("Found invalid D00 code in file \n%s.\n", fd->filename);
1145 gerbv_stats_add_error(stats->error_list,
1147 string,
1148 GERBV_MESSAGE_ERROR);
1149 g_free(string);
1150 stats->D_error++;
1151 break;
1152 case 1 : /* Exposure on */
1153 state->aperture_state = GERBV_APERTURE_STATE_ON;
1154 state->changed = 1;
1155 stats->D1++;
1156 break;
1157 case 2 : /* Exposure off */
1158 state->aperture_state = GERBV_APERTURE_STATE_OFF;
1159 state->changed = 1;
1160 stats->D2++;
1161 break;
1162 case 3 : /* Flash aperture */
1163 state->aperture_state = GERBV_APERTURE_STATE_FLASH;
1164 state->changed = 1;
1165 stats->D3++;
1166 break;
1167 default: /* Aperture in use */
1168 if ((a >= 0) && (a <= APERTURE_MAX)) {
1169 state->curr_aperture = a;
1171 } else {
1172 string = g_strdup_printf("Found out of bounds aperture D%d in file \n%s\n",
1173 a, fd->filename);
1174 gerbv_stats_add_error(stats->error_list,
1176 string,
1177 GERBV_MESSAGE_ERROR);
1178 g_free(string);
1179 stats->D_error++;
1181 state->changed = 0;
1182 break;
1185 return;
1186 } /* parse_D_code */
1189 /* ------------------------------------------------------------------ */
1190 static int
1191 parse_M_code(gerb_file_t *fd, gerbv_image_t *image)
1193 int op_int;
1194 gerbv_stats_t *stats = image->gerbv_stats;
1195 gchar *string;
1197 op_int=gerb_fgetint(fd, NULL);
1199 switch (op_int) {
1200 case 0: /* Program stop */
1201 stats->M0++;
1202 return 1;
1203 case 1: /* Optional stop */
1204 stats->M1++;
1205 return 2;
1206 case 2: /* End of program */
1207 stats->M2++;
1208 return 3;
1209 default:
1210 string = g_strdup_printf("Encountered unknown M code M%d in file \n%s\n",
1211 op_int, fd->filename);
1212 gerbv_stats_add_error(stats->error_list,
1214 string,
1215 GERBV_MESSAGE_ERROR);
1216 g_free(string);
1217 string = g_strdup_printf("Ignorning unknown M code M%d\n", op_int);
1218 gerbv_stats_add_error(stats->error_list,
1220 string,
1221 GERBV_MESSAGE_WARNING);
1222 g_free(string);
1223 stats->M_unknown++;
1225 return 0;
1226 } /* parse_M_code */
1229 /* ------------------------------------------------------------------ */
1230 static void
1231 parse_rs274x(gint levelOfRecursion, gerb_file_t *fd, gerbv_image_t *image,
1232 gerb_state_t *state, gerbv_net_t *curr_net, gerbv_stats_t *stats,
1233 gchar *directoryPath)
1235 int op[2];
1236 char str[3];
1237 int tmp;
1238 gerbv_aperture_t *a = NULL;
1239 gerbv_amacro_t *tmp_amacro;
1240 int ano;
1241 gdouble scale = 1.0;
1242 gchar *string;
1244 if (state->state->unit == GERBV_UNIT_MM)
1245 scale = 25.4;
1247 op[0] = gerb_fgetc(fd);
1248 op[1] = gerb_fgetc(fd);
1250 if ((op[0] == EOF) || (op[1] == EOF)) {
1251 string = g_strdup_printf("Unexpected EOF found in file \n%s\n", fd->filename);
1252 gerbv_stats_add_error(stats->error_list,
1254 string,
1255 GERBV_MESSAGE_ERROR);
1256 g_free(string);
1259 switch (A2I(op[0], op[1])){
1262 * Directive parameters
1264 case A2I('A','S'): /* Axis Select */
1265 op[0] = gerb_fgetc(fd);
1266 op[1] = gerb_fgetc(fd);
1267 state->state = gerbv_image_return_new_netstate (state->state);
1269 if ((op[0] == EOF) || (op[1] == EOF)) {
1270 string = g_strdup_printf("Unexpected EOF found in file \n%s\n", fd->filename);
1271 gerbv_stats_add_error(stats->error_list,
1273 string,
1274 GERBV_MESSAGE_ERROR);
1275 g_free(string);
1278 if (((op[0] == 'A') && (op[1] == 'Y')) ||
1279 ((op[0] == 'B') && (op[1] == 'X'))) {
1280 state->state->axisSelect = GERBV_AXIS_SELECT_SWAPAB;
1281 } else {
1282 state->state->axisSelect = GERBV_AXIS_SELECT_NOSELECT;
1285 op[0] = gerb_fgetc(fd);
1286 op[1] = gerb_fgetc(fd);
1288 if ((op[0] == EOF) || (op[1] == EOF)) {
1289 string = g_strdup_printf("Unexpected EOF found in file \n%s\n", fd->filename);
1290 gerbv_stats_add_error(stats->error_list,
1292 string,
1293 GERBV_MESSAGE_ERROR);
1294 g_free(string);
1297 if (((op[0] == 'A') && (op[1] == 'Y')) ||
1298 ((op[0] == 'B') && (op[1] == 'X'))) {
1299 state->state->axisSelect = GERBV_AXIS_SELECT_SWAPAB;
1300 } else {
1301 state->state->axisSelect = GERBV_AXIS_SELECT_NOSELECT;
1303 break;
1305 case A2I('F','S'): /* Format Statement */
1306 image->format = g_new0 (gerbv_format_t,1);
1308 switch (gerb_fgetc(fd)) {
1309 case 'L':
1310 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1311 break;
1312 case 'T':
1313 image->format->omit_zeros = GERBV_OMIT_ZEROS_TRAILING;
1314 break;
1315 case 'D':
1316 image->format->omit_zeros = GERBV_OMIT_ZEROS_EXPLICIT;
1317 break;
1318 default:
1319 string = g_strdup_printf("EagleCad bug detected: Undefined handling of zeros in format code in file \n%s\n",
1320 fd->filename);
1321 gerbv_stats_add_error(stats->error_list,
1323 string,
1324 GERBV_MESSAGE_ERROR);
1325 g_free(string);
1326 string = g_strdup_printf("Defaulting to omitting leading zeros.\n");
1327 gerbv_stats_add_error(stats->error_list,
1329 string,
1330 GERBV_MESSAGE_WARNING);
1331 g_free(string);
1332 gerb_ungetc(fd);
1333 image->format->omit_zeros = GERBV_OMIT_ZEROS_LEADING;
1336 switch (gerb_fgetc(fd)) {
1337 case 'A':
1338 image->format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1339 break;
1340 case 'I':
1341 image->format->coordinate = GERBV_COORDINATE_INCREMENTAL;
1342 break;
1343 default:
1344 string = g_strdup_printf("Invalid coordinate type defined in format code in file \n%s\n",
1345 fd->filename);
1346 gerbv_stats_add_error(stats->error_list,
1348 string,
1349 GERBV_MESSAGE_ERROR);
1350 g_free(string);
1351 string = g_strdup_printf("Defaulting to absolute coordinates.\n");
1352 gerbv_stats_add_error(stats->error_list,
1354 string,
1355 GERBV_MESSAGE_WARNING);
1356 g_free(string);
1357 image->format->coordinate = GERBV_COORDINATE_ABSOLUTE;
1359 op[0] = gerb_fgetc(fd);
1360 while((op[0] != '*')&&(op[0] != EOF)) {
1361 switch (op[0]) {
1362 case 'N':
1363 op[0] = (char)gerb_fgetc(fd);
1364 image->format->lim_seqno = op[0] - '0';
1365 break;
1366 case 'G':
1367 op[0] = (char)gerb_fgetc(fd);
1368 image->format->lim_gf = op[0] - '0';
1369 break;
1370 case 'D':
1371 op[0] = (char)gerb_fgetc(fd);
1372 image->format->lim_pf = op[0] - '0';
1373 break;
1374 case 'M':
1375 op[0] = (char)gerb_fgetc(fd);
1376 image->format->lim_mf = op[0] - '0';
1377 break;
1378 case 'X' :
1379 op[0] = gerb_fgetc(fd);
1380 if ((op[0] < '0') || (op[0] > '6')) {
1381 string = g_strdup_printf("Illegal format size %c in file \n%s\n",
1382 (char)op[0], fd->filename);
1383 gerbv_stats_add_error(stats->error_list,
1385 string,
1386 GERBV_MESSAGE_ERROR);
1387 g_free(string);
1389 image->format->x_int = op[0] - '0';
1390 op[0] = gerb_fgetc(fd);
1391 if ((op[0] < '0') || (op[0] > '6')) {
1392 string = g_strdup_printf("Illegal format size %c in file \n%s\n",
1393 (char)op[0], fd->filename);
1394 gerbv_stats_add_error(stats->error_list,
1396 string,
1397 GERBV_MESSAGE_ERROR);
1398 g_free(string);
1400 image->format->x_dec = op[0] - '0';
1401 break;
1402 case 'Y':
1403 op[0] = gerb_fgetc(fd);
1404 if ((op[0] < '0') || (op[0] > '6')) {
1405 string = g_strdup_printf("Illegal format size %c in file \n%s\n",
1406 (char)op[0], fd->filename);
1407 gerbv_stats_add_error(stats->error_list,
1409 string,
1410 GERBV_MESSAGE_ERROR);
1411 g_free(string);
1413 image->format->y_int = op[0] - '0';
1414 op[0] = gerb_fgetc(fd);
1415 if ((op[0] < '0') || (op[0] > '6')) {
1416 string = g_strdup_printf("Illegal format size %c in file \n%s\n",
1417 (char)op[0], fd->filename);
1418 gerbv_stats_add_error(stats->error_list,
1420 string,
1421 GERBV_MESSAGE_ERROR);
1422 g_free(string);
1424 image->format->y_dec = op[0] - '0';
1425 break;
1426 default :
1427 string = g_strdup_printf("Illegal format statement [%c] in file \n%s\n",
1428 op[0], fd->filename);
1429 gerbv_stats_add_error(stats->error_list,
1431 string,
1432 GERBV_MESSAGE_ERROR);
1433 g_free(string);
1434 string = g_strdup_printf("Ignoring invalid format statement.\n");
1435 gerbv_stats_add_error(stats->error_list,
1437 string,
1438 GERBV_MESSAGE_WARNING);
1439 g_free(string);
1441 op[0] = gerb_fgetc(fd);
1443 break;
1444 case A2I('M','I'): /* Mirror Image */
1445 op[0] = gerb_fgetc(fd);
1446 state->state = gerbv_image_return_new_netstate (state->state);
1448 while ((op[0] != '*')&&(op[0] != EOF)) {
1449 gint readValue=0;
1450 switch (op[0]) {
1451 case 'A' :
1452 readValue = gerb_fgetint(fd, NULL);
1453 if (readValue == 1) {
1454 if (state->state->mirrorState == GERBV_MIRROR_STATE_FLIPB)
1455 state->state->mirrorState=GERBV_MIRROR_STATE_FLIPAB;
1456 else
1457 state->state->mirrorState=GERBV_MIRROR_STATE_FLIPA;
1459 break;
1460 case 'B' :
1461 readValue = gerb_fgetint(fd, NULL);
1462 if (readValue == 1) {
1463 if (state->state->mirrorState == GERBV_MIRROR_STATE_FLIPA)
1464 state->state->mirrorState=GERBV_MIRROR_STATE_FLIPAB;
1465 else
1466 state->state->mirrorState=GERBV_MIRROR_STATE_FLIPB;
1468 break;
1469 default :
1470 string = g_strdup_printf("Wrong character in mirror:%c\n", op[0]);
1471 gerbv_stats_add_error(stats->error_list,
1473 string,
1474 GERBV_MESSAGE_ERROR);
1475 g_free(string);
1477 op[0] = gerb_fgetc(fd);
1479 break;
1480 case A2I('M','O'): /* Mode of Units */
1481 op[0] = gerb_fgetc(fd);
1482 op[1] = gerb_fgetc(fd);
1484 if ((op[0] == EOF) || (op[1] == EOF))
1485 gerbv_stats_add_error(stats->error_list,
1487 "Unexpected EOF found.\n",
1488 GERBV_MESSAGE_ERROR);
1489 switch (A2I(op[0],op[1])) {
1490 case A2I('I','N'):
1491 state->state = gerbv_image_return_new_netstate (state->state);
1492 state->state->unit = GERBV_UNIT_INCH;
1493 break;
1494 case A2I('M','M'):
1495 state->state = gerbv_image_return_new_netstate (state->state);
1496 state->state->unit = GERBV_UNIT_MM;
1497 break;
1498 default:
1499 string = g_strdup_printf("Illegal unit:%c%c\n", op[0], op[1]);
1500 gerbv_stats_add_error(stats->error_list,
1502 string,
1503 GERBV_MESSAGE_ERROR);
1504 g_free(string);
1506 break;
1507 case A2I('O','F'): /* Offset */
1508 op[0] = gerb_fgetc(fd);
1510 while ((op[0] != '*')&&(op[0] != EOF)) {
1511 switch (op[0]) {
1512 case 'A' :
1513 state->state->offsetA = gerb_fgetdouble(fd) / scale;
1514 break;
1515 case 'B' :
1516 state->state->offsetB = gerb_fgetdouble(fd) / scale;
1517 break;
1518 default :
1519 string = g_strdup_printf("Wrong character in offset:%c\n", op[0]);
1520 gerbv_stats_add_error(stats->error_list,
1522 string,
1523 GERBV_MESSAGE_ERROR);
1524 g_free(string);
1526 op[0] = gerb_fgetc(fd);
1528 break;
1529 case A2I('I','F'): /* Include file */
1531 gchar *includeFilename = gerb_fgetstring(fd, '*');
1533 if (includeFilename) {
1534 gchar *fullPath;
1535 if (!g_path_is_absolute(includeFilename)) {
1536 fullPath = g_build_filename (directoryPath, includeFilename, NULL);
1537 } else {
1538 fullPath = g_strdup (includeFilename);
1540 if (levelOfRecursion < 10) {
1541 gerb_file_t *includefd = NULL;
1543 includefd = gerb_fopen(fullPath);
1544 if (includefd) {
1545 gerber_parse_file_segment (levelOfRecursion + 1, image, state, curr_net, stats, includefd, directoryPath);
1546 gerb_fclose(includefd);
1547 } else {
1548 string = g_strdup_printf("In file %s,\nIncluded file %s cannot be found\n",
1549 fd->filename, fullPath);
1550 gerbv_stats_add_error(stats->error_list,
1552 string,
1553 GERBV_MESSAGE_ERROR);
1554 g_free(string);
1556 g_free (fullPath);
1557 } else {
1558 string = g_strdup_printf("Parser encountered more than 10 levels of include file recursion which is not allowed by the RS-274X spec\n");
1559 gerbv_stats_add_error(stats->error_list,
1561 string,
1562 GERBV_MESSAGE_ERROR);
1563 g_free(string);
1568 break;
1569 case A2I('I','O'): /* Image offset */
1570 op[0] = gerb_fgetc(fd);
1572 while ((op[0] != '*')&&(op[0] != EOF)) {
1573 switch (op[0]) {
1574 case 'A' :
1575 image->info->offsetA = gerb_fgetdouble(fd) / scale;
1576 break;
1577 case 'B' :
1578 image->info->offsetB = gerb_fgetdouble(fd) / scale;
1579 break;
1580 default :
1581 string = g_strdup_printf("In file %s,\nwrong character in image offset %c\n",
1582 fd->filename, op[0]);
1583 gerbv_stats_add_error(stats->error_list,
1585 string,
1586 GERBV_MESSAGE_ERROR);
1587 g_free(string);
1589 op[0] = gerb_fgetc(fd);
1591 break;
1592 case A2I('S','F'): /* Scale Factor */
1593 state->state = gerbv_image_return_new_netstate (state->state);
1594 if (gerb_fgetc(fd) == 'A')
1595 state->state->scaleA = gerb_fgetdouble(fd);
1596 else
1597 gerb_ungetc(fd);
1598 if (gerb_fgetc(fd) == 'B')
1599 state->state->scaleB = gerb_fgetdouble(fd);
1600 else
1601 gerb_ungetc(fd);
1602 break;
1603 case A2I('I','C'): /* Input Code */
1604 /* Thanks to Stephen Adam for providing this information. As he writes:
1605 * btw, here's a logic puzzle for you. If you need to
1606 * read the gerber file to see how it's encoded, then
1607 * how can you read it?
1609 op[0] = gerb_fgetc(fd);
1610 op[1] = gerb_fgetc(fd);
1612 if ((op[0] == EOF) || (op[1] == EOF)) {
1613 string = g_strdup_printf("Unexpected EOF found in file \n%s\n", fd->filename);
1614 gerbv_stats_add_error(stats->error_list,
1616 string,
1617 GERBV_MESSAGE_ERROR);
1618 g_free(string);
1620 switch (A2I(op[0],op[1])) {
1621 case A2I('A','S'):
1622 image->info->encoding = GERBV_ENCODING_ASCII;
1623 break;
1624 case A2I('E','B'):
1625 image->info->encoding = GERBV_ENCODING_EBCDIC;
1626 break;
1627 case A2I('B','C'):
1628 image->info->encoding = GERBV_ENCODING_BCD;
1629 break;
1630 case A2I('I','S'):
1631 image->info->encoding = GERBV_ENCODING_ISO_ASCII;
1632 break;
1633 case A2I('E','I'):
1634 image->info->encoding = GERBV_ENCODING_EIA;
1635 break;
1636 default:
1637 string = g_strdup_printf("In file %s, \nunknown input code (IC): %c%c\n",
1638 fd->filename, op[0], op[1]);
1639 gerbv_stats_add_error(stats->error_list,
1641 string,
1642 GERBV_MESSAGE_ERROR);
1643 g_free(string);
1645 break;
1647 /* Image parameters */
1648 case A2I('I','J'): /* Image Justify */
1649 op[0] = gerb_fgetc(fd);
1650 image->info->imageJustifyTypeA = GERBV_JUSTIFY_LOWERLEFT;
1651 image->info->imageJustifyTypeB = GERBV_JUSTIFY_LOWERLEFT;
1652 image->info->imageJustifyOffsetA = 0.0;
1653 image->info->imageJustifyOffsetB = 0.0;
1654 while ((op[0] != '*')&&(op[0] != EOF)) {
1655 switch (op[0]) {
1656 case 'A' :
1657 op[0] = gerb_fgetc(fd);
1658 if (op[0] == 'C') {
1659 image->info->imageJustifyTypeA = GERBV_JUSTIFY_CENTERJUSTIFY;
1660 } else if (op[0] == 'L') {
1661 image->info->imageJustifyTypeA = GERBV_JUSTIFY_LOWERLEFT;
1662 } else {
1663 gerb_ungetc (fd);
1664 image->info->imageJustifyOffsetA = gerb_fgetdouble(fd) / scale;
1666 break;
1667 case 'B' :
1668 op[0] = gerb_fgetc(fd);
1669 if (op[0] == 'C') {
1670 image->info->imageJustifyTypeB = GERBV_JUSTIFY_CENTERJUSTIFY;
1671 } else if (op[0] == 'L') {
1672 image->info->imageJustifyTypeB = GERBV_JUSTIFY_LOWERLEFT;
1673 } else {
1674 gerb_ungetc (fd);
1675 image->info->imageJustifyOffsetB = gerb_fgetdouble(fd) / scale;
1677 break;
1678 default :
1679 string = g_strdup_printf("In file %s,\nwrong character in image justify:%c\n",
1680 fd->filename, op[0]);
1681 gerbv_stats_add_error(stats->error_list,
1683 string,
1684 GERBV_MESSAGE_ERROR);
1685 g_free(string);
1687 op[0] = gerb_fgetc(fd);
1689 break;
1690 case A2I('I','N'): /* Image Name */
1691 image->info->name = gerb_fgetstring(fd, '*');
1692 break;
1693 case A2I('I','P'): /* Image Polarity */
1695 for (ano = 0; ano < 3; ano++) {
1696 op[0] = gerb_fgetc(fd);
1697 if (op[0] == EOF) {
1698 string = g_strdup_printf("In file %s,\nunexpected EOF while reading image polarity (IP)\n",
1699 fd->filename);
1700 gerbv_stats_add_error(stats->error_list,
1702 string,
1703 GERBV_MESSAGE_ERROR);
1704 g_free(string);
1706 str[ano] = (char)op[0];
1709 if (strncmp(str, "POS", 3) == 0)
1710 image->info->polarity = GERBV_POLARITY_POSITIVE;
1711 else if (strncmp(str, "NEG", 3) == 0)
1712 image->info->polarity = GERBV_POLARITY_NEGATIVE;
1713 else {
1714 string = g_strdup_printf("Unknown polarity : %c%c%c\n", str[0], str[1], str[2]);
1715 gerbv_stats_add_error(stats->error_list,
1717 string,
1718 GERBV_MESSAGE_ERROR);
1719 g_free(string);
1721 break;
1722 case A2I('I','R'): /* Image Rotation */
1723 tmp = gerb_fgetint(fd, NULL) % 360;
1724 if (tmp == 0)
1725 image->info->imageRotation = 0.0;
1726 else if (tmp == 90)
1727 image->info->imageRotation = M_PI / 2.0;
1728 else if (tmp == 180)
1729 image->info->imageRotation = M_PI;
1730 else if (tmp == 270)
1731 image->info->imageRotation = 3.0 * M_PI / 2.0;
1732 else {
1733 string = g_strdup_printf("Image rotation must be 0, 90, 180 or 270 (is actually %d)\n", tmp);
1734 gerbv_stats_add_error(stats->error_list,
1736 string,
1737 GERBV_MESSAGE_ERROR);
1738 g_free(string);
1740 break;
1741 case A2I('P','F'): /* Plotter Film */
1742 image->info->plotterFilm = gerb_fgetstring(fd, '*');
1743 break;
1745 /* Aperture parameters */
1746 case A2I('A','D'): /* Aperture Description */
1747 a = (gerbv_aperture_t *) g_new0 (gerbv_aperture_t,1);
1749 ano = parse_aperture_definition(fd, a, image, scale);
1750 if (ano == -1) {
1751 /* error with line parse, so just quietly ignore */
1753 else if ((ano >= 0) && (ano <= APERTURE_MAX)) {
1754 a->unit = state->state->unit;
1755 image->aperture[ano] = a;
1756 dprintf(" In parse_rs274x, adding new aperture to aperture list ...\n");
1757 gerbv_stats_add_aperture(stats->aperture_list,
1758 -1, ano,
1759 a->type,
1760 a->parameter);
1761 gerbv_stats_add_to_D_list(stats->D_code_list,
1762 ano);
1763 if (ano < APERTURE_MIN) {
1764 string = g_strdup_printf("In file %s,\naperture number out of bounds : %d\n",
1765 fd->filename, ano);
1766 gerbv_stats_add_error(stats->error_list,-1, string, GERBV_MESSAGE_ERROR);
1768 } else {
1769 string = g_strdup_printf("In file %s,\naperture number out of bounds : %d\n",
1770 fd->filename, ano);
1771 gerbv_stats_add_error(stats->error_list,
1773 string,
1774 GERBV_MESSAGE_ERROR);
1775 g_free(string);
1777 /* Add aperture info to stats->aperture_list here */
1779 break;
1780 case A2I('A','M'): /* Aperture Macro */
1781 tmp_amacro = image->amacro;
1782 image->amacro = parse_aperture_macro(fd);
1783 if (image->amacro) {
1784 image->amacro->next = tmp_amacro;
1785 #ifdef AMACRO_DEBUG
1786 print_program(image->amacro);
1787 #endif
1788 } else {
1789 string = g_strdup_printf("In file %s, \nfailed to parse aperture macro\n",
1790 fd->filename);
1791 gerbv_stats_add_error(stats->error_list,
1793 string,
1794 GERBV_MESSAGE_ERROR);
1795 g_free(string);
1797 // return, since we want to skip the later back-up loop
1798 return;
1799 /* Layer */
1800 case A2I('L','N'): /* Layer Name */
1801 state->layer = gerbv_image_return_new_layer (state->layer);
1802 state->layer->name = gerb_fgetstring(fd, '*');
1803 break;
1804 case A2I('L','P'): /* Layer Polarity */
1805 state->layer = gerbv_image_return_new_layer (state->layer);
1806 switch (gerb_fgetc(fd)) {
1807 case 'D': /* Dark Polarity (default) */
1808 state->layer->polarity = GERBV_POLARITY_DARK;
1809 break;
1810 case 'C': /* Clear Polarity */
1811 state->layer->polarity = GERBV_POLARITY_CLEAR;
1812 break;
1813 default:
1814 string = g_strdup_printf("In file %s,\nunknown Layer Polarity: %c\n",
1815 fd->filename, op[0]);
1816 gerbv_stats_add_error(stats->error_list,
1818 string,
1819 GERBV_MESSAGE_ERROR);
1820 g_free(string);
1822 break;
1823 case A2I('K','O'): /* Knock Out */
1824 state->layer = gerbv_image_return_new_layer (state->layer);
1825 gerber_update_any_running_knockout_measurements (image);
1826 /* reset any previous knockout measurements */
1827 knockoutMeasure = FALSE;
1828 op[0] = gerb_fgetc(fd);
1829 if (op[0] == '*') { /* Disable previous SR parameters */
1830 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_NOKNOCKOUT;
1831 break;
1832 } else if (op[0] == 'C') {
1833 state->layer->knockout.polarity = GERBV_POLARITY_CLEAR;
1834 } else if (op[0] == 'D') {
1835 state->layer->knockout.polarity = GERBV_POLARITY_DARK;
1836 } else {
1837 string = g_strdup_printf("In file %s,\nknockout must supply a polarity (C, D, or *)\n",
1838 fd->filename);
1839 gerbv_stats_add_error(stats->error_list,
1841 string,
1842 GERBV_MESSAGE_ERROR);
1843 g_free(string);
1845 state->layer->knockout.lowerLeftX = 0.0;
1846 state->layer->knockout.lowerLeftY = 0.0;
1847 state->layer->knockout.width = 0.0;
1848 state->layer->knockout.height = 0.0;
1849 state->layer->knockout.border = 0.0;
1850 state->layer->knockout.firstInstance = TRUE;
1851 op[0] = gerb_fgetc(fd);
1852 while ((op[0] != '*')&&(op[0] != EOF)) {
1853 switch (op[0]) {
1854 case 'X':
1855 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1856 state->layer->knockout.lowerLeftX = gerb_fgetdouble(fd) / scale;
1857 break;
1858 case 'Y':
1859 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1860 state->layer->knockout.lowerLeftY = gerb_fgetdouble(fd) / scale;
1861 break;
1862 case 'I':
1863 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1864 state->layer->knockout.width = gerb_fgetdouble(fd) / scale;
1865 break;
1866 case 'J':
1867 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_FIXEDKNOCK;
1868 state->layer->knockout.height = gerb_fgetdouble(fd) / scale;
1869 break;
1870 case 'K':
1871 state->layer->knockout.type = GERBV_KNOCKOUT_TYPE_BORDER;
1872 state->layer->knockout.border = gerb_fgetdouble(fd) / scale;
1873 /* this is a bordered knockout, so we need to start measuring the
1874 size of a square bordering all future components */
1875 knockoutMeasure = TRUE;
1876 knockoutLimitXmin = HUGE_VAL;
1877 knockoutLimitYmin = HUGE_VAL;
1878 knockoutLimitXmax = -HUGE_VAL;
1879 knockoutLimitYmax = -HUGE_VAL;
1880 knockoutLayer = state->layer;
1881 break;
1882 default:
1883 string = g_strdup_printf("In file %s, \nunknown variable in knockout",
1884 fd->filename);
1885 gerbv_stats_add_error(stats->error_list,
1887 string,
1888 GERBV_MESSAGE_ERROR);
1889 g_free(string);
1891 op[0] = gerb_fgetc(fd);
1893 break;
1894 case A2I('S','R'): /* Step and Repeat */
1895 /* start by generating a new layer (duplicating previous layer settings */
1896 state->layer = gerbv_image_return_new_layer (state->layer);
1897 op[0] = gerb_fgetc(fd);
1898 if (op[0] == '*') { /* Disable previous SR parameters */
1899 state->layer->stepAndRepeat.X = 1;
1900 state->layer->stepAndRepeat.Y = 1;
1901 state->layer->stepAndRepeat.dist_X = 0.0;
1902 state->layer->stepAndRepeat.dist_Y = 0.0;
1903 break;
1905 while ((op[0] != '*')&&(op[0] != EOF)) {
1906 switch (op[0]) {
1907 case 'X':
1908 state->layer->stepAndRepeat.X = gerb_fgetint(fd, NULL);
1909 break;
1910 case 'Y':
1911 state->layer->stepAndRepeat.Y = gerb_fgetint(fd, NULL);
1912 break;
1913 case 'I':
1914 state->layer->stepAndRepeat.dist_X = gerb_fgetdouble(fd) / scale;
1915 break;
1916 case 'J':
1917 state->layer->stepAndRepeat.dist_Y = gerb_fgetdouble(fd) / scale;
1918 break;
1919 default:
1920 string = g_strdup_printf("In file %s,\nstep-and-repeat parameter error\n",
1921 fd->filename);
1922 gerbv_stats_add_error(stats->error_list,
1924 string,
1925 GERBV_MESSAGE_ERROR);
1926 g_free(string);
1930 * Repeating 0 times in any direction would disable the whole plot, and
1931 * is probably not intended. At least one other tool (viewmate) seems
1932 * to interpret 0-time repeating as repeating just once too.
1934 if(state->layer->stepAndRepeat.X == 0)
1935 state->layer->stepAndRepeat.X = 1;
1936 if(state->layer->stepAndRepeat.Y == 0)
1937 state->layer->stepAndRepeat.Y = 1;
1939 op[0] = gerb_fgetc(fd);
1941 break;
1942 /* is this an actual RS274X command?? It isn't explainined in the spec... */
1943 case A2I('R','O'):
1944 state->layer = gerbv_image_return_new_layer (state->layer);
1946 state->layer->rotation = gerb_fgetdouble(fd) * M_PI / 180;
1947 op[0] = gerb_fgetc(fd);
1948 if (op[0] != '*') {
1949 string = g_strdup_printf("In file %s,\nerror in layer rotation command\n",
1950 fd->filename);
1951 gerbv_stats_add_error(stats->error_list,
1953 string,
1954 GERBV_MESSAGE_ERROR);
1955 g_free(string);
1957 break;
1958 default:
1959 string = g_strdup_printf("In file %s,\nunknown RS-274X extension found %%%c%c%%\n",
1960 fd->filename, op[0], op[1]);
1961 gerbv_stats_add_error(stats->error_list,
1963 string,
1964 GERBV_MESSAGE_ERROR);
1965 g_free(string);
1967 // make sure we read until the trailing * character
1968 // first, backspace once in case we already read the trailing *
1969 fd->ptr--;
1970 int c = gerb_fgetc(fd);
1971 while ((c != EOF) && (c != '*'))
1972 c = gerb_fgetc(fd);
1973 return;
1974 } /* parse_rs274x */
1978 * Stack declarations and operations to be used by the simple engine that
1979 * executes the parsed aperture macros.
1981 typedef struct {
1982 double *stack;
1983 int sp;
1984 } macro_stack_t;
1987 static macro_stack_t *
1988 new_stack(unsigned int stack_size)
1990 macro_stack_t *s;
1992 s = (macro_stack_t *) g_new0 (macro_stack_t,1);
1993 s->stack = (double *) g_new0 (double, stack_size);
1994 s->sp = 0;
1995 return s;
1996 } /* new_stack */
1999 static void
2000 free_stack(macro_stack_t *s)
2002 if (s && s->stack)
2003 free(s->stack);
2005 if (s)
2006 free(s);
2008 return;
2009 } /* free_stack */
2012 static void
2013 push(macro_stack_t *s, double val)
2015 s->stack[s->sp++] = val;
2016 return;
2017 } /* push */
2020 static int
2021 pop(macro_stack_t *s, double *value)
2023 /* Check if we try to pop an empty stack */
2024 if (s->sp == 0) {
2025 return -1;
2028 *value = s->stack[--s->sp];
2029 return 0;
2030 } /* pop */
2033 /* ------------------------------------------------------------------ */
2034 static int
2035 simplify_aperture_macro(gerbv_aperture_t *aperture, gdouble scale)
2037 const int extra_stack_size = 10;
2038 macro_stack_t *s;
2039 gerbv_instruction_t *ip;
2040 int handled = 1, nuf_parameters = 0, i, j, clearOperatorUsed = FALSE;
2041 double *lp; /* Local copy of parameters */
2042 double tmp[2] = {0.0, 0.0};
2043 gerbv_aperture_type_t type = GERBV_APTYPE_NONE;
2044 gerbv_simplified_amacro_t *sam;
2046 if (aperture == NULL)
2047 GERB_FATAL_ERROR("aperture NULL in simplify aperture macro\n");
2049 if (aperture->amacro == NULL)
2050 GERB_FATAL_ERROR("aperture->amacro NULL in simplify aperture macro\n");
2052 /* Allocate stack for VM */
2053 s = new_stack(aperture->amacro->nuf_push + extra_stack_size);
2054 if (s == NULL)
2055 GERB_FATAL_ERROR("malloc stack failed\n");
2057 /* Make a copy of the parameter list that we can rewrite if necessary */
2058 lp = g_new (double,APERTURE_PARAMETERS_MAX);
2060 memcpy(lp, aperture->parameter, sizeof(double) * APERTURE_PARAMETERS_MAX);
2062 for(ip = aperture->amacro->program; ip != NULL; ip = ip->next) {
2063 switch(ip->opcode) {
2064 case GERBV_OPCODE_NOP:
2065 break;
2066 case GERBV_OPCODE_PUSH :
2067 push(s, ip->data.fval);
2068 break;
2069 case GERBV_OPCODE_PPUSH :
2070 push(s, lp[ip->data.ival - 1]);
2071 break;
2072 case GERBV_OPCODE_PPOP:
2073 if (pop(s, &tmp[0]) < 0)
2074 GERB_FATAL_ERROR("Tried to pop an empty stack");
2075 lp[ip->data.ival - 1] = tmp[0];
2076 break;
2077 case GERBV_OPCODE_ADD :
2078 if (pop(s, &tmp[0]) < 0)
2079 GERB_FATAL_ERROR("Tried to pop an empty stack");
2080 if (pop(s, &tmp[1]) < 0)
2081 GERB_FATAL_ERROR("Tried to pop an empty stack");
2082 push(s, tmp[1] + tmp[0]);
2083 break;
2084 case GERBV_OPCODE_SUB :
2085 if (pop(s, &tmp[0]) < 0)
2086 GERB_FATAL_ERROR("Tried to pop an empty stack");
2087 if (pop(s, &tmp[1]) < 0)
2088 GERB_FATAL_ERROR("Tried to pop an empty stack");
2089 push(s, tmp[1] - tmp[0]);
2090 break;
2091 case GERBV_OPCODE_MUL :
2092 if (pop(s, &tmp[0]) < 0)
2093 GERB_FATAL_ERROR("Tried to pop an empty stack");
2094 if (pop(s, &tmp[1]) < 0)
2095 GERB_FATAL_ERROR("Tried to pop an empty stack");
2096 push(s, tmp[1] * tmp[0]);
2097 break;
2098 case GERBV_OPCODE_DIV :
2099 if (pop(s, &tmp[0]) < 0)
2100 GERB_FATAL_ERROR("Tried to pop an empty stack");
2101 if (pop(s, &tmp[1]) < 0)
2102 GERB_FATAL_ERROR("Tried to pop an empty stack");
2103 push(s, tmp[1] / tmp[0]);
2104 break;
2105 case GERBV_OPCODE_PRIM :
2107 * This handles the exposure thing in the aperture macro
2108 * The exposure is always the first element on stack independent
2109 * of aperture macro.
2111 switch(ip->data.ival) {
2112 case 1:
2113 dprintf(" Aperture macro circle [1] (");
2114 type = GERBV_APTYPE_MACRO_CIRCLE;
2115 nuf_parameters = 4;
2116 break;
2117 case 3:
2118 break;
2119 case 4 :
2120 dprintf(" Aperture macro outline [4] (");
2121 type = GERBV_APTYPE_MACRO_OUTLINE;
2123 * Number of parameters are:
2124 * - number of points defined in entry 1 of the stack +
2125 * start point. Times two since it is both X and Y.
2126 * - Then three more; exposure, nuf points and rotation.
2128 nuf_parameters = ((int)s->stack[1] + 1) * 2 + 3;
2129 break;
2130 case 5 :
2131 dprintf(" Aperture macro polygon [5] (");
2132 type = GERBV_APTYPE_MACRO_POLYGON;
2133 nuf_parameters = 6;
2134 break;
2135 case 6 :
2136 dprintf(" Aperture macro moiré [6] (");
2137 type = GERBV_APTYPE_MACRO_MOIRE;
2138 nuf_parameters = 9;
2139 break;
2140 case 7 :
2141 dprintf(" Aperture macro thermal [7] (");
2142 type = GERBV_APTYPE_MACRO_THERMAL;
2143 nuf_parameters = 6;
2144 break;
2145 case 2 :
2146 case 20 :
2147 dprintf(" Aperture macro line 20/2 (");
2148 type = GERBV_APTYPE_MACRO_LINE20;
2149 nuf_parameters = 7;
2150 break;
2151 case 21 :
2152 dprintf(" Aperture macro line 21 (");
2153 type = GERBV_APTYPE_MACRO_LINE21;
2154 nuf_parameters = 6;
2155 break;
2156 case 22 :
2157 dprintf(" Aperture macro line 22 (");
2158 type = GERBV_APTYPE_MACRO_LINE22;
2159 nuf_parameters = 6;
2160 break;
2161 default :
2162 handled = 0;
2165 if (type != GERBV_APTYPE_NONE) {
2166 if (nuf_parameters > APERTURE_PARAMETERS_MAX) {
2167 GERB_COMPILE_ERROR("Number of parameters to aperture macro are more than gerbv is able to store\n");
2171 * Create struct for simplified aperture macro and
2172 * start filling in the blanks.
2174 sam = g_new (gerbv_simplified_amacro_t, 1);
2175 sam->type = type;
2176 sam->next = NULL;
2177 memset(sam->parameter, 0,
2178 sizeof(double) * APERTURE_PARAMETERS_MAX);
2179 memcpy(sam->parameter, s->stack,
2180 sizeof(double) * nuf_parameters);
2182 /* convert any mm values to inches */
2183 switch (type) {
2184 case GERBV_APTYPE_MACRO_CIRCLE:
2185 if (fabs(sam->parameter[0]) < 0.001)
2186 clearOperatorUsed = TRUE;
2187 sam->parameter[1]/=scale;
2188 sam->parameter[2]/=scale;
2189 sam->parameter[3]/=scale;
2190 break;
2191 case GERBV_APTYPE_MACRO_OUTLINE:
2192 if (fabs(sam->parameter[0]) < 0.001)
2193 clearOperatorUsed = TRUE;
2194 for (j=2; j<nuf_parameters-1; j++){
2195 sam->parameter[j]/=scale;
2197 break;
2198 case GERBV_APTYPE_MACRO_POLYGON:
2199 if (fabs(sam->parameter[0]) < 0.001)
2200 clearOperatorUsed = TRUE;
2201 sam->parameter[2]/=scale;
2202 sam->parameter[3]/=scale;
2203 sam->parameter[4]/=scale;
2204 break;
2205 case GERBV_APTYPE_MACRO_MOIRE:
2206 sam->parameter[0]/=scale;
2207 sam->parameter[1]/=scale;
2208 sam->parameter[2]/=scale;
2209 sam->parameter[3]/=scale;
2210 sam->parameter[4]/=scale;
2211 sam->parameter[6]/=scale;
2212 sam->parameter[7]/=scale;
2213 break;
2214 case GERBV_APTYPE_MACRO_THERMAL:
2215 sam->parameter[0]/=scale;
2216 sam->parameter[1]/=scale;
2217 sam->parameter[2]/=scale;
2218 sam->parameter[3]/=scale;
2219 sam->parameter[4]/=scale;
2220 break;
2221 case GERBV_APTYPE_MACRO_LINE20:
2222 if (fabs(sam->parameter[0]) < 0.001)
2223 clearOperatorUsed = TRUE;
2224 sam->parameter[1]/=scale;
2225 sam->parameter[2]/=scale;
2226 sam->parameter[3]/=scale;
2227 sam->parameter[4]/=scale;
2228 sam->parameter[5]/=scale;
2229 break;
2230 case GERBV_APTYPE_MACRO_LINE21:
2231 case GERBV_APTYPE_MACRO_LINE22:
2232 if (fabs(sam->parameter[0]) < 0.001)
2233 clearOperatorUsed = TRUE;
2234 sam->parameter[1]/=scale;
2235 sam->parameter[2]/=scale;
2236 sam->parameter[3]/=scale;
2237 sam->parameter[4]/=scale;
2238 break;
2239 default:
2240 break;
2243 * Add this simplified aperture macro to the end of the list
2244 * of simplified aperture macros. If first entry, put it
2245 * in the top.
2247 if (aperture->simplified == NULL) {
2248 aperture->simplified = sam;
2249 } else {
2250 gerbv_simplified_amacro_t *tmp_sam;
2251 tmp_sam = aperture->simplified;
2252 while (tmp_sam->next != NULL) {
2253 tmp_sam = tmp_sam->next;
2255 tmp_sam->next = sam;
2258 #ifdef DEBUG
2259 for (i = 0; i < nuf_parameters; i++) {
2260 dprintf("%f, ", s->stack[i]);
2262 #endif /* DEBUG */
2263 dprintf(")\n");
2267 * Here we reset the stack pointer. It's not general correct
2268 * correct to do this, but since I know how the compiler works
2269 * I can do this. The correct way to do this should be to
2270 * subtract number of used elements in each primitive operation.
2272 s->sp = 0;
2273 break;
2274 default :
2275 break;
2278 free_stack(s);
2279 g_free (lp);
2281 /* store a flag to let the renderer know if it should expect any "clear"
2282 primatives */
2283 aperture->parameter[0]= (gdouble) clearOperatorUsed;
2284 return handled;
2285 } /* simplify_aperture_macro */
2288 /* ------------------------------------------------------------------ */
2289 static int
2290 parse_aperture_definition(gerb_file_t *fd, gerbv_aperture_t *aperture,
2291 gerbv_image_t *image, gdouble scale)
2293 int ano, i;
2294 char *ad;
2295 char *token;
2296 gerbv_amacro_t *curr_amacro;
2297 gerbv_amacro_t *amacro = image->amacro;
2298 gerbv_stats_t *stats = image->gerbv_stats;
2299 gdouble tempHolder;
2300 gchar *string;
2302 if (gerb_fgetc(fd) != 'D') {
2303 string = g_strdup_printf("Found AD code with no following 'D' in file \n%s\n",
2304 fd->filename);
2305 gerbv_stats_add_error(stats->error_list,
2307 string,
2308 GERBV_MESSAGE_ERROR);
2309 g_free(string);
2310 return -1;
2314 * Get aperture no
2316 ano = gerb_fgetint(fd, NULL);
2319 * Read in the whole aperture defintion and tokenize it
2321 ad = gerb_fgetstring(fd, '*');
2322 token = strtok(ad, ",");
2324 if (token == NULL) {
2325 string = g_strdup_printf("Invalid aperture definition in file \n%s\n",
2326 fd->filename);
2327 gerbv_stats_add_error(stats->error_list,
2329 string,
2330 GERBV_MESSAGE_ERROR);
2331 g_free(string);
2332 return -1;
2334 if (strlen(token) == 1) {
2335 switch (token[0]) {
2336 case 'C':
2337 aperture->type = GERBV_APTYPE_CIRCLE;
2338 break;
2339 case 'R' :
2340 aperture->type = GERBV_APTYPE_RECTANGLE;
2341 break;
2342 case 'O' :
2343 aperture->type = GERBV_APTYPE_OVAL;
2344 break;
2345 case 'P' :
2346 aperture->type = GERBV_APTYPE_POLYGON;
2347 break;
2349 /* Here a should a T be defined, but I don't know what it represents */
2350 } else {
2351 aperture->type = GERBV_APTYPE_MACRO;
2353 * In aperture definition, point to the aperture macro
2354 * used in the defintion
2356 curr_amacro = amacro;
2357 while (curr_amacro) {
2358 if ((strlen(curr_amacro->name) == strlen(token)) &&
2359 (strcmp(curr_amacro->name, token) == 0)) {
2360 aperture->amacro = curr_amacro;
2361 break;
2363 curr_amacro = curr_amacro->next;
2368 * Parse all parameters
2370 for (token = strtok(NULL, "X"), i = 0; token != NULL;
2371 token = strtok(NULL, "X"), i++) {
2372 if (i == APERTURE_PARAMETERS_MAX) {
2373 string = g_strdup_printf("In file %s,\nmaximum number of allowed parameters exceeded in aperture %d\n",
2374 fd->filename, ano);
2375 gerbv_stats_add_error(stats->error_list,
2377 string,
2378 GERBV_MESSAGE_ERROR);
2379 g_free(string);
2380 break;
2382 errno = 0;
2384 tempHolder = strtod(token, NULL);
2385 /* convert any MM values to inches */
2386 /* don't scale polygon angles or side numbers, or macro parmaeters */
2387 if (!(((aperture->type == GERBV_APTYPE_POLYGON) && ((i==1) || (i==2)))||
2388 (aperture->type == GERBV_APTYPE_MACRO))) {
2389 tempHolder /= scale;
2392 aperture->parameter[i] = tempHolder;
2393 if (errno) {
2394 string = g_strdup_printf("Failed to read all parameters exceeded in aperture %d\n", ano);
2395 gerbv_stats_add_error(stats->error_list,
2397 string,
2398 GERBV_MESSAGE_WARNING);
2399 g_free(string);
2400 aperture->parameter[i] = 0.0;
2404 aperture->nuf_parameters = i;
2406 gerb_ungetc(fd);
2408 if (aperture->type == GERBV_APTYPE_MACRO) {
2409 dprintf("Simplifying aperture %d using aperture macro \"%s\"\n", ano,
2410 aperture->amacro->name);
2411 simplify_aperture_macro(aperture, scale);
2412 dprintf("Done simplifying\n");
2415 g_free(ad);
2417 return ano;
2418 } /* parse_aperture_definition */
2421 /* ------------------------------------------------------------------ */
2422 static void
2423 calc_cirseg_sq(struct gerbv_net *net, int cw,
2424 double delta_cp_x, double delta_cp_y)
2426 double d1x, d1y, d2x, d2y;
2427 double alfa, beta;
2428 int quadrant = 0;
2432 * Quadrant detection (based on ccw, converted below if cw)
2433 * Y ^
2434 * /!\
2436 * ---->X
2438 if (net->start_x > net->stop_x)
2439 /* 1st and 2nd quadrant */
2440 if (net->start_y < net->stop_y)
2441 quadrant = 1;
2442 else
2443 quadrant = 2;
2444 else
2445 /* 3rd and 4th quadrant */
2446 if (net->start_y > net->stop_y)
2447 quadrant = 3;
2448 else
2449 quadrant = 4;
2452 * If clockwise, rotate quadrant
2454 if (cw) {
2455 switch (quadrant) {
2456 case 1 :
2457 quadrant = 3;
2458 break;
2459 case 2 :
2460 quadrant = 4;
2461 break;
2462 case 3 :
2463 quadrant = 1;
2464 break;
2465 case 4 :
2466 quadrant = 2;
2467 break;
2468 default :
2469 GERB_COMPILE_ERROR("Unknow quadrant value while converting to cw\n");
2474 * Calculate arc center point
2476 switch (quadrant) {
2477 case 1 :
2478 net->cirseg->cp_x = net->start_x - delta_cp_x;
2479 net->cirseg->cp_y = net->start_y - delta_cp_y;
2480 break;
2481 case 2 :
2482 net->cirseg->cp_x = net->start_x + delta_cp_x;
2483 net->cirseg->cp_y = net->start_y - delta_cp_y;
2484 break;
2485 case 3 :
2486 net->cirseg->cp_x = net->start_x + delta_cp_x;
2487 net->cirseg->cp_y = net->start_y + delta_cp_y;
2488 break;
2489 case 4 :
2490 net->cirseg->cp_x = net->start_x - delta_cp_x;
2491 net->cirseg->cp_y = net->start_y + delta_cp_y;
2492 break;
2493 default :
2494 GERB_COMPILE_ERROR("Strange quadrant : %d\n", quadrant);
2498 * Some good values
2500 d1x = fabs(net->start_x - net->cirseg->cp_x);
2501 d1y = fabs(net->start_y - net->cirseg->cp_y);
2502 d2x = fabs(net->stop_x - net->cirseg->cp_x);
2503 d2y = fabs(net->stop_y - net->cirseg->cp_y);
2505 alfa = atan2(d1y, d1x);
2506 beta = atan2(d2y, d2x);
2509 * Avoid divide by zero when sin(0) = 0 and cos(90) = 0
2511 net->cirseg->width = alfa < beta ?
2512 2 * (d1x / cos(alfa)) : 2 * (d2x / cos(beta));
2513 net->cirseg->height = alfa > beta ?
2514 2 * (d1y / sin(alfa)) : 2 * (d2y / sin(beta));
2516 if (alfa < 0.000001 && beta < 0.000001) {
2517 net->cirseg->height = 0;
2520 #define RAD2DEG(a) (a * 180 / M_PI)
2522 switch (quadrant) {
2523 case 1 :
2524 net->cirseg->angle1 = RAD2DEG(alfa);
2525 net->cirseg->angle2 = RAD2DEG(beta);
2526 break;
2527 case 2 :
2528 net->cirseg->angle1 = 180.0 - RAD2DEG(alfa);
2529 net->cirseg->angle2 = 180.0 - RAD2DEG(beta);
2530 break;
2531 case 3 :
2532 net->cirseg->angle1 = 180.0 + RAD2DEG(alfa);
2533 net->cirseg->angle2 = 180.0 + RAD2DEG(beta);
2534 break;
2535 case 4 :
2536 net->cirseg->angle1 = 360.0 - RAD2DEG(alfa);
2537 net->cirseg->angle2 = 360.0 - RAD2DEG(beta);
2538 break;
2539 default :
2540 GERB_COMPILE_ERROR("Strange quadrant : %d\n", quadrant);
2543 if (net->cirseg->width < 0.0)
2544 GERB_COMPILE_WARNING("Negative width [%f] in quadrant %d [%f][%f]\n",
2545 net->cirseg->width, quadrant, alfa, beta);
2547 if (net->cirseg->height < 0.0)
2548 GERB_COMPILE_WARNING("Negative height [%f] in quadrant %d [%f][%f]\n",
2549 net->cirseg->height, quadrant, RAD2DEG(alfa), RAD2DEG(beta));
2551 return;
2553 } /* calc_cirseg_sq */
2556 /* ------------------------------------------------------------------ */
2557 static void
2558 calc_cirseg_mq(struct gerbv_net *net, int cw,
2559 double delta_cp_x, double delta_cp_y)
2561 double d1x, d1y, d2x, d2y;
2562 double alfa, beta;
2564 net->cirseg->cp_x = net->start_x + delta_cp_x;
2565 net->cirseg->cp_y = net->start_y + delta_cp_y;
2568 * Some good values
2570 d1x = net->start_x - net->cirseg->cp_x;
2571 d1y = net->start_y - net->cirseg->cp_y;
2572 d2x = net->stop_x - net->cirseg->cp_x;
2573 d2y = net->stop_y - net->cirseg->cp_y;
2575 alfa = atan2(d1y, d1x);
2576 beta = atan2(d2y, d2x);
2578 net->cirseg->width = sqrt(delta_cp_x*delta_cp_x + delta_cp_y*delta_cp_y);
2579 net->cirseg->width *= 2.0;
2580 net->cirseg->height = net->cirseg->width;
2582 net->cirseg->angle1 = RAD2DEG(alfa);
2583 net->cirseg->angle2 = RAD2DEG(beta);
2586 * Make sure it's always positive angles
2588 if (net->cirseg->angle1 < 0.0) {
2589 net->cirseg->angle1 += 360.0;
2590 net->cirseg->angle2 += 360.0;
2593 if (net->cirseg->angle2 < 0.0)
2594 net->cirseg->angle2 += 360.0;
2596 if(net->cirseg->angle2 == 0.0)
2597 net->cirseg->angle2 = 360.0;
2600 * This is a sanity check for angles after the nature of atan2.
2601 * If cw we must make sure angle1-angle2 are always positive,
2602 * If ccw we must make sure angle2-angle1 are always negative.
2603 * We should really return one angle and the difference as GTK
2604 * uses them. But what the heck, it works for me.
2606 if (cw) {
2607 if (net->cirseg->angle1 <= net->cirseg->angle2)
2608 net->cirseg->angle2 -= 360.0;
2609 } else {
2610 if (net->cirseg->angle1 >= net->cirseg->angle2)
2611 net->cirseg->angle2 += 360.0;
2614 return;
2615 } /* calc_cirseg_mq */
2618 static void
2619 gerber_update_any_running_knockout_measurements (gerbv_image_t *image)
2621 if (knockoutMeasure) {
2622 knockoutLayer->knockout.lowerLeftX = knockoutLimitXmin;
2623 knockoutLayer->knockout.lowerLeftY = knockoutLimitYmin;
2624 knockoutLayer->knockout.width = knockoutLimitXmax - knockoutLimitXmin;
2625 knockoutLayer->knockout.height = knockoutLimitYmax - knockoutLimitYmin;
2626 knockoutMeasure = FALSE;
2631 static void
2632 gerber_calculate_final_justify_effects(gerbv_image_t *image)
2634 gdouble translateA = 0.0, translateB = 0.0;
2636 if (image->info->imageJustifyTypeA != GERBV_JUSTIFY_NOJUSTIFY) {
2637 if (image->info->imageJustifyTypeA == GERBV_JUSTIFY_CENTERJUSTIFY)
2638 translateA = (image->info->max_x - image->info->min_x) / 2.0;
2639 else
2640 translateA = -image->info->min_x;
2642 if (image->info->imageJustifyTypeB != GERBV_JUSTIFY_NOJUSTIFY) {
2643 if (image->info->imageJustifyTypeB == GERBV_JUSTIFY_CENTERJUSTIFY)
2644 translateB = (image->info->max_y - image->info->min_y) / 2.0;
2645 else
2646 translateB = -image->info->min_y;
2649 /* update the min/max values so the autoscale function can correctly
2650 centered a justified image */
2651 image->info->min_x += translateA+ image->info->imageJustifyOffsetA;
2652 image->info->max_x += translateA+ image->info->imageJustifyOffsetA;
2653 image->info->min_y += translateB+ image->info->imageJustifyOffsetB;
2654 image->info->max_y += translateB+ image->info->imageJustifyOffsetB;
2656 /* store the absolute offset for the justify so we can quickly offset
2657 the rendered picture during drawing */
2658 image->info->imageJustifyOffsetActualA = translateA +
2659 image->info->imageJustifyOffsetA;
2660 image->info->imageJustifyOffsetActualB = translateB +
2661 image->info->imageJustifyOffsetB;
2662 } /* gerber_calculate_final_justify_effects */
2665 void gerber_update_image_min_max (gerbv_render_size_t *boundingBox, double repeat_off_X,
2666 double repeat_off_Y, gerbv_image_t* image) {
2667 if (boundingBox->left < image->info->min_x)
2668 image->info->min_x = boundingBox->left;
2669 if (boundingBox->right+repeat_off_X > image->info->max_x)
2670 image->info->max_x = boundingBox->right+repeat_off_X;
2671 if (boundingBox->bottom < image->info->min_y)
2672 image->info->min_y = boundingBox->bottom;
2673 if (boundingBox->top+repeat_off_Y > image->info->max_y)
2674 image->info->max_y = boundingBox->top+repeat_off_Y;
2677 void
2678 gerber_update_min_and_max(gerbv_render_size_t *boundingBox,
2679 gdouble x, gdouble y, gdouble apertureSizeX1,
2680 gdouble apertureSizeX2,gdouble apertureSizeY1,
2681 gdouble apertureSizeY2)
2683 gdouble ourX1 = x - apertureSizeX1, ourY1 = y - apertureSizeY1;
2684 gdouble ourX2 = x + apertureSizeX2, ourY2 = y + apertureSizeY2;
2686 /* transform the point to the final rendered position, accounting
2687 for any scaling, offsets, mirroring, etc */
2688 /* NOTE: we need to already add/subtract in the aperture size since
2689 the final rendering may be scaled */
2690 cairo_matrix_transform_point (&currentMatrix, &ourX1, &ourY1);
2691 cairo_matrix_transform_point (&currentMatrix, &ourX2, &ourY2);
2693 /* check both points against the min/max, since depending on the rotation,
2694 mirroring, etc, either point could possibly be a min or max */
2695 if(boundingBox->left > ourX1)
2696 boundingBox->left = ourX1;
2697 if(boundingBox->left > ourX2)
2698 boundingBox->left = ourX2;
2699 if(boundingBox->right < ourX1)
2700 boundingBox->right = ourX1;
2701 if(boundingBox->right < ourX2)
2702 boundingBox->right = ourX2;
2703 if(boundingBox->bottom > ourY1)
2704 boundingBox->bottom = ourY1;
2705 if(boundingBox->bottom > ourY2)
2706 boundingBox->bottom = ourY2;
2707 if(boundingBox->top < ourY1)
2708 boundingBox->top = ourY1;
2709 if(boundingBox->top < ourY2)
2710 boundingBox->top = ourY2;
2711 } /* gerber_update_min_and_max */