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