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