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