1 /*
2  * gEDA - GNU Electronic Design Automation
3  * This file is a part of gerbv.
4  *
5  *   Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
20  */
21 
22 /** \file pick-and-place.c
23     \brief PNP (pick-and-place) parsing functions
24     \ingroup libgerbv
25 */
26 
27 #include "gerbv.h"
28 
29 #include <assert.h>
30 #include <ctype.h>
31 #include <math.h>
32 #include <stdlib.h>
33 
34 #include "gerber.h"
35 #include "common.h"
36 #include "csv.h"
37 #include "pick-and-place.h"
38 
39 static gerbv_net_t *pnp_new_net(gerbv_net_t *net);
40 static void pnp_reset_bbox (gerbv_net_t *net);
41 static void pnp_init_net(gerbv_net_t *net, gerbv_image_t *image,
42 		const char *label,
43 		gerbv_aperture_state_t apert_state,
44 		gerbv_interpolation_t interpol);
45 
gerb_transf_free(gerbv_transf_t * transf)46 void gerb_transf_free(gerbv_transf_t *transf)
47 {
48     g_free(transf);
49 }
50 
51 
gerb_transf_reset(gerbv_transf_t * transf)52 void gerb_transf_reset(gerbv_transf_t* transf)
53 {
54     memset(transf,0,sizeof(gerbv_transf_t));
55 
56     transf->r_mat[0][0] = transf->r_mat[1][1] = 1.0; /*off-diagonals 0 diagonals 1 */
57     //transf->r_mat[1][0] = transf->r_mat[0][1] = 0.0;
58     transf->scale = 1.0;
59     //transf->offset[0] = transf->offset[1] = 0.0;
60 
61 } /* gerb_transf_reset */
62 
63 
gerb_transf_new(void)64 gerbv_transf_t* gerb_transf_new(void)
65 {
66     gerbv_transf_t *transf;
67 
68     transf = g_new(gerbv_transf_t, 1);
69     gerb_transf_reset(transf);
70 
71     return transf;
72 } /* gerb_transf_new */
73 
74 
75 //!Rotation
76 /*! append rotation to transformation.
77 @param transf transformation to be modified
78 @param angle in rad (counterclockwise rotation) */
79 
gerb_transf_rotate(gerbv_transf_t * transf,double angle)80 void gerb_transf_rotate(gerbv_transf_t* transf, double angle)
81 {
82     double m[2][2];
83     double s = sin(angle), c = cos(angle);
84 
85     memcpy(m, transf->r_mat, sizeof(m));
86     transf->r_mat[0][0] = c * m[0][0] - s * m[1][0];
87     transf->r_mat[0][1] = c * m[0][1] - s * m[1][1];
88     transf->r_mat[1][0] = s * m[0][0] + c * m[1][0];
89     transf->r_mat[1][1] = s * m[0][1] + c * m[1][1];
90 //    transf->offset[0] = transf->offset[1] = 0.0; CHECK ME
91 
92 } /* gerb_transf_rotate */
93 
94 //!Translation
95 /*! append translation to transformation.
96 @param transf transformation to be modified
97 @param shift_x translation in x direction
98 @param shift_y translation in y direction */
99 
gerb_transf_shift(gerbv_transf_t * transf,double shift_x,double shift_y)100 void gerb_transf_shift(gerbv_transf_t* transf, double shift_x, double shift_y)
101 {
102 
103     transf->offset[0] += shift_x;
104     transf->offset[1] += shift_y;
105 
106 } /* gerb_transf_shift */
107 
gerb_transf_apply(double x,double y,gerbv_transf_t * transf,double * out_x,double * out_y)108 void gerb_transf_apply(double x, double y, gerbv_transf_t* transf, double *out_x, double *out_y)
109 {
110 
111 //    x += transf->offset[0];
112 //    y += transf->offset[1];
113     *out_x = (x * transf->r_mat[0][0] + y * transf->r_mat[0][1]) * transf->scale;
114     *out_y = (x * transf->r_mat[1][0] + y * transf->r_mat[1][1]) * transf->scale;
115     *out_x += transf->offset[0];
116     *out_y += transf->offset[1];
117 
118 
119 } /* gerb_transf_apply */
120 
121 void
pick_and_place_reset_bounding_box(gerbv_net_t * net)122 pick_and_place_reset_bounding_box (gerbv_net_t *net) {
123 	net->boundingBox.left = -HUGE_VAL;
124 	net->boundingBox.right = HUGE_VAL;
125 	net->boundingBox.bottom = -HUGE_VAL;
126 	net->boundingBox.top = HUGE_VAL;
127 }
128 
129 /* Parses a string representing float number with a unit.
130  * Default unit can be specified with def_unit. */
131 static double
pick_and_place_get_float_unit(const char * str,const char * def_unit)132 pick_and_place_get_float_unit(const char *str, const char *def_unit)
133 {
134     double x = 0.0;
135     char unit_str[41] = {0,};
136     const char *unit = unit_str;
137 
138     /* float, optional space, optional unit mm,cm,in,mil */
139     sscanf(str, "%lf %40s", &x, unit_str);
140 
141     if (unit_str[0] == '\0')
142 	unit = def_unit;
143 
144     /* NOTE: in order of comparability,
145      * i.e. "mm" before "m", as "m" will match "mm" */
146     if (strstr(unit, "mm")) {
147 	x /= 25.4;
148     } else if (strstr(unit, "in")) {
149 	/* NOTE: "in" is without scaling. */
150     } else if (strstr(unit, "cmil")) {
151 	x /= 1e5;
152     } else if (strstr(unit, "dmil")) {
153 	x /= 1e4;
154     } else if (strstr(unit, "mil")) {
155 	x /= 1e3;
156     } else if (strstr(unit, "km")) {
157 	x /= 25.4/1e6;
158     } else if (strstr(unit, "dm")) {
159 	x /= 25.4/100;
160     } else if (strstr(unit, "cm")) {
161 	x /= 25.4/10;
162     } else if (strstr(unit, "um")) {
163 	x /= 25.4*1e3;
164     } else if (strstr(unit, "nm")) {
165 	x /= 25.4*1e6;
166     } else if (strstr(unit,  "m")) {
167 	x /= 25.4/1e3;
168     } else { /* default to "mil" */
169 	x /= 1e3;
170     }
171 
172     return x;
173 } /* pick_and_place_get_float_unit */
174 
175 
176 /** search a string for a delimiter.
177  Must occur at least n times. */
178 int
pick_and_place_screen_for_delimiter(char * str,int n)179 pick_and_place_screen_for_delimiter(char *str, int n)
180 {
181     char *ptr;
182     char delimiter[4] = "|,;:";
183     int counter[4];
184     int idx, idx_max = 0;
185 
186     memset(counter, 0, sizeof(counter));
187     for(ptr = str; *ptr; ptr++) {
188 	switch(*ptr) {
189 	case '|':
190 	    idx = 0;
191 	    break;
192 	case ',':
193 	    idx = 1;
194 	    break;
195 	case ';':
196 	    idx = 2;
197 	    break;
198 	case ':':
199 	    idx = 3;
200 	    break;
201 	default:
202 	    continue;
203 	    break;
204 	}
205 	counter[idx]++;
206 	if(counter[idx] > counter[idx_max]) {
207 	    idx_max = idx;
208 	}
209     }
210 
211     if (counter[idx_max] > n) {
212 	return (unsigned char) delimiter[idx_max];
213     } else {
214 	return -1;
215     }
216 } /* pick_and_place_screen_for_delimiter */
217 
218 
219 /**Parses the PNP data.
220    two lists are filled with the row data.\n One for the scrollable list in the search and select parts interface, the other one a mere two columned list, which drives the autocompletion when entering a search.\n
221    It also tries to determine the shape of a part and sets  pnp_state->shape accordingly which will be used when drawing the selections as an overlay on screen.
222    @return the initial node of the pnp_state netlist
223  */
224 
225 GArray *
pick_and_place_parse_file(gerb_file_t * fd)226 pick_and_place_parse_file(gerb_file_t *fd)
227 {
228     PnpPartData   pnpPartData;
229     int           lineCounter = 0, parsedLines = 0;
230     int           ret;
231     char          *row[12];
232     char          buf[MAXL+2], buf0[MAXL+2];
233     char          def_unit[41] = {0,};
234     double        tmp_x, tmp_y;
235     gerbv_transf_t *tr_rot = gerb_transf_new();
236     GArray 	*pnpParseDataArray = g_array_new (FALSE, FALSE, sizeof(PnpPartData));
237     gboolean foundValidDataRow = FALSE;
238     /* Unit declaration for "PcbXY Version 1.0" files as exported by pcb */
239     const char *def_unit_prefix = "# X,Y in ";
240 
241     /*
242      * many locales redefine "." as "," and so on, so sscanf has problems when
243      * reading Pick and Place files using %f format
244      */
245     setlocale(LC_NUMERIC, "C" );
246 
247     while ( fgets(buf, MAXL, fd->fd) != NULL ) {
248 	int len = strlen(buf)-1;
249 	int i_length = 0, i_width = 0;
250 
251 	lineCounter += 1; /*next line*/
252 	if(lineCounter < 2) {
253 	    /*
254 	     * TODO in principle column names could be read and interpreted
255 	     * but we skip the first line with names of columns for this time
256 	     */
257 	    continue;
258 	}
259 	if(len >= 0 && buf[len] == '\n') {
260 	    buf[len--] = 0;
261 	}
262 	if(len >= 0 && buf[len] == '\r') {
263 	    buf[len--] = 0;
264 	}
265 	if (0 == strncmp(buf, def_unit_prefix, strlen(def_unit_prefix))) {
266 	    sscanf(&buf[strlen(def_unit_prefix)], "%40s.", def_unit);
267 	}
268 	if (len <= 11)  {  //lets check a minimum length of 11
269 	    continue;
270 	}
271 
272 	if ((len > 0) && (buf[0] == '%')) {
273 	    continue;
274 	}
275 
276 	/* Abort if we see a G54 */
277 	if ((len > 4) && (strncmp(buf,"G54 ", 4) == 0)) {
278 	    g_array_free (pnpParseDataArray, TRUE);
279 	    return NULL;
280 	}
281 
282 	/* abort if we see a G04 code */
283 	if ((len > 4) && (strncmp(buf,"G04 ", 4) == 0)) {
284 	    g_array_free (pnpParseDataArray, TRUE);
285 	    return NULL;
286 	}
287 
288 	/* this accepts file both with and without quotes */
289 /* 	if (!pnp_state) { /\* we are in first line *\/ */
290 /* 	   if ((delimiter = pnp_screen_for_delimiter(buf, 8)) < 0) { */
291 /* 	   continue; */
292 /* 	   } */
293 /* 	} */
294 
295 	ret = csv_row_parse(buf, MAXL,  buf0, MAXL, row, 11, ',',   CSV_QUOTES);
296 
297 	if (ret > 0) {
298 	    foundValidDataRow = TRUE;
299 	} else {
300 	    continue;
301 	}
302 /* 	printf("direct:%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,  %s, ret %d\n", row[0], row[1], row[2],row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], ret);        */
303 /* 	g_warning ("FFF %s %s\n",row[8],row[6]); */
304 
305 	if (row[0] && row[8]) { // here could be some better check for the syntax
306 	    snprintf (pnpPartData.designator, sizeof(pnpPartData.designator)-1, "%s", row[0]);
307 	    snprintf (pnpPartData.footprint, sizeof(pnpPartData.footprint)-1, "%s", row[1]);
308 	    snprintf (pnpPartData.layer, sizeof(pnpPartData.layer)-1, "%s", row[8]);
309 	    if (row[10] != NULL) {
310 		if ( ! g_utf8_validate(row[10], -1, NULL)) {
311 		    gchar * str = g_convert(row[10], strlen(row[10]), "UTF-8", "ISO-8859-1",
312 					    NULL, NULL, NULL);
313 		    // I have not decided yet whether it is better to use always
314 		    // "ISO-8859-1" or current locale.
315 		    // str = g_locale_to_utf8(row[10], -1, NULL, NULL, NULL);
316 		    snprintf (pnpPartData.comment, sizeof(pnpPartData.comment)-1, "%s", str);
317 		    g_free(str);
318 		} else {
319 		    snprintf (pnpPartData.comment, sizeof(pnpPartData.comment)-1, "%s", row[10]);
320 		}
321 	    }
322 	    /*
323 	      gchar* g_convert(const gchar *str, gssize len, const gchar *to_codeset, const gchar *from_codeset, gsize *bytes_read, gsize *bytes_written, GError **error);
324 	    */
325 	    pnpPartData.mid_x = pick_and_place_get_float_unit(row[2], def_unit);
326 	    pnpPartData.mid_y = pick_and_place_get_float_unit(row[3], def_unit);
327 	    pnpPartData.ref_x = pick_and_place_get_float_unit(row[4], def_unit);
328 	    pnpPartData.ref_y = pick_and_place_get_float_unit(row[5], def_unit);
329 	    pnpPartData.pad_x = pick_and_place_get_float_unit(row[6], def_unit);
330 	    pnpPartData.pad_y = pick_and_place_get_float_unit(row[7], def_unit);
331 	    /* This line causes segfault if we accidently starts parsing
332 	     * a gerber file. It is crap crap crap */
333 	    if (row[9])
334 		sscanf(row[9], "%lf", &pnpPartData.rotation); // no units, always deg
335 	}
336 	/* for now, default back to PCB program format
337 	 * TODO: implement better checking for format
338 	 */
339 	else if (row[0] && row[1] && row[2] && row[3] && row[4] && row[5] && row[6]) {
340 	    snprintf (pnpPartData.designator, sizeof(pnpPartData.designator)-1, "%s", row[0]);
341 	    snprintf (pnpPartData.footprint, sizeof(pnpPartData.footprint)-1, "%s", row[1]);
342 	    snprintf (pnpPartData.layer, sizeof(pnpPartData.layer)-1, "%s", row[6]);
343 	    pnpPartData.mid_x = pick_and_place_get_float_unit(row[3], def_unit);
344 	    pnpPartData.mid_y = pick_and_place_get_float_unit(row[4], def_unit);
345 	    pnpPartData.pad_x = pnpPartData.mid_x + 0.03;
346 	    pnpPartData.pad_y = pnpPartData.mid_y + 0.03;
347 	    sscanf(row[5], "%lf", &pnpPartData.rotation); // no units, always deg
348 	    /* check for coordinate sanity, and abort if it fails
349 	     * Note: this is mainly to catch comment lines that get parsed
350 	     */
351 	    if ((fabs(pnpPartData.mid_x) < 0.001)&&(fabs(pnpPartData.mid_y) < 0.001)) {
352 		continue;
353 	    }
354 	} else {
355 	    continue;
356 	}
357 
358 
359 	/*
360 	 * now, try and figure out the actual footprint shape to draw, or just
361 	 * guess something reasonable
362 	 */
363 	if(sscanf(pnpPartData.footprint, "%02d%02d", &i_length, &i_width) == 2) {
364 	    // parse footprints like 0805 or 1206
365 	    pnpPartData.length = 0.01 * i_length;
366 	    pnpPartData.width = 0.01 * i_width;
367 	    pnpPartData.shape = PART_SHAPE_RECTANGLE;
368 	} else {
369 	    gerb_transf_reset(tr_rot);
370 	    gerb_transf_rotate(tr_rot, -DEG2RAD(pnpPartData.rotation));/* rotate it back to get dimensions */
371 	    gerb_transf_apply( pnpPartData.pad_x -  pnpPartData.mid_x,
372 			       pnpPartData.pad_y - pnpPartData.mid_y, tr_rot, &tmp_x, &tmp_y);
373 	    if ((fabs(tmp_y) > fabs(tmp_x/100)) && (fabs(tmp_x) > fabs(tmp_y/100))){
374 		pnpPartData.length = 2 * fabs(tmp_x);/* get dimensions*/
375 		pnpPartData.width = 2 * fabs(tmp_y);
376 		pnpPartData.shape = PART_SHAPE_STD;
377 	    } else {
378 		pnpPartData.length = 0.015;
379 		pnpPartData.width = 0.015;
380 		pnpPartData.shape = PART_SHAPE_UNKNOWN;
381 	    }
382 	}
383 	g_array_append_val (pnpParseDataArray, pnpPartData);
384 	parsedLines += 1;
385     }
386     gerb_transf_free(tr_rot);
387     /* fd->ptr=0; */
388     /* rewind(fd->fd); */
389 
390     /* so a sanity check and see if this is a valid pnp file */
391     if ((((float) parsedLines / (float) lineCounter) < 0.3) ||
392 	(!foundValidDataRow)) {
393 	/* this doesn't look like a valid PNP file, so return error */
394 	g_array_free (pnpParseDataArray, TRUE);
395 	return NULL;
396     }
397     return pnpParseDataArray;
398 } /* pick_and_place_parse_file */
399 
400 
401 /*	------------------------------------------------------------------
402  *	pick_and_place_check_file_type
403  *	------------------------------------------------------------------
404  *	Description: Tries to parse the given file into a pick-and-place
405  *		data set. If it fails to read any good rows, then returns
406  *		FALSE, otherwise it returns TRUE.
407  *	Notes:
408  *	------------------------------------------------------------------
409  */
410 gboolean
pick_and_place_check_file_type(gerb_file_t * fd,gboolean * returnFoundBinary)411 pick_and_place_check_file_type(gerb_file_t *fd, gboolean *returnFoundBinary)
412 {
413     char *buf;
414     int len = 0;
415     int i;
416     char *letter;
417     gboolean found_binary = FALSE;
418     gboolean found_G54 = FALSE;
419     gboolean found_M0 = FALSE;
420     gboolean found_M2 = FALSE;
421     gboolean found_G2 = FALSE;
422     gboolean found_ADD = FALSE;
423     gboolean found_comma = FALSE;
424     gboolean found_R = FALSE;
425     gboolean found_U = FALSE;
426     gboolean found_C = FALSE;
427     gboolean found_boardside = FALSE;
428 
429     buf = malloc(MAXL);
430     if (buf == NULL)
431 	GERB_FATAL_ERROR("malloc buf failed in %s()", __FUNCTION__);
432 
433     while (fgets(buf, MAXL, fd->fd) != NULL) {
434 	len = strlen(buf);
435 
436 	/* First look through the file for indications of its type */
437 
438 	/* check for non-binary file */
439 	for (i = 0; i < len; i++) {
440 	    if (!isprint((int) buf[i]) && (buf[i] != '\r') &&
441                 (buf[i] != '\n') && (buf[i] != '\t')) {
442 		found_binary = TRUE;
443 	    }
444 	}
445 
446 	if (g_strstr_len(buf, len, "G54")) {
447 	    found_G54 = TRUE;
448 	}
449 	if (g_strstr_len(buf, len, "M00")) {
450 	    found_M0 = TRUE;
451 	}
452 	if (g_strstr_len(buf, len, "M02")) {
453 	    found_M2 = TRUE;
454 	}
455 	if (g_strstr_len(buf, len, "G02")) {
456 	    found_G2 = TRUE;
457 	}
458 	if (g_strstr_len(buf, len, "ADD")) {
459 	    found_ADD = TRUE;
460 	}
461 	if (g_strstr_len(buf, len, ",")) {
462 	    found_comma = TRUE;
463 	}
464 	/* Semicolon can be separator too */
465 	if (g_strstr_len(buf, len, ";")) {
466 	    found_comma = TRUE;
467 	}
468 
469 	/* Look for refdes -- This is dumb, but what else can we do? */
470 	if ((letter = g_strstr_len(buf, len, "R")) != NULL) {
471 	    if (isdigit((int) letter[1])) { /* grab char after R */
472 		found_R = TRUE;
473 	    }
474 	}
475 	if ((letter = g_strstr_len(buf, len, "C")) != NULL) {
476 	    if (isdigit((int) letter[1])) { /* grab char after C */
477 		found_C = TRUE;
478 	    }
479 	}
480 	if ((letter = g_strstr_len(buf, len, "U")) != NULL) {
481 	    if (isdigit((int) letter[1])) { /* grab char after U */
482 		found_U = TRUE;
483 	    }
484 	}
485 
486 	/* Look for board side indicator since this is required
487 	 * by many vendors */
488 	if (g_strstr_len(buf, len, "top")) {
489 	    found_boardside = TRUE;
490 	}
491 	if (g_strstr_len(buf, len, "Top")) {
492 	    found_boardside = TRUE;
493 	}
494 	if (g_strstr_len(buf, len, "TOP")) {
495 	    found_boardside = TRUE;
496 	}
497 	/* Also look for evidence of "Layer" in header.... */
498 	if (g_strstr_len(buf, len, "ayer")) {
499 	    found_boardside = TRUE;
500 	}
501 	if (g_strstr_len(buf, len, "AYER")) {
502 	    found_boardside = TRUE;
503 	}
504 
505     }
506     rewind(fd->fd);
507     free(buf);
508 
509     /* Now form logical expression determining if this is a pick-place file */
510     *returnFoundBinary = found_binary;
511     if (found_G54)
512 	return FALSE;
513     if (found_M0)
514 	return FALSE;
515     if (found_M2)
516 	return FALSE;
517     if (found_G2)
518 	return FALSE;
519     if (found_ADD)
520 	return FALSE;
521     if (found_comma && (found_R || found_C || found_U) &&
522 	found_boardside)
523 	return TRUE;
524 
525     return FALSE;
526 
527 } /* pick_and_place_check_file_type */
528 
529 
530 /*	------------------------------------------------------------------
531  *	pick_and_place_convert_pnp_data_to_image
532  *	------------------------------------------------------------------
533  *	Description: Render a parsedPickAndPlaceData array into a gerb_image.
534  *	Notes:
535  *	------------------------------------------------------------------
536  */
537 gerbv_image_t *
pick_and_place_convert_pnp_data_to_image(GArray * parsedPickAndPlaceData,gint boardSide)538 pick_and_place_convert_pnp_data_to_image(GArray *parsedPickAndPlaceData, gint boardSide)
539 {
540     gerbv_image_t *image = NULL;
541     gerbv_net_t *curr_net = NULL;
542     int i;
543     gerbv_transf_t *tr_rot = gerb_transf_new();
544     gerbv_drill_stats_t *stats;  /* Eventually replace with pick_place_stats */
545     gboolean foundElement = FALSE;
546     const double draw_width = 0.01;
547 
548     /* step through and make sure we have an element on the layer before
549        we actually create a new image for it and fill it */
550     for (i = 0; i < parsedPickAndPlaceData->len; i++) {
551 	PnpPartData partData = g_array_index(parsedPickAndPlaceData, PnpPartData, i);
552 
553 	if ((boardSide == 0) && !((partData.layer[0]=='b') || (partData.layer[0]=='B')))
554 		continue;
555 	if ((boardSide == 1) && !((partData.layer[0]=='t') || (partData.layer[0]=='T')))
556 		continue;
557 
558 	foundElement = TRUE;
559     }
560     if (!foundElement)
561     	return NULL;
562 
563     image = gerbv_create_image(image, "Pick and Place (X-Y) File");
564     if (image == NULL) {
565 	GERB_FATAL_ERROR("malloc image failed in %s()", __FUNCTION__);
566     }
567 
568     image->format = g_new0(gerbv_format_t, 1);
569     if (image->format == NULL) {
570 	GERB_FATAL_ERROR("malloc format failed in %s()", __FUNCTION__);
571     }
572 
573     /* Separate top/bot layer type is needed for reload purpose */
574     if (boardSide == 1)
575 	    image->layertype = GERBV_LAYERTYPE_PICKANDPLACE_TOP;
576     else
577 	    image->layertype = GERBV_LAYERTYPE_PICKANDPLACE_BOT;
578 
579     stats = gerbv_drill_stats_new();
580     if (stats == NULL)
581         GERB_FATAL_ERROR("malloc pick_place_stats failed in %s()",
582 			__FUNCTION__);
583     image->drill_stats = stats;
584 
585 
586     curr_net = image->netlist;
587     curr_net->layer = image->layers;
588     curr_net->state = image->states;
589     pnp_reset_bbox (curr_net);
590     image->info->min_x = HUGE_VAL;
591     image->info->min_y = HUGE_VAL;
592     image->info->max_x = -HUGE_VAL;
593     image->info->max_y = -HUGE_VAL;
594 
595     image->aperture[0] = g_new0(gerbv_aperture_t, 1);
596     assert(image->aperture[0] != NULL);
597     image->aperture[0]->type = GERBV_APTYPE_CIRCLE;
598     image->aperture[0]->amacro = NULL;
599     image->aperture[0]->parameter[0] = draw_width;
600     image->aperture[0]->nuf_parameters = 1;
601 
602     for (i = 0; i < parsedPickAndPlaceData->len; i++) {
603 	PnpPartData partData = g_array_index(parsedPickAndPlaceData, PnpPartData, i);
604 	float radius,labelOffset;
605 
606 	curr_net = pnp_new_net(curr_net);
607 	curr_net->layer = image->layers;
608 	curr_net->state = image->states;
609 
610 	if ((partData.rotation > 89) && (partData.rotation < 91))
611 		labelOffset = fabs(partData.length/2);
612 	else if ((partData.rotation > 179) && (partData.rotation < 181))
613 		labelOffset = fabs(partData.width/2);
614 	else if ((partData.rotation > 269) && (partData.rotation < 271))
615 		labelOffset = fabs(partData.length/2);
616 	else if ((partData.rotation > -91) && (partData.rotation < -89))
617 		labelOffset = fabs(partData.length/2);
618 	else if ((partData.rotation > -181) && (partData.rotation < -179))
619 		labelOffset = fabs(partData.width/2);
620 	else if ((partData.rotation > -271) && (partData.rotation < -269))
621 		labelOffset = fabs(partData.length/2);
622 	else labelOffset = fabs(partData.width/2);
623 
624 	partData.rotation = DEG2RAD(partData.rotation);
625 
626 	/* check if the entry is on the specified layer */
627 	if ((boardSide == 0) && !((partData.layer[0]=='b') || (partData.layer[0]=='B')))
628 		continue;
629 	if ((boardSide == 1) && !((partData.layer[0]=='t') || (partData.layer[0]=='T')))
630 		continue;
631 
632 	curr_net = pnp_new_net(curr_net);
633 	pnp_init_net(curr_net, image, partData.designator,
634 			GERBV_APERTURE_STATE_OFF,
635 			GERBV_INTERPOLATION_LINEARx1);
636 
637 	/* First net of PNP is just a label holder, so calculate the lower left
638 	 * location to line up above the element */
639 	curr_net->start_x = curr_net->stop_x = partData.mid_x;
640 	curr_net->start_y = curr_net->stop_y =
641 		partData.mid_y + labelOffset + draw_width;
642 
643 	gerb_transf_reset(tr_rot);
644 	gerb_transf_shift(tr_rot, partData.mid_x, partData.mid_y);
645 	gerb_transf_rotate(tr_rot, -partData.rotation);
646 
647 	if ((partData.shape == PART_SHAPE_RECTANGLE) ||
648 	    (partData.shape == PART_SHAPE_STD)) {
649 	    // TODO: draw rectangle length x width taking into account rotation or pad x,y
650 
651 	    curr_net = pnp_new_net(curr_net);
652 	    pnp_init_net(curr_net, image, partData.designator,
653 			    GERBV_APERTURE_STATE_ON,
654 			    GERBV_INTERPOLATION_LINEARx1);
655 
656 	    gerb_transf_apply(partData.length/2, partData.width/2, tr_rot,
657 			      &curr_net->start_x, &curr_net->start_y);
658 	    gerb_transf_apply(-partData.length/2, partData.width/2, tr_rot,
659 			      &curr_net->stop_x, &curr_net->stop_y);
660 
661 /* TODO: write unifying function */
662 
663 	    curr_net = pnp_new_net(curr_net);
664 	    pnp_init_net(curr_net, image, partData.designator,
665 			    GERBV_APERTURE_STATE_ON,
666 			    GERBV_INTERPOLATION_LINEARx1);
667 
668 	    gerb_transf_apply(-partData.length/2, partData.width/2, tr_rot,
669 			      &curr_net->start_x, &curr_net->start_y);
670 	    gerb_transf_apply(-partData.length/2, -partData.width/2, tr_rot,
671 			      &curr_net->stop_x, &curr_net->stop_y);
672 
673 	    curr_net = pnp_new_net(curr_net);
674 	    pnp_init_net(curr_net, image, partData.designator,
675 			    GERBV_APERTURE_STATE_ON,
676 			    GERBV_INTERPOLATION_LINEARx1);
677 
678 	    gerb_transf_apply(-partData.length/2, -partData.width/2, tr_rot,
679 			      &curr_net->start_x, &curr_net->start_y);
680 	    gerb_transf_apply(partData.length/2, -partData.width/2, tr_rot,
681 			      &curr_net->stop_x, &curr_net->stop_y);
682 
683 	    curr_net = pnp_new_net(curr_net);
684 	    pnp_init_net(curr_net, image, partData.designator,
685 			    GERBV_APERTURE_STATE_ON,
686 			    GERBV_INTERPOLATION_LINEARx1);
687 
688 	    gerb_transf_apply(partData.length/2, -partData.width/2, tr_rot,
689 			      &curr_net->start_x, &curr_net->start_y);
690 	    gerb_transf_apply(partData.length/2, partData.width/2, tr_rot,
691 			      &curr_net->stop_x, &curr_net->stop_y);
692 
693 	    curr_net = pnp_new_net(curr_net);
694 	    pnp_init_net(curr_net, image, partData.designator,
695 			    GERBV_APERTURE_STATE_ON,
696 			    GERBV_INTERPOLATION_LINEARx1);
697 
698 	    if (partData.shape == PART_SHAPE_RECTANGLE) {
699 		gerb_transf_apply(partData.length/4, -partData.width/2, tr_rot,
700 				  &curr_net->start_x, &curr_net->start_y);
701 		gerb_transf_apply(partData.length/4, partData.width/2, tr_rot,
702 				  &curr_net->stop_x, &curr_net->stop_y);
703 	    } else {
704 		gerb_transf_apply(partData.length/4, partData.width/2, tr_rot,
705 				  &curr_net->start_x, &curr_net->start_y);
706 		gerb_transf_apply(partData.length/4, partData.width/4, tr_rot,
707 				  &curr_net->stop_x, &curr_net->stop_y);
708 
709 		curr_net = pnp_new_net(curr_net);
710 		pnp_init_net(curr_net, image, partData.designator,
711 				GERBV_APERTURE_STATE_ON,
712 				GERBV_INTERPOLATION_LINEARx1);
713 
714 		gerb_transf_apply(partData.length/2, partData.width/4, tr_rot,
715 				  &curr_net->start_x, &curr_net->start_y);
716 		gerb_transf_apply(partData.length/4, partData.width/4, tr_rot,
717 				  &curr_net->stop_x, &curr_net->stop_y);
718 	    }
719 
720 	    /* calculate a rough radius for the min/max screen calcs later */
721 	    radius = MAX(partData.length/2, partData.width/2);
722 	} else {
723 	    gdouble tmp_x,tmp_y;
724 
725 	    pnp_init_net(curr_net, image, partData.designator,
726 			    GERBV_APERTURE_STATE_ON,
727 			    GERBV_INTERPOLATION_LINEARx1);
728 
729 	    curr_net->start_x = partData.mid_x;
730 	    curr_net->start_y = partData.mid_y;
731 	    gerb_transf_apply( partData.pad_x -  partData.mid_x,
732 			       partData.pad_y - partData.mid_y, tr_rot, &tmp_x, &tmp_y);
733 
734 	    curr_net->stop_x = tmp_x;
735 	    curr_net->stop_y = tmp_y;
736 
737 
738 	    curr_net = pnp_new_net(curr_net);
739 	    pnp_init_net(curr_net, image, partData.designator,
740 			    GERBV_APERTURE_STATE_ON,
741 			    GERBV_INTERPOLATION_CW_CIRCULAR);
742 
743 	    curr_net->start_x = partData.mid_x;
744 	    curr_net->start_y = partData.mid_y;
745 	    curr_net->stop_x = partData.pad_x;
746 	    curr_net->stop_y = partData.pad_y;
747 
748 	    curr_net->cirseg = g_new0 (gerbv_cirseg_t, 1);
749 	    curr_net->cirseg->angle1 = 0.0;
750 	    curr_net->cirseg->angle2 = 360.0;
751 	    curr_net->cirseg->cp_x = partData.mid_x;
752 	    curr_net->cirseg->cp_y = partData.mid_y;
753 	    radius = hypot(partData.pad_x - partData.mid_x,
754 			    partData.pad_y-partData.mid_y);
755 	    if (radius < 0.001)
756 	    	radius = 0.1;
757 	    curr_net->cirseg->width = 2*radius; /* fabs(pad_x-mid_x) */
758 	    curr_net->cirseg->height = 2*radius;
759 	}
760 
761 	/*
762 	 * update min and max numbers so the screen zoom-to-fit
763 	 *function will work
764 	 */
765 	image->info->min_x = MIN(image->info->min_x, (partData.mid_x - radius - 0.02));
766 	image->info->min_y = MIN(image->info->min_y, (partData.mid_y - radius - 0.02));
767 	image->info->max_x = MAX(image->info->max_x, (partData.mid_x + radius + 0.02));
768 	image->info->max_y = MAX(image->info->max_y, (partData.mid_y + radius + 0.02));
769     }
770     curr_net->next = NULL;
771 
772     gerb_transf_free(tr_rot);
773     return image;
774 } /* pick_and_place_convert_pnp_data_to_image */
775 
776 
777 /*	------------------------------------------------------------------
778  *	pick_and_place_parse_file_to_images
779  *	------------------------------------------------------------------
780  *	Description: Renders a pick and place file to a gerb_image.
781  *	If image pointer is not NULL, then corresponding image will not be
782  *	populated.
783  *	Notes: The file format should already be verified before calling
784  *       this function, since it does very little sanity checking itself.
785  *	------------------------------------------------------------------
786  */
787 void
pick_and_place_parse_file_to_images(gerb_file_t * fd,gerbv_image_t ** topImage,gerbv_image_t ** bottomImage)788 pick_and_place_parse_file_to_images(gerb_file_t *fd, gerbv_image_t **topImage,
789 			gerbv_image_t **bottomImage)
790 {
791 	GArray *parsedPickAndPlaceData = pick_and_place_parse_file (fd);
792 
793 	if (parsedPickAndPlaceData != NULL) {
794 		/* Non NULL pointer is used as "not to reload" mark */
795 		if (*bottomImage == NULL)
796 			*bottomImage = pick_and_place_convert_pnp_data_to_image(parsedPickAndPlaceData, 0);
797 
798 		if (*topImage == NULL)
799 			*topImage = pick_and_place_convert_pnp_data_to_image(parsedPickAndPlaceData, 1);
800 
801 		g_array_free (parsedPickAndPlaceData, TRUE);
802 	}
803 } /* pick_and_place_parse_file_to_images */
804 
805 static gerbv_net_t *
pnp_new_net(gerbv_net_t * net)806 pnp_new_net(gerbv_net_t *net)
807 {
808 	gerbv_net_t *n;
809 	net->next = g_new0(gerbv_net_t, 1);
810 	n = net->next;
811 	assert(n != NULL);
812 
813 	pnp_reset_bbox (n);
814 
815 	return n;
816 }
817 
818 static void
pnp_reset_bbox(gerbv_net_t * net)819 pnp_reset_bbox (gerbv_net_t *net)
820 {
821 	net->boundingBox.left = -HUGE_VAL;
822 	net->boundingBox.right = HUGE_VAL;
823 	net->boundingBox.bottom = -HUGE_VAL;
824 	net->boundingBox.top = HUGE_VAL;
825 }
826 
827 static void
pnp_init_net(gerbv_net_t * net,gerbv_image_t * image,const char * label,gerbv_aperture_state_t apert_state,gerbv_interpolation_t interpol)828 pnp_init_net(gerbv_net_t *net, gerbv_image_t *image, const char *label,
829 		gerbv_aperture_state_t apert_state,
830 		gerbv_interpolation_t interpol)
831 {
832 	net->aperture = 0;
833 	net->aperture_state = apert_state;
834 	net->interpolation = interpol;
835 	net->layer = image->layers;
836 	net->state = image->states;
837 
838 	if (strlen(label) > 0) {
839 		net->label = g_string_new (label);
840 	}
841 }
842