4 * \brief Misc functions used by several modules.
8 * <h1><b>Copyright.</b></h1>\n
10 * PCB, interactive printed circuit board design
12 * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
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.
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.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 * Contact addresses for paper mail and Email:
30 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
32 * Thomas.Nau@rz.uni-ulm.de
47 #include <sys/param.h>
49 #include <sys/types.h>
61 #include "crosshair.h"
70 #include "pcb-printf.h"
75 #include "rubberband.h"
81 #ifdef HAVE_LIBDMALLOC
85 /* forward declarations */
86 static char *BumpName (char *);
87 static void GetGridLockCoordinates (int, void *, void *, void *,
94 * \brief Used by SaveStackAndVisibility() and
95 * RestoreStackAndVisibility().
99 bool ElementOn
, InvisibleObjectsOn
, PinOn
, ViaOn
, RatOn
;
100 int LayerStack
[MAX_ALL_LAYER
];
101 bool LayerOn
[MAX_ALL_LAYER
];
106 * \brief Distance() should be used so that there is only one place to
107 * deal with overflow/precision errors.
110 Distance (double x1
, double y1
, double x2
, double y2
)
112 return hypot(x2
- x1
, y2
- y1
);
116 * \brief Bring an angle into [0, 360] range.
119 NormalizeAngle (Angle a
)
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 +/-
134 GetValue (const char *val
, const char *units
, bool * absolute
)
136 return GetValueEx(val
, units
, absolute
, NULL
, "cmil");
140 GetValueEx (const char *val
, const char *units
, bool * absolute
, UnitList extra_units
, const char *default_unit
)
147 /* Allow NULL to be passed for absolute */
151 /* if the first character is a sign we have to add the
152 * value to the current one
157 if (sscanf (val
+1, "%lf%n", &value
, &n
) < 1)
163 if (isdigit ((int) *val
))
167 if (sscanf (val
, "%lf%n", &value
, &n
) < 1)
173 while (units
&& *units
== ' ')
179 const Unit
*unit
= get_unit_struct (units
);
182 value
= unit_to_coord (unit
, value
);
187 for (i
= 0; *extra_units
[i
].suffix
; ++i
)
189 if (strncmp (units
, extra_units
[i
].suffix
, strlen(extra_units
[i
].suffix
)) == 0)
191 value
*= extra_units
[i
].scale
;
192 if (extra_units
[i
].flags
& UNIT_PERCENT
)
199 /* Apply default unit */
200 if (!scaled
&& default_unit
&& *default_unit
)
203 const Unit
*unit
= get_unit_struct (default_unit
);
205 for (i
= 0; *extra_units
[i
].suffix
; ++i
)
206 if (strcmp (extra_units
[i
].suffix
, default_unit
) == 0)
208 value
*= extra_units
[i
].scale
;
209 if (extra_units
[i
].flags
& UNIT_PERCENT
)
213 if (!scaled
&& unit
!= NULL
)
214 value
= unit_to_coord (unit
, value
);
221 * \brief Extract a unit-less value from a string.
223 * \param val String containing the value to be read.
225 * \param absolute Returns wether the returned value is an absolute one.
227 * \return The value read, with sign.
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
233 double GetUnitlessValue (const char *val
, bool *absolute
) {
243 if (isdigit ((int) *val
))
249 if (sscanf (val
, "%lf", &value
) < 1)
256 * \brief Sets the bounding box of a point (which is silly).
259 SetPointBoundingBox (PointType
*Pnt
)
261 Pnt
->X2
= Pnt
->X
+ 1;
262 Pnt
->Y2
= Pnt
->Y
+ 1;
266 * \brief Sets the bounding box of a pin or via.
269 SetPinBoundingBox (PinType
*Pin
)
273 /* the bounding box covers the extent of influence
274 * so it must include the clearance values too
276 width
= MAX (Pin
->Clearance
+ PIN_SIZE (Pin
), Pin
->Mask
) / 2;
278 /* Adjust for our discrete polygon approximation */
279 width
= (double)width
* POLY_CIRC_RADIUS_ADJ
+ 0.5;
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
);
289 * \brief Sets the bounding box of a pad.
292 SetPadBoundingBox (PadType
*Pad
)
298 /* the bounding box covers the extent of influence
299 * so it must include the clearance values too
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
;
306 if (TEST_FLAG (SQUAREFLAG
, Pad
) && deltax
!= 0 && deltay
!= 0)
308 /* slanted square pad */
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);
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
));
330 /* Adjust for our discrete polygon approximation */
331 width
= (double)width
* POLY_CIRC_RADIUS_ADJ
+ 0.5;
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
;
338 close_box(&Pad
->BoundingBox
);
342 * \brief Sets the bounding box of a line.
345 SetLineBoundingBox (LineType
*Line
)
347 Coord width
= (Line
->Thickness
+ Line
->Clearance
+ 1) / 2;
349 /* Adjust for our discrete polygon approximation */
350 width
= (double)width
* POLY_CIRC_RADIUS_ADJ
+ 0.5;
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
);
362 * \brief Sets the bounding box of a polygons.
365 SetPolygonBoundingBox (PolygonType
*Polygon
)
367 Polygon
->BoundingBox
.X1
= Polygon
->BoundingBox
.Y1
= MAX_COORD
;
368 Polygon
->BoundingBox
.X2
= Polygon
->BoundingBox
.Y2
= 0;
369 POLYGONPOINT_LOOP (Polygon
);
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
);
376 /* boxes don't include the lower right corner */
377 close_box(&Polygon
->BoundingBox
);
382 * \brief Sets the bounding box of an element.
385 SetElementBoundingBox (DataType
*Data
, ElementType
*Element
,
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
);
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);
401 r_insert_entry (Data
->name_tree
[n
], (BoxType
*) text
, 0);
405 /* do not include the elementnames bounding box which
406 * is handled separately
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
);
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);
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
);
437 if (Data
&& Data
->pin_tree
)
438 r_delete_entry (Data
->pin_tree
, (BoxType
*) pin
);
439 SetPinBoundingBox (pin
);
443 Data
->pin_tree
= r_create_tree (NULL
, 0, 0);
444 r_insert_entry (Data
->pin_tree
, (BoxType
*) pin
, 0);
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);
458 if (Data
&& Data
->pad_tree
)
459 r_delete_entry (Data
->pad_tree
, (BoxType
*) pad
);
460 SetPadBoundingBox (pad
);
464 Data
->pad_tree
= r_create_tree (NULL
, 0, 0);
465 r_insert_entry (Data
->pad_tree
, (BoxType
*) pad
, 0);
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
);
472 MIN (pad
->Point1
.X
, pad
->Point2
.X
) - pad
->Thickness
/ 2);
474 MIN (pad
->Point1
.Y
, pad
->Point2
.Y
) - pad
->Thickness
/ 2);
476 MAX (pad
->Point1
.X
, pad
->Point2
.X
) + pad
->Thickness
/ 2);
478 MAX (pad
->Point1
.Y
, pad
->Point2
.Y
) + pad
->Thickness
/ 2);
481 /* now we set the EDGE2FLAG of the pad if Point2
482 * is closer to the outside edge than Point1
486 if (pad
->Point1
.Y
== pad
->Point2
.Y
)
489 if (box
->X2
- pad
->Point2
.X
< pad
->Point1
.X
- box
->X1
)
490 SET_FLAG (EDGE2FLAG
, pad
);
492 CLEAR_FLAG (EDGE2FLAG
, pad
);
497 if (box
->Y2
- pad
->Point2
.Y
< pad
->Point1
.Y
- box
->Y1
)
498 SET_FLAG (EDGE2FLAG
, pad
);
500 CLEAR_FLAG (EDGE2FLAG
, pad
);
505 /* mark pins with component orientation */
506 if ((box
->X2
- box
->X1
) > (box
->Y2
- box
->Y1
))
510 SET_FLAG (EDGE2FLAG
, pin
);
518 CLEAR_FLAG (EDGE2FLAG
, pin
);
524 if (Data
&& !Data
->element_tree
)
525 Data
->element_tree
= r_create_tree (NULL
, 0, 0);
527 r_insert_entry (Data
->element_tree
, box
, 0);
531 * \brief Creates the bounding box of a text object.
534 SetTextBoundingBox (FontType
*FontPtr
, TextType
*Text
)
536 SymbolType
*symbol
= FontPtr
->Symbol
;
537 unsigned char *s
= (unsigned char *) Text
->TextString
;
541 Coord minx
, miny
, maxx
, maxy
, tx
;
542 Coord min_final_radius
;
543 Coord min_unscaled_radius
;
544 bool first_time
= true;
546 minx
= miny
= maxx
= maxy
= tx
= 0;
548 /* Calculate the bounding box based on the larger of the thicknesses
549 * the text might clamped at on silk or copper layers.
551 min_final_radius
= MAX (PCB
->minWid
, PCB
->minSlk
) / 2;
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
557 min_unscaled_radius
= UNSCALE_TEXT (min_final_radius
, Text
->Scale
);
559 /* calculate size of the bounding box */
562 if (*s
<= MAX_FONTPOSITION
&& symbol
[*s
].Valid
)
564 LineType
*line
= symbol
[*s
].Line
;
565 for (i
= 0; i
< symbol
[*s
].LineN
; line
++, i
++)
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.
573 Coord unscaled_radius
= MAX (min_unscaled_radius
, line
->Thickness
/ 4);
577 minx
= maxx
= line
->Point1
.X
;
578 miny
= maxy
= line
->Point1
.Y
;
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
);
591 space
= symbol
[*s
].Delta
;
595 BoxType
*ds
= &FontPtr
->DefaultSymbol
;
596 Coord w
= ds
->X2
- ds
->X1
;
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
);
609 tx
+= symbol
[*s
].Width
+ space
;
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
);
618 /* set upper-left and lower-right corner;
619 * swap coordinates if necessary (origin is already in 'swapped')
623 if (TEST_FLAG (ONSOLDERFLAG
, Text
))
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);
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
);
642 /* the bounding box covers the extent of influence
643 * so it must include the clearance values too
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
);
653 * \brief Returns true if data area is empty.
656 IsDataEmpty (DataType
*Data
)
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
);
672 FlagIsDataEmpty (int parm
)
674 int i
= IsDataEmpty (PCB
->Data
);
675 return parm
? !i
: i
;
678 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
679 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
682 IsLayerEmpty (LayerType
*layer
)
684 return (layer
->LineN
== 0
686 && layer
->PolygonN
== 0
687 && layer
->ArcN
== 0);
691 IsLayerNumEmpty (int num
)
693 return IsLayerEmpty (PCB
->Data
->Layer
+num
);
697 IsLayerGroupEmpty (int num
)
700 for (i
=0; i
<PCB
->LayerGroups
.Number
[num
]; i
++)
701 if (!IsLayerNumEmpty (PCB
->LayerGroups
.Entries
[num
][i
]))
707 IsPasteEmpty (int side
)
709 bool paste_empty
= true;
710 ALLPAD_LOOP (PCB
->Data
);
712 if (ON_SIDE (pad
, side
) && !TEST_FLAG (NOPASTEFLAG
, pad
) && pad
->Mask
> 0)
730 hole_counting_callback (const BoxType
* b
, void *cl
)
732 PinType
*pin
= (PinType
*) b
;
733 HoleCountStruct
*hcs
= (HoleCountStruct
*) cl
;
734 if (TEST_FLAG (HOLEFLAG
, pin
))
742 * \brief Counts the number of plated and unplated holes in the design
743 * within a given area of the board.
745 * To count for the whole board, pass NULL to the within_area.
748 CountHoles (int *plated
, int *unplated
, const BoxType
*within_area
)
750 HoleCountStruct hcs
= {0, 0};
752 r_search (PCB
->Data
->pin_tree
, within_area
, NULL
, hole_counting_callback
, &hcs
);
753 r_search (PCB
->Data
->via_tree
, within_area
, NULL
, hole_counting_callback
, &hcs
);
755 if (plated
!= NULL
) *plated
= hcs
.nplated
;
756 if (unplated
!= NULL
) *unplated
= hcs
.nunplated
;
761 * \brief Gets minimum and maximum coordinates.
763 * \return NULL if layout is empty.
766 GetDataBoundingBox (DataType
*Data
)
769 /* FIX ME: use r_search to do this much faster */
771 /* preset identifiers with highest and lowest possible values */
772 box
.X1
= box
.Y1
= MAX_COORD
;
773 box
.X2
= box
.Y2
= -MAX_COORD
;
775 /* now scan for the lowest/highest X and Y coordinate */
778 box
.X1
= MIN (box
.X1
, via
->X
- via
->Thickness
/ 2);
779 box
.Y1
= MIN (box
.Y1
, via
->Y
- via
->Thickness
/ 2);
780 box
.X2
= MAX (box
.X2
, via
->X
+ via
->Thickness
/ 2);
781 box
.Y2
= MAX (box
.Y2
, via
->Y
+ via
->Thickness
/ 2);
786 box
.X1
= MIN (box
.X1
, element
->BoundingBox
.X1
);
787 box
.Y1
= MIN (box
.Y1
, element
->BoundingBox
.Y1
);
788 box
.X2
= MAX (box
.X2
, element
->BoundingBox
.X2
);
789 box
.Y2
= MAX (box
.Y2
, element
->BoundingBox
.Y2
);
791 TextType
*text
= &NAMEONPCB_TEXT (element
);
792 box
.X1
= MIN (box
.X1
, text
->BoundingBox
.X1
);
793 box
.Y1
= MIN (box
.Y1
, text
->BoundingBox
.Y1
);
794 box
.X2
= MAX (box
.X2
, text
->BoundingBox
.X2
);
795 box
.Y2
= MAX (box
.Y2
, text
->BoundingBox
.Y2
);
801 box
.X1
= MIN (box
.X1
, line
->Point1
.X
- line
->Thickness
/ 2);
802 box
.Y1
= MIN (box
.Y1
, line
->Point1
.Y
- line
->Thickness
/ 2);
803 box
.X1
= MIN (box
.X1
, line
->Point2
.X
- line
->Thickness
/ 2);
804 box
.Y1
= MIN (box
.Y1
, line
->Point2
.Y
- line
->Thickness
/ 2);
805 box
.X2
= MAX (box
.X2
, line
->Point1
.X
+ line
->Thickness
/ 2);
806 box
.Y2
= MAX (box
.Y2
, line
->Point1
.Y
+ line
->Thickness
/ 2);
807 box
.X2
= MAX (box
.X2
, line
->Point2
.X
+ line
->Thickness
/ 2);
808 box
.Y2
= MAX (box
.Y2
, line
->Point2
.Y
+ line
->Thickness
/ 2);
813 box
.X1
= MIN (box
.X1
, arc
->BoundingBox
.X1
);
814 box
.Y1
= MIN (box
.Y1
, arc
->BoundingBox
.Y1
);
815 box
.X2
= MAX (box
.X2
, arc
->BoundingBox
.X2
);
816 box
.Y2
= MAX (box
.Y2
, arc
->BoundingBox
.Y2
);
821 box
.X1
= MIN (box
.X1
, text
->BoundingBox
.X1
);
822 box
.Y1
= MIN (box
.Y1
, text
->BoundingBox
.Y1
);
823 box
.X2
= MAX (box
.X2
, text
->BoundingBox
.X2
);
824 box
.Y2
= MAX (box
.Y2
, text
->BoundingBox
.Y2
);
827 ALLPOLYGON_LOOP (Data
);
829 box
.X1
= MIN (box
.X1
, polygon
->BoundingBox
.X1
);
830 box
.Y1
= MIN (box
.Y1
, polygon
->BoundingBox
.Y1
);
831 box
.X2
= MAX (box
.X2
, polygon
->BoundingBox
.X2
);
832 box
.Y2
= MAX (box
.Y2
, polygon
->BoundingBox
.Y2
);
835 return (IsDataEmpty (Data
) ? NULL
: &box
);
839 * \brief Centers the displayed PCB around the specified point (X,Y),
840 * and move the crosshair there.
842 * If warp_pointer is true warp the pointer to the crosshair.
845 CenterDisplay (Coord X
, Coord Y
, bool warp_pointer
)
847 Coord save_grid
= PCB
->Grid
;
851 if (MoveCrosshairAbsolute (X
, Y
))
852 notify_crosshair_change (true);
855 gui
->set_crosshair (Crosshair
.X
, Crosshair
.Y
,
856 HID_SC_CENTER_IN_VIEWPORT_AND_WARP_POINTER
);
858 gui
->set_crosshair (Crosshair
.X
, Crosshair
.Y
, HID_SC_CENTER_IN_VIEWPORT
);
860 PCB
->Grid
= save_grid
;
864 * \brief Transforms symbol coordinates so that the left edge of each
865 * symbol is at the zero position.
867 * The y coordinates are moved so that min(y) = 0.
871 SetFontInfo (FontType
*Ptr
)
876 Coord totalminy
= MAX_COORD
;
878 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
879 * maximum cell width and height
880 * minimum x and y position of all lines
882 Ptr
->MaxWidth
= DEFAULT_CELLSIZE
;
883 Ptr
->MaxHeight
= DEFAULT_CELLSIZE
;
884 for (i
= 0, symbol
= Ptr
->Symbol
; i
<= MAX_FONTPOSITION
; i
++, symbol
++)
886 Coord minx
, miny
, maxx
, maxy
;
888 /* next one if the index isn't used or symbol is empty (SPACE) */
889 if (!symbol
->Valid
|| !symbol
->LineN
)
892 minx
= miny
= MAX_COORD
;
894 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
896 minx
= MIN (minx
, line
->Point1
.X
);
897 miny
= MIN (miny
, line
->Point1
.Y
);
898 minx
= MIN (minx
, line
->Point2
.X
);
899 miny
= MIN (miny
, line
->Point2
.Y
);
900 maxx
= MAX (maxx
, line
->Point1
.X
);
901 maxy
= MAX (maxy
, line
->Point1
.Y
);
902 maxx
= MAX (maxx
, line
->Point2
.X
);
903 maxy
= MAX (maxy
, line
->Point2
.Y
);
906 /* move symbol to left edge */
907 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
908 MOVE_LINE_LOWLEVEL (line
, -minx
, 0);
910 /* set symbol bounding box with a minimum cell size of (1,1) */
911 symbol
->Width
= maxx
- minx
+ 1;
912 symbol
->Height
= maxy
+ 1;
914 /* check total min/max */
915 Ptr
->MaxWidth
= MAX (Ptr
->MaxWidth
, symbol
->Width
);
916 Ptr
->MaxHeight
= MAX (Ptr
->MaxHeight
, symbol
->Height
);
917 totalminy
= MIN (totalminy
, miny
);
920 /* move coordinate system to the upper edge (lowest y on screen) */
921 for (i
= 0, symbol
= Ptr
->Symbol
; i
<= MAX_FONTPOSITION
; i
++, symbol
++)
924 symbol
->Height
-= totalminy
;
925 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
926 MOVE_LINE_LOWLEVEL (line
, 0, -totalminy
);
929 /* setup the box for the default symbol */
930 Ptr
->DefaultSymbol
.X1
= Ptr
->DefaultSymbol
.Y1
= 0;
931 Ptr
->DefaultSymbol
.X2
= Ptr
->DefaultSymbol
.X1
+ Ptr
->MaxWidth
;
932 Ptr
->DefaultSymbol
.Y2
= Ptr
->DefaultSymbol
.Y1
+ Ptr
->MaxHeight
;
936 GetNum (char **s
, const char *default_unit
)
939 Coord ret_val
= GetValueEx (*s
, NULL
, NULL
, NULL
, default_unit
);
940 /* Advance pointer */
941 while(isalnum(**s
) || **s
== '.')
947 * \brief Serializes the route style list .
949 * Right now n_styles should always be set to NUM_STYLES,
950 * since that is the number of route styles ParseRouteString()
954 make_route_string (RouteStyleType rs
[], int n_styles
)
956 GString
*str
= g_string_new ("");
959 for (i
= 0; i
< n_styles
; ++i
)
961 char *r_string
= pcb_g_strdup_printf ("%s,%$mr,%$mr,%$mr,%$mr", rs
[i
].Name
,
962 rs
[i
].Thick
, rs
[i
].Diameter
,
963 rs
[i
].Hole
, rs
[i
].Keepaway
);
965 g_string_append_c (str
, ':');
966 g_string_append (str
, r_string
);
969 return g_string_free (str
, FALSE
);
973 * \brief Parses the routes definition string
975 * Which is a colon separated list of comma separated Name, Dimension,
976 * Dimension, Dimension, Dimension.
978 * \example Signal,20,40,20,10:Power,40,60,28,10:...
981 ParseRouteString (char *s
, RouteStyleType
*routeStyle
, const char *default_unit
)
986 memset (routeStyle
, 0, NUM_STYLES
* sizeof (RouteStyleType
));
987 for (style
= 0; style
< NUM_STYLES
; style
++, routeStyle
++)
989 while (*s
&& isspace ((int) *s
))
991 for (i
= 0; *s
&& *s
!= ','; i
++)
994 routeStyle
->Name
= strdup (Name
);
995 if (!isdigit ((int) *++s
))
997 routeStyle
->Thick
= GetNum (&s
, default_unit
);
998 while (*s
&& isspace ((int) *s
))
1002 while (*s
&& isspace ((int) *s
))
1004 if (!isdigit ((int) *s
))
1006 routeStyle
->Diameter
= GetNum (&s
, default_unit
);
1007 while (*s
&& isspace ((int) *s
))
1011 while (*s
&& isspace ((int) *s
))
1013 if (!isdigit ((int) *s
))
1015 routeStyle
->Hole
= GetNum (&s
, default_unit
);
1016 /* for backwards-compatibility, we use a 10-mil default
1017 * for styles which omit the keepaway specification. */
1019 routeStyle
->Keepaway
= MIL_TO_COORD(10);
1023 while (*s
&& isspace ((int) *s
))
1025 if (!isdigit ((int) *s
))
1027 routeStyle
->Keepaway
= GetNum (&s
, default_unit
);
1028 while (*s
&& isspace ((int) *s
))
1031 if (style
< NUM_STYLES
- 1)
1033 while (*s
&& isspace ((int) *s
))
1042 memset (routeStyle
, 0, NUM_STYLES
* sizeof (RouteStyleType
));
1047 * \brief Parses the group definition string.
1049 * Which is a colon separated list of comma separated layer numbers.
1051 * \example (1,2,b:4,6,8,t)
1054 ParseGroupString (char *group_string
, LayerGroupType
*LayerGroup
, int *LayerN
)
1057 int group
, member
, layer
;
1058 bool c_set
= false, /* flags for the two special layers to */
1059 s_set
= false; /* provide a default setting for old formats */
1060 int groupnum
[MAX_ALL_LAYER
];
1064 /* Deterimine the maximum layer number */
1065 for (s
= group_string
; s
&& *s
; s
++)
1067 while (*s
&& isspace ((int) *s
))
1083 if (!isdigit ((int) *s
))
1085 *LayerN
= MAX (*LayerN
, atoi (s
));
1089 while (*++s
&& isdigit ((int) *s
));
1091 /* ignore white spaces and check for separator */
1092 while (*s
&& isspace ((int) *s
))
1098 if (*s
!= ':' && *s
!= ',')
1103 memset (LayerGroup
, 0, sizeof (LayerGroupType
));
1105 /* Clear assignments */
1106 for (layer
= 0; layer
< MAX_ALL_LAYER
; layer
++)
1107 groupnum
[layer
] = -1;
1109 /* loop over all groups */
1110 for (s
= group_string
, group
= 0;
1111 s
&& *s
&& group
< *LayerN
;
1114 while (*s
&& isspace ((int) *s
))
1117 /* loop over all group members */
1118 for (member
= 0; *s
; s
++)
1120 /* ignore white spaces and get layernumber */
1121 while (*s
&& isspace ((int) *s
))
1129 layer
= *LayerN
+ TOP_SILK_LAYER
;
1137 layer
= *LayerN
+ BOTTOM_SILK_LAYER
;
1142 layer
= atoi (s
) - 1;
1145 if (layer
> *LayerN
+ MAX (BOTTOM_SILK_LAYER
, TOP_SILK_LAYER
) ||
1146 member
>= *LayerN
+ 1)
1148 groupnum
[layer
] = group
;
1149 LayerGroup
->Entries
[group
][member
++] = layer
;
1150 while (*++s
&& isdigit ((int) *s
));
1152 /* ignore white spaces and check for separator */
1153 while (*s
&& isspace ((int) *s
))
1155 if (!*s
|| *s
== ':')
1158 LayerGroup
->Number
[group
] = member
;
1163 /* If no explicit solder or component layer group was found in the layer
1164 * group string, make group 0 the bottom side, and group 1 the top side.
1165 * This is done by assigning the relevant silkscreen layers to those groups.
1168 LayerGroup
->Entries
[0][LayerGroup
->Number
[0]++] = *LayerN
+ BOTTOM_SILK_LAYER
;
1170 LayerGroup
->Entries
[1][LayerGroup
->Number
[1]++] = *LayerN
+ TOP_SILK_LAYER
;
1172 /* Assign a unique layer group to each layer that was not explicitly
1173 * assigned a particular group by its presence in the layer group string.
1175 for (layer
= 0; layer
< *LayerN
&& group
< *LayerN
; layer
++)
1176 if (groupnum
[layer
] == -1)
1178 LayerGroup
->Entries
[group
][0] = layer
;
1179 LayerGroup
->Number
[group
] = 1;
1184 /* reset structure on error */
1186 memset (LayerGroup
, 0, sizeof (LayerGroupType
));
1191 * \brief Sets up any remaining layer type guesses.
1194 AssignDefaultLayerTypes()
1197 Cardinal outline_layer
= -1;
1200 * There can be only one outline layer. During parsing guess_layertype()
1201 * applied well known cases already, but as this function operates on a
1202 * single layer only, it might end up with more than one hit for the whole
1203 * file. Especially after loading an older layout without saved flags.
1206 LAYER_TYPE_LOOP (PCB
->Data
, max_copper_layer
, LT_OUTLINE
)
1212 /* No or duplicate outline! Try to find a layer which is named exactly
1214 LAYER_TYPE_LOOP (PCB
->Data
, max_copper_layer
, LT_OUTLINE
)
1215 if ( ! strcasecmp (layer
->Name
, "outline"))
1224 /* Next, try to find a layer which is named exactly "route". */
1225 LAYER_TYPE_LOOP (PCB
->Data
, max_copper_layer
, LT_OUTLINE
)
1226 if ( ! strcasecmp (layer
->Name
, "route"))
1235 /* As last resort, take the first layer claiming to be outline. */
1236 LAYER_TYPE_LOOP (PCB
->Data
, max_copper_layer
, LT_OUTLINE
)
1242 /* Make sure our found outline layer is the only one. */
1243 LAYER_TYPE_LOOP (PCB
->Data
, max_copper_layer
, LT_OUTLINE
)
1244 if (n
== outline_layer
)
1245 layer
->Type
= LT_OUTLINE
;
1247 layer
->Type
= LT_ROUTE
; /* best guess */
1251 extern void pcb_main_uninit(void);
1254 * \brief Quits application.
1257 QuitApplication (void)
1260 * save data if necessary. It not needed, then don't trigger EmergencySave
1261 * via our atexit() registering of EmergencySave(). We presumeably wanted to
1262 * exit here and thus it is not an emergency.
1264 if (PCB
->Changed
&& Settings
.SaveInTMP
)
1267 DisableEmergencySave ();
1269 if (gui
->do_exit
== NULL
)
1279 * \brief Creates a filename from a template.
1281 * "%f" is replaced by the filename.
1283 * "%p" is replaced by the searchpath.
1286 EvaluateFilename (char *Template
, char *Path
, char *Filename
, char *Parameter
)
1288 static DynamicStringType command
;
1291 if (Settings
.verbose
)
1293 printf ("EvaluateFilename:\n");
1294 printf ("\tTemplate: \033[33m%s\033[0m\n", Template
);
1295 printf ("\tPath: \033[33m%s\033[0m\n", Path
);
1296 printf ("\tFilename: \033[33m%s\033[0m\n", Filename
);
1297 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter
);
1300 DSClearString (&command
);
1302 for (p
= Template
; p
&& *p
; p
++)
1304 /* copy character or add string to command */
1306 && (*(p
+ 1) == 'f' || *(p
+ 1) == 'p' || *(p
+ 1) == 'a'))
1310 DSAddString (&command
, Parameter
);
1313 DSAddString (&command
, Filename
);
1316 DSAddString (&command
, Path
);
1320 DSAddCharacter (&command
, *p
);
1322 DSAddCharacter (&command
, '\0');
1323 if (Settings
.verbose
)
1324 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command
.Data
);
1326 return strdup (command
.Data
);
1330 * \brief Concatenates directory and filename.
1332 * If directory != NULL expands them with a shell and returns the found
1336 ExpandFilename (char *Dirname
, char *Filename
)
1338 static DynamicStringType answer
;
1343 /* allocate memory for commandline and build it */
1344 DSClearString (&answer
);
1347 command
= (char *)calloc (strlen (Filename
) + strlen (Dirname
) + 7,
1349 sprintf (command
, "echo %s/%s", Dirname
, Filename
);
1353 command
= (char *)calloc (strlen (Filename
) + 6, sizeof (char));
1354 sprintf (command
, "echo %s", Filename
);
1357 /* execute it with shell */
1358 if ((pipe
= popen (command
, "r")) != NULL
)
1360 /* discard all but the first returned line */
1363 if ((c
= fgetc (pipe
)) == EOF
|| c
== '\n' || c
== '\r')
1366 DSAddCharacter (&answer
, c
);
1370 return (pclose (pipe
) ? NULL
: answer
.Data
);
1373 /* couldn't be expanded by the shell */
1374 PopenErrorMessage (command
);
1381 * \brief Returns the layer number for the passed pointer.
1384 GetLayerNumber (DataType
*Data
, LayerType
*Layer
)
1388 for (i
= 0; i
< MAX_ALL_LAYER
; i
++)
1389 if (Layer
== &Data
->Layer
[i
])
1395 * \brief Move layer (number is passed in) to top of layerstack.
1398 PushOnTopOfLayerStack (int NewTop
)
1402 /* ignore silk and other extra layers */
1403 if (NewTop
< max_copper_layer
)
1405 /* first find position of passed one */
1406 for (i
= 0; i
< max_copper_layer
; i
++)
1407 if (LayerStack
[i
] == NewTop
)
1410 /* bring this element to the top of the stack */
1412 LayerStack
[i
] = LayerStack
[i
- 1];
1413 LayerStack
[0] = NewTop
;
1419 * \brief Changes the visibility of all layers in a group.
1421 * \return The number of changed layers.
1424 ChangeGroupVisibility (int Layer
, bool On
, bool ChangeStackOrder
)
1426 int group
, i
, changed
= 1; /* at least the current layer changes */
1428 /* Warning: these special case values must agree with what gui-top-window.c
1432 if (Settings
.verbose
)
1433 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1434 Layer
, On
, ChangeStackOrder
);
1436 /* decrement 'i' to keep stack in order of layergroup */
1437 group
= GetLayerGroupNumberByNumber (Layer
);
1438 for (i
= PCB
->LayerGroups
.Number
[group
]; i
;)
1440 int layer
= PCB
->LayerGroups
.Entries
[group
][--i
];
1442 /* don't count the passed member of the group */
1443 if (layer
!= Layer
&& layer
< max_copper_layer
)
1445 PCB
->Data
->Layer
[layer
].On
= On
;
1447 /* push layer on top of stack if switched on */
1448 if (On
&& ChangeStackOrder
)
1449 PushOnTopOfLayerStack (layer
);
1454 /* change at least the passed layer */
1455 PCB
->Data
->Layer
[Layer
].On
= On
;
1456 if (On
&& ChangeStackOrder
)
1457 PushOnTopOfLayerStack (Layer
);
1459 /* update control panel and exit */
1460 hid_action ("LayersChanged");
1465 * \brief Given a string description of a layer stack, adjust the layer
1466 * stack to correspond.
1469 LayerStringToLayerStack (char *s
)
1471 static int listed_layers
= 0;
1478 args
= (char **) malloc (l
* sizeof (char *));
1501 for (i
= 0; i
< max_copper_layer
+ SILK_LAYER
; i
++)
1503 if (i
< max_copper_layer
)
1505 PCB
->Data
->Layer
[i
].On
= false;
1507 PCB
->ElementOn
= false;
1508 PCB
->InvisibleObjectsOn
= false;
1512 CLEAR_FLAG (SHOWMASKFLAG
, PCB
);
1513 Settings
.ShowBottomSide
= 0;
1515 for (i
=argn
-1; i
>=0; i
--)
1517 if (strcasecmp (args
[i
], "rats") == 0)
1519 else if (strcasecmp (args
[i
], "invisible") == 0)
1520 PCB
->InvisibleObjectsOn
= true;
1521 else if (strcasecmp (args
[i
], "pins") == 0)
1523 else if (strcasecmp (args
[i
], "vias") == 0)
1525 else if (strcasecmp (args
[i
], "elements") == 0
1526 || strcasecmp (args
[i
], "silk") == 0)
1527 PCB
->ElementOn
= true;
1528 else if (strcasecmp (args
[i
], "mask") == 0)
1529 SET_FLAG (SHOWMASKFLAG
, PCB
);
1530 else if (strcasecmp (args
[i
], "solderside") == 0)
1531 Settings
.ShowBottomSide
= 1;
1532 else if (isdigit ((int) args
[i
][0]))
1534 lno
= atoi (args
[i
]);
1535 ChangeGroupVisibility (lno
, true, true);
1540 for (lno
= 0; lno
< max_copper_layer
; lno
++)
1541 if (strcasecmp (args
[i
], PCB
->Data
->Layer
[lno
].Name
) == 0)
1543 ChangeGroupVisibility (lno
, true, true);
1549 fprintf(stderr
, _("Warning: layer \"%s\" not known\n"), args
[i
]);
1552 fprintf (stderr
, _("Named layers in this board are:\n"));
1554 for (lno
=0; lno
< max_copper_layer
; lno
++)
1555 fprintf(stderr
, "\t%s\n", PCB
->Data
->Layer
[lno
].Name
);
1556 fprintf(stderr
, _("Also: component, solder, rats, invisible, "
1557 "pins, vias, elements or silk, mask, solderside.\n"));
1565 * \brief Returns the layergroup number for the passed pointer.
1568 GetLayerGroupNumberByPointer (LayerType
*Layer
)
1570 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB
->Data
, Layer
)));
1574 * \brief Returns the layergroup number for the passed layer number.
1577 GetLayerGroupNumberByNumber (Cardinal Layer
)
1581 for (group
= 0; group
< max_group
; group
++)
1582 for (entry
= 0; entry
< PCB
->LayerGroups
.Number
[group
]; entry
++)
1583 if (PCB
->LayerGroups
.Entries
[group
][entry
] == Layer
)
1586 /* since every layer belongs to a group it is safe to return
1587 * the value without boundary checking
1593 * \brief Returns the layergroup number for the passed side (TOP_SIDE or
1597 GetLayerGroupNumberBySide (int side
)
1599 /* Find the relavant board side layer group by determining the
1600 * layer group associated with the relevant side's silk-screen
1602 return GetLayerGroupNumberByNumber(
1603 side
== TOP_SIDE
? top_silk_layer
: bottom_silk_layer
);
1607 * \brief Returns a pointer to an objects bounding box.
1609 * Data is valid until the routine is called again.
1612 GetObjectBoundingBox (int Type
, void *Ptr1
, void *Ptr2
, void *Ptr3
)
1622 case ELEMENTNAME_TYPE
:
1623 return (BoxType
*)Ptr2
;
1626 return (BoxType
*)Ptr1
;
1627 case POLYGONPOINT_TYPE
:
1628 case LINEPOINT_TYPE
:
1629 return (BoxType
*)Ptr3
;
1631 Message (_("Request for bounding box of unsupported type %d\n"), Type
);
1632 return (BoxType
*)Ptr2
;
1637 * \brief Computes the bounding box of an arc.
1640 SetArcBoundingBox (ArcType
*Arc
)
1642 double ca1
, ca2
, sa1
, sa2
;
1643 double minx
, maxx
, miny
, maxy
;
1647 /* first put angles into standard form:
1648 * ang1 < ang2, both angles between 0 and 720 */
1649 Arc
->Delta
= CLAMP (Arc
->Delta
, -360, 360);
1653 ang1
= NormalizeAngle (Arc
->StartAngle
);
1654 ang2
= NormalizeAngle (Arc
->StartAngle
+ Arc
->Delta
);
1658 ang1
= NormalizeAngle (Arc
->StartAngle
+ Arc
->Delta
);
1659 ang2
= NormalizeAngle (Arc
->StartAngle
);
1663 /* Make sure full circles aren't treated as zero-length arcs */
1664 if (Arc
->Delta
== 360 || Arc
->Delta
== -360)
1667 /* calculate sines, cosines */
1668 sa1
= sin (M180
* ang1
);
1669 ca1
= cos (M180
* ang1
);
1670 sa2
= sin (M180
* ang2
);
1671 ca2
= cos (M180
* ang2
);
1673 minx
= MIN (ca1
, ca2
);
1674 maxx
= MAX (ca1
, ca2
);
1675 miny
= MIN (sa1
, sa2
);
1676 maxy
= MAX (sa1
, sa2
);
1678 /* Check for extreme angles */
1679 if ((ang1
<= 0 && ang2
>= 0) || (ang1
<= 360 && ang2
>= 360)) maxx
= 1;
1680 if ((ang1
<= 90 && ang2
>= 90) || (ang1
<= 450 && ang2
>= 450)) maxy
= 1;
1681 if ((ang1
<= 180 && ang2
>= 180) || (ang1
<= 540 && ang2
>= 540)) minx
= -1;
1682 if ((ang1
<= 270 && ang2
>= 270) || (ang1
<= 630 && ang2
>= 630)) miny
= -1;
1684 /* Finally, calcate bounds, converting sane geometry into pcb geometry */
1685 Arc
->BoundingBox
.X1
= Arc
->X
- Arc
->Width
* maxx
;
1686 Arc
->BoundingBox
.X2
= Arc
->X
- Arc
->Width
* minx
;
1687 Arc
->BoundingBox
.Y1
= Arc
->Y
+ Arc
->Height
* miny
;
1688 Arc
->BoundingBox
.Y2
= Arc
->Y
+ Arc
->Height
* maxy
;
1690 width
= (Arc
->Thickness
+ Arc
->Clearance
) / 2;
1692 /* Adjust for our discrete polygon approximation */
1693 width
= (double)width
* MAX (POLY_CIRC_RADIUS_ADJ
, (1.0 + POLY_ARC_MAX_DEVIATION
)) + 0.5;
1695 Arc
->BoundingBox
.X1
-= width
;
1696 Arc
->BoundingBox
.X2
+= width
;
1697 Arc
->BoundingBox
.Y1
-= width
;
1698 Arc
->BoundingBox
.Y2
+= width
;
1699 close_box(&Arc
->BoundingBox
);
1701 /* Update the arc end-points */
1702 Arc
->Point1
.X
= Arc
->X
- (double)Arc
->Width
* ca1
;
1703 Arc
->Point1
.Y
= Arc
->Y
+ (double)Arc
->Height
* sa1
;
1704 Arc
->Point2
.X
= Arc
->X
- (double)Arc
->Width
* ca2
;
1705 Arc
->Point2
.Y
= Arc
->Y
+ (double)Arc
->Height
* sa2
;
1709 * \brief Resets the layerstack setting.
1712 ResetStackAndVisibility (void)
1717 for (i
= 0; i
< max_copper_layer
+ SILK_LAYER
; i
++)
1719 if (i
< max_copper_layer
)
1721 PCB
->Data
->Layer
[i
].On
= true;
1723 PCB
->ElementOn
= true;
1724 PCB
->InvisibleObjectsOn
= true;
1729 /* Bring the component group to the front and make it active. */
1730 top_group
= GetLayerGroupNumberBySide (TOP_SIDE
);
1731 ChangeGroupVisibility (PCB
->LayerGroups
.Entries
[top_group
][0], 1, 1);
1735 * \brief Saves the layerstack setting.
1738 SaveStackAndVisibility (void)
1741 static bool run
= false;
1749 if (SavedStack
.cnt
!= 0)
1752 "SaveStackAndVisibility() layerstack was already saved and not"
1753 "yet restored. cnt = %d\n", SavedStack
.cnt
);
1756 for (i
= 0; i
< max_copper_layer
+ SILK_LAYER
; i
++)
1758 if (i
< max_copper_layer
)
1759 SavedStack
.LayerStack
[i
] = LayerStack
[i
];
1760 SavedStack
.LayerOn
[i
] = PCB
->Data
->Layer
[i
].On
;
1762 SavedStack
.ElementOn
= PCB
->ElementOn
;
1763 SavedStack
.InvisibleObjectsOn
= PCB
->InvisibleObjectsOn
;
1764 SavedStack
.PinOn
= PCB
->PinOn
;
1765 SavedStack
.ViaOn
= PCB
->ViaOn
;
1766 SavedStack
.RatOn
= PCB
->RatOn
;
1771 * \brief Restores the layerstack setting.
1774 RestoreStackAndVisibility (void)
1778 if (SavedStack
.cnt
== 0)
1780 fprintf (stderr
, "RestoreStackAndVisibility() layerstack has not"
1781 " been saved. cnt = %d\n", SavedStack
.cnt
);
1784 else if (SavedStack
.cnt
!= 1)
1786 fprintf (stderr
, "RestoreStackAndVisibility() layerstack save count is"
1787 " wrong. cnt = %d\n", SavedStack
.cnt
);
1790 for (i
= 0; i
< max_copper_layer
+ SILK_LAYER
; i
++)
1792 if (i
< max_copper_layer
)
1793 LayerStack
[i
] = SavedStack
.LayerStack
[i
];
1794 PCB
->Data
->Layer
[i
].On
= SavedStack
.LayerOn
[i
];
1796 PCB
->ElementOn
= SavedStack
.ElementOn
;
1797 PCB
->InvisibleObjectsOn
= SavedStack
.InvisibleObjectsOn
;
1798 PCB
->PinOn
= SavedStack
.PinOn
;
1799 PCB
->ViaOn
= SavedStack
.ViaOn
;
1800 PCB
->RatOn
= SavedStack
.RatOn
;
1806 * \brief Returns pointer to current working directory.
1808 * If 'path' is not NULL, then the current working directory is copied
1809 * to the array pointed to by 'path'.
1812 GetWorkingDirectory (char *path
)
1815 return getcwd (path
, MAXPATHLEN
);
1817 /* seems that some BSD releases lack of a prototype for getwd() */
1818 return getwd (path
);
1824 * \brief Write a string to the passed file pointer.
1826 * Some special characters are quoted.
1829 CreateQuotedString (DynamicStringType
*DS
, char *S
)
1832 DSAddCharacter (DS
, '"');
1835 if (*S
== '"' || *S
== '\\')
1836 DSAddCharacter (DS
, '\\');
1837 DSAddCharacter (DS
, *S
++);
1839 DSAddCharacter (DS
, '"');
1843 GetArcEnds (ArcType
*Arc
)
1846 box
.X1
= Arc
->X
- Arc
->Width
* cos (Arc
->StartAngle
* M180
);
1847 box
.Y1
= Arc
->Y
+ Arc
->Height
* sin (Arc
->StartAngle
* M180
);
1848 box
.X2
= Arc
->X
- Arc
->Width
* cos ((Arc
->StartAngle
+ Arc
->Delta
) * M180
);
1849 box
.Y2
= Arc
->Y
+ Arc
->Height
* sin ((Arc
->StartAngle
+ Arc
->Delta
) * M180
);
1855 * \todo Doesn't this belong in change.c ??
1858 ChangeArcAngles (LayerType
*Layer
, ArcType
*a
,
1859 Angle new_sa
, Angle new_da
)
1866 RestoreToPolygon (PCB
->Data
, ARC_TYPE
, Layer
, a
);
1867 r_delete_entry (Layer
->arc_tree
, (BoxType
*) a
);
1868 AddObjectToChangeAnglesUndoList (ARC_TYPE
, a
, a
, a
);
1869 a
->StartAngle
= new_sa
;
1871 SetArcBoundingBox (a
);
1872 r_insert_entry (Layer
->arc_tree
, (BoxType
*) a
, 0);
1873 ClearFromPolygon (PCB
->Data
, ARC_TYPE
, Layer
, a
);
1877 BumpName (char *Name
)
1881 static char temp
[256];
1884 /* seek end of string */
1887 /* back up to potential number */
1888 for (Name
--; isdigit ((int) *Name
); Name
--);
1891 num
= atoi (Name
) + 1;
1896 sprintf (temp
, "%s%d", start
, num
);
1897 /* if this is not our string, put back the blown character */
1904 * \brief Make a unique name for the name on board.
1906 * This can alter the contents of the input string.
1909 UniqueElementName (DataType
*Data
, char *Name
)
1912 /* null strings are ok */
1913 if (!Name
|| !*Name
)
1918 ELEMENT_LOOP (Data
);
1920 if (NAMEONPCB_NAME (element
) &&
1921 NSTRCMP (NAMEONPCB_NAME (element
), Name
) == 0)
1923 Name
= BumpName (Name
);
1936 GetGridLockCoordinates (int type
, void *ptr1
,
1937 void *ptr2
, void *ptr3
, Coord
* x
,
1943 *x
= ((PinType
*) ptr2
)->X
;
1944 *y
= ((PinType
*) ptr2
)->Y
;
1947 *x
= ((LineType
*) ptr2
)->Point1
.X
;
1948 *y
= ((LineType
*) ptr2
)->Point1
.Y
;
1951 case ELEMENTNAME_TYPE
:
1952 *x
= ((TextType
*) ptr2
)->X
;
1953 *y
= ((TextType
*) ptr2
)->Y
;
1956 *x
= ((ElementType
*) ptr2
)->MarkX
;
1957 *y
= ((ElementType
*) ptr2
)->MarkY
;
1960 *x
= ((PolygonType
*) ptr2
)->Points
[0].X
;
1961 *y
= ((PolygonType
*) ptr2
)->Points
[0].Y
;
1964 case LINEPOINT_TYPE
:
1965 case POLYGONPOINT_TYPE
:
1966 *x
= ((PointType
*) ptr3
)->X
;
1967 *y
= ((PointType
*) ptr3
)->Y
;
1973 box
= GetArcEnds ((ArcType
*) ptr2
);
1982 AttachForCopy (Coord PlaceX
, Coord PlaceY
)
1985 Coord mx
= 0, my
= 0;
1987 Crosshair
.AttachedObject
.RubberbandN
= 0;
1988 if (! TEST_FLAG (SNAPPINFLAG
, PCB
))
1990 /* dither the grab point so that the mark, center, etc
1991 * will end up on a grid coordinate
1993 GetGridLockCoordinates (Crosshair
.AttachedObject
.Type
,
1994 Crosshair
.AttachedObject
.Ptr1
,
1995 Crosshair
.AttachedObject
.Ptr2
,
1996 Crosshair
.AttachedObject
.Ptr3
, &mx
, &my
);
1997 mx
= GridFit (mx
, PCB
->Grid
, PCB
->GridOffsetX
) - mx
;
1998 my
= GridFit (my
, PCB
->Grid
, PCB
->GridOffsetY
) - my
;
2000 Crosshair
.AttachedObject
.X
= PlaceX
- mx
;
2001 Crosshair
.AttachedObject
.Y
= PlaceY
- my
;
2002 if (!Marked
.status
|| TEST_FLAG (LOCALREFFLAG
, PCB
))
2003 SetLocalRef (PlaceX
- mx
, PlaceY
- my
, true);
2004 Crosshair
.AttachedObject
.State
= STATE_SECOND
;
2006 /* get boundingbox of object and set cursor range */
2007 box
= GetObjectBoundingBox (Crosshair
.AttachedObject
.Type
,
2008 Crosshair
.AttachedObject
.Ptr1
,
2009 Crosshair
.AttachedObject
.Ptr2
,
2010 Crosshair
.AttachedObject
.Ptr3
);
2011 SetCrosshairRange (Crosshair
.AttachedObject
.X
- box
->X1
,
2012 Crosshair
.AttachedObject
.Y
- box
->Y1
,
2013 PCB
->MaxWidth
- (box
->X2
- Crosshair
.AttachedObject
.X
),
2014 PCB
->MaxHeight
- (box
->Y2
- Crosshair
.AttachedObject
.Y
));
2016 /* get all attached objects if necessary */
2017 if ((Settings
.Mode
!= COPY_MODE
) && TEST_FLAG (RUBBERBANDFLAG
, PCB
))
2018 LookupRubberbandLines (Crosshair
.AttachedObject
.Type
,
2019 Crosshair
.AttachedObject
.Ptr1
,
2020 Crosshair
.AttachedObject
.Ptr2
,
2021 Crosshair
.AttachedObject
.Ptr3
);
2022 if (Settings
.Mode
!= COPY_MODE
&&
2023 (Crosshair
.AttachedObject
.Type
== ELEMENT_TYPE
||
2024 Crosshair
.AttachedObject
.Type
== VIA_TYPE
||
2025 Crosshair
.AttachedObject
.Type
== LINE_TYPE
||
2026 Crosshair
.AttachedObject
.Type
== LINEPOINT_TYPE
))
2027 LookupRatLines (Crosshair
.AttachedObject
.Type
,
2028 Crosshair
.AttachedObject
.Ptr1
,
2029 Crosshair
.AttachedObject
.Ptr2
,
2030 Crosshair
.AttachedObject
.Ptr3
);
2034 * \brief Return nonzero if the given file exists and is readable.
2037 FileExists (const char *name
)
2040 f
= fopen (name
, "r");
2050 Concat (const char *first
, ...)
2056 len
= strlen (first
);
2057 rv
= (char *) malloc (len
+ 1);
2060 va_start (a
, first
);
2063 const char *s
= va_arg (a
, const char *);
2067 rv
= (char *) realloc (rv
, len
+ 1);
2075 mem_any_set (unsigned char *ptr
, int bytes
)
2084 * \brief This just fills in a FlagType with current flags.
2087 MakeFlags (unsigned int flags
)
2090 memset (&rv
, 0, sizeof (rv
));
2096 * \brief This converts old flag bits (from saved PCB files) to new
2100 OldFlags (unsigned int flags
)
2104 memset (&rv
, 0, sizeof (rv
));
2105 /* If we move flag bits around, this is where we map old bits to them. */
2106 rv
.f
= flags
& 0xffff;
2108 for (i
= 0; i
< 8; i
++)
2110 /* use the closest thing to the old thermal style */
2112 rv
.t
[i
/ 2] |= (1 << (4 * (i
% 2)));
2119 AddFlags (FlagType flag
, unsigned int flags
)
2126 MaskFlags (FlagType flag
, unsigned int flags
)
2132 /* Layer Group Functions. */
2135 * \brief Returns group actually moved to (i.e. either group or
2139 MoveLayerToGroup (int layer
, int group
)
2143 if (layer
< 0 || layer
> max_copper_layer
+ 1)
2145 prev
= GetLayerGroupNumberByNumber (layer
);
2146 if ((layer
== bottom_silk_layer
2147 && group
== GetLayerGroupNumberByNumber (top_silk_layer
))
2148 || (layer
== top_silk_layer
2149 && group
== GetLayerGroupNumberByNumber (bottom_silk_layer
))
2150 || (group
< 0 || group
>= max_group
) || (prev
== group
))
2153 /* Remove layer from prev group */
2154 for (j
= i
= 0; i
< PCB
->LayerGroups
.Number
[prev
]; i
++)
2155 if (PCB
->LayerGroups
.Entries
[prev
][i
] != layer
)
2156 PCB
->LayerGroups
.Entries
[prev
][j
++] = PCB
->LayerGroups
.Entries
[prev
][i
];
2157 PCB
->LayerGroups
.Number
[prev
]--;
2159 /* Add layer to new group. */
2160 i
= PCB
->LayerGroups
.Number
[group
]++;
2161 PCB
->LayerGroups
.Entries
[group
][i
] = layer
;
2167 * \brief Returns pointer to private buffer.
2170 LayerGroupsToString (LayerGroupType
*lg
)
2172 #if MAX_ALL_LAYER < 9999
2173 /* Allows for layer numbers 0..9999 */
2174 static char buf
[(MAX_ALL_LAYER
) * 5 + 1];
2179 for (group
= 0; group
< max_group
; group
++)
2180 if (PCB
->LayerGroups
.Number
[group
])
2185 for (entry
= 0; entry
< PCB
->LayerGroups
.Number
[group
]; entry
++)
2187 int layer
= PCB
->LayerGroups
.Entries
[group
][entry
];
2188 if (layer
== top_silk_layer
)
2192 else if (layer
== bottom_silk_layer
)
2198 sprintf (cp
, "%d", layer
+ 1);
2202 if (entry
!= PCB
->LayerGroups
.Number
[group
] - 1)
2213 #ifdef HAVE_GETPWUID
2214 static struct passwd
*pwentry
;
2215 static char *fab_author
= 0;
2219 if (Settings
.FabAuthor
&& Settings
.FabAuthor
[0])
2220 fab_author
= Settings
.FabAuthor
;
2224 char *comma
, *gecos
;
2227 pwentry
= getpwuid (getuid ());
2228 gecos
= pwentry
->pw_gecos
;
2229 comma
= strchr (gecos
, ',');
2231 len
= comma
- gecos
;
2233 len
= strlen (gecos
);
2234 fab_author
= (char *)malloc (len
+ 1);
2237 perror ("pcb: out of memory.\n");
2240 memcpy (fab_author
, gecos
, len
);
2241 fab_author
[len
] = 0;
2252 * \brief Returns NULL if the name isn't found, else the value for that
2256 AttributeGetFromList (AttributeListType
*list
, char *name
)
2259 for (i
=0; i
<list
->Number
; i
++)
2260 if (strcmp (name
, list
->List
[i
].name
) == 0)
2261 return list
->List
[i
].value
;
2266 * \brief Adds an attribute to the list.
2268 * If the attribute already exists, whether it's replaced or a second
2269 * copy added depends on REPLACE.
2271 * \return Non-zero if an existing attribute was replaced.
2274 AttributePutToList (AttributeListType
*list
, const char *name
, const char *value
, int replace
)
2278 /* If we're allowed to replace an existing attribute, see if we
2282 for (i
=0; i
<list
->Number
; i
++)
2283 if (strcmp (name
, list
->List
[i
].name
) == 0)
2285 free (list
->List
[i
].value
);
2286 list
->List
[i
].value
= STRDUP (value
);
2291 /* At this point, we're going to need to add a new attribute to the
2292 list. See if there's room. */
2293 if (list
->Number
>= list
->Max
)
2296 list
->List
= (AttributeType
*) realloc (list
->List
,
2297 list
->Max
* sizeof (AttributeType
));
2300 /* Now add the new attribute. */
2302 list
->List
[i
].name
= STRDUP (name
);
2303 list
->List
[i
].value
= STRDUP (value
);
2309 * \brief Remove an attribute by name.
2312 AttributeRemoveFromList(AttributeListType
*list
, char *name
)
2315 for (i
=0; i
<list
->Number
; i
++)
2316 if (strcmp (name
, list
->List
[i
].name
) == 0)
2318 free (list
->List
[i
].name
);
2319 free (list
->List
[i
].value
);
2320 for (j
=i
; j
<list
->Number
-i
; j
++)
2321 list
->List
[j
] = list
->List
[j
+1];
2327 * \todo In future all use of this should be supplanted by pcb-printf
2330 * These act like you'd expect, except always in the C locale.
2335 static char buf
[100];
2344 d
+= 0.0000005; /* rounding */
2347 sprintf (bufp
, "%d", i
);
2348 bufp
+= strlen (bufp
);
2351 f
= floor (d
* 1000000.0);
2352 sprintf (bufp
, "%06d", f
);
2357 r_delete_element (DataType
* data
, ElementType
* element
)
2359 r_delete_entry (data
->element_tree
, (BoxType
*) element
);
2362 r_delete_entry (data
->pin_tree
, (BoxType
*) pin
);
2367 r_delete_entry (data
->pad_tree
, (BoxType
*) pad
);
2370 ELEMENTTEXT_LOOP (element
);
2372 r_delete_entry (data
->name_tree
[n
], (BoxType
*) text
);
2379 * \brief Returns a string that has a bunch of information about the
2382 * Can be used for things like "about" dialog boxes.
2385 GetInfoString (void)
2389 static DynamicStringType info
;
2390 static int first_time
= 1;
2398 _("This is PCB, an interactive\n"
2399 "printed circuit board editor\n"
2403 "Compiled on " __DATE__
" at " __TIME__
"\n\n"
2404 "by harry eaton\n\n"
2405 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n"
2406 "Copyright (C) harry eaton 1998-2007\n"
2407 "Copyright (C) C. Scott Ananian 2001\n"
2408 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n"
2409 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2411 _("It is licensed under the terms of the GNU\n"
2412 "General Public License version 2\n"
2413 "See the LICENSE file for more information\n\n"
2414 "For more information see:\n"));
2415 DSAddString (&info
, _("PCB homepage: "));
2416 DSAddString (&info
, "http://pcb.geda-project.org\n");
2417 DSAddString (&info
, _("gEDA homepage: "));
2418 DSAddString (&info
, "http://www.geda-project.org\n");
2419 DSAddString (&info
, _("gEDA Wiki: "));
2420 DSAddString (&info
, "http://wiki.geda-project.org\n");
2422 DSAddString (&info
, _("\n----- Compile Time Options -----\n"));
2423 hids
= hid_enumerate ();
2424 DSAddString (&info
, _("GUI:\n"));
2425 for (i
= 0; hids
[i
]; i
++)
2429 DSAddString (&info
, TAB
);
2430 DSAddString (&info
, hids
[i
]->name
);
2431 DSAddString (&info
, " : ");
2432 DSAddString (&info
, hids
[i
]->description
);
2433 DSAddString (&info
, "\n");
2437 DSAddString (&info
, _("Exporters:\n"));
2438 for (i
= 0; hids
[i
]; i
++)
2440 if (hids
[i
]->exporter
)
2442 DSAddString (&info
, TAB
);
2443 DSAddString (&info
, hids
[i
]->name
);
2444 DSAddString (&info
, " : ");
2445 DSAddString (&info
, hids
[i
]->description
);
2446 DSAddString (&info
, "\n");
2450 DSAddString (&info
, _("Printers:\n"));
2451 for (i
= 0; hids
[i
]; i
++)
2453 if (hids
[i
]->printer
)
2455 DSAddString (&info
, TAB
);
2456 DSAddString (&info
, hids
[i
]->name
);
2457 DSAddString (&info
, " : ");
2458 DSAddString (&info
, hids
[i
]->description
);
2459 DSAddString (&info
, "\n");
2468 #ifdef MKDIR_IS_PCBMKDIR
2469 #error "Don't know how to create a directory on this system."
2473 * \brief mkdir() implentation, mostly for plugins, which don't have our
2477 pcb_mkdir (const char *path
, int mode
)
2479 return MKDIR (path
, mode
);
2483 * \brief Returns a best guess about the orientation of an element.
2485 * The value corresponds to the rotation; a difference is the right
2486 * value to pass to RotateElementLowLevel.
2487 * However, the actual value is no indication of absolute rotation; only
2488 * relative rotation is meaningful.
2490 * \return a relative rotation for an element, useful only for comparing
2491 * two similar footprints.
2494 ElementOrientation (ElementType
*e
)
2496 Coord pin1x
, pin1y
, pin2x
, pin2y
, dx
, dy
;
2497 bool found_pin1
= 0;
2498 bool found_pin2
= 0;
2500 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2508 if (NSTRCMP (pin
->Number
, "1") == 0)
2514 else if (NSTRCMP (pin
->Number
, "2") == 0)
2525 if (NSTRCMP (pad
->Number
, "1") == 0)
2527 pin1x
= (pad
->Point1
.X
+ pad
->Point2
.X
) / 2;
2528 pin1y
= (pad
->Point1
.Y
+ pad
->Point2
.Y
) / 2;
2531 else if (NSTRCMP (pad
->Number
, "2") == 0)
2533 pin2x
= (pad
->Point1
.X
+ pad
->Point2
.X
) / 2;
2534 pin2y
= (pad
->Point1
.Y
+ pad
->Point2
.Y
) / 2;
2540 if (found_pin1
&& found_pin2
)
2545 else if (found_pin1
&& (pin1x
|| pin1y
))
2550 else if (found_pin2
&& (pin2x
|| pin2y
))
2558 if (abs(dx
) > abs(dy
))
2559 return dx
> 0 ? 0 : 2;
2560 return dy
> 0 ? 3 : 1;
2564 ActionListRotations(int argc
, char **argv
, Coord x
, Coord y
)
2566 ELEMENT_LOOP (PCB
->Data
);
2568 printf("%d %s\n", ElementOrientation(element
), NAMEONPCB_NAME(element
));
2575 HID_Action misc_action_list
[] = {
2576 {"ListRotations", 0, ActionListRotations
,
2580 REGISTER_ACTIONS (misc_action_list
)