4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
28 /* misc functions used by several modules
43 #include <sys/param.h>
45 #include <sys/types.h>
57 #include "crosshair.h"
66 #include "pcb-printf.h"
71 #include "rubberband.h"
77 #ifdef HAVE_LIBDMALLOC
81 /* forward declarations */
82 static char *BumpName (char *);
83 static void GetGridLockCoordinates (int, void *, void *, void *,
90 * Used by SaveStackAndVisibility() and
91 * RestoreStackAndVisibility()
96 bool ElementOn
, InvisibleObjectsOn
, PinOn
, ViaOn
, RatOn
;
97 int LayerStack
[MAX_LAYER
];
98 bool LayerOn
[MAX_LAYER
];
102 /* Distance() should be used so that there is only one
103 * place to deal with overflow/precision errors
106 Distance (double x1
, double y1
, double x2
, double y2
)
108 double delta_x
= (x2
- x1
);
109 double delta_y
= (y2
- y1
);
110 return sqrt(delta_x
* delta_x
+ delta_y
* delta_y
);
113 /* Bring an angle into [0, 360) range */
115 NormalizeAngle (Angle a
)
124 /* Get Value returns a numeric value passed from the string and sets the
125 * bool variable absolute to false if it leads with a +/- character
128 GetValue (const char *val
, const char *units
, bool * absolute
)
130 return GetValueEx(val
, units
, absolute
, NULL
, "cmil");
134 GetValueEx (const char *val
, const char *units
, bool * absolute
, UnitList extra_units
, const char *default_unit
)
141 /* Allow NULL to be passed for absolute */
145 /* if the first character is a sign we have to add the
146 * value to the current one
151 if (sscanf (val
+1, "%lf%n", &value
, &n
) < 1)
157 if (isdigit ((int) *val
))
161 if (sscanf (val
, "%lf%n", &value
, &n
) < 1)
167 while (units
&& *units
== ' ')
173 const Unit
*unit
= get_unit_struct (units
);
176 value
= unit_to_coord (unit
, value
);
181 for (i
= 0; *extra_units
[i
].suffix
; ++i
)
183 if (strncmp (units
, extra_units
[i
].suffix
, strlen(extra_units
[i
].suffix
)) == 0)
185 value
*= extra_units
[i
].scale
;
186 if (extra_units
[i
].flags
& UNIT_PERCENT
)
193 /* Apply default unit */
194 if (!scaled
&& default_unit
&& *default_unit
)
197 const Unit
*unit
= get_unit_struct (default_unit
);
199 for (i
= 0; *extra_units
[i
].suffix
; ++i
)
200 if (strcmp (extra_units
[i
].suffix
, default_unit
) == 0)
202 value
*= extra_units
[i
].scale
;
203 if (extra_units
[i
].flags
& UNIT_PERCENT
)
207 if (!scaled
&& unit
!= NULL
)
208 value
= unit_to_coord (unit
, value
);
214 /* ---------------------------------------------------------------------------
215 * sets the bounding box of a point (which is silly)
218 SetPointBoundingBox (PointType
*Pnt
)
220 Pnt
->X2
= Pnt
->X
+ 1;
221 Pnt
->Y2
= Pnt
->Y
+ 1;
224 /* ---------------------------------------------------------------------------
225 * sets the bounding box of a pin or via
228 SetPinBoundingBox (PinType
*Pin
)
232 /* the bounding box covers the extent of influence
233 * so it must include the clearance values too
235 width
= MAX (Pin
->Clearance
+ PIN_SIZE (Pin
), Pin
->Mask
) / 2;
237 /* Adjust for our discrete polygon approximation */
238 width
= (double)width
* POLY_CIRC_RADIUS_ADJ
+ 0.5;
240 Pin
->BoundingBox
.X1
= Pin
->X
- width
;
241 Pin
->BoundingBox
.Y1
= Pin
->Y
- width
;
242 Pin
->BoundingBox
.X2
= Pin
->X
+ width
;
243 Pin
->BoundingBox
.Y2
= Pin
->Y
+ width
;
244 close_box(&Pin
->BoundingBox
);
247 /* ---------------------------------------------------------------------------
248 * sets the bounding box of a pad
251 SetPadBoundingBox (PadType
*Pad
)
257 /* the bounding box covers the extent of influence
258 * so it must include the clearance values too
260 width
= (Pad
->Thickness
+ Pad
->Clearance
+ 1) / 2;
261 width
= MAX (width
, (Pad
->Mask
+ 1) / 2);
262 deltax
= Pad
->Point2
.X
- Pad
->Point1
.X
;
263 deltay
= Pad
->Point2
.Y
- Pad
->Point1
.Y
;
265 if (TEST_FLAG (SQUAREFLAG
, Pad
) && deltax
!= 0 && deltay
!= 0)
267 /* slanted square pad */
271 /* T is a vector half a thickness long, in the direction of
272 one of the corners. */
273 theta
= atan2 (deltay
, deltax
);
274 btx
= width
* cos (theta
+ M_PI
/4) * sqrt(2.0);
275 bty
= width
* sin (theta
+ M_PI
/4) * sqrt(2.0);
278 Pad
->BoundingBox
.X1
= MIN (MIN (Pad
->Point1
.X
- btx
, Pad
->Point1
.X
- bty
),
279 MIN (Pad
->Point2
.X
+ btx
, Pad
->Point2
.X
+ bty
));
280 Pad
->BoundingBox
.X2
= MAX (MAX (Pad
->Point1
.X
- btx
, Pad
->Point1
.X
- bty
),
281 MAX (Pad
->Point2
.X
+ btx
, Pad
->Point2
.X
+ bty
));
282 Pad
->BoundingBox
.Y1
= MIN (MIN (Pad
->Point1
.Y
+ btx
, Pad
->Point1
.Y
- bty
),
283 MIN (Pad
->Point2
.Y
- btx
, Pad
->Point2
.Y
+ bty
));
284 Pad
->BoundingBox
.Y2
= MAX (MAX (Pad
->Point1
.Y
+ btx
, Pad
->Point1
.Y
- bty
),
285 MAX (Pad
->Point2
.Y
- btx
, Pad
->Point2
.Y
+ bty
));
289 /* Adjust for our discrete polygon approximation */
290 width
= (double)width
* POLY_CIRC_RADIUS_ADJ
+ 0.5;
292 Pad
->BoundingBox
.X1
= MIN (Pad
->Point1
.X
, Pad
->Point2
.X
) - width
;
293 Pad
->BoundingBox
.X2
= MAX (Pad
->Point1
.X
, Pad
->Point2
.X
) + width
;
294 Pad
->BoundingBox
.Y1
= MIN (Pad
->Point1
.Y
, Pad
->Point2
.Y
) - width
;
295 Pad
->BoundingBox
.Y2
= MAX (Pad
->Point1
.Y
, Pad
->Point2
.Y
) + width
;
297 close_box(&Pad
->BoundingBox
);
300 /* ---------------------------------------------------------------------------
301 * sets the bounding box of a line
304 SetLineBoundingBox (LineType
*Line
)
306 Coord width
= (Line
->Thickness
+ Line
->Clearance
+ 1) / 2;
308 /* Adjust for our discrete polygon approximation */
309 width
= (double)width
* POLY_CIRC_RADIUS_ADJ
+ 0.5;
311 Line
->BoundingBox
.X1
= MIN (Line
->Point1
.X
, Line
->Point2
.X
) - width
;
312 Line
->BoundingBox
.X2
= MAX (Line
->Point1
.X
, Line
->Point2
.X
) + width
;
313 Line
->BoundingBox
.Y1
= MIN (Line
->Point1
.Y
, Line
->Point2
.Y
) - width
;
314 Line
->BoundingBox
.Y2
= MAX (Line
->Point1
.Y
, Line
->Point2
.Y
) + width
;
315 close_box(&Line
->BoundingBox
);
316 SetPointBoundingBox (&Line
->Point1
);
317 SetPointBoundingBox (&Line
->Point2
);
320 /* ---------------------------------------------------------------------------
321 * sets the bounding box of a polygons
324 SetPolygonBoundingBox (PolygonType
*Polygon
)
326 Polygon
->BoundingBox
.X1
= Polygon
->BoundingBox
.Y1
= MAX_COORD
;
327 Polygon
->BoundingBox
.X2
= Polygon
->BoundingBox
.Y2
= 0;
328 POLYGONPOINT_LOOP (Polygon
);
330 MAKEMIN (Polygon
->BoundingBox
.X1
, point
->X
);
331 MAKEMIN (Polygon
->BoundingBox
.Y1
, point
->Y
);
332 MAKEMAX (Polygon
->BoundingBox
.X2
, point
->X
);
333 MAKEMAX (Polygon
->BoundingBox
.Y2
, point
->Y
);
335 /* boxes don't include the lower right corner */
336 close_box(&Polygon
->BoundingBox
);
340 /* ---------------------------------------------------------------------------
341 * sets the bounding box of an elements
344 SetElementBoundingBox (DataType
*Data
, ElementType
*Element
,
349 if (Data
&& Data
->element_tree
)
350 r_delete_entry (Data
->element_tree
, (BoxType
*) Element
);
351 /* first update the text objects */
352 ELEMENTTEXT_LOOP (Element
);
354 if (Data
&& Data
->name_tree
[n
])
355 r_delete_entry (Data
->name_tree
[n
], (BoxType
*) text
);
356 SetTextBoundingBox (Font
, text
);
357 if (Data
&& !Data
->name_tree
[n
])
358 Data
->name_tree
[n
] = r_create_tree (NULL
, 0, 0);
360 r_insert_entry (Data
->name_tree
[n
], (BoxType
*) text
, 0);
364 /* do not include the elementnames bounding box which
365 * is handled separately
367 box
= &Element
->BoundingBox
;
368 vbox
= &Element
->VBox
;
369 box
->X1
= box
->Y1
= MAX_COORD
;
370 box
->X2
= box
->Y2
= 0;
371 ELEMENTLINE_LOOP (Element
);
373 SetLineBoundingBox (line
);
374 MAKEMIN (box
->X1
, line
->Point1
.X
- (line
->Thickness
+ 1) / 2);
375 MAKEMIN (box
->Y1
, line
->Point1
.Y
- (line
->Thickness
+ 1) / 2);
376 MAKEMIN (box
->X1
, line
->Point2
.X
- (line
->Thickness
+ 1) / 2);
377 MAKEMIN (box
->Y1
, line
->Point2
.Y
- (line
->Thickness
+ 1) / 2);
378 MAKEMAX (box
->X2
, line
->Point1
.X
+ (line
->Thickness
+ 1) / 2);
379 MAKEMAX (box
->Y2
, line
->Point1
.Y
+ (line
->Thickness
+ 1) / 2);
380 MAKEMAX (box
->X2
, line
->Point2
.X
+ (line
->Thickness
+ 1) / 2);
381 MAKEMAX (box
->Y2
, line
->Point2
.Y
+ (line
->Thickness
+ 1) / 2);
386 SetArcBoundingBox (arc
);
387 MAKEMIN (box
->X1
, arc
->BoundingBox
.X1
);
388 MAKEMIN (box
->Y1
, arc
->BoundingBox
.Y1
);
389 MAKEMAX (box
->X2
, arc
->BoundingBox
.X2
);
390 MAKEMAX (box
->Y2
, arc
->BoundingBox
.Y2
);
396 if (Data
&& Data
->pin_tree
)
397 r_delete_entry (Data
->pin_tree
, (BoxType
*) pin
);
398 SetPinBoundingBox (pin
);
402 Data
->pin_tree
= r_create_tree (NULL
, 0, 0);
403 r_insert_entry (Data
->pin_tree
, (BoxType
*) pin
, 0);
405 MAKEMIN (box
->X1
, pin
->BoundingBox
.X1
);
406 MAKEMIN (box
->Y1
, pin
->BoundingBox
.Y1
);
407 MAKEMAX (box
->X2
, pin
->BoundingBox
.X2
);
408 MAKEMAX (box
->Y2
, pin
->BoundingBox
.Y2
);
409 MAKEMIN (vbox
->X1
, pin
->X
- pin
->Thickness
/ 2);
410 MAKEMIN (vbox
->Y1
, pin
->Y
- pin
->Thickness
/ 2);
411 MAKEMAX (vbox
->X2
, pin
->X
+ pin
->Thickness
/ 2);
412 MAKEMAX (vbox
->Y2
, pin
->Y
+ pin
->Thickness
/ 2);
417 if (Data
&& Data
->pad_tree
)
418 r_delete_entry (Data
->pad_tree
, (BoxType
*) pad
);
419 SetPadBoundingBox (pad
);
423 Data
->pad_tree
= r_create_tree (NULL
, 0, 0);
424 r_insert_entry (Data
->pad_tree
, (BoxType
*) pad
, 0);
426 MAKEMIN (box
->X1
, pad
->BoundingBox
.X1
);
427 MAKEMIN (box
->Y1
, pad
->BoundingBox
.Y1
);
428 MAKEMAX (box
->X2
, pad
->BoundingBox
.X2
);
429 MAKEMAX (box
->Y2
, pad
->BoundingBox
.Y2
);
431 MIN (pad
->Point1
.X
, pad
->Point2
.X
) - pad
->Thickness
/ 2);
433 MIN (pad
->Point1
.Y
, pad
->Point2
.Y
) - pad
->Thickness
/ 2);
435 MAX (pad
->Point1
.X
, pad
->Point2
.X
) + pad
->Thickness
/ 2);
437 MAX (pad
->Point1
.Y
, pad
->Point2
.Y
) + pad
->Thickness
/ 2);
440 /* now we set the EDGE2FLAG of the pad if Point2
441 * is closer to the outside edge than Point1
445 if (pad
->Point1
.Y
== pad
->Point2
.Y
)
448 if (box
->X2
- pad
->Point2
.X
< pad
->Point1
.X
- box
->X1
)
449 SET_FLAG (EDGE2FLAG
, pad
);
451 CLEAR_FLAG (EDGE2FLAG
, pad
);
456 if (box
->Y2
- pad
->Point2
.Y
< pad
->Point1
.Y
- box
->Y1
)
457 SET_FLAG (EDGE2FLAG
, pad
);
459 CLEAR_FLAG (EDGE2FLAG
, pad
);
464 /* mark pins with component orientation */
465 if ((box
->X2
- box
->X1
) > (box
->Y2
- box
->Y1
))
469 SET_FLAG (EDGE2FLAG
, pin
);
477 CLEAR_FLAG (EDGE2FLAG
, pin
);
483 if (Data
&& !Data
->element_tree
)
484 Data
->element_tree
= r_create_tree (NULL
, 0, 0);
486 r_insert_entry (Data
->element_tree
, box
, 0);
489 /* ---------------------------------------------------------------------------
490 * creates the bounding box of a text object
493 SetTextBoundingBox (FontType
*FontPtr
, TextType
*Text
)
495 SymbolType
*symbol
= FontPtr
->Symbol
;
496 unsigned char *s
= (unsigned char *) Text
->TextString
;
500 Coord minx
, miny
, maxx
, maxy
, tx
;
501 Coord min_final_radius
;
502 Coord min_unscaled_radius
;
503 bool first_time
= true;
505 minx
= miny
= maxx
= maxy
= tx
= 0;
507 /* Calculate the bounding box based on the larger of the thicknesses
508 * the text might clamped at on silk or copper layers.
510 min_final_radius
= MAX (PCB
->minWid
, PCB
->minSlk
) / 2;
512 /* Pre-adjust the line radius for the fact we are initially computing the
513 * bounds of the un-scaled text, and the thickness clamping applies to
516 min_unscaled_radius
= UNSCALE_TEXT (min_final_radius
, Text
->Scale
);
518 /* calculate size of the bounding box */
521 if (*s
<= MAX_FONTPOSITION
&& symbol
[*s
].Valid
)
523 LineType
*line
= symbol
[*s
].Line
;
524 for (i
= 0; i
< symbol
[*s
].LineN
; line
++, i
++)
526 /* Clamp the width of text lines at the minimum thickness.
527 * NB: Divide 4 in thickness calculation is comprised of a factor
528 * of 1/2 to get a radius from the center-line, and a factor
529 * of 1/2 because some stupid reason we render our glyphs
530 * at half their defined stroke-width.
532 Coord unscaled_radius
= MAX (min_unscaled_radius
, line
->Thickness
/ 4);
536 minx
= maxx
= line
->Point1
.X
;
537 miny
= maxy
= line
->Point1
.Y
;
541 minx
= MIN (minx
, line
->Point1
.X
- unscaled_radius
+ tx
);
542 miny
= MIN (miny
, line
->Point1
.Y
- unscaled_radius
);
543 minx
= MIN (minx
, line
->Point2
.X
- unscaled_radius
+ tx
);
544 miny
= MIN (miny
, line
->Point2
.Y
- unscaled_radius
);
545 maxx
= MAX (maxx
, line
->Point1
.X
+ unscaled_radius
+ tx
);
546 maxy
= MAX (maxy
, line
->Point1
.Y
+ unscaled_radius
);
547 maxx
= MAX (maxx
, line
->Point2
.X
+ unscaled_radius
+ tx
);
548 maxy
= MAX (maxy
, line
->Point2
.Y
+ unscaled_radius
);
550 space
= symbol
[*s
].Delta
;
554 BoxType
*ds
= &FontPtr
->DefaultSymbol
;
555 Coord w
= ds
->X2
- ds
->X1
;
557 minx
= MIN (minx
, ds
->X1
+ tx
);
558 miny
= MIN (miny
, ds
->Y1
);
559 minx
= MIN (minx
, ds
->X2
+ tx
);
560 miny
= MIN (miny
, ds
->Y2
);
561 maxx
= MAX (maxx
, ds
->X1
+ tx
);
562 maxy
= MAX (maxy
, ds
->Y1
);
563 maxx
= MAX (maxx
, ds
->X2
+ tx
);
564 maxy
= MAX (maxy
, ds
->Y2
);
568 tx
+= symbol
[*s
].Width
+ space
;
572 minx
= SCALE_TEXT (minx
, Text
->Scale
);
573 miny
= SCALE_TEXT (miny
, Text
->Scale
);
574 maxx
= SCALE_TEXT (maxx
, Text
->Scale
);
575 maxy
= SCALE_TEXT (maxy
, Text
->Scale
);
577 /* set upper-left and lower-right corner;
578 * swap coordinates if necessary (origin is already in 'swapped')
582 if (TEST_FLAG (ONSOLDERFLAG
, Text
))
584 Text
->BoundingBox
.X1
= Text
->X
+ minx
;
585 Text
->BoundingBox
.Y1
= Text
->Y
- miny
;
586 Text
->BoundingBox
.X2
= Text
->X
+ maxx
;
587 Text
->BoundingBox
.Y2
= Text
->Y
- maxy
;
588 RotateBoxLowLevel (&Text
->BoundingBox
, Text
->X
, Text
->Y
,
589 (4 - Text
->Direction
) & 0x03);
593 Text
->BoundingBox
.X1
= Text
->X
+ minx
;
594 Text
->BoundingBox
.Y1
= Text
->Y
+ miny
;
595 Text
->BoundingBox
.X2
= Text
->X
+ maxx
;
596 Text
->BoundingBox
.Y2
= Text
->Y
+ maxy
;
597 RotateBoxLowLevel (&Text
->BoundingBox
,
598 Text
->X
, Text
->Y
, Text
->Direction
);
601 /* the bounding box covers the extent of influence
602 * so it must include the clearance values too
604 Text
->BoundingBox
.X1
-= PCB
->Bloat
;
605 Text
->BoundingBox
.Y1
-= PCB
->Bloat
;
606 Text
->BoundingBox
.X2
+= PCB
->Bloat
;
607 Text
->BoundingBox
.Y2
+= PCB
->Bloat
;
608 close_box(&Text
->BoundingBox
);
611 /* ---------------------------------------------------------------------------
612 * returns true if data area is empty
615 IsDataEmpty (DataType
*Data
)
620 hasNoObjects
= (Data
->ViaN
== 0);
621 hasNoObjects
&= (Data
->ElementN
== 0);
622 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
623 hasNoObjects
= hasNoObjects
&&
624 Data
->Layer
[i
].LineN
== 0 &&
625 Data
->Layer
[i
].ArcN
== 0 &&
626 Data
->Layer
[i
].TextN
== 0 && Data
->Layer
[i
].PolygonN
== 0;
627 return (hasNoObjects
);
631 FlagIsDataEmpty (int parm
)
633 int i
= IsDataEmpty (PCB
->Data
);
634 return parm
? !i
: i
;
637 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
638 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
641 IsLayerEmpty (LayerType
*layer
)
643 return (layer
->LineN
== 0
645 && layer
->PolygonN
== 0
646 && layer
->ArcN
== 0);
650 IsLayerNumEmpty (int num
)
652 return IsLayerEmpty (PCB
->Data
->Layer
+num
);
656 IsLayerGroupEmpty (int num
)
659 for (i
=0; i
<PCB
->LayerGroups
.Number
[num
]; i
++)
660 if (!IsLayerNumEmpty (PCB
->LayerGroups
.Entries
[num
][i
]))
666 IsPasteEmpty (int side
)
668 bool paste_empty
= true;
669 ALLPAD_LOOP (PCB
->Data
);
671 if (ON_SIDE (pad
, side
) && !TEST_FLAG (NOPASTEFLAG
, pad
) && pad
->Mask
> 0)
689 hole_counting_callback (const BoxType
* b
, void *cl
)
691 PinType
*pin
= (PinType
*) b
;
692 HoleCountStruct
*hcs
= (HoleCountStruct
*) cl
;
693 if (TEST_FLAG (HOLEFLAG
, pin
))
700 /* ---------------------------------------------------------------------------
701 * counts the number of plated and unplated holes in the design within
702 * a given area of the board. To count for the whole board, pass NULL
706 CountHoles (int *plated
, int *unplated
, const BoxType
*within_area
)
708 HoleCountStruct hcs
= {0, 0};
710 r_search (PCB
->Data
->pin_tree
, within_area
, NULL
, hole_counting_callback
, &hcs
);
711 r_search (PCB
->Data
->via_tree
, within_area
, NULL
, hole_counting_callback
, &hcs
);
713 if (plated
!= NULL
) *plated
= hcs
.nplated
;
714 if (unplated
!= NULL
) *unplated
= hcs
.nunplated
;
718 /* ---------------------------------------------------------------------------
719 * gets minimum and maximum coordinates
720 * returns NULL if layout is empty
723 GetDataBoundingBox (DataType
*Data
)
726 /* FIX ME: use r_search to do this much faster */
728 /* preset identifiers with highest and lowest possible values */
729 box
.X1
= box
.Y1
= MAX_COORD
;
730 box
.X2
= box
.Y2
= -MAX_COORD
;
732 /* now scan for the lowest/highest X and Y coordinate */
735 box
.X1
= MIN (box
.X1
, via
->X
- via
->Thickness
/ 2);
736 box
.Y1
= MIN (box
.Y1
, via
->Y
- via
->Thickness
/ 2);
737 box
.X2
= MAX (box
.X2
, via
->X
+ via
->Thickness
/ 2);
738 box
.Y2
= MAX (box
.Y2
, via
->Y
+ via
->Thickness
/ 2);
743 box
.X1
= MIN (box
.X1
, element
->BoundingBox
.X1
);
744 box
.Y1
= MIN (box
.Y1
, element
->BoundingBox
.Y1
);
745 box
.X2
= MAX (box
.X2
, element
->BoundingBox
.X2
);
746 box
.Y2
= MAX (box
.Y2
, element
->BoundingBox
.Y2
);
748 TextType
*text
= &NAMEONPCB_TEXT (element
);
749 box
.X1
= MIN (box
.X1
, text
->BoundingBox
.X1
);
750 box
.Y1
= MIN (box
.Y1
, text
->BoundingBox
.Y1
);
751 box
.X2
= MAX (box
.X2
, text
->BoundingBox
.X2
);
752 box
.Y2
= MAX (box
.Y2
, text
->BoundingBox
.Y2
);
758 box
.X1
= MIN (box
.X1
, line
->Point1
.X
- line
->Thickness
/ 2);
759 box
.Y1
= MIN (box
.Y1
, line
->Point1
.Y
- line
->Thickness
/ 2);
760 box
.X1
= MIN (box
.X1
, line
->Point2
.X
- line
->Thickness
/ 2);
761 box
.Y1
= MIN (box
.Y1
, line
->Point2
.Y
- line
->Thickness
/ 2);
762 box
.X2
= MAX (box
.X2
, line
->Point1
.X
+ line
->Thickness
/ 2);
763 box
.Y2
= MAX (box
.Y2
, line
->Point1
.Y
+ line
->Thickness
/ 2);
764 box
.X2
= MAX (box
.X2
, line
->Point2
.X
+ line
->Thickness
/ 2);
765 box
.Y2
= MAX (box
.Y2
, line
->Point2
.Y
+ line
->Thickness
/ 2);
770 box
.X1
= MIN (box
.X1
, arc
->BoundingBox
.X1
);
771 box
.Y1
= MIN (box
.Y1
, arc
->BoundingBox
.Y1
);
772 box
.X2
= MAX (box
.X2
, arc
->BoundingBox
.X2
);
773 box
.Y2
= MAX (box
.Y2
, arc
->BoundingBox
.Y2
);
778 box
.X1
= MIN (box
.X1
, text
->BoundingBox
.X1
);
779 box
.Y1
= MIN (box
.Y1
, text
->BoundingBox
.Y1
);
780 box
.X2
= MAX (box
.X2
, text
->BoundingBox
.X2
);
781 box
.Y2
= MAX (box
.Y2
, text
->BoundingBox
.Y2
);
784 ALLPOLYGON_LOOP (Data
);
786 box
.X1
= MIN (box
.X1
, polygon
->BoundingBox
.X1
);
787 box
.Y1
= MIN (box
.Y1
, polygon
->BoundingBox
.Y1
);
788 box
.X2
= MAX (box
.X2
, polygon
->BoundingBox
.X2
);
789 box
.Y2
= MAX (box
.Y2
, polygon
->BoundingBox
.Y2
);
792 return (IsDataEmpty (Data
) ? NULL
: &box
);
795 /* ---------------------------------------------------------------------------
796 * centers the displayed PCB around the specified point (X,Y)
799 CenterDisplay (Coord X
, Coord Y
)
801 Coord save_grid
= PCB
->Grid
;
803 if (MoveCrosshairAbsolute (X
, Y
))
804 notify_crosshair_change (true);
805 gui
->set_crosshair (Crosshair
.X
, Crosshair
.Y
, HID_SC_WARP_POINTER
);
806 PCB
->Grid
= save_grid
;
809 /* ---------------------------------------------------------------------------
810 * transforms symbol coordinates so that the left edge of each symbol
811 * is at the zero position. The y coordinates are moved so that min(y) = 0
815 SetFontInfo (FontType
*Ptr
)
820 Coord totalminy
= MAX_COORD
;
822 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
823 * maximum cell width and height
824 * minimum x and y position of all lines
826 Ptr
->MaxWidth
= DEFAULT_CELLSIZE
;
827 Ptr
->MaxHeight
= DEFAULT_CELLSIZE
;
828 for (i
= 0, symbol
= Ptr
->Symbol
; i
<= MAX_FONTPOSITION
; i
++, symbol
++)
830 Coord minx
, miny
, maxx
, maxy
;
832 /* next one if the index isn't used or symbol is empty (SPACE) */
833 if (!symbol
->Valid
|| !symbol
->LineN
)
836 minx
= miny
= MAX_COORD
;
838 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
840 minx
= MIN (minx
, line
->Point1
.X
);
841 miny
= MIN (miny
, line
->Point1
.Y
);
842 minx
= MIN (minx
, line
->Point2
.X
);
843 miny
= MIN (miny
, line
->Point2
.Y
);
844 maxx
= MAX (maxx
, line
->Point1
.X
);
845 maxy
= MAX (maxy
, line
->Point1
.Y
);
846 maxx
= MAX (maxx
, line
->Point2
.X
);
847 maxy
= MAX (maxy
, line
->Point2
.Y
);
850 /* move symbol to left edge */
851 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
852 MOVE_LINE_LOWLEVEL (line
, -minx
, 0);
854 /* set symbol bounding box with a minimum cell size of (1,1) */
855 symbol
->Width
= maxx
- minx
+ 1;
856 symbol
->Height
= maxy
+ 1;
858 /* check total min/max */
859 Ptr
->MaxWidth
= MAX (Ptr
->MaxWidth
, symbol
->Width
);
860 Ptr
->MaxHeight
= MAX (Ptr
->MaxHeight
, symbol
->Height
);
861 totalminy
= MIN (totalminy
, miny
);
864 /* move coordinate system to the upper edge (lowest y on screen) */
865 for (i
= 0, symbol
= Ptr
->Symbol
; i
<= MAX_FONTPOSITION
; i
++, symbol
++)
868 symbol
->Height
-= totalminy
;
869 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
870 MOVE_LINE_LOWLEVEL (line
, 0, -totalminy
);
873 /* setup the box for the default symbol */
874 Ptr
->DefaultSymbol
.X1
= Ptr
->DefaultSymbol
.Y1
= 0;
875 Ptr
->DefaultSymbol
.X2
= Ptr
->DefaultSymbol
.X1
+ Ptr
->MaxWidth
;
876 Ptr
->DefaultSymbol
.Y2
= Ptr
->DefaultSymbol
.Y1
+ Ptr
->MaxHeight
;
880 GetNum (char **s
, const char *default_unit
)
883 Coord ret_val
= GetValueEx (*s
, NULL
, NULL
, NULL
, default_unit
);
884 /* Advance pointer */
885 while(isalnum(**s
) || **s
== '.')
890 /*! \brief Serializes the route style list
891 * \par Function Description
892 * Right now n_styles should always be set to NUM_STYLES,
893 * since that is the number of route styles ParseRouteString()
897 make_route_string (RouteStyleType rs
[], int n_styles
)
899 GString
*str
= g_string_new ("");
902 for (i
= 0; i
< n_styles
; ++i
)
904 char *r_string
= pcb_g_strdup_printf ("%s,%mc,%mc,%mc,%mc", rs
[i
].Name
,
905 rs
[i
].Thick
, rs
[i
].Diameter
,
906 rs
[i
].Hole
, rs
[i
].Keepaway
);
908 g_string_append_c (str
, ':');
909 g_string_append (str
, r_string
);
911 return g_string_free (str
, FALSE
);
914 /* ----------------------------------------------------------------------
915 * parses the routes definition string which is a colon separated list of
916 * comma separated Name, Dimension, Dimension, Dimension, Dimension
917 * e.g. Signal,20,40,20,10:Power,40,60,28,10:...
920 ParseRouteString (char *s
, RouteStyleType
*routeStyle
, const char *default_unit
)
925 memset (routeStyle
, 0, NUM_STYLES
* sizeof (RouteStyleType
));
926 for (style
= 0; style
< NUM_STYLES
; style
++, routeStyle
++)
928 while (*s
&& isspace ((int) *s
))
930 for (i
= 0; *s
&& *s
!= ','; i
++)
933 routeStyle
->Name
= strdup (Name
);
934 if (!isdigit ((int) *++s
))
936 routeStyle
->Thick
= GetNum (&s
, default_unit
);
937 while (*s
&& isspace ((int) *s
))
941 while (*s
&& isspace ((int) *s
))
943 if (!isdigit ((int) *s
))
945 routeStyle
->Diameter
= GetNum (&s
, default_unit
);
946 while (*s
&& isspace ((int) *s
))
950 while (*s
&& isspace ((int) *s
))
952 if (!isdigit ((int) *s
))
954 routeStyle
->Hole
= GetNum (&s
, default_unit
);
955 /* for backwards-compatibility, we use a 10-mil default
956 * for styles which omit the keepaway specification. */
958 routeStyle
->Keepaway
= MIL_TO_COORD(10);
962 while (*s
&& isspace ((int) *s
))
964 if (!isdigit ((int) *s
))
966 routeStyle
->Keepaway
= GetNum (&s
, default_unit
);
967 while (*s
&& isspace ((int) *s
))
970 if (style
< NUM_STYLES
- 1)
972 while (*s
&& isspace ((int) *s
))
981 memset (routeStyle
, 0, NUM_STYLES
* sizeof (RouteStyleType
));
985 /* ----------------------------------------------------------------------
986 * parses the group definition string which is a colon separated list of
987 * comma separated layer numbers (1,2,b:4,6,8,t)
990 ParseGroupString (char *group_string
, LayerGroupType
*LayerGroup
, int *LayerN
)
993 int group
, member
, layer
;
994 bool c_set
= false, /* flags for the two special layers to */
995 s_set
= false; /* provide a default setting for old formats */
996 int groupnum
[MAX_LAYER
+ 2];
1000 /* Deterimine the maximum layer number */
1001 for (s
= group_string
; s
&& *s
; s
++)
1003 while (*s
&& isspace ((int) *s
))
1019 if (!isdigit ((int) *s
))
1021 *LayerN
= MAX (*LayerN
, atoi (s
));
1025 while (*++s
&& isdigit ((int) *s
));
1027 /* ignore white spaces and check for separator */
1028 while (*s
&& isspace ((int) *s
))
1034 if (*s
!= ':' && *s
!= ',')
1039 memset (LayerGroup
, 0, sizeof (LayerGroupType
));
1041 /* Clear assignments */
1042 for (layer
= 0; layer
< MAX_LAYER
+ 2; layer
++)
1043 groupnum
[layer
] = -1;
1045 /* loop over all groups */
1046 for (s
= group_string
, group
= 0;
1047 s
&& *s
&& group
< *LayerN
;
1050 while (*s
&& isspace ((int) *s
))
1053 /* loop over all group members */
1054 for (member
= 0; *s
; s
++)
1056 /* ignore white spaces and get layernumber */
1057 while (*s
&& isspace ((int) *s
))
1065 layer
= *LayerN
+ TOP_SILK_LAYER
;
1073 layer
= *LayerN
+ BOTTOM_SILK_LAYER
;
1078 layer
= atoi (s
) - 1;
1081 if (layer
> *LayerN
+ MAX (BOTTOM_SILK_LAYER
, TOP_SILK_LAYER
) ||
1082 member
>= *LayerN
+ 1)
1084 groupnum
[layer
] = group
;
1085 LayerGroup
->Entries
[group
][member
++] = layer
;
1086 while (*++s
&& isdigit ((int) *s
));
1088 /* ignore white spaces and check for separator */
1089 while (*s
&& isspace ((int) *s
))
1091 if (!*s
|| *s
== ':')
1094 LayerGroup
->Number
[group
] = member
;
1099 /* If no explicit solder or component layer group was found in the layer
1100 * group string, make group 0 the bottom side, and group 1 the top side.
1101 * This is done by assigning the relevant silkscreen layers to those groups.
1104 LayerGroup
->Entries
[0][LayerGroup
->Number
[0]++] = *LayerN
+ BOTTOM_SILK_LAYER
;
1106 LayerGroup
->Entries
[1][LayerGroup
->Number
[1]++] = *LayerN
+ TOP_SILK_LAYER
;
1108 /* Assign a unique layer group to each layer that was not explicitly
1109 * assigned a particular group by its presence in the layer group string.
1111 for (layer
= 0; layer
< *LayerN
&& group
< *LayerN
; layer
++)
1112 if (groupnum
[layer
] == -1)
1114 LayerGroup
->Entries
[group
][0] = layer
;
1115 LayerGroup
->Number
[group
] = 1;
1120 /* reset structure on error */
1122 memset (LayerGroup
, 0, sizeof (LayerGroupType
));
1126 /* ---------------------------------------------------------------------------
1130 QuitApplication (void)
1133 * save data if necessary. It not needed, then don't trigger EmergencySave
1134 * via our atexit() registering of EmergencySave(). We presumeably wanted to
1135 * exit here and thus it is not an emergency.
1137 if (PCB
->Changed
&& Settings
.SaveInTMP
)
1140 DisableEmergencySave ();
1142 /* Free up memory allocated to the PCB. Why bother when we're about to exit ?
1143 * Because it removes some false positives from heap bug detectors such as
1151 /* ---------------------------------------------------------------------------
1152 * creates a filename from a template
1153 * %f is replaced by the filename
1154 * %p by the searchpath
1157 EvaluateFilename (char *Template
, char *Path
, char *Filename
, char *Parameter
)
1159 static DynamicStringType command
;
1162 if (Settings
.verbose
)
1164 printf ("EvaluateFilename:\n");
1165 printf ("\tTemplate: \033[33m%s\033[0m\n", Template
);
1166 printf ("\tPath: \033[33m%s\033[0m\n", Path
);
1167 printf ("\tFilename: \033[33m%s\033[0m\n", Filename
);
1168 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter
);
1171 DSClearString (&command
);
1173 for (p
= Template
; p
&& *p
; p
++)
1175 /* copy character or add string to command */
1177 && (*(p
+ 1) == 'f' || *(p
+ 1) == 'p' || *(p
+ 1) == 'a'))
1181 DSAddString (&command
, Parameter
);
1184 DSAddString (&command
, Filename
);
1187 DSAddString (&command
, Path
);
1191 DSAddCharacter (&command
, *p
);
1193 DSAddCharacter (&command
, '\0');
1194 if (Settings
.verbose
)
1195 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command
.Data
);
1197 return strdup (command
.Data
);
1200 /* ---------------------------------------------------------------------------
1201 * concatenates directory and filename if directory != NULL,
1202 * expands them with a shell and returns the found name(s) or NULL
1205 ExpandFilename (char *Dirname
, char *Filename
)
1207 static DynamicStringType answer
;
1212 /* allocate memory for commandline and build it */
1213 DSClearString (&answer
);
1216 command
= (char *)calloc (strlen (Filename
) + strlen (Dirname
) + 7,
1218 sprintf (command
, "echo %s/%s", Dirname
, Filename
);
1222 command
= (char *)calloc (strlen (Filename
) + 6, sizeof (char));
1223 sprintf (command
, "echo %s", Filename
);
1226 /* execute it with shell */
1227 if ((pipe
= popen (command
, "r")) != NULL
)
1229 /* discard all but the first returned line */
1232 if ((c
= fgetc (pipe
)) == EOF
|| c
== '\n' || c
== '\r')
1235 DSAddCharacter (&answer
, c
);
1239 return (pclose (pipe
) ? NULL
: answer
.Data
);
1242 /* couldn't be expanded by the shell */
1243 PopenErrorMessage (command
);
1249 /* ---------------------------------------------------------------------------
1250 * returns the layer number for the passed pointer
1253 GetLayerNumber (DataType
*Data
, LayerType
*Layer
)
1257 for (i
= 0; i
< MAX_LAYER
+ 2; i
++)
1258 if (Layer
== &Data
->Layer
[i
])
1263 /* ---------------------------------------------------------------------------
1264 * move layer (number is passed in) to top of layerstack
1267 PushOnTopOfLayerStack (int NewTop
)
1271 /* ignore silk and other extra layers */
1272 if (NewTop
< max_copper_layer
)
1274 /* first find position of passed one */
1275 for (i
= 0; i
< max_copper_layer
; i
++)
1276 if (LayerStack
[i
] == NewTop
)
1279 /* bring this element to the top of the stack */
1281 LayerStack
[i
] = LayerStack
[i
- 1];
1282 LayerStack
[0] = NewTop
;
1287 /* ----------------------------------------------------------------------
1288 * changes the visibility of all layers in a group
1289 * returns the number of changed layers
1292 ChangeGroupVisibility (int Layer
, bool On
, bool ChangeStackOrder
)
1294 int group
, i
, changed
= 1; /* at least the current layer changes */
1296 /* Warning: these special case values must agree with what gui-top-window.c
1300 if (Settings
.verbose
)
1301 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1302 Layer
, On
, ChangeStackOrder
);
1304 /* decrement 'i' to keep stack in order of layergroup */
1305 group
= GetLayerGroupNumberByNumber (Layer
);
1306 for (i
= PCB
->LayerGroups
.Number
[group
]; i
;)
1308 int layer
= PCB
->LayerGroups
.Entries
[group
][--i
];
1310 /* don't count the passed member of the group */
1311 if (layer
!= Layer
&& layer
< max_copper_layer
)
1313 PCB
->Data
->Layer
[layer
].On
= On
;
1315 /* push layer on top of stack if switched on */
1316 if (On
&& ChangeStackOrder
)
1317 PushOnTopOfLayerStack (layer
);
1322 /* change at least the passed layer */
1323 PCB
->Data
->Layer
[Layer
].On
= On
;
1324 if (On
&& ChangeStackOrder
)
1325 PushOnTopOfLayerStack (Layer
);
1327 /* update control panel and exit */
1328 hid_action ("LayersChanged");
1332 /* ----------------------------------------------------------------------
1333 * Given a string description of a layer stack, adjust the layer stack
1338 LayerStringToLayerStack (char *s
)
1340 static int listed_layers
= 0;
1347 args
= (char **) malloc (l
* sizeof (char *));
1370 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
1372 if (i
< max_copper_layer
)
1374 PCB
->Data
->Layer
[i
].On
= false;
1376 PCB
->ElementOn
= false;
1377 PCB
->InvisibleObjectsOn
= false;
1381 CLEAR_FLAG (SHOWMASKFLAG
, PCB
);
1382 Settings
.ShowBottomSide
= 0;
1384 for (i
=argn
-1; i
>=0; i
--)
1386 if (strcasecmp (args
[i
], "rats") == 0)
1388 else if (strcasecmp (args
[i
], "invisible") == 0)
1389 PCB
->InvisibleObjectsOn
= true;
1390 else if (strcasecmp (args
[i
], "pins") == 0)
1392 else if (strcasecmp (args
[i
], "vias") == 0)
1394 else if (strcasecmp (args
[i
], "elements") == 0
1395 || strcasecmp (args
[i
], "silk") == 0)
1396 PCB
->ElementOn
= true;
1397 else if (strcasecmp (args
[i
], "mask") == 0)
1398 SET_FLAG (SHOWMASKFLAG
, PCB
);
1399 else if (strcasecmp (args
[i
], "solderside") == 0)
1400 Settings
.ShowBottomSide
= 1;
1401 else if (isdigit ((int) args
[i
][0]))
1403 lno
= atoi (args
[i
]);
1404 ChangeGroupVisibility (lno
, true, true);
1409 for (lno
= 0; lno
< max_copper_layer
; lno
++)
1410 if (strcasecmp (args
[i
], PCB
->Data
->Layer
[lno
].Name
) == 0)
1412 ChangeGroupVisibility (lno
, true, true);
1418 fprintf(stderr
, _("Warning: layer \"%s\" not known\n"), args
[i
]);
1421 fprintf (stderr
, _("Named layers in this board are:\n"));
1423 for (lno
=0; lno
< max_copper_layer
; lno
++)
1424 fprintf(stderr
, "\t%s\n", PCB
->Data
->Layer
[lno
].Name
);
1425 fprintf(stderr
, _("Also: component, solder, rats, invisible, "
1426 "pins, vias, elements or silk, mask, solderside.\n"));
1433 /* ---------------------------------------------------------------------------
1434 * returns the layergroup number for the passed pointer
1437 GetLayerGroupNumberByPointer (LayerType
*Layer
)
1439 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB
->Data
, Layer
)));
1442 /* ---------------------------------------------------------------------------
1443 * returns the layergroup number for the passed layernumber
1446 GetLayerGroupNumberByNumber (Cardinal Layer
)
1450 for (group
= 0; group
< max_group
; group
++)
1451 for (entry
= 0; entry
< PCB
->LayerGroups
.Number
[group
]; entry
++)
1452 if (PCB
->LayerGroups
.Entries
[group
][entry
] == Layer
)
1455 /* since every layer belongs to a group it is safe to return
1456 * the value without boundary checking
1461 /* ---------------------------------------------------------------------------
1462 * returns the layergroup number for the passed side (TOP_LAYER or BOTTOM_LAYER)
1465 GetLayerGroupNumberBySide (int side
)
1467 /* Find the relavant board side layer group by determining the
1468 * layer group associated with the relevant side's silk-screen
1470 return GetLayerGroupNumberByNumber(
1471 side
== TOP_SIDE
? top_silk_layer
: bottom_silk_layer
);
1474 /* ---------------------------------------------------------------------------
1475 * returns a pointer to an objects bounding box;
1476 * data is valid until the routine is called again
1479 GetObjectBoundingBox (int Type
, void *Ptr1
, void *Ptr2
, void *Ptr3
)
1489 case ELEMENTNAME_TYPE
:
1490 return (BoxType
*)Ptr2
;
1493 return (BoxType
*)Ptr1
;
1494 case POLYGONPOINT_TYPE
:
1495 case LINEPOINT_TYPE
:
1496 return (BoxType
*)Ptr3
;
1498 Message (_("Request for bounding box of unsupported type %d\n"), Type
);
1499 return (BoxType
*)Ptr2
;
1503 /* ---------------------------------------------------------------------------
1504 * computes the bounding box of an arc
1507 SetArcBoundingBox (ArcType
*Arc
)
1509 double ca1
, ca2
, sa1
, sa2
;
1510 double minx
, maxx
, miny
, maxy
;
1514 /* first put angles into standard form:
1515 * ang1 < ang2, both angles between 0 and 720 */
1516 Arc
->Delta
= CLAMP (Arc
->Delta
, -360, 360);
1520 ang1
= NormalizeAngle (Arc
->StartAngle
);
1521 ang2
= NormalizeAngle (Arc
->StartAngle
+ Arc
->Delta
);
1525 ang1
= NormalizeAngle (Arc
->StartAngle
+ Arc
->Delta
);
1526 ang2
= NormalizeAngle (Arc
->StartAngle
);
1530 /* Make sure full circles aren't treated as zero-length arcs */
1531 if (Arc
->Delta
== 360 || Arc
->Delta
== -360)
1534 /* calculate sines, cosines */
1535 sa1
= sin (M180
* ang1
);
1536 ca1
= cos (M180
* ang1
);
1537 sa2
= sin (M180
* ang2
);
1538 ca2
= cos (M180
* ang2
);
1540 minx
= MIN (ca1
, ca2
);
1541 maxx
= MAX (ca1
, ca2
);
1542 miny
= MIN (sa1
, sa2
);
1543 maxy
= MAX (sa1
, sa2
);
1545 /* Check for extreme angles */
1546 if ((ang1
<= 0 && ang2
>= 0) || (ang1
<= 360 && ang2
>= 360)) maxx
= 1;
1547 if ((ang1
<= 90 && ang2
>= 90) || (ang1
<= 450 && ang2
>= 450)) maxy
= 1;
1548 if ((ang1
<= 180 && ang2
>= 180) || (ang1
<= 540 && ang2
>= 540)) minx
= -1;
1549 if ((ang1
<= 270 && ang2
>= 270) || (ang1
<= 630 && ang2
>= 630)) miny
= -1;
1551 /* Finally, calcate bounds, converting sane geometry into pcb geometry */
1552 Arc
->BoundingBox
.X1
= Arc
->X
- Arc
->Width
* maxx
;
1553 Arc
->BoundingBox
.X2
= Arc
->X
- Arc
->Width
* minx
;
1554 Arc
->BoundingBox
.Y1
= Arc
->Y
+ Arc
->Height
* miny
;
1555 Arc
->BoundingBox
.Y2
= Arc
->Y
+ Arc
->Height
* maxy
;
1557 width
= (Arc
->Thickness
+ Arc
->Clearance
) / 2;
1559 /* Adjust for our discrete polygon approximation */
1560 width
= (double)width
* MAX (POLY_CIRC_RADIUS_ADJ
, (1.0 + POLY_ARC_MAX_DEVIATION
)) + 0.5;
1562 Arc
->BoundingBox
.X1
-= width
;
1563 Arc
->BoundingBox
.X2
+= width
;
1564 Arc
->BoundingBox
.Y1
-= width
;
1565 Arc
->BoundingBox
.Y2
+= width
;
1566 close_box(&Arc
->BoundingBox
);
1568 /* Update the arc end-points */
1569 Arc
->Point1
.X
= Arc
->X
- (double)Arc
->Width
* ca1
;
1570 Arc
->Point1
.Y
= Arc
->Y
+ (double)Arc
->Height
* sa1
;
1571 Arc
->Point2
.X
= Arc
->X
- (double)Arc
->Width
* ca2
;
1572 Arc
->Point2
.Y
= Arc
->Y
+ (double)Arc
->Height
* sa2
;
1575 /* ---------------------------------------------------------------------------
1576 * resets the layerstack setting
1579 ResetStackAndVisibility (void)
1584 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
1586 if (i
< max_copper_layer
)
1588 PCB
->Data
->Layer
[i
].On
= true;
1590 PCB
->ElementOn
= true;
1591 PCB
->InvisibleObjectsOn
= true;
1596 /* Bring the component group to the front and make it active. */
1597 top_group
= GetLayerGroupNumberBySide (TOP_SIDE
);
1598 ChangeGroupVisibility (PCB
->LayerGroups
.Entries
[top_group
][0], 1, 1);
1601 /* ---------------------------------------------------------------------------
1602 * saves the layerstack setting
1605 SaveStackAndVisibility (void)
1608 static bool run
= false;
1616 if (SavedStack
.cnt
!= 0)
1619 "SaveStackAndVisibility() layerstack was already saved and not"
1620 "yet restored. cnt = %d\n", SavedStack
.cnt
);
1623 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
1625 if (i
< max_copper_layer
)
1626 SavedStack
.LayerStack
[i
] = LayerStack
[i
];
1627 SavedStack
.LayerOn
[i
] = PCB
->Data
->Layer
[i
].On
;
1629 SavedStack
.ElementOn
= PCB
->ElementOn
;
1630 SavedStack
.InvisibleObjectsOn
= PCB
->InvisibleObjectsOn
;
1631 SavedStack
.PinOn
= PCB
->PinOn
;
1632 SavedStack
.ViaOn
= PCB
->ViaOn
;
1633 SavedStack
.RatOn
= PCB
->RatOn
;
1637 /* ---------------------------------------------------------------------------
1638 * restores the layerstack setting
1641 RestoreStackAndVisibility (void)
1645 if (SavedStack
.cnt
== 0)
1647 fprintf (stderr
, "RestoreStackAndVisibility() layerstack has not"
1648 " been saved. cnt = %d\n", SavedStack
.cnt
);
1651 else if (SavedStack
.cnt
!= 1)
1653 fprintf (stderr
, "RestoreStackAndVisibility() layerstack save count is"
1654 " wrong. cnt = %d\n", SavedStack
.cnt
);
1657 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
1659 if (i
< max_copper_layer
)
1660 LayerStack
[i
] = SavedStack
.LayerStack
[i
];
1661 PCB
->Data
->Layer
[i
].On
= SavedStack
.LayerOn
[i
];
1663 PCB
->ElementOn
= SavedStack
.ElementOn
;
1664 PCB
->InvisibleObjectsOn
= SavedStack
.InvisibleObjectsOn
;
1665 PCB
->PinOn
= SavedStack
.PinOn
;
1666 PCB
->ViaOn
= SavedStack
.ViaOn
;
1667 PCB
->RatOn
= SavedStack
.RatOn
;
1672 /* ----------------------------------------------------------------------
1673 * returns pointer to current working directory. If 'path' is not
1674 * NULL, then the current working directory is copied to the array
1675 * pointed to by 'path'
1678 GetWorkingDirectory (char *path
)
1681 return getcwd (path
, MAXPATHLEN
);
1683 /* seems that some BSD releases lack of a prototype for getwd() */
1684 return getwd (path
);
1689 /* ---------------------------------------------------------------------------
1690 * writes a string to the passed file pointer
1691 * some special characters are quoted
1694 CreateQuotedString (DynamicStringType
*DS
, char *S
)
1697 DSAddCharacter (DS
, '"');
1700 if (*S
== '"' || *S
== '\\')
1701 DSAddCharacter (DS
, '\\');
1702 DSAddCharacter (DS
, *S
++);
1704 DSAddCharacter (DS
, '"');
1708 GetArcEnds (ArcType
*Arc
)
1711 box
.X1
= Arc
->X
- Arc
->Width
* cos (Arc
->StartAngle
* M180
);
1712 box
.Y1
= Arc
->Y
+ Arc
->Height
* sin (Arc
->StartAngle
* M180
);
1713 box
.X2
= Arc
->X
- Arc
->Width
* cos ((Arc
->StartAngle
+ Arc
->Delta
) * M180
);
1714 box
.Y2
= Arc
->Y
+ Arc
->Height
* sin ((Arc
->StartAngle
+ Arc
->Delta
) * M180
);
1719 /* doesn't this belong in change.c ?? */
1721 ChangeArcAngles (LayerType
*Layer
, ArcType
*a
,
1722 Angle new_sa
, Angle new_da
)
1729 RestoreToPolygon (PCB
->Data
, ARC_TYPE
, Layer
, a
);
1730 r_delete_entry (Layer
->arc_tree
, (BoxType
*) a
);
1731 AddObjectToChangeAnglesUndoList (ARC_TYPE
, a
, a
, a
);
1732 a
->StartAngle
= new_sa
;
1734 SetArcBoundingBox (a
);
1735 r_insert_entry (Layer
->arc_tree
, (BoxType
*) a
, 0);
1736 ClearFromPolygon (PCB
->Data
, ARC_TYPE
, Layer
, a
);
1740 BumpName (char *Name
)
1744 static char temp
[256];
1747 /* seek end of string */
1750 /* back up to potential number */
1751 for (Name
--; isdigit ((int) *Name
); Name
--);
1754 num
= atoi (Name
) + 1;
1759 sprintf (temp
, "%s%d", start
, num
);
1760 /* if this is not our string, put back the blown character */
1767 * make a unique name for the name on board
1768 * this can alter the contents of the input string
1771 UniqueElementName (DataType
*Data
, char *Name
)
1774 /* null strings are ok */
1775 if (!Name
|| !*Name
)
1780 ELEMENT_LOOP (Data
);
1782 if (NAMEONPCB_NAME (element
) &&
1783 NSTRCMP (NAMEONPCB_NAME (element
), Name
) == 0)
1785 Name
= BumpName (Name
);
1798 GetGridLockCoordinates (int type
, void *ptr1
,
1799 void *ptr2
, void *ptr3
, Coord
* x
,
1805 *x
= ((PinType
*) ptr2
)->X
;
1806 *y
= ((PinType
*) ptr2
)->Y
;
1809 *x
= ((LineType
*) ptr2
)->Point1
.X
;
1810 *y
= ((LineType
*) ptr2
)->Point1
.Y
;
1813 case ELEMENTNAME_TYPE
:
1814 *x
= ((TextType
*) ptr2
)->X
;
1815 *y
= ((TextType
*) ptr2
)->Y
;
1818 *x
= ((ElementType
*) ptr2
)->MarkX
;
1819 *y
= ((ElementType
*) ptr2
)->MarkY
;
1822 *x
= ((PolygonType
*) ptr2
)->Points
[0].X
;
1823 *y
= ((PolygonType
*) ptr2
)->Points
[0].Y
;
1826 case LINEPOINT_TYPE
:
1827 case POLYGONPOINT_TYPE
:
1828 *x
= ((PointType
*) ptr3
)->X
;
1829 *y
= ((PointType
*) ptr3
)->Y
;
1835 box
= GetArcEnds ((ArcType
*) ptr2
);
1844 AttachForCopy (Coord PlaceX
, Coord PlaceY
)
1847 Coord mx
= 0, my
= 0;
1849 Crosshair
.AttachedObject
.RubberbandN
= 0;
1850 if (! TEST_FLAG (SNAPPINFLAG
, PCB
))
1852 /* dither the grab point so that the mark, center, etc
1853 * will end up on a grid coordinate
1855 GetGridLockCoordinates (Crosshair
.AttachedObject
.Type
,
1856 Crosshair
.AttachedObject
.Ptr1
,
1857 Crosshair
.AttachedObject
.Ptr2
,
1858 Crosshair
.AttachedObject
.Ptr3
, &mx
, &my
);
1859 mx
= GridFit (mx
, PCB
->Grid
, PCB
->GridOffsetX
) - mx
;
1860 my
= GridFit (my
, PCB
->Grid
, PCB
->GridOffsetY
) - my
;
1862 Crosshair
.AttachedObject
.X
= PlaceX
- mx
;
1863 Crosshair
.AttachedObject
.Y
= PlaceY
- my
;
1864 if (!Marked
.status
|| TEST_FLAG (LOCALREFFLAG
, PCB
))
1865 SetLocalRef (PlaceX
- mx
, PlaceY
- my
, true);
1866 Crosshair
.AttachedObject
.State
= STATE_SECOND
;
1868 /* get boundingbox of object and set cursor range */
1869 box
= GetObjectBoundingBox (Crosshair
.AttachedObject
.Type
,
1870 Crosshair
.AttachedObject
.Ptr1
,
1871 Crosshair
.AttachedObject
.Ptr2
,
1872 Crosshair
.AttachedObject
.Ptr3
);
1873 SetCrosshairRange (Crosshair
.AttachedObject
.X
- box
->X1
,
1874 Crosshair
.AttachedObject
.Y
- box
->Y1
,
1875 PCB
->MaxWidth
- (box
->X2
- Crosshair
.AttachedObject
.X
),
1876 PCB
->MaxHeight
- (box
->Y2
- Crosshair
.AttachedObject
.Y
));
1878 /* get all attached objects if necessary */
1879 if ((Settings
.Mode
!= COPY_MODE
) && TEST_FLAG (RUBBERBANDFLAG
, PCB
))
1880 LookupRubberbandLines (Crosshair
.AttachedObject
.Type
,
1881 Crosshair
.AttachedObject
.Ptr1
,
1882 Crosshair
.AttachedObject
.Ptr2
,
1883 Crosshair
.AttachedObject
.Ptr3
);
1884 if (Settings
.Mode
!= COPY_MODE
&&
1885 (Crosshair
.AttachedObject
.Type
== ELEMENT_TYPE
||
1886 Crosshair
.AttachedObject
.Type
== VIA_TYPE
||
1887 Crosshair
.AttachedObject
.Type
== LINE_TYPE
||
1888 Crosshair
.AttachedObject
.Type
== LINEPOINT_TYPE
))
1889 LookupRatLines (Crosshair
.AttachedObject
.Type
,
1890 Crosshair
.AttachedObject
.Ptr1
,
1891 Crosshair
.AttachedObject
.Ptr2
,
1892 Crosshair
.AttachedObject
.Ptr3
);
1896 * Return nonzero if the given file exists and is readable.
1899 FileExists (const char *name
)
1902 f
= fopen (name
, "r");
1912 Concat (const char *first
, ...)
1918 len
= strlen (first
);
1919 rv
= (char *) malloc (len
+ 1);
1922 va_start (a
, first
);
1925 const char *s
= va_arg (a
, const char *);
1929 rv
= (char *) realloc (rv
, len
+ 1);
1937 mem_any_set (unsigned char *ptr
, int bytes
)
1945 /* This just fills in a FlagType with current flags. */
1947 MakeFlags (unsigned int flags
)
1950 memset (&rv
, 0, sizeof (rv
));
1955 /* This converts old flag bits (from saved PCB files) to new format. */
1957 OldFlags (unsigned int flags
)
1961 memset (&rv
, 0, sizeof (rv
));
1962 /* If we move flag bits around, this is where we map old bits to them. */
1963 rv
.f
= flags
& 0xffff;
1965 for (i
= 0; i
< 8; i
++)
1967 /* use the closest thing to the old thermal style */
1969 rv
.t
[i
/ 2] |= (1 << (4 * (i
% 2)));
1976 AddFlags (FlagType flag
, unsigned int flags
)
1983 MaskFlags (FlagType flag
, unsigned int flags
)
1989 /***********************************************************************
1990 * Layer Group Functions
1994 MoveLayerToGroup (int layer
, int group
)
1998 if (layer
< 0 || layer
> max_copper_layer
+ 1)
2000 prev
= GetLayerGroupNumberByNumber (layer
);
2001 if ((layer
== bottom_silk_layer
2002 && group
== GetLayerGroupNumberByNumber (top_silk_layer
))
2003 || (layer
== top_silk_layer
2004 && group
== GetLayerGroupNumberByNumber (bottom_silk_layer
))
2005 || (group
< 0 || group
>= max_group
) || (prev
== group
))
2008 /* Remove layer from prev group */
2009 for (j
= i
= 0; i
< PCB
->LayerGroups
.Number
[prev
]; i
++)
2010 if (PCB
->LayerGroups
.Entries
[prev
][i
] != layer
)
2011 PCB
->LayerGroups
.Entries
[prev
][j
++] = PCB
->LayerGroups
.Entries
[prev
][i
];
2012 PCB
->LayerGroups
.Number
[prev
]--;
2014 /* Add layer to new group. */
2015 i
= PCB
->LayerGroups
.Number
[group
]++;
2016 PCB
->LayerGroups
.Entries
[group
][i
] = layer
;
2022 LayerGroupsToString (LayerGroupType
*lg
)
2024 #if MAX_LAYER < 9998
2025 /* Allows for layer numbers 0..9999 */
2026 static char buf
[(MAX_LAYER
+ 2) * 5 + 1];
2031 for (group
= 0; group
< max_group
; group
++)
2032 if (PCB
->LayerGroups
.Number
[group
])
2037 for (entry
= 0; entry
< PCB
->LayerGroups
.Number
[group
]; entry
++)
2039 int layer
= PCB
->LayerGroups
.Entries
[group
][entry
];
2040 if (layer
== top_silk_layer
)
2044 else if (layer
== bottom_silk_layer
)
2050 sprintf (cp
, "%d", layer
+ 1);
2054 if (entry
!= PCB
->LayerGroups
.Number
[group
] - 1)
2065 #ifdef HAVE_GETPWUID
2066 static struct passwd
*pwentry
;
2067 static char *fab_author
= 0;
2071 if (Settings
.FabAuthor
&& Settings
.FabAuthor
[0])
2072 fab_author
= Settings
.FabAuthor
;
2076 char *comma
, *gecos
;
2079 pwentry
= getpwuid (getuid ());
2080 gecos
= pwentry
->pw_gecos
;
2081 comma
= strchr (gecos
, ',');
2083 len
= comma
- gecos
;
2085 len
= strlen (gecos
);
2086 fab_author
= (char *)malloc (len
+ 1);
2089 perror ("pcb: out of memory.\n");
2092 memcpy (fab_author
, gecos
, len
);
2093 fab_author
[len
] = 0;
2104 AttributeGetFromList (AttributeListType
*list
, char *name
)
2107 for (i
=0; i
<list
->Number
; i
++)
2108 if (strcmp (name
, list
->List
[i
].name
) == 0)
2109 return list
->List
[i
].value
;
2114 AttributePutToList (AttributeListType
*list
, const char *name
, const char *value
, int replace
)
2118 /* If we're allowed to replace an existing attribute, see if we
2122 for (i
=0; i
<list
->Number
; i
++)
2123 if (strcmp (name
, list
->List
[i
].name
) == 0)
2125 free (list
->List
[i
].value
);
2126 list
->List
[i
].value
= STRDUP (value
);
2131 /* At this point, we're going to need to add a new attribute to the
2132 list. See if there's room. */
2133 if (list
->Number
>= list
->Max
)
2136 list
->List
= (AttributeType
*) realloc (list
->List
,
2137 list
->Max
* sizeof (AttributeType
));
2140 /* Now add the new attribute. */
2142 list
->List
[i
].name
= STRDUP (name
);
2143 list
->List
[i
].value
= STRDUP (value
);
2149 AttributeRemoveFromList(AttributeListType
*list
, char *name
)
2152 for (i
=0; i
<list
->Number
; i
++)
2153 if (strcmp (name
, list
->List
[i
].name
) == 0)
2155 free (list
->List
[i
].name
);
2156 free (list
->List
[i
].value
);
2157 for (j
=i
; j
<list
->Number
-i
; j
++)
2158 list
->List
[j
] = list
->List
[j
+1];
2163 /* In future all use of this should be supplanted by
2164 * pcb-printf and %mr/%m# spec */
2168 static char buf
[100];
2177 d
+= 0.0000005; /* rounding */
2180 sprintf (bufp
, "%d", i
);
2181 bufp
+= strlen (bufp
);
2184 f
= floor (d
* 1000000.0);
2185 sprintf (bufp
, "%06d", f
);
2190 r_delete_element (DataType
* data
, ElementType
* element
)
2192 r_delete_entry (data
->element_tree
, (BoxType
*) element
);
2195 r_delete_entry (data
->pin_tree
, (BoxType
*) pin
);
2200 r_delete_entry (data
->pad_tree
, (BoxType
*) pad
);
2203 ELEMENTTEXT_LOOP (element
);
2205 r_delete_entry (data
->name_tree
[n
], (BoxType
*) text
);
2211 /* ---------------------------------------------------------------------------
2212 * Returns a string that has a bunch of information about the program.
2213 * Can be used for things like "about" dialog boxes.
2217 GetInfoString (void)
2221 static DynamicStringType info
;
2222 static int first_time
= 1;
2230 _("This is PCB, an interactive\n"
2231 "printed circuit board editor\n"
2235 "Compiled on " __DATE__
" at " __TIME__
"\n\n"
2236 "by harry eaton\n\n"
2237 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n"
2238 "Copyright (C) harry eaton 1998-2007\n"
2239 "Copyright (C) C. Scott Ananian 2001\n"
2240 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n"
2241 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2243 _("It is licensed under the terms of the GNU\n"
2244 "General Public License version 2\n"
2245 "See the LICENSE file for more information\n\n"
2246 "For more information see:\n"));
2247 DSAddString (&info
, _("PCB homepage: "));
2248 DSAddString (&info
, "http://pcb.geda-project.org\n");
2249 DSAddString (&info
, _("gEDA homepage: "));
2250 DSAddString (&info
, "http://www.geda-project.org\n");
2251 DSAddString (&info
, _("gEDA Wiki: "));
2252 DSAddString (&info
, "http://wiki.geda-project.org\n");
2254 DSAddString (&info
, _("\n----- Compile Time Options -----\n"));
2255 hids
= hid_enumerate ();
2256 DSAddString (&info
, _("GUI:\n"));
2257 for (i
= 0; hids
[i
]; i
++)
2261 DSAddString (&info
, TAB
);
2262 DSAddString (&info
, hids
[i
]->name
);
2263 DSAddString (&info
, " : ");
2264 DSAddString (&info
, hids
[i
]->description
);
2265 DSAddString (&info
, "\n");
2269 DSAddString (&info
, _("Exporters:\n"));
2270 for (i
= 0; hids
[i
]; i
++)
2272 if (hids
[i
]->exporter
)
2274 DSAddString (&info
, TAB
);
2275 DSAddString (&info
, hids
[i
]->name
);
2276 DSAddString (&info
, " : ");
2277 DSAddString (&info
, hids
[i
]->description
);
2278 DSAddString (&info
, "\n");
2282 DSAddString (&info
, _("Printers:\n"));
2283 for (i
= 0; hids
[i
]; i
++)
2285 if (hids
[i
]->printer
)
2287 DSAddString (&info
, TAB
);
2288 DSAddString (&info
, hids
[i
]->name
);
2289 DSAddString (&info
, " : ");
2290 DSAddString (&info
, hids
[i
]->description
);
2291 DSAddString (&info
, "\n");
2300 /* ---------------------------------------------------------------------------
2301 * mkdir() implentation, mostly for plugins, which don't have our config.h.
2304 #ifdef MKDIR_IS_PCBMKDIR
2305 #error "Don't know how to create a directory on this system."
2309 pcb_mkdir (const char *path
, int mode
)
2311 return MKDIR (path
, mode
);
2314 /* ---------------------------------------------------------------------------
2315 * Returns a best guess about the orientation of an element. The
2316 * value corresponds to the rotation; a difference is the right value
2317 * to pass to RotateElementLowLevel. However, the actual value is no
2318 * indication of absolute rotation; only relative rotation is
2323 ElementOrientation (ElementType
*e
)
2325 Coord pin1x
, pin1y
, pin2x
, pin2y
, dx
, dy
;
2326 bool found_pin1
= 0;
2327 bool found_pin2
= 0;
2329 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2337 if (NSTRCMP (pin
->Number
, "1") == 0)
2343 else if (NSTRCMP (pin
->Number
, "2") == 0)
2354 if (NSTRCMP (pad
->Number
, "1") == 0)
2356 pin1x
= (pad
->Point1
.X
+ pad
->Point2
.X
) / 2;
2357 pin1y
= (pad
->Point1
.Y
+ pad
->Point2
.Y
) / 2;
2360 else if (NSTRCMP (pad
->Number
, "2") == 0)
2362 pin2x
= (pad
->Point1
.X
+ pad
->Point2
.X
) / 2;
2363 pin2y
= (pad
->Point1
.Y
+ pad
->Point2
.Y
) / 2;
2369 if (found_pin1
&& found_pin2
)
2374 else if (found_pin1
&& (pin1x
|| pin1y
))
2379 else if (found_pin2
&& (pin2x
|| pin2y
))
2387 if (abs(dx
) > abs(dy
))
2388 return dx
> 0 ? 0 : 2;
2389 return dy
> 0 ? 3 : 1;
2393 ActionListRotations(int argc
, char **argv
, Coord x
, Coord y
)
2395 ELEMENT_LOOP (PCB
->Data
);
2397 printf("%d %s\n", ElementOrientation(element
), NAMEONPCB_NAME(element
));
2404 HID_Action misc_action_list
[] = {
2405 {"ListRotations", 0, ActionListRotations
,
2409 REGISTER_ACTIONS (misc_action_list
)