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