1 /*!
2 * \file src/misc.c
3 *
4 * \brief Misc functions used by several modules.
5 *
6 * <hr>
7 *
8 * <h1><b>Copyright.</b></h1>\n
9 *
10 * PCB, interactive printed circuit board design
11 *
12 * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 *
28 * Contact addresses for paper mail and Email:
29 *
30 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
31 *
32 * Thomas.Nau@rz.uni-ulm.de
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #ifdef HAVE_STRING_H
42 #include <string.h>
43 #endif
44 #include <memory.h>
45 #include <ctype.h>
46 #include <signal.h>
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 #include <math.h>
51 #ifdef HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54 #ifdef HAVE_PWD_H
55 #include <pwd.h>
56 #endif
57
58 #include "global.h"
59
60 #include "box.h"
61 #include "crosshair.h"
62 #include "create.h"
63 #include "data.h"
64 #include "draw.h"
65 #include "file.h"
66 #include "error.h"
67 #include "mymem.h"
68 #include "misc.h"
69 #include "move.h"
70 #include "pcb-printf.h"
71 #include "polygon.h"
72 #include "remove.h"
73 #include "rtree.h"
74 #include "rotate.h"
75 #include "rubberband.h"
76 #include "search.h"
77 #include "set.h"
78 #include "undo.h"
79 #include "action.h"
80
81 #ifdef HAVE_LIBDMALLOC
82 #include <dmalloc.h>
83 #endif
84
85 /* forward declarations */
86 static char *BumpName (char *);
87 static void GetGridLockCoordinates (int, void *, void *, void *,
88 Coord *, Coord *);
89
90
91 /* Local variables */
92
93 /*!
94 * \brief Used by SaveStackAndVisibility() and
95 * RestoreStackAndVisibility().
96 */
97 static struct
98 {
99 bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
100 int LayerStack[MAX_ALL_LAYER];
101 bool LayerOn[MAX_ALL_LAYER];
102 int cnt;
103 } SavedStack;
104
105 /*!
106 * \brief Distance() should be used so that there is only one place to
107 * deal with overflow/precision errors.
108 */
109 double
Distance(double x1,double y1,double x2,double y2)110 Distance (double x1, double y1, double x2, double y2)
111 {
112 return hypot(x2 - x1, y2 - y1);
113 }
114
115 /*!
116 * \brief Bring an angle into [0, 360] range.
117 */
118 Angle
NormalizeAngle(Angle a)119 NormalizeAngle (Angle a)
120 {
121 while (a < 0)
122 a += 360.0;
123 while (a >= 360.0)
124 a -= 360.0;
125 return a;
126 }
127
128 /*!
129 * \brief GetValue() returns a numeric value passed from the string and
130 * sets the bool variable absolute to false if it leads with a +/-
131 * character.
132 */
133 double
GetValue(const char * val,const char * units,bool * absolute)134 GetValue (const char *val, const char *units, bool * absolute)
135 {
136 return GetValueEx(val, units, absolute, NULL, "cmil");
137 }
138
139 double
GetValueEx(const char * val,const char * units,bool * absolute,UnitList extra_units,const char * default_unit)140 GetValueEx (const char *val, const char *units, bool * absolute, UnitList extra_units, const char *default_unit)
141 {
142 double value;
143 int n = -1;
144 bool scaled = 0;
145 bool dummy;
146
147 /* Allow NULL to be passed for absolute */
148 if(absolute == NULL)
149 absolute = &dummy;
150
151 /* if the first character is a sign we have to add the
152 * value to the current one
153 */
154 if (*val == '=')
155 {
156 *absolute = true;
157 if (sscanf (val+1, "%lf%n", &value, &n) < 1)
158 return 0;
159 n++;
160 }
161 else
162 {
163 if (isdigit ((int) *val))
164 *absolute = true;
165 else
166 *absolute = false;
167 if (sscanf (val, "%lf%n", &value, &n) < 1)
168 return 0;
169 }
170 if (!units && n > 0)
171 units = val + n;
172
173 while (units && *units == ' ')
174 units ++;
175
176 if (units && *units)
177 {
178 int i;
179 const Unit *unit = get_unit_struct (units);
180 if (unit != NULL)
181 {
182 value = unit_to_coord (unit, value);
183 scaled = 1;
184 }
185 if (extra_units)
186 {
187 for (i = 0; *extra_units[i].suffix; ++i)
188 {
189 if (strncmp (units, extra_units[i].suffix, strlen(extra_units[i].suffix)) == 0)
190 {
191 value *= extra_units[i].scale;
192 if (extra_units[i].flags & UNIT_PERCENT)
193 value /= 100.0;
194 scaled = 1;
195 }
196 }
197 }
198 }
199 /* Apply default unit */
200 if (!scaled && default_unit && *default_unit)
201 {
202 int i;
203 const Unit *unit = get_unit_struct (default_unit);
204 if (extra_units)
205 for (i = 0; *extra_units[i].suffix; ++i)
206 if (strcmp (extra_units[i].suffix, default_unit) == 0)
207 {
208 value *= extra_units[i].scale;
209 if (extra_units[i].flags & UNIT_PERCENT)
210 value /= 100.0;
211 scaled = 1;
212 }
213 if (!scaled && unit != NULL)
214 value = unit_to_coord (unit, value);
215 }
216
217 return value;
218 }
219
220 /*!
221 * \brief Extract a unit-less value from a string.
222 *
223 * \param val String containing the value to be read.
224 *
225 * \param absolute Returns whether the returned value is an absolute one.
226 *
227 * \return The value read, with sign.
228 *
229 * This is the same as GetValue() and GetValueEx(), but totally ignoring units.
230 * Typical application is a list selector, like the type of thermal to apply
231 * to a pin.
232 */
GetUnitlessValue(const char * val,bool * absolute)233 double GetUnitlessValue (const char *val, bool *absolute) {
234 double value;
235
236 if (*val == '=')
237 {
238 *absolute = true;
239 val++;
240 }
241 else
242 {
243 if (isdigit ((int) *val))
244 *absolute = true;
245 else
246 *absolute = false;
247 }
248
249 if (sscanf (val, "%lf", &value) < 1)
250 return 0.;
251
252 return value;
253 }
254
255 /*!
256 * \brief Sets the bounding box of a point (which is silly).
257 */
258 void
SetPointBoundingBox(PointType * Pnt)259 SetPointBoundingBox (PointType *Pnt)
260 {
261 Pnt->X2 = Pnt->X + 1;
262 Pnt->Y2 = Pnt->Y + 1;
263 }
264
265 /*!
266 * \brief Sets the bounding box of a pin or via.
267 */
268 void
SetPinBoundingBox(PinType * Pin)269 SetPinBoundingBox (PinType *Pin)
270 {
271 Coord width;
272
273 /* the bounding box covers the extent of influence
274 * so it must include the clearance values too
275 */
276 width = MAX (Pin->Clearance + PIN_SIZE (Pin), Pin->Mask) / 2;
277
278 /* Adjust for our discrete polygon approximation */
279 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
280
281 Pin->BoundingBox.X1 = Pin->X - width;
282 Pin->BoundingBox.Y1 = Pin->Y - width;
283 Pin->BoundingBox.X2 = Pin->X + width;
284 Pin->BoundingBox.Y2 = Pin->Y + width;
285 close_box(&Pin->BoundingBox);
286 }
287
288 /*!
289 * \brief Sets the bounding box of a pad.
290 */
291 void
SetPadBoundingBox(PadType * Pad)292 SetPadBoundingBox (PadType *Pad)
293 {
294 Coord width;
295 Coord deltax;
296 Coord deltay;
297
298 /* the bounding box covers the extent of influence
299 * so it must include the clearance values too
300 */
301 width = (Pad->Thickness + Pad->Clearance + 1) / 2;
302 width = MAX (width, (Pad->Mask + 1) / 2);
303 deltax = Pad->Point2.X - Pad->Point1.X;
304 deltay = Pad->Point2.Y - Pad->Point1.Y;
305
306 if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0)
307 {
308 /* slanted square pad */
309 double theta;
310 Coord btx, bty;
311
312 /* T is a vector half a thickness long, in the direction of
313 one of the corners. */
314 theta = atan2 (deltay, deltax);
315 btx = width * cos (theta + M_PI/4) * sqrt(2.0);
316 bty = width * sin (theta + M_PI/4) * sqrt(2.0);
317
318
319 Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty),
320 MIN (Pad->Point2.X + btx, Pad->Point2.X + bty));
321 Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty),
322 MAX (Pad->Point2.X + btx, Pad->Point2.X + bty));
323 Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
324 MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
325 Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
326 MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
327 }
328 else
329 {
330 /* Adjust for our discrete polygon approximation */
331 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
332
333 Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width;
334 Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width;
335 Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width;
336 Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width;
337 }
338 close_box(&Pad->BoundingBox);
339 }
340
341 /*!
342 * \brief Sets the bounding box of a line.
343 */
344 void
SetLineBoundingBox(LineType * Line)345 SetLineBoundingBox (LineType *Line)
346 {
347 Coord width = (Line->Thickness + Line->Clearance + 1) / 2;
348
349 /* Adjust for our discrete polygon approximation */
350 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
351
352 Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width;
353 Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width;
354 Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width;
355 Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width;
356 close_box(&Line->BoundingBox);
357 SetPointBoundingBox (&Line->Point1);
358 SetPointBoundingBox (&Line->Point2);
359 }
360
361 /*!
362 * \brief Sets the bounding box of a polygons.
363 */
364 void
SetPolygonBoundingBox(PolygonType * Polygon)365 SetPolygonBoundingBox (PolygonType *Polygon)
366 {
367 Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
368 Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
369 POLYGONPOINT_LOOP (Polygon);
370 {
371 MAKEMIN (Polygon->BoundingBox.X1, point->X);
372 MAKEMIN (Polygon->BoundingBox.Y1, point->Y);
373 MAKEMAX (Polygon->BoundingBox.X2, point->X);
374 MAKEMAX (Polygon->BoundingBox.Y2, point->Y);
375 }
376 /* boxes don't include the lower right corner */
377 close_box(&Polygon->BoundingBox);
378 END_LOOP;
379 }
380
381 /*!
382 * \brief Sets the bounding box of an element.
383 */
384 void
SetElementBoundingBox(DataType * Data,ElementType * Element,FontType * Font)385 SetElementBoundingBox (DataType *Data, ElementType *Element,
386 FontType *Font)
387 {
388 BoxType *box, *vbox;
389
390 if (Data && Data->element_tree)
391 r_delete_entry (Data->element_tree, (BoxType *) Element);
392 /* first update the text objects */
393 ELEMENTTEXT_LOOP (Element);
394 {
395 if (Data && Data->name_tree[n])
396 r_delete_entry (Data->name_tree[n], (BoxType *) text);
397 SetTextBoundingBox (Font, text);
398 if (Data && !Data->name_tree[n])
399 Data->name_tree[n] = r_create_tree (NULL, 0, 0);
400 if (Data)
401 r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
402 }
403 END_LOOP;
404
405 /* do not include the elementnames bounding box which
406 * is handled separately
407 */
408 box = &Element->BoundingBox;
409 vbox = &Element->VBox;
410 box->X1 = box->Y1 = MAX_COORD;
411 box->X2 = box->Y2 = 0;
412 ELEMENTLINE_LOOP (Element);
413 {
414 SetLineBoundingBox (line);
415 MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2);
416 MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
417 MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2);
418 MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
419 MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2);
420 MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
421 MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2);
422 MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
423 }
424 END_LOOP;
425 ARC_LOOP (Element);
426 {
427 SetArcBoundingBox (arc);
428 MAKEMIN (box->X1, arc->BoundingBox.X1);
429 MAKEMIN (box->Y1, arc->BoundingBox.Y1);
430 MAKEMAX (box->X2, arc->BoundingBox.X2);
431 MAKEMAX (box->Y2, arc->BoundingBox.Y2);
432 }
433 END_LOOP;
434 *vbox = *box;
435 PIN_LOOP (Element);
436 {
437 if (Data && Data->pin_tree)
438 r_delete_entry (Data->pin_tree, (BoxType *) pin);
439 SetPinBoundingBox (pin);
440 if (Data)
441 {
442 if (!Data->pin_tree)
443 Data->pin_tree = r_create_tree (NULL, 0, 0);
444 r_insert_entry (Data->pin_tree, (BoxType *) pin, 0);
445 }
446 MAKEMIN (box->X1, pin->BoundingBox.X1);
447 MAKEMIN (box->Y1, pin->BoundingBox.Y1);
448 MAKEMAX (box->X2, pin->BoundingBox.X2);
449 MAKEMAX (box->Y2, pin->BoundingBox.Y2);
450 MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2);
451 MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2);
452 MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2);
453 MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2);
454 }
455 END_LOOP;
456 PAD_LOOP (Element);
457 {
458 if (Data && Data->pad_tree)
459 r_delete_entry (Data->pad_tree, (BoxType *) pad);
460 SetPadBoundingBox (pad);
461 if (Data)
462 {
463 if (!Data->pad_tree)
464 Data->pad_tree = r_create_tree (NULL, 0, 0);
465 r_insert_entry (Data->pad_tree, (BoxType *) pad, 0);
466 }
467 MAKEMIN (box->X1, pad->BoundingBox.X1);
468 MAKEMIN (box->Y1, pad->BoundingBox.Y1);
469 MAKEMAX (box->X2, pad->BoundingBox.X2);
470 MAKEMAX (box->Y2, pad->BoundingBox.Y2);
471 MAKEMIN (vbox->X1,
472 MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
473 MAKEMIN (vbox->Y1,
474 MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
475 MAKEMAX (vbox->X2,
476 MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
477 MAKEMAX (vbox->Y2,
478 MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
479 }
480 END_LOOP;
481 /* now we set the EDGE2FLAG of the pad if Point2
482 * is closer to the outside edge than Point1
483 */
484 PAD_LOOP (Element);
485 {
486 if (pad->Point1.Y == pad->Point2.Y)
487 {
488 /* horizontal pad */
489 if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
490 SET_FLAG (EDGE2FLAG, pad);
491 else
492 CLEAR_FLAG (EDGE2FLAG, pad);
493 }
494 else
495 {
496 /* vertical pad */
497 if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
498 SET_FLAG (EDGE2FLAG, pad);
499 else
500 CLEAR_FLAG (EDGE2FLAG, pad);
501 }
502 }
503 END_LOOP;
504
505 /* mark pins with component orientation */
506 if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
507 {
508 PIN_LOOP (Element);
509 {
510 SET_FLAG (EDGE2FLAG, pin);
511 }
512 END_LOOP;
513 }
514 else
515 {
516 PIN_LOOP (Element);
517 {
518 CLEAR_FLAG (EDGE2FLAG, pin);
519 }
520 END_LOOP;
521 }
522 close_box(box);
523 close_box(vbox);
524 if (Data && !Data->element_tree)
525 Data->element_tree = r_create_tree (NULL, 0, 0);
526 if (Data)
527 r_insert_entry (Data->element_tree, box, 0);
528 }
529
530 /*!
531 * \brief Creates the bounding box of a text object.
532 */
533 void
SetTextBoundingBox(FontType * FontPtr,TextType * Text)534 SetTextBoundingBox (FontType *FontPtr, TextType *Text)
535 {
536 SymbolType *symbol = FontPtr->Symbol;
537 unsigned char *s = (unsigned char *) Text->TextString;
538 int i;
539 int space;
540
541 Coord minx, miny, maxx, maxy, tx;
542 Coord min_final_radius;
543 Coord min_unscaled_radius;
544 bool first_time = true;
545
546 minx = miny = maxx = maxy = tx = 0;
547
548 /* Calculate the bounding box based on the larger of the thicknesses
549 * the text might clamped at on silk or copper layers.
550 */
551 min_final_radius = MAX (PCB->minWid, PCB->minSlk) / 2;
552
553 /* Pre-adjust the line radius for the fact we are initially computing the
554 * bounds of the un-scaled text, and the thickness clamping applies to
555 * scaled text.
556 */
557 min_unscaled_radius = UNSCALE_TEXT (min_final_radius, Text->Scale);
558
559 /* calculate size of the bounding box */
560 for (; s && *s; s++)
561 {
562 if (*s <= MAX_FONTPOSITION && symbol[*s].Valid)
563 {
564 LineType *line = symbol[*s].Line;
565 for (i = 0; i < symbol[*s].LineN; line++, i++)
566 {
567 /* Clamp the width of text lines at the minimum thickness.
568 * NB: Divide 4 in thickness calculation is comprised of a factor
569 * of 1/2 to get a radius from the center-line, and a factor
570 * of 1/2 because some stupid reason we render our glyphs
571 * at half their defined stroke-width.
572 */
573 Coord unscaled_radius = MAX (min_unscaled_radius, line->Thickness / 4);
574
575 if (first_time)
576 {
577 minx = maxx = line->Point1.X;
578 miny = maxy = line->Point1.Y;
579 first_time = false;
580 }
581
582 minx = MIN (minx, line->Point1.X - unscaled_radius + tx);
583 miny = MIN (miny, line->Point1.Y - unscaled_radius);
584 minx = MIN (minx, line->Point2.X - unscaled_radius + tx);
585 miny = MIN (miny, line->Point2.Y - unscaled_radius);
586 maxx = MAX (maxx, line->Point1.X + unscaled_radius + tx);
587 maxy = MAX (maxy, line->Point1.Y + unscaled_radius);
588 maxx = MAX (maxx, line->Point2.X + unscaled_radius + tx);
589 maxy = MAX (maxy, line->Point2.Y + unscaled_radius);
590 }
591 space = symbol[*s].Delta;
592 }
593 else
594 {
595 BoxType *ds = &FontPtr->DefaultSymbol;
596 Coord w = ds->X2 - ds->X1;
597
598 minx = MIN (minx, ds->X1 + tx);
599 miny = MIN (miny, ds->Y1);
600 minx = MIN (minx, ds->X2 + tx);
601 miny = MIN (miny, ds->Y2);
602 maxx = MAX (maxx, ds->X1 + tx);
603 maxy = MAX (maxy, ds->Y1);
604 maxx = MAX (maxx, ds->X2 + tx);
605 maxy = MAX (maxy, ds->Y2);
606
607 space = w / 5;
608 }
609 tx += symbol[*s].Width + space;
610 }
611
612 /* scale values */
613 minx = SCALE_TEXT (minx, Text->Scale);
614 miny = SCALE_TEXT (miny, Text->Scale);
615 maxx = SCALE_TEXT (maxx, Text->Scale);
616 maxy = SCALE_TEXT (maxy, Text->Scale);
617
618 /* set upper-left and lower-right corner;
619 * swap coordinates if necessary (origin is already in 'swapped')
620 * and rotate box
621 */
622
623 if (TEST_FLAG (ONSOLDERFLAG, Text))
624 {
625 Text->BoundingBox.X1 = Text->X + minx;
626 Text->BoundingBox.Y1 = Text->Y - miny;
627 Text->BoundingBox.X2 = Text->X + maxx;
628 Text->BoundingBox.Y2 = Text->Y - maxy;
629 RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y,
630 (4 - Text->Direction) & 0x03);
631 }
632 else
633 {
634 Text->BoundingBox.X1 = Text->X + minx;
635 Text->BoundingBox.Y1 = Text->Y + miny;
636 Text->BoundingBox.X2 = Text->X + maxx;
637 Text->BoundingBox.Y2 = Text->Y + maxy;
638 RotateBoxLowLevel (&Text->BoundingBox,
639 Text->X, Text->Y, Text->Direction);
640 }
641
642 /* the bounding box covers the extent of influence
643 * so it must include the clearance values too
644 */
645 Text->BoundingBox.X1 -= PCB->Bloat;
646 Text->BoundingBox.Y1 -= PCB->Bloat;
647 Text->BoundingBox.X2 += PCB->Bloat;
648 Text->BoundingBox.Y2 += PCB->Bloat;
649 close_box(&Text->BoundingBox);
650 }
651
652 /*!
653 * \brief Returns true if data area is empty.
654 */
655 bool
IsDataEmpty(DataType * Data)656 IsDataEmpty (DataType *Data)
657 {
658 bool hasNoObjects;
659 Cardinal i;
660
661 hasNoObjects = (Data->ViaN == 0);
662 hasNoObjects &= (Data->ElementN == 0);
663 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
664 hasNoObjects = hasNoObjects &&
665 Data->Layer[i].LineN == 0 &&
666 Data->Layer[i].ArcN == 0 &&
667 Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0;
668 return (hasNoObjects);
669 }
670
671 int
FlagIsDataEmpty(int parm)672 FlagIsDataEmpty (int parm)
673 {
674 int i = IsDataEmpty (PCB->Data);
675 return parm ? !i : i;
676 }
677
678 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
679 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
680
681 bool
IsLayerEmpty(LayerType * layer)682 IsLayerEmpty (LayerType *layer)
683 {
684 return (layer->LineN == 0
685 && layer->TextN == 0
686 && layer->PolygonN == 0
687 && layer->ArcN == 0);
688 }
689
690 bool
IsLayerNumEmpty(int num)691 IsLayerNumEmpty (int num)
692 {
693 return IsLayerEmpty (PCB->Data->Layer+num);
694 }
695
696 bool
IsLayerGroupEmpty(int num)697 IsLayerGroupEmpty (int num)
698 {
699 int i;
700 for (i=0; i<PCB->LayerGroups.Number[num]; i++)
701 if (!IsLayerNumEmpty (PCB->LayerGroups.Entries[num][i]))
702 return false;
703 return true;
704 }
705
706 bool
IsPasteEmpty(int side)707 IsPasteEmpty (int side)
708 {
709 bool paste_empty = true;
710 ALLPAD_LOOP (PCB->Data);
711 {
712 if (ON_SIDE (pad, side) && !TEST_FLAG (NOPASTEFLAG, pad) && pad->Mask > 0)
713 {
714 paste_empty = false;
715 break;
716 }
717 }
718 ENDALL_LOOP;
719 return paste_empty;
720 }
721
722
723 typedef struct
724 {
725 int nplated;
726 int nunplated;
727 Cardinal group_from;
728 Cardinal group_to;
729 } HoleCountStruct;
730
731 static int
hole_counting_callback(const BoxType * b,void * cl)732 hole_counting_callback (const BoxType * b, void *cl)
733 {
734 PinType *pin = (PinType *) b;
735 HoleCountStruct *hcs = (HoleCountStruct *) cl;
736
737 if (hcs->group_from != 0
738 || hcs->group_to != 0)
739 {
740 if (VIA_IS_BURIED (pin))
741 {
742 if (hcs->group_from == GetLayerGroupNumberByNumber (pin->BuriedFrom)
743 && hcs->group_to == GetLayerGroupNumberByNumber (pin->BuriedTo))
744 goto count;
745 }
746 return 1;
747 }
748
749 count:
750 if (TEST_FLAG (HOLEFLAG, pin))
751 hcs->nunplated++;
752 else
753 hcs->nplated++;
754 return 1;
755 }
756
757 /*!
758 * \brief Counts the number of plated and unplated holes in the design
759 * within a given area of the board.
760 *
761 * To count for the whole board, pass NULL to the within_area.
762 */
763 void
CountHoles(int * plated,int * unplated,const BoxType * within_area)764 CountHoles (int *plated, int *unplated, const BoxType *within_area)
765 {
766 HoleCountStruct hcs = {0, 0, 0, 0};
767
768 r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
769 r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
770
771 if (plated != NULL) *plated = hcs.nplated;
772 if (unplated != NULL) *unplated = hcs.nunplated;
773 }
774
775 /*!
776 * \brief Counts the number of plated and unplated holes in the design
777 * within a given area of the board.
778 *
779 * To count for the whole board, pass NULL to the within_area.
780 */
781 void
CountHolesEx(int * plated,int * unplated,const BoxType * within_area,Cardinal group_from,Cardinal group_to)782 CountHolesEx (int *plated, int *unplated, const BoxType *within_area, Cardinal group_from, Cardinal group_to)
783 {
784 HoleCountStruct hcs = {0, 0, group_from, group_to};
785
786 r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
787 r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
788
789 if (plated != NULL) *plated = hcs.nplated;
790 if (unplated != NULL) *unplated = hcs.nunplated;
791 }
792
793 /*!
794 * \brief Gets minimum and maximum coordinates.
795 *
796 * \return NULL if layout is empty.
797 */
798 BoxType *
GetDataBoundingBox(DataType * Data)799 GetDataBoundingBox (DataType *Data)
800 {
801 static BoxType box;
802 /* FIX ME: use r_search to do this much faster */
803
804 /* preset identifiers with highest and lowest possible values */
805 box.X1 = box.Y1 = MAX_COORD;
806 box.X2 = box.Y2 = -MAX_COORD;
807
808 /* now scan for the lowest/highest X and Y coordinate */
809 VIA_LOOP (Data);
810 {
811 box.X1 = MIN (box.X1, via->X - via->Thickness / 2);
812 box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2);
813 box.X2 = MAX (box.X2, via->X + via->Thickness / 2);
814 box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2);
815 }
816 END_LOOP;
817 ELEMENT_LOOP (Data);
818 {
819 box.X1 = MIN (box.X1, element->BoundingBox.X1);
820 box.Y1 = MIN (box.Y1, element->BoundingBox.Y1);
821 box.X2 = MAX (box.X2, element->BoundingBox.X2);
822 box.Y2 = MAX (box.Y2, element->BoundingBox.Y2);
823 {
824 TextType *text = &NAMEONPCB_TEXT (element);
825 box.X1 = MIN (box.X1, text->BoundingBox.X1);
826 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
827 box.X2 = MAX (box.X2, text->BoundingBox.X2);
828 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
829 };
830 }
831 END_LOOP;
832 ALLLINE_LOOP (Data);
833 {
834 box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2);
835 box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2);
836 box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2);
837 box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2);
838 box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2);
839 box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2);
840 box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2);
841 box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2);
842 }
843 ENDALL_LOOP;
844 ALLARC_LOOP (Data);
845 {
846 box.X1 = MIN (box.X1, arc->BoundingBox.X1);
847 box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1);
848 box.X2 = MAX (box.X2, arc->BoundingBox.X2);
849 box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2);
850 }
851 ENDALL_LOOP;
852 ALLTEXT_LOOP (Data);
853 {
854 box.X1 = MIN (box.X1, text->BoundingBox.X1);
855 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
856 box.X2 = MAX (box.X2, text->BoundingBox.X2);
857 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
858 }
859 ENDALL_LOOP;
860 ALLPOLYGON_LOOP (Data);
861 {
862 box.X1 = MIN (box.X1, polygon->BoundingBox.X1);
863 box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1);
864 box.X2 = MAX (box.X2, polygon->BoundingBox.X2);
865 box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2);
866 }
867 ENDALL_LOOP;
868 return (IsDataEmpty (Data) ? NULL : &box);
869 }
870
871 /*!
872 * \brief Centers the displayed PCB around the specified point (X,Y),
873 * and move the crosshair there.
874 *
875 * If warp_pointer is true warp the pointer to the crosshair.
876 */
877 void
CenterDisplay(Coord X,Coord Y,bool warp_pointer)878 CenterDisplay (Coord X, Coord Y, bool warp_pointer)
879 {
880 Coord save_grid = PCB->Grid;
881
882 PCB->Grid = 1;
883
884 if (MoveCrosshairAbsolute (X, Y))
885 notify_crosshair_change (true);
886
887 if (warp_pointer)
888 gui->set_crosshair (Crosshair.X, Crosshair.Y,
889 HID_SC_CENTER_IN_VIEWPORT_AND_WARP_POINTER );
890 else
891 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_CENTER_IN_VIEWPORT);
892
893 PCB->Grid = save_grid;
894 }
895
896 /*!
897 * \brief Transforms symbol coordinates so that the left edge of each
898 * symbol is at the zero position.
899 *
900 * The y coordinates are moved so that min(y) = 0.
901 *
902 */
903 void
SetFontInfo(FontType * Ptr)904 SetFontInfo (FontType *Ptr)
905 {
906 Cardinal i, j;
907 SymbolType *symbol;
908 LineType *line;
909 Coord totalminy = MAX_COORD;
910
911 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
912 * maximum cell width and height
913 * minimum x and y position of all lines
914 */
915 Ptr->MaxWidth = DEFAULT_CELLSIZE;
916 Ptr->MaxHeight = DEFAULT_CELLSIZE;
917 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
918 {
919 Coord minx, miny, maxx, maxy;
920
921 /* next one if the index isn't used or symbol is empty (SPACE) */
922 if (!symbol->Valid || !symbol->LineN)
923 continue;
924
925 minx = miny = MAX_COORD;
926 maxx = maxy = 0;
927 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
928 {
929 minx = MIN (minx, line->Point1.X);
930 miny = MIN (miny, line->Point1.Y);
931 minx = MIN (minx, line->Point2.X);
932 miny = MIN (miny, line->Point2.Y);
933 maxx = MAX (maxx, line->Point1.X);
934 maxy = MAX (maxy, line->Point1.Y);
935 maxx = MAX (maxx, line->Point2.X);
936 maxy = MAX (maxy, line->Point2.Y);
937 }
938
939 /* move symbol to left edge */
940 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
941 MOVE_LINE_LOWLEVEL (line, -minx, 0);
942
943 /* set symbol bounding box with a minimum cell size of (1,1) */
944 symbol->Width = maxx - minx + 1;
945 symbol->Height = maxy + 1;
946
947 /* check total min/max */
948 Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width);
949 Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height);
950 totalminy = MIN (totalminy, miny);
951 }
952
953 /* move coordinate system to the upper edge (lowest y on screen) */
954 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
955 if (symbol->Valid)
956 {
957 symbol->Height -= totalminy;
958 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
959 MOVE_LINE_LOWLEVEL (line, 0, -totalminy);
960 }
961
962 /* setup the box for the default symbol */
963 Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
964 Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
965 Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
966 }
967
968 static Coord
GetNum(char ** s,const char * default_unit)969 GetNum (char **s, const char *default_unit)
970 {
971 /* Read value */
972 Coord ret_val = GetValueEx (*s, NULL, NULL, NULL, default_unit);
973 /* Advance pointer */
974 while(isalnum(**s) || **s == '.')
975 (*s)++;
976 return ret_val;
977 }
978
979 /*!
980 * \brief Serializes the route style list .
981 *
982 * Right now n_styles should always be set to NUM_STYLES,
983 * since that is the number of route styles ParseRouteString()
984 * expects to parse.
985 */
986 char *
make_route_string(RouteStyleType rs[],int n_styles)987 make_route_string (RouteStyleType rs[], int n_styles)
988 {
989 GString *str = g_string_new ("");
990 gint i;
991
992 for (i = 0; i < n_styles; ++i)
993 {
994 char *r_string;
995 // don't much like how this is done, but it'll have to do for now
996 if (rs[i].ViaMask != 0)
997 r_string = pcb_g_strdup_printf ("%s,%$mr,%$mr,%$mr,%$mr,%$mr",
998 rs[i].Name,
999 rs[i].Thick, rs[i].Diameter,
1000 rs[i].Hole, rs[i].Keepaway,
1001 rs[i].ViaMask);
1002 else
1003 r_string = pcb_g_strdup_printf ("%s,%$mr,%$mr,%$mr,%$mr", rs[i].Name,
1004 rs[i].Thick, rs[i].Diameter,
1005 rs[i].Hole, rs[i].Keepaway);
1006 if (i > 0)
1007 g_string_append_c (str, ':');
1008 g_string_append (str, r_string);
1009 g_free (r_string);
1010 }
1011 return g_string_free (str, FALSE);
1012 }
1013
1014 /*!
1015 * \brief Parses the routes definition string
1016 *
1017 * Which is a colon separated list of comma separated Name, Dimension,
1018 * Dimension, Dimension[, Dimension, Dimension].
1019 *
1020 * \example Signal,20,40,20,10,0:Power,40,60,28,10,0:...
1021 */
1022 int
ParseRouteString(char * s,RouteStyleType * routeStyle,const char * default_unit)1023 ParseRouteString (char *s, RouteStyleType *routeStyle, const char *default_unit)
1024 {
1025 int i, n, style;
1026 char Name[256];
1027 char *orig_s = s;
1028
1029 Coord *style_items[5];
1030
1031 style = 0;
1032 while (style < NUM_STYLES)
1033 {
1034 // Clear any existing data
1035 memset (routeStyle, 0, sizeof (RouteStyleType));
1036 // Extract the name
1037 while (*s && isspace ((int) *s)) s++;
1038 // this will break for style names greater than 256 characters
1039 for (i = 0; *s && *s != ','; i++) Name[i] = *s++;
1040 Name[i] = '\0';
1041 routeStyle->Name = strdup (Name);
1042
1043 // setup some default values
1044 routeStyle->Keepaway = MIL_TO_COORD(10);
1045 routeStyle->ViaMask = 0;
1046
1047 // Now extract the numerical parameters
1048 style_items[0] = &(routeStyle->Thick);
1049 style_items[1] = &(routeStyle->Diameter);
1050 style_items[2] = &(routeStyle->Hole);
1051 style_items[3] = &(routeStyle->Keepaway);
1052 style_items[4] = &(routeStyle->ViaMask);
1053 for (n = 0; n < 5; n++)
1054 {
1055 if (!isdigit ((int) *++s)) goto error;
1056 *style_items[n] = GetNum(&s, default_unit);
1057 // find the next field, skipping any white space
1058 while (*s && isspace ((int) *s)) s++;
1059 if (*s != ',') break;
1060 while (*s && isspace ((int) *s)) s++;
1061 }
1062
1063 // how did we get out of the loop?
1064 // if we're at the end of the string, we're done
1065 if (*s == '\0') break;
1066 // there's another route style to parse, advance past the delimiter
1067 else if (*s == ':') s++;
1068 // otherwise, we got an unexpected character, which is an error
1069 else {
1070 fprintf(stderr, "unexpected character: %c\n", *s);
1071 goto error;
1072 }
1073
1074 // otherwise, prepare for the next loop
1075 style++; routeStyle++;
1076 }
1077 return (0);
1078
1079 error:
1080 fprintf(stderr, "error parsing route string: %s\n", orig_s);
1081 fprintf(stderr, "parsed %ld characters.\n", s - orig_s);
1082 fprintf(stderr, "on style number %d\n", style+1);
1083 fprintf(stderr, "character that caused the error: %c\n", *s);
1084 fprintf(stderr, "values of current struct: \n");
1085 for (n=0; n < 5; n++) fprintf(stderr, "%d: %ld ", n, *style_items[n]);
1086 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
1087 return (1);
1088 }
1089
1090 /*!
1091 * \brief Parses the group definition string.
1092 *
1093 * Which is a colon separated list of comma separated layer numbers.
1094 *
1095 * \example (1,2,b:4,6,8,t)
1096 */
1097 int
ParseGroupString(char * group_string,LayerGroupType * LayerGroup,int * LayerN)1098 ParseGroupString (char *group_string, LayerGroupType *LayerGroup, int *LayerN)
1099 {
1100 char *s;
1101 int group, member, layer;
1102 bool c_set = false, /* flags for the two special layers to */
1103 s_set = false; /* provide a default setting for old formats */
1104 int groupnum[MAX_ALL_LAYER];
1105
1106 *LayerN = 0;
1107
1108 /* Deterimine the maximum layer number */
1109 for (s = group_string; s && *s; s++)
1110 {
1111 while (*s && isspace ((int) *s))
1112 s++;
1113
1114 switch (*s)
1115 {
1116 case 'c':
1117 case 'C':
1118 case 't':
1119 case 'T':
1120 case 's':
1121 case 'S':
1122 case 'b':
1123 case 'B':
1124 break;
1125
1126 default:
1127 if (!isdigit ((int) *s))
1128 goto error;
1129 *LayerN = MAX (*LayerN, atoi (s));
1130 break;
1131 }
1132
1133 while (*++s && isdigit ((int) *s));
1134
1135 /* ignore white spaces and check for separator */
1136 while (*s && isspace ((int) *s))
1137 s++;
1138
1139 if (*s == '\0')
1140 break;
1141
1142 if (*s != ':' && *s != ',')
1143 goto error;
1144 }
1145
1146 /* clear struct */
1147 memset (LayerGroup, 0, sizeof (LayerGroupType));
1148
1149 /* Clear assignments */
1150 for (layer = 0; layer < MAX_ALL_LAYER; layer++)
1151 groupnum[layer] = -1;
1152
1153 /* loop over all groups */
1154 for (s = group_string, group = 0;
1155 s && *s && group < *LayerN;
1156 group++)
1157 {
1158 while (*s && isspace ((int) *s))
1159 s++;
1160
1161 /* loop over all group members */
1162 for (member = 0; *s; s++)
1163 {
1164 /* ignore white spaces and get layernumber */
1165 while (*s && isspace ((int) *s))
1166 s++;
1167 switch (*s)
1168 {
1169 case 'c':
1170 case 'C':
1171 case 't':
1172 case 'T':
1173 layer = *LayerN + TOP_SILK_LAYER;
1174 c_set = true;
1175 break;
1176
1177 case 's':
1178 case 'S':
1179 case 'b':
1180 case 'B':
1181 layer = *LayerN + BOTTOM_SILK_LAYER;
1182 s_set = true;
1183 break;
1184
1185 default:
1186 layer = atoi (s) - 1;
1187 break;
1188 }
1189 if (layer > *LayerN + MAX (BOTTOM_SILK_LAYER, TOP_SILK_LAYER) ||
1190 member >= *LayerN + 1)
1191 goto error;
1192 groupnum[layer] = group;
1193 LayerGroup->Entries[group][member++] = layer;
1194 while (*++s && isdigit ((int) *s));
1195
1196 /* ignore white spaces and check for separator */
1197 while (*s && isspace ((int) *s))
1198 s++;
1199 if (!*s || *s == ':')
1200 break;
1201 }
1202 LayerGroup->Number[group] = member;
1203 if (*s == ':')
1204 s++;
1205 }
1206
1207 /* If no explicit solder or component layer group was found in the layer
1208 * group string, make group 0 the bottom side, and group 1 the top side.
1209 * This is done by assigning the relevant silkscreen layers to those groups.
1210 */
1211 if (!s_set)
1212 LayerGroup->Entries[0][LayerGroup->Number[0]++] = *LayerN + BOTTOM_SILK_LAYER;
1213 if (!c_set)
1214 LayerGroup->Entries[1][LayerGroup->Number[1]++] = *LayerN + TOP_SILK_LAYER;
1215
1216 /* Assign a unique layer group to each layer that was not explicitly
1217 * assigned a particular group by its presence in the layer group string.
1218 */
1219 for (layer = 0; layer < *LayerN && group < *LayerN; layer++)
1220 if (groupnum[layer] == -1)
1221 {
1222 LayerGroup->Entries[group][0] = layer;
1223 LayerGroup->Number[group] = 1;
1224 group++;
1225 }
1226 return (0);
1227
1228 /* reset structure on error */
1229 error:
1230 memset (LayerGroup, 0, sizeof (LayerGroupType));
1231 return (1);
1232 }
1233
1234 /*!
1235 * \brief Sets up any remaining layer type guesses.
1236 */
1237 void
AssignDefaultLayerTypes()1238 AssignDefaultLayerTypes()
1239 {
1240 int num_found;
1241 Cardinal outline_layer = -1;
1242
1243 /*
1244 * There can be only one outline layer. During parsing guess_layertype()
1245 * applied well known cases already, but as this function operates on a
1246 * single layer only, it might end up with more than one hit for the whole
1247 * file. Especially after loading an older layout without saved flags.
1248 */
1249 num_found = 0;
1250 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1251 outline_layer = n;
1252 num_found++;
1253 END_LOOP;
1254
1255 if (num_found != 1)
1256 /* No or duplicate outline! Try to find a layer which is named exactly
1257 "outline". */
1258 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1259 if ( ! strcasecmp (layer->Name, "outline"))
1260 {
1261 outline_layer = n;
1262 num_found = 1;
1263 break;
1264 }
1265 END_LOOP;
1266
1267 if (num_found != 1)
1268 /* Next, try to find a layer which is named exactly "route". */
1269 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1270 if ( ! strcasecmp (layer->Name, "route"))
1271 {
1272 outline_layer = n;
1273 num_found = 1;
1274 break;
1275 }
1276 END_LOOP;
1277
1278 if (num_found != 1)
1279 /* As last resort, take the first layer claiming to be outline. */
1280 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1281 outline_layer = n;
1282 num_found = 1;
1283 break;
1284 END_LOOP;
1285
1286 /* Make sure our found outline layer is the only one. */
1287 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1288 if (n == outline_layer)
1289 layer->Type = LT_OUTLINE;
1290 else
1291 layer->Type = LT_ROUTE; /* best guess */
1292 END_LOOP;
1293 }
1294
1295 extern void pcb_main_uninit(void);
1296
1297 /*!
1298 * \brief Quits application.
1299 */
1300 void
QuitApplication(void)1301 QuitApplication (void)
1302 {
1303 /*
1304 * save data if necessary. It not needed, then don't trigger EmergencySave
1305 * via our atexit() registering of EmergencySave(). We presumeably wanted to
1306 * exit here and thus it is not an emergency.
1307 */
1308 if (PCB->Changed && Settings.SaveInTMP)
1309 EmergencySave ();
1310 else
1311 DisableEmergencySave ();
1312
1313 if (gui->do_exit == NULL)
1314 {
1315 pcb_main_uninit ();
1316 exit (0);
1317 }
1318 else
1319 gui->do_exit (gui);
1320 }
1321
1322 /*!
1323 * \brief Creates a filename from a template.
1324 *
1325 * "%f" is replaced by the filename.
1326 *
1327 * "%p" is replaced by the searchpath.
1328 */
1329 char *
EvaluateFilename(char * Template,char * Path,char * Filename,char * Parameter)1330 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
1331 {
1332 static DynamicStringType command;
1333 char *p;
1334
1335 if (Settings.verbose)
1336 {
1337 printf ("EvaluateFilename:\n");
1338 printf ("\tTemplate: \033[33m%s\033[0m\n", Template);
1339 printf ("\tPath: \033[33m%s\033[0m\n", Path);
1340 printf ("\tFilename: \033[33m%s\033[0m\n", Filename);
1341 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter);
1342 }
1343
1344 DSClearString (&command);
1345
1346 for (p = Template; p && *p; p++)
1347 {
1348 /* copy character or add string to command */
1349 if (*p == '%'
1350 && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
1351 switch (*(++p))
1352 {
1353 case 'a':
1354 DSAddString (&command, Parameter);
1355 break;
1356 case 'f':
1357 DSAddString (&command, Filename);
1358 break;
1359 case 'p':
1360 DSAddString (&command, Path);
1361 break;
1362 }
1363 else
1364 DSAddCharacter (&command, *p);
1365 }
1366 DSAddCharacter (&command, '\0');
1367 if (Settings.verbose)
1368 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data);
1369
1370 return strdup (command.Data);
1371 }
1372
1373 /*!
1374 * \brief Concatenates directory and filename.
1375 *
1376 * If directory != NULL expands them with a shell and returns the found
1377 * name(s) or NULL.
1378 */
1379 char *
ExpandFilename(char * Dirname,char * Filename)1380 ExpandFilename (char *Dirname, char *Filename)
1381 {
1382 static DynamicStringType answer;
1383 char *command;
1384 FILE *pipe;
1385 int c;
1386
1387 /* allocate memory for commandline and build it */
1388 DSClearString (&answer);
1389 if (Dirname)
1390 {
1391 command = (char *)calloc (strlen (Filename) + strlen (Dirname) + 7,
1392 sizeof (char));
1393 sprintf (command, "echo %s/%s", Dirname, Filename);
1394 }
1395 else
1396 {
1397 command = (char *)calloc (strlen (Filename) + 6, sizeof (char));
1398 sprintf (command, "echo %s", Filename);
1399 }
1400
1401 /* execute it with shell */
1402 if ((pipe = popen (command, "r")) != NULL)
1403 {
1404 /* discard all but the first returned line */
1405 for (;;)
1406 {
1407 if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
1408 break;
1409 else
1410 DSAddCharacter (&answer, c);
1411 }
1412
1413 free (command);
1414 return (pclose (pipe) ? NULL : answer.Data);
1415 }
1416
1417 /* couldn't be expanded by the shell */
1418 PopenErrorMessage (command);
1419 free (command);
1420 return (NULL);
1421 }
1422
1423
1424 /*!
1425 * \brief Returns the layer number for the passed pointer.
1426 */
1427 int
GetLayerNumber(DataType * Data,LayerType * Layer)1428 GetLayerNumber (DataType *Data, LayerType *Layer)
1429 {
1430 int i;
1431
1432 for (i = 0; i < MAX_ALL_LAYER; i++)
1433 if (Layer == &Data->Layer[i])
1434 break;
1435 return (i);
1436 }
1437
1438 /*!
1439 * \brief Move layer (number is passed in) to top of layerstack.
1440 */
1441 static void
PushOnTopOfLayerStack(int NewTop)1442 PushOnTopOfLayerStack (int NewTop)
1443 {
1444 int i;
1445
1446 /* ignore silk and other extra layers */
1447 if (NewTop < max_copper_layer)
1448 {
1449 /* first find position of passed one */
1450 for (i = 0; i < max_copper_layer; i++)
1451 if (LayerStack[i] == NewTop)
1452 break;
1453
1454 /* bring this element to the top of the stack */
1455 for (; i; i--)
1456 LayerStack[i] = LayerStack[i - 1];
1457 LayerStack[0] = NewTop;
1458 }
1459 }
1460
1461
1462 /*!
1463 * \brief Changes the visibility of all layers in a group.
1464 *
1465 * \return The number of changed layers.
1466 */
1467 int
ChangeGroupVisibility(int Layer,bool On,bool ChangeStackOrder)1468 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder)
1469 {
1470 int group, i, changed = 1; /* at least the current layer changes */
1471
1472 /* Warning: these special case values must agree with what gui-top-window.c
1473 | thinks the are.
1474 */
1475
1476 if (Settings.verbose)
1477 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1478 Layer, On, ChangeStackOrder);
1479
1480 /* decrement 'i' to keep stack in order of layergroup */
1481 group = GetLayerGroupNumberByNumber (Layer);
1482 for (i = PCB->LayerGroups.Number[group]; i;)
1483 {
1484 int layer = PCB->LayerGroups.Entries[group][--i];
1485
1486 /* don't count the passed member of the group */
1487 if (layer != Layer && layer < max_copper_layer)
1488 {
1489 PCB->Data->Layer[layer].On = On;
1490
1491 /* push layer on top of stack if switched on */
1492 if (On && ChangeStackOrder)
1493 PushOnTopOfLayerStack (layer);
1494 changed++;
1495 }
1496 }
1497
1498 /* change at least the passed layer */
1499 PCB->Data->Layer[Layer].On = On;
1500 if (On && ChangeStackOrder)
1501 PushOnTopOfLayerStack (Layer);
1502
1503 /* update control panel and exit */
1504 hid_action ("LayersChanged");
1505 return (changed);
1506 }
1507
1508 /*!
1509 * \brief Given a string description of a layer stack, adjust the layer
1510 * stack to correspond.
1511 */
1512 void
LayerStringToLayerStack(char * s)1513 LayerStringToLayerStack (char *s)
1514 {
1515 static int listed_layers = 0;
1516 int l = strlen (s);
1517 char **args;
1518 int i, argn, lno;
1519 int prev_sep = 1;
1520
1521 s = strdup (s);
1522 args = (char **) malloc (l * sizeof (char *));
1523 argn = 0;
1524
1525 for (i=0; i<l; i++)
1526 {
1527 switch (s[i])
1528 {
1529 case ' ':
1530 case '\t':
1531 case ',':
1532 case ';':
1533 case ':':
1534 prev_sep = 1;
1535 s[i] = '\0';
1536 break;
1537 default:
1538 if (prev_sep)
1539 args[argn++] = s+i;
1540 prev_sep = 0;
1541 break;
1542 }
1543 }
1544
1545 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1546 {
1547 if (i < max_copper_layer)
1548 LayerStack[i] = i;
1549 PCB->Data->Layer[i].On = false;
1550 }
1551 PCB->ElementOn = false;
1552 PCB->InvisibleObjectsOn = false;
1553 PCB->PinOn = false;
1554 PCB->ViaOn = false;
1555 PCB->RatOn = false;
1556 CLEAR_FLAG (SHOWMASKFLAG, PCB);
1557 Settings.ShowBottomSide = 0;
1558
1559 for (i=argn-1; i>=0; i--)
1560 {
1561 if (strcasecmp (args[i], "rats") == 0)
1562 PCB->RatOn = true;
1563 else if (strcasecmp (args[i], "invisible") == 0)
1564 PCB->InvisibleObjectsOn = true;
1565 else if (strcasecmp (args[i], "pins") == 0)
1566 PCB->PinOn = true;
1567 else if (strcasecmp (args[i], "vias") == 0)
1568 PCB->ViaOn = true;
1569 else if (strcasecmp (args[i], "elements") == 0
1570 || strcasecmp (args[i], "silk") == 0)
1571 PCB->ElementOn = true;
1572 else if (strcasecmp (args[i], "mask") == 0)
1573 SET_FLAG (SHOWMASKFLAG, PCB);
1574 else if (strcasecmp (args[i], "solderside") == 0)
1575 Settings.ShowBottomSide = 1;
1576 else if (isdigit ((int) args[i][0]))
1577 {
1578 lno = atoi (args[i]);
1579 ChangeGroupVisibility (lno, true, true);
1580 }
1581 else
1582 {
1583 int found = 0;
1584 for (lno = 0; lno < max_copper_layer; lno++)
1585 if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
1586 {
1587 ChangeGroupVisibility (lno, true, true);
1588 found = 1;
1589 break;
1590 }
1591 if (!found)
1592 {
1593 fprintf(stderr, _("Warning: layer \"%s\" not known\n"), args[i]);
1594 if (!listed_layers)
1595 {
1596 fprintf (stderr, _("Named layers in this board are:\n"));
1597 listed_layers = 1;
1598 for (lno=0; lno < max_copper_layer; lno ++)
1599 fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
1600 fprintf(stderr, _("Also: component, solder, rats, invisible, "
1601 "pins, vias, elements or silk, mask, solderside.\n"));
1602 }
1603 }
1604 }
1605 }
1606 }
1607
1608 /*!
1609 * \brief Returns the layergroup number for the passed pointer.
1610 */
1611 int
GetLayerGroupNumberByPointer(LayerType * Layer)1612 GetLayerGroupNumberByPointer (LayerType *Layer)
1613 {
1614 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer)));
1615 }
1616
1617 /*!
1618 * \brief Returns the layergroup number for the passed layer number.
1619 */
1620 int
GetLayerGroupNumberByNumber(Cardinal Layer)1621 GetLayerGroupNumberByNumber (Cardinal Layer)
1622 {
1623 int group, entry;
1624
1625 for (group = 0; group < max_group; group++)
1626 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1627 if (PCB->LayerGroups.Entries[group][entry] == Layer)
1628 return (group);
1629
1630 /* since every layer belongs to a group it is safe to return
1631 * the value without boundary checking
1632 */
1633 return (group);
1634 }
1635
1636 /*!
1637 * \brief Returns the layergroup number for the passed side (TOP_SIDE or
1638 * BOTTOM_SIDE).
1639 */
1640 int
GetLayerGroupNumberBySide(int side)1641 GetLayerGroupNumberBySide (int side)
1642 {
1643 /* Find the relavant board side layer group by determining the
1644 * layer group associated with the relevant side's silk-screen
1645 */
1646 return GetLayerGroupNumberByNumber (
1647 side == TOP_SIDE ? top_silk_layer : bottom_silk_layer);
1648 }
1649
1650 /*!
1651 * \brief Returns a pointer to an objects bounding box.
1652 *
1653 * Data is valid until the routine is called again.
1654 */
1655 BoxType *
GetObjectBoundingBox(int Type,void * Ptr1,void * Ptr2,void * Ptr3)1656 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1657 {
1658 switch (Type)
1659 {
1660 case LINE_TYPE:
1661 case ARC_TYPE:
1662 case TEXT_TYPE:
1663 case POLYGON_TYPE:
1664 case PAD_TYPE:
1665 case PIN_TYPE:
1666 case ELEMENTNAME_TYPE:
1667 return (BoxType *)Ptr2;
1668 case VIA_TYPE:
1669 case ELEMENT_TYPE:
1670 return (BoxType *)Ptr1;
1671 case POLYGONPOINT_TYPE:
1672 case LINEPOINT_TYPE:
1673 return (BoxType *)Ptr3;
1674 default:
1675 Message (_("Request for bounding box of unsupported type %d\n"), Type);
1676 return (BoxType *)Ptr2;
1677 }
1678 }
1679
1680 /*!
1681 * \brief Computes the bounding box of an arc.
1682 */
1683 void
SetArcBoundingBox(ArcType * Arc)1684 SetArcBoundingBox (ArcType *Arc)
1685 {
1686 double ca1, ca2, sa1, sa2;
1687 double minx, maxx, miny, maxy;
1688 Angle ang1, ang2;
1689 Coord width;
1690
1691 /* first put angles into standard form:
1692 * ang1 < ang2, both angles between 0 and 720 */
1693 Arc->Delta = CLAMP (Arc->Delta, -360, 360);
1694
1695 if (Arc->Delta > 0)
1696 {
1697 ang1 = NormalizeAngle (Arc->StartAngle);
1698 ang2 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1699 }
1700 else
1701 {
1702 ang1 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1703 ang2 = NormalizeAngle (Arc->StartAngle);
1704 }
1705 if (ang1 > ang2)
1706 ang2 += 360;
1707 /* Make sure full circles aren't treated as zero-length arcs */
1708 if (Arc->Delta == 360 || Arc->Delta == -360)
1709 ang2 = ang1 + 360;
1710
1711 /* calculate sines, cosines */
1712 sa1 = sin (M180 * ang1);
1713 ca1 = cos (M180 * ang1);
1714 sa2 = sin (M180 * ang2);
1715 ca2 = cos (M180 * ang2);
1716
1717 minx = MIN (ca1, ca2);
1718 maxx = MAX (ca1, ca2);
1719 miny = MIN (sa1, sa2);
1720 maxy = MAX (sa1, sa2);
1721
1722 /* Check for extreme angles */
1723 if ((ang1 <= 0 && ang2 >= 0) || (ang1 <= 360 && ang2 >= 360)) maxx = 1;
1724 if ((ang1 <= 90 && ang2 >= 90) || (ang1 <= 450 && ang2 >= 450)) maxy = 1;
1725 if ((ang1 <= 180 && ang2 >= 180) || (ang1 <= 540 && ang2 >= 540)) minx = -1;
1726 if ((ang1 <= 270 && ang2 >= 270) || (ang1 <= 630 && ang2 >= 630)) miny = -1;
1727
1728 /* Finally, calcate bounds, converting sane geometry into pcb geometry */
1729 Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx;
1730 Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx;
1731 Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny;
1732 Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy;
1733
1734 width = (Arc->Thickness + Arc->Clearance) / 2;
1735
1736 /* Adjust for our discrete polygon approximation */
1737 width = (double)width * MAX (POLY_CIRC_RADIUS_ADJ, (1.0 + POLY_ARC_MAX_DEVIATION)) + 0.5;
1738
1739 Arc->BoundingBox.X1 -= width;
1740 Arc->BoundingBox.X2 += width;
1741 Arc->BoundingBox.Y1 -= width;
1742 Arc->BoundingBox.Y2 += width;
1743 close_box(&Arc->BoundingBox);
1744
1745 /* Update the arc end-points */
1746 Arc->Point1.X = Arc->X - (double)Arc->Width * ca1;
1747 Arc->Point1.Y = Arc->Y + (double)Arc->Height * sa1;
1748 Arc->Point2.X = Arc->X - (double)Arc->Width * ca2;
1749 Arc->Point2.Y = Arc->Y + (double)Arc->Height * sa2;
1750 }
1751
1752 /*!
1753 * \brief Resets the layerstack setting.
1754 */
1755 void
ResetStackAndVisibility(void)1756 ResetStackAndVisibility (void)
1757 {
1758 int top_group;
1759 Cardinal i;
1760
1761 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1762 {
1763 if (i < max_copper_layer)
1764 LayerStack[i] = i;
1765 PCB->Data->Layer[i].On = true;
1766 }
1767 PCB->ElementOn = true;
1768 PCB->InvisibleObjectsOn = true;
1769 PCB->PinOn = true;
1770 PCB->ViaOn = true;
1771 PCB->RatOn = true;
1772
1773 /* Bring the component group to the front and make it active. */
1774 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
1775 ChangeGroupVisibility (PCB->LayerGroups.Entries[top_group][0], 1, 1);
1776 }
1777
1778 /*!
1779 * \brief Saves the layerstack setting.
1780 */
1781 void
SaveStackAndVisibility(void)1782 SaveStackAndVisibility (void)
1783 {
1784 Cardinal i;
1785 static bool run = false;
1786
1787 if (run == false)
1788 {
1789 SavedStack.cnt = 0;
1790 run = true;
1791 }
1792
1793 if (SavedStack.cnt != 0)
1794 {
1795 fprintf (stderr,
1796 "SaveStackAndVisibility() layerstack was already saved and not"
1797 "yet restored. cnt = %d\n", SavedStack.cnt);
1798 }
1799
1800 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1801 {
1802 if (i < max_copper_layer)
1803 SavedStack.LayerStack[i] = LayerStack[i];
1804 SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
1805 }
1806 SavedStack.ElementOn = PCB->ElementOn;
1807 SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
1808 SavedStack.PinOn = PCB->PinOn;
1809 SavedStack.ViaOn = PCB->ViaOn;
1810 SavedStack.RatOn = PCB->RatOn;
1811 SavedStack.cnt++;
1812 }
1813
1814 /*!
1815 * \brief Restores the layerstack setting.
1816 */
1817 void
RestoreStackAndVisibility(void)1818 RestoreStackAndVisibility (void)
1819 {
1820 Cardinal i;
1821
1822 if (SavedStack.cnt == 0)
1823 {
1824 fprintf (stderr, "RestoreStackAndVisibility() layerstack has not"
1825 " been saved. cnt = %d\n", SavedStack.cnt);
1826 return;
1827 }
1828 else if (SavedStack.cnt != 1)
1829 {
1830 fprintf (stderr, "RestoreStackAndVisibility() layerstack save count is"
1831 " wrong. cnt = %d\n", SavedStack.cnt);
1832 }
1833
1834 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1835 {
1836 if (i < max_copper_layer)
1837 LayerStack[i] = SavedStack.LayerStack[i];
1838 PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
1839 }
1840 PCB->ElementOn = SavedStack.ElementOn;
1841 PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
1842 PCB->PinOn = SavedStack.PinOn;
1843 PCB->ViaOn = SavedStack.ViaOn;
1844 PCB->RatOn = SavedStack.RatOn;
1845
1846 SavedStack.cnt--;
1847 }
1848
1849 /*!
1850 * \brief Returns pointer to current working directory.
1851 *
1852 * If 'path' is not NULL, then the current working directory is copied
1853 * to the array pointed to by 'path'.
1854 */
1855 char *
GetWorkingDirectory(char * path)1856 GetWorkingDirectory (char *path)
1857 {
1858 #ifdef HAVE_GETCWD
1859 return getcwd (path, MAXPATHLEN);
1860 #else
1861 /* seems that some BSD releases lack of a prototype for getwd() */
1862 return getwd (path);
1863 #endif
1864
1865 }
1866
1867 /*!
1868 * \brief Write a string to the passed file pointer.
1869 *
1870 * Some special characters are quoted.
1871 */
1872 void
CreateQuotedString(DynamicStringType * DS,char * S)1873 CreateQuotedString (DynamicStringType *DS, char *S)
1874 {
1875 DSClearString (DS);
1876 DSAddCharacter (DS, '"');
1877 while (*S)
1878 {
1879 if (*S == '"' || *S == '\\')
1880 DSAddCharacter (DS, '\\');
1881 DSAddCharacter (DS, *S++);
1882 }
1883 DSAddCharacter (DS, '"');
1884 }
1885
1886 BoxType *
GetArcEnds(ArcType * Arc)1887 GetArcEnds (ArcType *Arc)
1888 {
1889 static BoxType box;
1890 box.X1 = Arc->X - Arc->Width * cos (Arc->StartAngle * M180);
1891 box.Y1 = Arc->Y + Arc->Height * sin (Arc->StartAngle * M180);
1892 box.X2 = Arc->X - Arc->Width * cos ((Arc->StartAngle + Arc->Delta) * M180);
1893 box.Y2 = Arc->Y + Arc->Height * sin ((Arc->StartAngle + Arc->Delta) * M180);
1894 return &box;
1895 }
1896
1897
1898 /*!
1899 * \todo Doesn't this belong in change.c ??
1900 */
1901 void
ChangeArcAngles(LayerType * Layer,ArcType * a,Angle new_sa,Angle new_da)1902 ChangeArcAngles (LayerType *Layer, ArcType *a,
1903 Angle new_sa, Angle new_da)
1904 {
1905 if (new_da >= 360)
1906 {
1907 new_da = 360;
1908 new_sa = 0;
1909 }
1910 RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a);
1911 r_delete_entry (Layer->arc_tree, (BoxType *) a);
1912 AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a);
1913 a->StartAngle = new_sa;
1914 a->Delta = new_da;
1915 SetArcBoundingBox (a);
1916 r_insert_entry (Layer->arc_tree, (BoxType *) a, 0);
1917 ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
1918 }
1919
1920 static char *
BumpName(char * Name)1921 BumpName (char *Name)
1922 {
1923 int num;
1924 char c, *start;
1925 static char temp[256];
1926
1927 start = Name;
1928 /* seek end of string */
1929 while (*Name != 0)
1930 Name++;
1931 /* back up to potential number */
1932 for (Name--; isdigit ((int) *Name); Name--);
1933 Name++;
1934 if (*Name)
1935 num = atoi (Name) + 1;
1936 else
1937 num = 1;
1938 c = *Name;
1939 *Name = 0;
1940 sprintf (temp, "%s%d", start, num);
1941 /* if this is not our string, put back the blown character */
1942 if (start != temp)
1943 *Name = c;
1944 return (temp);
1945 }
1946
1947 /*!
1948 * \brief Make a unique name for the name on board.
1949 *
1950 * This can alter the contents of the input string.
1951 */
1952 char *
UniqueElementName(DataType * Data,char * Name)1953 UniqueElementName (DataType *Data, char *Name)
1954 {
1955 bool unique = true;
1956 /* null strings are ok */
1957 if (!Name || !*Name)
1958 return (Name);
1959
1960 for (;;)
1961 {
1962 ELEMENT_LOOP (Data);
1963 {
1964 if (NAMEONPCB_NAME (element) &&
1965 NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
1966 {
1967 Name = BumpName (Name);
1968 unique = false;
1969 break;
1970 }
1971 }
1972 END_LOOP;
1973 if (unique)
1974 return (Name);
1975 unique = true;
1976 }
1977 }
1978
1979 static void
GetGridLockCoordinates(int type,void * ptr1,void * ptr2,void * ptr3,Coord * x,Coord * y)1980 GetGridLockCoordinates (int type, void *ptr1,
1981 void *ptr2, void *ptr3, Coord * x,
1982 Coord * y)
1983 {
1984 switch (type)
1985 {
1986 case VIA_TYPE:
1987 *x = ((PinType *) ptr2)->X;
1988 *y = ((PinType *) ptr2)->Y;
1989 break;
1990 case LINE_TYPE:
1991 *x = ((LineType *) ptr2)->Point1.X;
1992 *y = ((LineType *) ptr2)->Point1.Y;
1993 break;
1994 case TEXT_TYPE:
1995 case ELEMENTNAME_TYPE:
1996 *x = ((TextType *) ptr2)->X;
1997 *y = ((TextType *) ptr2)->Y;
1998 break;
1999 case ELEMENT_TYPE:
2000 *x = ((ElementType *) ptr2)->MarkX;
2001 *y = ((ElementType *) ptr2)->MarkY;
2002 break;
2003 case POLYGON_TYPE:
2004 *x = ((PolygonType *) ptr2)->Points[0].X;
2005 *y = ((PolygonType *) ptr2)->Points[0].Y;
2006 break;
2007
2008 case LINEPOINT_TYPE:
2009 case POLYGONPOINT_TYPE:
2010 *x = ((PointType *) ptr3)->X;
2011 *y = ((PointType *) ptr3)->Y;
2012 break;
2013 case ARC_TYPE:
2014 {
2015 BoxType *box;
2016
2017 box = GetArcEnds ((ArcType *) ptr2);
2018 *x = box->X1;
2019 *y = box->Y1;
2020 break;
2021 }
2022 }
2023 }
2024
2025 void
AttachForCopy(Coord PlaceX,Coord PlaceY)2026 AttachForCopy (Coord PlaceX, Coord PlaceY)
2027 {
2028 Coord mx = 0, my = 0;
2029
2030 Crosshair.AttachedObject.RubberbandN = 0;
2031 if (! TEST_FLAG (SNAPPINFLAG, PCB))
2032 {
2033 /* dither the grab point so that the mark, center, etc
2034 * will end up on a grid coordinate
2035 */
2036 GetGridLockCoordinates (Crosshair.AttachedObject.Type,
2037 Crosshair.AttachedObject.Ptr1,
2038 Crosshair.AttachedObject.Ptr2,
2039 Crosshair.AttachedObject.Ptr3, &mx, &my);
2040 mx = GridFit (mx, PCB->Grid, PCB->GridOffsetX) - mx;
2041 my = GridFit (my, PCB->Grid, PCB->GridOffsetY) - my;
2042 }
2043 Crosshair.AttachedObject.X = PlaceX - mx;
2044 Crosshair.AttachedObject.Y = PlaceY - my;
2045 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
2046 SetLocalRef (PlaceX - mx, PlaceY - my, true);
2047 Crosshair.AttachedObject.State = STATE_SECOND;
2048
2049 /* get boundingbox of object and set cursor range */
2050 crosshair_update_range();
2051
2052 /* get all attached objects if necessary */
2053 if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB))
2054 LookupRubberbandLines (Crosshair.AttachedObject.Type,
2055 Crosshair.AttachedObject.Ptr1,
2056 Crosshair.AttachedObject.Ptr2,
2057 Crosshair.AttachedObject.Ptr3);
2058 if (Settings.Mode != COPY_MODE &&
2059 (Crosshair.AttachedObject.Type == ELEMENT_TYPE ||
2060 Crosshair.AttachedObject.Type == VIA_TYPE ||
2061 Crosshair.AttachedObject.Type == LINE_TYPE ||
2062 Crosshair.AttachedObject.Type == LINEPOINT_TYPE))
2063 LookupRatLines (Crosshair.AttachedObject.Type,
2064 Crosshair.AttachedObject.Ptr1,
2065 Crosshair.AttachedObject.Ptr2,
2066 Crosshair.AttachedObject.Ptr3);
2067 }
2068
2069 /*!
2070 * \brief Return nonzero if the given file exists and is readable.
2071 */
2072 int
FileExists(const char * name)2073 FileExists (const char *name)
2074 {
2075 FILE *f;
2076 f = fopen (name, "r");
2077 if (f)
2078 {
2079 fclose (f);
2080 return 1;
2081 }
2082 return 0;
2083 }
2084
2085 char *
Concat(const char * first,...)2086 Concat (const char *first, ...)
2087 {
2088 char *rv;
2089 int len;
2090 va_list a;
2091
2092 len = strlen (first);
2093 rv = (char *) malloc (len + 1);
2094 strcpy (rv, first);
2095
2096 va_start (a, first);
2097 while (1)
2098 {
2099 const char *s = va_arg (a, const char *);
2100 if (!s)
2101 break;
2102 len += strlen (s);
2103 rv = (char *) realloc (rv, len + 1);
2104 strcat (rv, s);
2105 }
2106 va_end (a);
2107 return rv;
2108 }
2109
2110 /* Layer Group Functions. */
2111
2112 /*!
2113 * \brief Returns group actually moved to (i.e. either group or
2114 * previous).
2115 */
2116 int
MoveLayerToGroup(int layer,int group)2117 MoveLayerToGroup (int layer, int group)
2118 {
2119 int prev, i, j;
2120
2121 if (layer < 0 || layer > max_copper_layer + 1)
2122 return -1;
2123 prev = GetLayerGroupNumberByNumber (layer);
2124 if ((layer == bottom_silk_layer
2125 && group == GetLayerGroupNumberByNumber (top_silk_layer))
2126 || (layer == top_silk_layer
2127 && group == GetLayerGroupNumberByNumber (bottom_silk_layer))
2128 || (group < 0 || group >= max_group) || (prev == group))
2129 return prev;
2130
2131 /* Remove layer from prev group */
2132 for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
2133 if (PCB->LayerGroups.Entries[prev][i] != layer)
2134 PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
2135 PCB->LayerGroups.Number[prev]--;
2136
2137 /* Add layer to new group. */
2138 i = PCB->LayerGroups.Number[group]++;
2139 PCB->LayerGroups.Entries[group][i] = layer;
2140
2141 return group;
2142 }
2143
2144 /*!
2145 * \brief Returns pointer to private buffer.
2146 */
2147 char *
LayerGroupsToString(LayerGroupType * lg)2148 LayerGroupsToString (LayerGroupType *lg)
2149 {
2150 #if MAX_ALL_LAYER < 9999
2151 /* Allows for layer numbers 0..9999 */
2152 static char buf[(MAX_ALL_LAYER) * 5 + 1];
2153 #endif
2154 char *cp = buf;
2155 char sep = 0;
2156 int group, entry;
2157 for (group = 0; group < max_group; group++)
2158 if (PCB->LayerGroups.Number[group])
2159 {
2160 if (sep)
2161 *cp++ = ':';
2162 sep = 1;
2163 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
2164 {
2165 int layer = PCB->LayerGroups.Entries[group][entry];
2166 if (layer == top_silk_layer)
2167 {
2168 *cp++ = 'c';
2169 }
2170 else if (layer == bottom_silk_layer)
2171 {
2172 *cp++ = 's';
2173 }
2174 else
2175 {
2176 sprintf (cp, "%d", layer + 1);
2177 while (*++cp)
2178 ;
2179 }
2180 if (entry != PCB->LayerGroups.Number[group] - 1)
2181 *cp++ = ',';
2182 }
2183 }
2184 *cp++ = 0;
2185 return buf;
2186 }
2187
2188 int
GetMaxBottomLayer()2189 GetMaxBottomLayer ()
2190 {
2191 int l = 0;
2192 int group, i;
2193
2194 group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
2195 for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
2196 {
2197 if (PCB->LayerGroups.Entries[group][i] < max_copper_layer)
2198 l = max (l, PCB->LayerGroups.Entries[group][i]);
2199 }
2200 return l;
2201 }
2202
2203
2204 /*!
2205 * \brief Sanitize buried via
2206 *
2207 * - remove degraded vias
2208 * - ensure correct order of layers
2209 * - change full size vias to TH vias
2210 */
2211 void
SanitizeBuriedVia(PinType * via)2212 SanitizeBuriedVia (PinType *via)
2213 {
2214 /* we expect that via is burried/blind */
2215
2216 if (via->BuriedFrom > via->BuriedTo)
2217 {
2218 int tmp = via->BuriedFrom;
2219 via->BuriedFrom = via->BuriedTo;
2220 via->BuriedTo = tmp;
2221 }
2222
2223 /* check, if via was extended to full stack (after first layer removal)*/
2224 /* convert it in TH via in such case */
2225 if (via->BuriedTo == GetMaxBottomLayer ()
2226 && via->BuriedFrom == 0)
2227 via->BuriedTo = 0;
2228 }
2229
2230 #if 0
2231 bool
2232 IsLayerMoveSafe (int old_index, int new_index)
2233 {
2234 VIA_LOOP (PCB->Data);
2235 {
2236 if (VIA_IS_BURIED (via))
2237 {
2238 /* moving from below to inside */
2239 if ((old_index < via->BuriedFrom)
2240 && (new_index < via->BuriedTo))
2241 return false;
2242
2243 /* moving from above to inside */
2244 if ((old_index > via->BuriedTo)
2245 && (new_index > via->BuriedFrom))
2246 return false;
2247 }
2248 }
2249 END_LOOP;
2250
2251 return true;
2252 }
2253
2254 #endif
2255
2256 /*!
2257 * \brief Update buried vias after layer move
2258 *
2259 */
2260
2261 void
ChangeBuriedViasAfterLayerMove(int old_index,int new_index)2262 ChangeBuriedViasAfterLayerMove (int old_index, int new_index)
2263 {
2264 VIA_LOOP (PCB->Data);
2265 {
2266 if (VIA_IS_BURIED (via))
2267 {
2268 /* do nothing, if both layers are below the via */
2269 if ((old_index < via->BuriedFrom)
2270 && (new_index < via->BuriedFrom))
2271 continue;
2272
2273 /* do nothing, if both layers are above the via */
2274 if ((old_index > via->BuriedTo)
2275 && (new_index > via->BuriedTo))
2276 continue;
2277
2278 /* moving via top layer - via top follows layer */
2279 if (old_index == via->BuriedFrom)
2280 {
2281 AddObjectToSetViaLayersUndoList (via, via, via);
2282 via->BuriedFrom = new_index;
2283 }
2284
2285 /* moving via bottom layer - via bottom follows layer */
2286 else if (old_index == via->BuriedTo)
2287 {
2288 AddObjectToSetViaLayersUndoList (via, via, via);
2289 via->BuriedTo = new_index;
2290 }
2291
2292 /* moving layer covered by via inside the via (except top and bottom) - do nothing */
2293 else if (VIA_ON_LAYER (via, old_index)
2294 && VIA_ON_LAYER (via, new_index))
2295 continue;
2296
2297 /* moving from inside to below - shrink via DANGEROUS op*/
2298 else if (VIA_ON_LAYER (via, old_index)
2299 && (new_index < via->BuriedFrom))
2300 {
2301 AddObjectToSetViaLayersUndoList (via, via, via);
2302 via->BuriedFrom++;
2303 }
2304
2305 /* moving from inside to above - shrink via DANGEROUS op*/
2306 else if (VIA_ON_LAYER (via, old_index)
2307 && (new_index > via->BuriedTo))
2308 {
2309 AddObjectToSetViaLayersUndoList (via, via, via);
2310 via->BuriedTo--;
2311 }
2312
2313 /* moving from above to below - shift via up */
2314 else if ((old_index > via->BuriedTo)
2315 && (new_index < via->BuriedFrom))
2316 {
2317 AddObjectToSetViaLayersUndoList (via, via, via);
2318 via->BuriedFrom++;
2319 via->BuriedTo++;
2320 }
2321
2322 /* moving from below to above - shift via down */
2323 else if ((old_index < via->BuriedFrom)
2324 && (new_index >= via->BuriedTo))
2325 {
2326 AddObjectToSetViaLayersUndoList (via, via, via);
2327 via->BuriedFrom--;
2328 via->BuriedTo--;
2329 }
2330
2331 /* moving from below to inside - extend via down DANGEROUS op*/
2332 else if ((old_index < via->BuriedFrom)
2333 && (new_index < via->BuriedTo))
2334 {
2335 AddObjectToSetViaLayersUndoList (via, via, via);
2336 via->BuriedFrom--;
2337 }
2338
2339 /* moving from above to inside - extend via up DANGEROUS op*/
2340 else if ((old_index > via->BuriedTo)
2341 && (new_index > via->BuriedFrom))
2342 {
2343 AddObjectToSetViaLayersUndoList ( via, via, via);
2344 via->BuriedTo++;
2345 }
2346 else
2347 {
2348 Message (_("Layer move: failed via update: %d -> %d, via: %d:%d\n"), old_index, new_index, via->BuriedFrom, via->BuriedTo);
2349 continue;
2350 }
2351
2352 SanitizeBuriedVia (via);
2353 }
2354 }
2355 END_LOOP;
2356 RemoveDegradedVias ();
2357 }
2358
2359 /*!
2360 * \brief Update buried vias after new layer create
2361 *
2362 */
2363 void
ChangeBuriedViasAfterLayerCreate(int index)2364 ChangeBuriedViasAfterLayerCreate (int index)
2365 {
2366 VIA_LOOP (PCB->Data);
2367 {
2368 if (VIA_IS_BURIED (via))
2369 {
2370 if (index <= via->BuriedFrom
2371 || index <= via->BuriedTo)
2372 AddObjectToSetViaLayersUndoList (via, via, via);
2373
2374 if (index <= via->BuriedFrom)
2375 via->BuriedFrom++;
2376 if (index <= via->BuriedTo)
2377 via->BuriedTo++;
2378 }
2379 }
2380 END_LOOP;
2381 }
2382
2383 /*!
2384 * \brief Update buried vias after layer delete
2385 *
2386 */
2387 void
ChangeBuriedViasAfterLayerDelete(int index)2388 ChangeBuriedViasAfterLayerDelete (int index)
2389 {
2390 VIA_LOOP (PCB->Data);
2391 {
2392 if (VIA_IS_BURIED (via))
2393 {
2394 if (index < via->BuriedFrom
2395 || index <= via->BuriedTo)
2396 AddObjectToSetViaLayersUndoList (via, via, via);
2397
2398 if (index < via->BuriedFrom)
2399 via->BuriedFrom--;
2400 if (index <= via->BuriedTo)
2401 via->BuriedTo--;
2402
2403 SanitizeBuriedVia (via);
2404 }
2405 }
2406 END_LOOP;
2407 RemoveDegradedVias ();
2408 }
2409
2410 /*!
2411 * \brief Check if via penetrates layer group
2412 *
2413 */
2414 bool
ViaIsOnLayerGroup(PinType * via,int group)2415 ViaIsOnLayerGroup (PinType *via, int group)
2416 {
2417 Cardinal layer;
2418
2419 if (!VIA_IS_BURIED (via))
2420 return true;
2421
2422 for (layer = via->BuriedFrom; layer <= via->BuriedTo; layer++)
2423 {
2424 if (GetLayerGroupNumberByNumber (layer) == group)
2425 return true;
2426 }
2427
2428 return false;
2429 }
2430
2431 /*!
2432 * \brief Check if via penetrates any visible layer
2433 *
2434 */
2435 bool
ViaIsOnAnyVisibleLayer(PinType * via)2436 ViaIsOnAnyVisibleLayer (PinType *via)
2437 {
2438 Cardinal layer;
2439
2440 if (!VIA_IS_BURIED (via))
2441 return true;
2442
2443 for (layer = via->BuriedFrom; layer <= via->BuriedTo; layer ++)
2444 {
2445 if (PCB->Data->Layer[layer].On)
2446 return true;
2447 }
2448
2449 return false;
2450 }
2451
2452
2453 char *
pcb_author(void)2454 pcb_author (void)
2455 {
2456 #ifdef HAVE_GETPWUID
2457 static struct passwd *pwentry;
2458 static char *fab_author = 0;
2459
2460 if (!fab_author)
2461 {
2462 if (Settings.FabAuthor && Settings.FabAuthor[0])
2463 fab_author = Settings.FabAuthor;
2464 else
2465 {
2466 int len;
2467 char *comma, *gecos;
2468
2469 /* ID the user. */
2470 pwentry = getpwuid (getuid ());
2471 gecos = pwentry->pw_gecos;
2472 comma = strchr (gecos, ',');
2473 if (comma)
2474 len = comma - gecos;
2475 else
2476 len = strlen (gecos);
2477 fab_author = (char *)malloc (len + 1);
2478 if (!fab_author)
2479 {
2480 perror ("pcb: out of memory.\n");
2481 exit (-1);
2482 }
2483 memcpy (fab_author, gecos, len);
2484 fab_author[len] = 0;
2485 }
2486 }
2487 return fab_author;
2488 #else
2489 return "Unknown";
2490 #endif
2491 }
2492
2493
2494 /*!
2495 * \brief Returns NULL if the name isn't found, else the value for that
2496 * named attribute.
2497 */
2498 char *
AttributeGetFromList(AttributeListType * list,char * name)2499 AttributeGetFromList (AttributeListType *list, char *name)
2500 {
2501 int i;
2502 for (i=0; i<list->Number; i++)
2503 if (strcmp (name, list->List[i].name) == 0)
2504 return list->List[i].value;
2505 return NULL;
2506 }
2507
2508 /*!
2509 * \brief Adds an attribute to the list.
2510 *
2511 * If the attribute already exists, whether it's replaced or a second
2512 * copy added depends on REPLACE.
2513 *
2514 * \return Non-zero if an existing attribute was replaced.
2515 */
2516 int
AttributePutToList(AttributeListType * list,const char * name,const char * value,int replace)2517 AttributePutToList (AttributeListType *list, const char *name, const char *value, int replace)
2518 {
2519 int i;
2520
2521 /* If we're allowed to replace an existing attribute, see if we
2522 can. */
2523 if (replace)
2524 {
2525 for (i=0; i<list->Number; i++)
2526 if (strcmp (name, list->List[i].name) == 0)
2527 {
2528 free (list->List[i].value);
2529 list->List[i].value = STRDUP (value);
2530 return 1;
2531 }
2532 }
2533
2534 /* At this point, we're going to need to add a new attribute to the
2535 list. See if there's room. */
2536 if (list->Number >= list->Max)
2537 {
2538 list->Max += 10;
2539 list->List = (AttributeType *) realloc (list->List,
2540 list->Max * sizeof (AttributeType));
2541 }
2542
2543 /* Now add the new attribute. */
2544 i = list->Number;
2545 list->List[i].name = STRDUP (name);
2546 list->List[i].value = STRDUP (value);
2547 list->Number ++;
2548 return 0;
2549 }
2550
2551 /*!
2552 * \brief Remove an attribute by name.
2553 */
2554 void
AttributeRemoveFromList(AttributeListType * list,char * name)2555 AttributeRemoveFromList(AttributeListType *list, char *name)
2556 {
2557 int i, j;
2558 for (i=0; i<list->Number; i++)
2559 if (strcmp (name, list->List[i].name) == 0)
2560 {
2561 free (list->List[i].name);
2562 free (list->List[i].value);
2563 for (j=i; j<list->Number-1; j++)
2564 list->List[j] = list->List[j+1];
2565 list->Number --;
2566 }
2567 }
2568
2569 /*!
2570 * \todo In future all use of this should be supplanted by pcb-printf
2571 * and %mr/%m# spec.
2572 *
2573 * These act like you'd expect, except always in the C locale.
2574 */
2575 const char *
c_dtostr(double d)2576 c_dtostr (double d)
2577 {
2578 static char buf[100];
2579 int i, f;
2580 char *bufp = buf;
2581
2582 if (d < 0)
2583 {
2584 *bufp++ = '-';
2585 d = -d;
2586 }
2587 d += 0.0000005; /* rounding */
2588 i = floor (d);
2589 d -= i;
2590 sprintf (bufp, "%d", i);
2591 bufp += strlen (bufp);
2592 *bufp++ = '.';
2593
2594 f = floor (d * 1000000.0);
2595 sprintf (bufp, "%06d", f);
2596 return buf;
2597 }
2598
2599 void
r_delete_element(DataType * data,ElementType * element)2600 r_delete_element (DataType * data, ElementType * element)
2601 {
2602 r_delete_entry (data->element_tree, (BoxType *) element);
2603 PIN_LOOP (element);
2604 {
2605 r_delete_entry (data->pin_tree, (BoxType *) pin);
2606 }
2607 END_LOOP;
2608 PAD_LOOP (element);
2609 {
2610 r_delete_entry (data->pad_tree, (BoxType *) pad);
2611 }
2612 END_LOOP;
2613 ELEMENTTEXT_LOOP (element);
2614 {
2615 r_delete_entry (data->name_tree[n], (BoxType *) text);
2616 }
2617 END_LOOP;
2618 }
2619
2620
2621 /*!
2622 * \brief Returns a string that has a bunch of information about the
2623 * program.
2624 *
2625 * Can be used for things like "about" dialog boxes.
2626 */
2627 char *
GetInfoString(void)2628 GetInfoString (void)
2629 {
2630 HID **hids;
2631 int i;
2632 static DynamicStringType info;
2633 static int first_time = 1;
2634
2635 #define TAB " "
2636
2637 if (first_time)
2638 {
2639 first_time = 0;
2640 DSAddString (&info,
2641 _("This is PCB, an interactive\n"
2642 "printed circuit board editor\n"
2643 "version "));
2644 DSAddString (&info,
2645 VERSION "\n\n"
2646 "Compiled on " __DATE__ " at " __TIME__ "\n\n"
2647 "by harry eaton\n\n"
2648 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n"
2649 "Copyright (C) harry eaton 1998-2007\n"
2650 "Copyright (C) C. Scott Ananian 2001\n"
2651 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n"
2652 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2653 DSAddString (&info,
2654 _("It is licensed under the terms of the GNU\n"
2655 "General Public License version 2\n"
2656 "See the LICENSE file for more information\n\n"
2657 "For more information see:\n"));
2658 DSAddString (&info, _("PCB homepage: "));
2659 DSAddString (&info, "http://pcb.geda-project.org\n");
2660 DSAddString (&info, _("gEDA homepage: "));
2661 DSAddString (&info, "http://www.geda-project.org\n");
2662 DSAddString (&info, _("gEDA Wiki: "));
2663 DSAddString (&info, "http://wiki.geda-project.org\n");
2664
2665 DSAddString (&info, _("\n----- Compile Time Options -----\n"));
2666 hids = hid_enumerate ();
2667 DSAddString (&info, _("GUI:\n"));
2668 for (i = 0; hids[i]; i++)
2669 {
2670 if (hids[i]->gui)
2671 {
2672 DSAddString (&info, TAB);
2673 DSAddString (&info, hids[i]->name);
2674 DSAddString (&info, " : ");
2675 DSAddString (&info, hids[i]->description);
2676 DSAddString (&info, "\n");
2677 }
2678 }
2679
2680 DSAddString (&info, _("Exporters:\n"));
2681 for (i = 0; hids[i]; i++)
2682 {
2683 if (hids[i]->exporter)
2684 {
2685 DSAddString (&info, TAB);
2686 DSAddString (&info, hids[i]->name);
2687 DSAddString (&info, " : ");
2688 DSAddString (&info, hids[i]->description);
2689 DSAddString (&info, "\n");
2690 }
2691 }
2692
2693 DSAddString (&info, _("Printers:\n"));
2694 for (i = 0; hids[i]; i++)
2695 {
2696 if (hids[i]->printer)
2697 {
2698 DSAddString (&info, TAB);
2699 DSAddString (&info, hids[i]->name);
2700 DSAddString (&info, " : ");
2701 DSAddString (&info, hids[i]->description);
2702 DSAddString (&info, "\n");
2703 }
2704 }
2705 }
2706 #undef TAB
2707
2708 return info.Data;
2709 }
2710
2711 #ifdef MKDIR_IS_PCBMKDIR
2712 #error "Don't know how to create a directory on this system."
2713 #endif
2714
2715 /*!
2716 * \brief mkdir() implentation, mostly for plugins, which don't have our
2717 * config.h.
2718 */
2719 int
pcb_mkdir(const char * path,int mode)2720 pcb_mkdir (const char *path, int mode)
2721 {
2722 return MKDIR (path, mode);
2723 }
2724
2725 /*!
2726 * \brief Returns a best guess about the orientation of an element.
2727 *
2728 * The value corresponds to the rotation; a difference is the right
2729 * value to pass to RotateElementLowLevel.
2730 * However, the actual value is no indication of absolute rotation; only
2731 * relative rotation is meaningful.
2732 *
2733 * \return a relative rotation for an element, useful only for comparing
2734 * two similar footprints.
2735 */
2736 int
ElementOrientation(ElementType * e)2737 ElementOrientation (ElementType *e)
2738 {
2739 Coord pin1x, pin1y, pin2x, pin2y, dx, dy;
2740 bool found_pin1 = 0;
2741 bool found_pin2 = 0;
2742
2743 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2744 pin1x = 0;
2745 pin1y = 0;
2746 pin2x = 0;
2747 pin2y = 0;
2748
2749 PIN_LOOP (e);
2750 {
2751 if (NSTRCMP (pin->Number, "1") == 0)
2752 {
2753 pin1x = pin->X;
2754 pin1y = pin->Y;
2755 found_pin1 = 1;
2756 }
2757 else if (NSTRCMP (pin->Number, "2") == 0)
2758 {
2759 pin2x = pin->X;
2760 pin2y = pin->Y;
2761 found_pin2 = 1;
2762 }
2763 }
2764 END_LOOP;
2765
2766 PAD_LOOP (e);
2767 {
2768 if (NSTRCMP (pad->Number, "1") == 0)
2769 {
2770 pin1x = (pad->Point1.X + pad->Point2.X) / 2;
2771 pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
2772 found_pin1 = 1;
2773 }
2774 else if (NSTRCMP (pad->Number, "2") == 0)
2775 {
2776 pin2x = (pad->Point1.X + pad->Point2.X) / 2;
2777 pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
2778 found_pin2 = 1;
2779 }
2780 }
2781 END_LOOP;
2782
2783 if (found_pin1 && found_pin2)
2784 {
2785 dx = pin2x - pin1x;
2786 dy = pin2y - pin1y;
2787 }
2788 else if (found_pin1 && (pin1x || pin1y))
2789 {
2790 dx = pin1x;
2791 dy = pin1y;
2792 }
2793 else if (found_pin2 && (pin2x || pin2y))
2794 {
2795 dx = pin2x;
2796 dy = pin2y;
2797 }
2798 else
2799 return 0;
2800
2801 if (abs(dx) > abs(dy))
2802 return dx > 0 ? 0 : 2;
2803 return dy > 0 ? 3 : 1;
2804 }
2805
2806 int
ActionListRotations(int argc,char ** argv,Coord x,Coord y)2807 ActionListRotations(int argc, char **argv, Coord x, Coord y)
2808 {
2809 ELEMENT_LOOP (PCB->Data);
2810 {
2811 printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
2812 }
2813 END_LOOP;
2814
2815 return 0;
2816 }
2817
2818 HID_Action misc_action_list[] = {
2819 {"ListRotations", 0, ActionListRotations,
2820 0,0},
2821 };
2822
2823 REGISTER_ACTIONS (misc_action_list)
2824