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