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  char *tmp;
436  gboolean found_binary = FALSE;
437  gboolean found_G54 = FALSE;
438  gboolean found_M0 = FALSE;
439  gboolean found_M2 = FALSE;
440  gboolean found_G2 = FALSE;
441  gboolean found_ADD = FALSE;
442  gboolean found_comma = FALSE;
443  gboolean found_R = FALSE;
444  gboolean found_U = FALSE;
445  gboolean found_C = FALSE;
446  gboolean found_boardside = FALSE;
447 
448  buf = malloc(MAXL);
449  if (buf == NULL)
450  GERB_FATAL_ERROR("malloc buf failed in %s()", __FUNCTION__);
451 
452  while (fgets(buf, MAXL, fd->fd) != NULL) {
453  len = strlen(buf);
454 
455  /* First look through the file for indications of its type */
456 
457  /* check for non-binary file */
458  for (i = 0; i < len; i++) {
459  if (!isprint((int) buf[i]) && (buf[i] != '\r') &&
460  (buf[i] != '\n') && (buf[i] != '\t')) {
461  found_binary = TRUE;
462  }
463  }
464 
465  if ((tmp = g_strstr_len(buf, len, "G54")) && (tmp - buf < 2)) {
466  found_G54 = TRUE;
467  }
468  if ((tmp = g_strstr_len(buf, len, "M00")) && (tmp - buf < 2)) {
469  found_M0 = TRUE;
470  }
471  if ((tmp = g_strstr_len(buf, len, "M02")) && (tmp - buf < 2)) {
472  found_M2 = TRUE;
473  }
474  if ((tmp = g_strstr_len(buf, len, "G02")) && (tmp - buf < 2)) {
475  found_G2 = TRUE;
476  }
477  if ((tmp = g_strstr_len(buf, len, "ADD")) && (tmp - buf < 2)) {
478  found_ADD = TRUE;
479  }
480  if (g_strstr_len(buf, len, ",")) {
481  found_comma = TRUE;
482  }
483  /* Semicolon can be separator too */
484  if (g_strstr_len(buf, len, ";")) {
485  found_comma = TRUE;
486  }
487 
488  /* Look for refdes -- This is dumb, but what else can we do? */
489  if ((letter = g_strstr_len(buf, len, "R")) != NULL) {
490  if (isdigit((int) letter[1])) { /* grab char after R */
491  found_R = TRUE;
492  }
493  }
494  if ((letter = g_strstr_len(buf, len, "C")) != NULL) {
495  if (isdigit((int) letter[1])) { /* grab char after C */
496  found_C = TRUE;
497  }
498  }
499  if ((letter = g_strstr_len(buf, len, "U")) != NULL) {
500  if (isdigit((int) letter[1])) { /* grab char after U */
501  found_U = TRUE;
502  }
503  }
504 
505  /* Look for board side indicator since this is required
506  * by many vendors */
507  if (g_strstr_len(buf, len, "top")) {
508  found_boardside = TRUE;
509  }
510  if (g_strstr_len(buf, len, "Top")) {
511  found_boardside = TRUE;
512  }
513  if (g_strstr_len(buf, len, "TOP")) {
514  found_boardside = TRUE;
515  }
516  /* Also look for evidence of "Layer" in header.... */
517  if (g_strstr_len(buf, len, "ayer")) {
518  found_boardside = TRUE;
519  }
520  if (g_strstr_len(buf, len, "AYER")) {
521  found_boardside = TRUE;
522  }
523 
524  }
525  rewind(fd->fd);
526  free(buf);
527 
528  /* Now form logical expression determining if this is a pick-place file */
529  *returnFoundBinary = found_binary;
530  if (found_G54)
531  return FALSE;
532  if (found_M0)
533  return FALSE;
534  if (found_M2)
535  return FALSE;
536  if (found_G2)
537  return FALSE;
538  if (found_ADD)
539  return FALSE;
540  if (found_comma && (found_R || found_C || found_U) &&
541  found_boardside)
542  return TRUE;
543 
544  return FALSE;
545 
546 } /* pick_and_place_check_file_type */
547 
548 
549 /* ------------------------------------------------------------------
550  * pick_and_place_convert_pnp_data_to_image
551  * ------------------------------------------------------------------
552  * Description: Render a parsedPickAndPlaceData array into a gerb_image.
553  * Notes:
554  * ------------------------------------------------------------------
555  */
557 pick_and_place_convert_pnp_data_to_image(GArray *parsedPickAndPlaceData, gint boardSide)
558 {
559  gerbv_image_t *image = NULL;
560  gerbv_net_t *curr_net = NULL;
561  gerbv_transf_t *tr_rot = gerb_transf_new();
562  gerbv_drill_stats_t *stats; /* Eventually replace with pick_place_stats */
563  gboolean foundElement = FALSE;
564  const double draw_width = 0.01;
565 
566  /* step through and make sure we have an element on the layer before
567  we actually create a new image for it and fill it */
568  for (guint i = 0; i < parsedPickAndPlaceData->len; i++) {
569  PnpPartData partData = g_array_index(parsedPickAndPlaceData, PnpPartData, i);
570 
571  if ((boardSide == 0) && !((partData.layer[0]=='b') || (partData.layer[0]=='B')))
572  continue;
573  if ((boardSide == 1) && !((partData.layer[0]=='t') || (partData.layer[0]=='T')))
574  continue;
575 
576  foundElement = TRUE;
577  }
578  if (!foundElement)
579  return NULL;
580 
581  image = gerbv_create_image(image, "Pick and Place (X-Y) File");
582  if (image == NULL) {
583  GERB_FATAL_ERROR("malloc image failed in %s()", __FUNCTION__);
584  }
585 
586  image->format = g_new0(gerbv_format_t, 1);
587  if (image->format == NULL) {
588  GERB_FATAL_ERROR("malloc format failed in %s()", __FUNCTION__);
589  }
590 
591  /* Separate top/bot layer type is needed for reload purpose */
592  if (boardSide == 1)
594  else
596 
597  stats = gerbv_drill_stats_new();
598  if (stats == NULL)
599  GERB_FATAL_ERROR("malloc pick_place_stats failed in %s()",
600  __FUNCTION__);
601  image->drill_stats = stats;
602 
603 
604  curr_net = image->netlist;
605  curr_net->layer = image->layers;
606  curr_net->state = image->states;
607  pnp_reset_bbox (curr_net);
608  image->info->min_x = HUGE_VAL;
609  image->info->min_y = HUGE_VAL;
610  image->info->max_x = -HUGE_VAL;
611  image->info->max_y = -HUGE_VAL;
612 
613  image->aperture[0] = g_new0(gerbv_aperture_t, 1);
614  assert(image->aperture[0] != NULL);
615  image->aperture[0]->type = GERBV_APTYPE_CIRCLE;
616  image->aperture[0]->amacro = NULL;
617  image->aperture[0]->parameter[0] = draw_width;
618  image->aperture[0]->nuf_parameters = 1;
619 
620  for (guint i = 0; i < parsedPickAndPlaceData->len; i++) {
621  PnpPartData partData = g_array_index(parsedPickAndPlaceData, PnpPartData, i);
622  float radius,labelOffset;
623 
624  curr_net = pnp_new_net(curr_net);
625  curr_net->layer = image->layers;
626  curr_net->state = image->states;
627 
628  if ((partData.rotation > 89) && (partData.rotation < 91))
629  labelOffset = fabs(partData.length/2);
630  else if ((partData.rotation > 179) && (partData.rotation < 181))
631  labelOffset = fabs(partData.width/2);
632  else if ((partData.rotation > 269) && (partData.rotation < 271))
633  labelOffset = fabs(partData.length/2);
634  else if ((partData.rotation > -91) && (partData.rotation < -89))
635  labelOffset = fabs(partData.length/2);
636  else if ((partData.rotation > -181) && (partData.rotation < -179))
637  labelOffset = fabs(partData.width/2);
638  else if ((partData.rotation > -271) && (partData.rotation < -269))
639  labelOffset = fabs(partData.length/2);
640  else labelOffset = fabs(partData.width/2);
641 
642  partData.rotation = DEG2RAD(partData.rotation);
643 
644  /* check if the entry is on the specified layer */
645  if ((boardSide == 0) && !((partData.layer[0]=='b') || (partData.layer[0]=='B')))
646  continue;
647  if ((boardSide == 1) && !((partData.layer[0]=='t') || (partData.layer[0]=='T')))
648  continue;
649 
650  curr_net = pnp_new_net(curr_net);
651  pnp_init_net(curr_net, image, partData.designator,
654 
655  /* First net of PNP is just a label holder, so calculate the lower left
656  * location to line up above the element */
657  curr_net->start_x = curr_net->stop_x = partData.mid_x;
658  curr_net->start_y = curr_net->stop_y =
659  partData.mid_y + labelOffset + draw_width;
660 
661  gerb_transf_reset(tr_rot);
662  gerb_transf_shift(tr_rot, partData.mid_x, partData.mid_y);
663  gerb_transf_rotate(tr_rot, -partData.rotation);
664 
665  if ((partData.shape == PART_SHAPE_RECTANGLE) ||
666  (partData.shape == PART_SHAPE_STD)) {
667  // TODO: draw rectangle length x width taking into account rotation or pad x,y
668 
669  curr_net = pnp_new_net(curr_net);
670  pnp_init_net(curr_net, image, partData.designator,
673 
674  gerb_transf_apply(partData.length/2, partData.width/2, tr_rot,
675  &curr_net->start_x, &curr_net->start_y);
676  gerb_transf_apply(-partData.length/2, partData.width/2, tr_rot,
677  &curr_net->stop_x, &curr_net->stop_y);
678 
679 /* TODO: write unifying function */
680 
681  curr_net = pnp_new_net(curr_net);
682  pnp_init_net(curr_net, image, partData.designator,
685 
686  gerb_transf_apply(-partData.length/2, partData.width/2, tr_rot,
687  &curr_net->start_x, &curr_net->start_y);
688  gerb_transf_apply(-partData.length/2, -partData.width/2, tr_rot,
689  &curr_net->stop_x, &curr_net->stop_y);
690 
691  curr_net = pnp_new_net(curr_net);
692  pnp_init_net(curr_net, image, partData.designator,
695 
696  gerb_transf_apply(-partData.length/2, -partData.width/2, tr_rot,
697  &curr_net->start_x, &curr_net->start_y);
698  gerb_transf_apply(partData.length/2, -partData.width/2, tr_rot,
699  &curr_net->stop_x, &curr_net->stop_y);
700 
701  curr_net = pnp_new_net(curr_net);
702  pnp_init_net(curr_net, image, partData.designator,
705 
706  gerb_transf_apply(partData.length/2, -partData.width/2, tr_rot,
707  &curr_net->start_x, &curr_net->start_y);
708  gerb_transf_apply(partData.length/2, partData.width/2, tr_rot,
709  &curr_net->stop_x, &curr_net->stop_y);
710 
711  curr_net = pnp_new_net(curr_net);
712  pnp_init_net(curr_net, image, partData.designator,
715 
716  if (partData.shape == PART_SHAPE_RECTANGLE) {
717  gerb_transf_apply(partData.length/4, -partData.width/2, tr_rot,
718  &curr_net->start_x, &curr_net->start_y);
719  gerb_transf_apply(partData.length/4, partData.width/2, tr_rot,
720  &curr_net->stop_x, &curr_net->stop_y);
721  } else {
722  gerb_transf_apply(partData.length/4, partData.width/2, tr_rot,
723  &curr_net->start_x, &curr_net->start_y);
724  gerb_transf_apply(partData.length/4, partData.width/4, tr_rot,
725  &curr_net->stop_x, &curr_net->stop_y);
726 
727  curr_net = pnp_new_net(curr_net);
728  pnp_init_net(curr_net, image, partData.designator,
731 
732  gerb_transf_apply(partData.length/2, partData.width/4, tr_rot,
733  &curr_net->start_x, &curr_net->start_y);
734  gerb_transf_apply(partData.length/4, partData.width/4, tr_rot,
735  &curr_net->stop_x, &curr_net->stop_y);
736  }
737 
738  /* calculate a rough radius for the min/max screen calcs later */
739  radius = MAX(partData.length/2, partData.width/2);
740  } else {
741  gdouble tmp_x,tmp_y;
742 
743  pnp_init_net(curr_net, image, partData.designator,
746 
747  curr_net->start_x = partData.mid_x;
748  curr_net->start_y = partData.mid_y;
749  gerb_transf_apply( partData.pad_x - partData.mid_x,
750  partData.pad_y - partData.mid_y, tr_rot, &tmp_x, &tmp_y);
751 
752  curr_net->stop_x = tmp_x;
753  curr_net->stop_y = tmp_y;
754 
755 
756  curr_net = pnp_new_net(curr_net);
757  pnp_init_net(curr_net, image, partData.designator,
760 
761  curr_net->start_x = partData.mid_x;
762  curr_net->start_y = partData.mid_y;
763  curr_net->stop_x = partData.pad_x;
764  curr_net->stop_y = partData.pad_y;
765 
766  curr_net->cirseg = g_new0 (gerbv_cirseg_t, 1);
767  curr_net->cirseg->angle1 = 0.0;
768  curr_net->cirseg->angle2 = 360.0;
769  curr_net->cirseg->cp_x = partData.mid_x;
770  curr_net->cirseg->cp_y = partData.mid_y;
771  radius = hypot(partData.pad_x - partData.mid_x,
772  partData.pad_y-partData.mid_y);
773  if (radius < 0.001)
774  radius = 0.1;
775  curr_net->cirseg->width = 2*radius; /* fabs(pad_x-mid_x) */
776  curr_net->cirseg->height = 2*radius;
777  }
778 
779  /*
780  * update min and max numbers so the screen zoom-to-fit
781  *function will work
782  */
783  image->info->min_x = MIN(image->info->min_x, (partData.mid_x - radius - 0.02));
784  image->info->min_y = MIN(image->info->min_y, (partData.mid_y - radius - 0.02));
785  image->info->max_x = MAX(image->info->max_x, (partData.mid_x + radius + 0.02));
786  image->info->max_y = MAX(image->info->max_y, (partData.mid_y + radius + 0.02));
787  }
788  curr_net->next = NULL;
789 
790  gerb_transf_free(tr_rot);
791  return image;
792 } /* pick_and_place_convert_pnp_data_to_image */
793 
794 
795 /* ------------------------------------------------------------------
796  * pick_and_place_parse_file_to_images
797  * ------------------------------------------------------------------
798  * Description: Renders a pick and place file to a gerb_image.
799  * If image pointer is not NULL, then corresponding image will not be
800  * populated.
801  * Notes: The file format should already be verified before calling
802  * this function, since it does very little sanity checking itself.
803  * ------------------------------------------------------------------
804  */
805 void
806 pick_and_place_parse_file_to_images(gerb_file_t *fd, gerbv_image_t **topImage,
807  gerbv_image_t **bottomImage)
808 {
809  GArray *parsedPickAndPlaceData = pick_and_place_parse_file (fd);
810 
811  if (parsedPickAndPlaceData != NULL) {
812  /* Non NULL pointer is used as "not to reload" mark */
813  if (*bottomImage == NULL)
814  *bottomImage = pick_and_place_convert_pnp_data_to_image(parsedPickAndPlaceData, 0);
815 
816  if (*topImage == NULL)
817  *topImage = pick_and_place_convert_pnp_data_to_image(parsedPickAndPlaceData, 1);
818 
819  g_array_free (parsedPickAndPlaceData, TRUE);
820  }
821 } /* pick_and_place_parse_file_to_images */
822 
823 static gerbv_net_t *
824 pnp_new_net(gerbv_net_t *net)
825 {
826  gerbv_net_t *n;
827  net->next = g_new0(gerbv_net_t, 1);
828  n = net->next;
829  assert(n != NULL);
830 
831  pnp_reset_bbox (n);
832 
833  return n;
834 }
835 
836 static void
837 pnp_reset_bbox (gerbv_net_t *net)
838 {
839  net->boundingBox.left = -HUGE_VAL;
840  net->boundingBox.right = HUGE_VAL;
841  net->boundingBox.bottom = -HUGE_VAL;
842  net->boundingBox.top = HUGE_VAL;
843 }
844 
845 static void
846 pnp_init_net(gerbv_net_t *net, gerbv_image_t *image, const char *label,
847  gerbv_aperture_state_t apert_state,
848  gerbv_interpolation_t interpol)
849 {
850  net->aperture = 0;
851  net->aperture_state = apert_state;
852  net->interpolation = interpol;
853  net->layer = image->layers;
854  net->state = image->states;
855 
856  if (strlen(label) > 0) {
857  net->label = g_string_new (label);
858  }
859 }
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