6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * Contact addresses for paper mail and Email:
24 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
25 * Thomas.Nau@rz.uni-ulm.de
30 /* misc functions used by several modules
45 #include <sys/param.h>
47 #include <sys/types.h>
59 #include "crosshair.h"
72 #include "rubberband.h"
78 #ifdef HAVE_LIBDMALLOC
85 /* forward declarations */
86 static char *BumpName (char *);
87 static void RightAngles (int, float *, float *);
88 static void GetGridLockCoordinates (int, void *, void *, void *,
89 LocationType
*, LocationType
*);
95 * Used by SaveStackAndVisibility() and
96 * RestoreStackAndVisibility()
101 bool ElementOn
, InvisibleObjectsOn
, PinOn
, ViaOn
, RatOn
;
102 int LayerStack
[MAX_LAYER
];
103 bool LayerOn
[MAX_LAYER
];
107 /* Get Value returns a numeric value passed from the string and sets the
108 * bool variable absolute to false if it leads with a +/- character
111 GetValue (const char *val
, const char *units
, bool * absolute
)
116 /* if the first character is a sign we have to add the
117 * value to the current one
122 sscanf (val
+1, "%lf%n", &value
, &n
);
127 if (isdigit ((int) *val
))
131 sscanf (val
, "%lf%n", &value
, &n
);
138 if (strncasecmp (units
, "mm", 2) == 0)
140 else if (strncasecmp (units
, "cm", 2) == 0)
141 value
*= MM_TO_COOR
* 10;
142 else if (strncasecmp (units
, "mil", 3) == 0)
144 else if (strncasecmp (units
, "in", 3) == 0)
150 /* ---------------------------------------------------------------------------
151 * sets the bounding box of a point (which is silly)
154 SetPointBoundingBox (PointTypePtr Pnt
)
156 Pnt
->X2
= Pnt
->X
+ 1;
157 Pnt
->Y2
= Pnt
->Y
+ 1;
160 /* ---------------------------------------------------------------------------
161 * sets the bounding box of a pin or via
164 SetPinBoundingBox (PinTypePtr Pin
)
168 /* the bounding box covers the extent of influence
169 * so it must include the clearance values too
171 width
= (Pin
->Clearance
+ Pin
->Thickness
+ 1) / 2;
172 width
= MAX (width
, (Pin
->Mask
+ 1) / 2);
173 Pin
->BoundingBox
.X1
= Pin
->X
- width
;
174 Pin
->BoundingBox
.Y1
= Pin
->Y
- width
;
175 Pin
->BoundingBox
.X2
= Pin
->X
+ width
;
176 Pin
->BoundingBox
.Y2
= Pin
->Y
+ width
;
177 close_box(&Pin
->BoundingBox
);
180 /* ---------------------------------------------------------------------------
181 * sets the bounding box of a pad
184 SetPadBoundingBox (PadTypePtr Pad
)
190 /* the bounding box covers the extent of influence
191 * so it must include the clearance values too
193 width
= (Pad
->Thickness
+ Pad
->Clearance
+ 1) / 2;
194 width
= MAX (width
, (Pad
->Mask
+ 1) / 2);
195 deltax
= Pad
->Point2
.X
- Pad
->Point1
.X
;
196 deltay
= Pad
->Point2
.Y
- Pad
->Point1
.Y
;
198 if (TEST_FLAG (SQUAREFLAG
, Pad
) && deltax
!= 0 && deltay
!= 0)
200 /* slanted square pad */
204 theta
= atan2 (deltay
, deltax
);
206 /* T is a vector half a thickness long, in the direction of
207 one of the corners. */
208 tx
= width
* cos (theta
+ M_PI
/4) * sqrt(2.0);
209 ty
= width
* sin (theta
+ M_PI
/4) * sqrt(2.0);
211 /* cast back to this integer type */
215 Pad
->BoundingBox
.X1
= MIN (MIN (Pad
->Point1
.X
- btx
, Pad
->Point1
.X
- bty
),
216 MIN (Pad
->Point2
.X
+ btx
, Pad
->Point2
.X
+ bty
));
217 Pad
->BoundingBox
.X2
= MAX (MAX (Pad
->Point1
.X
- btx
, Pad
->Point1
.X
- bty
),
218 MAX (Pad
->Point2
.X
+ btx
, Pad
->Point2
.X
+ bty
));
219 Pad
->BoundingBox
.Y1
= MIN (MIN (Pad
->Point1
.Y
+ btx
, Pad
->Point1
.Y
- bty
),
220 MIN (Pad
->Point2
.Y
- btx
, Pad
->Point2
.Y
+ bty
));
221 Pad
->BoundingBox
.Y2
= MAX (MAX (Pad
->Point1
.Y
+ btx
, Pad
->Point1
.Y
- bty
),
222 MAX (Pad
->Point2
.Y
- btx
, Pad
->Point2
.Y
+ bty
));
226 Pad
->BoundingBox
.X1
= MIN (Pad
->Point1
.X
, Pad
->Point2
.X
) - width
;
227 Pad
->BoundingBox
.X2
= MAX (Pad
->Point1
.X
, Pad
->Point2
.X
) + width
;
228 Pad
->BoundingBox
.Y1
= MIN (Pad
->Point1
.Y
, Pad
->Point2
.Y
) - width
;
229 Pad
->BoundingBox
.Y2
= MAX (Pad
->Point1
.Y
, Pad
->Point2
.Y
) + width
;
231 close_box(&Pad
->BoundingBox
);
234 /* ---------------------------------------------------------------------------
235 * sets the bounding box of a line
238 SetLineBoundingBox (LineTypePtr Line
)
242 width
= (Line
->Thickness
+ Line
->Clearance
+ 1) / 2;
244 Line
->BoundingBox
.X1
= MIN (Line
->Point1
.X
, Line
->Point2
.X
) - width
;
245 Line
->BoundingBox
.X2
= MAX (Line
->Point1
.X
, Line
->Point2
.X
) + width
;
246 Line
->BoundingBox
.Y1
= MIN (Line
->Point1
.Y
, Line
->Point2
.Y
) - width
;
247 Line
->BoundingBox
.Y2
= MAX (Line
->Point1
.Y
, Line
->Point2
.Y
) + width
;
248 close_box(&Line
->BoundingBox
);
249 SetPointBoundingBox (&Line
->Point1
);
250 SetPointBoundingBox (&Line
->Point2
);
253 /* ---------------------------------------------------------------------------
254 * sets the bounding box of a polygons
257 SetPolygonBoundingBox (PolygonTypePtr Polygon
)
259 Polygon
->BoundingBox
.X1
= Polygon
->BoundingBox
.Y1
= MAX_COORD
;
260 Polygon
->BoundingBox
.X2
= Polygon
->BoundingBox
.Y2
= 0;
261 POLYGONPOINT_LOOP (Polygon
);
263 MAKEMIN (Polygon
->BoundingBox
.X1
, point
->X
);
264 MAKEMIN (Polygon
->BoundingBox
.Y1
, point
->Y
);
265 MAKEMAX (Polygon
->BoundingBox
.X2
, point
->X
);
266 MAKEMAX (Polygon
->BoundingBox
.Y2
, point
->Y
);
268 /* boxes don't include the lower right corner */
269 close_box(&Polygon
->BoundingBox
);
273 /* ---------------------------------------------------------------------------
274 * sets the bounding box of an elements
277 SetElementBoundingBox (DataTypePtr Data
, ElementTypePtr Element
,
280 BoxTypePtr box
, vbox
;
282 if (Data
&& Data
->element_tree
)
283 r_delete_entry (Data
->element_tree
, (BoxType
*) Element
);
284 /* first update the text objects */
285 ELEMENTTEXT_LOOP (Element
);
287 if (Data
&& Data
->name_tree
[n
])
288 r_delete_entry (Data
->name_tree
[n
], (BoxType
*) text
);
289 SetTextBoundingBox (Font
, text
);
290 if (Data
&& !Data
->name_tree
[n
])
291 Data
->name_tree
[n
] = r_create_tree (NULL
, 0, 0);
293 r_insert_entry (Data
->name_tree
[n
], (BoxType
*) text
, 0);
297 /* do not include the elementnames bounding box which
298 * is handled separately
300 box
= &Element
->BoundingBox
;
301 vbox
= &Element
->VBox
;
302 box
->X1
= box
->Y1
= MAX_COORD
;
303 box
->X2
= box
->Y2
= 0;
304 ELEMENTLINE_LOOP (Element
);
306 SetLineBoundingBox (line
);
307 MAKEMIN (box
->X1
, line
->Point1
.X
- (line
->Thickness
+ 1) / 2);
308 MAKEMIN (box
->Y1
, line
->Point1
.Y
- (line
->Thickness
+ 1) / 2);
309 MAKEMIN (box
->X1
, line
->Point2
.X
- (line
->Thickness
+ 1) / 2);
310 MAKEMIN (box
->Y1
, line
->Point2
.Y
- (line
->Thickness
+ 1) / 2);
311 MAKEMAX (box
->X2
, line
->Point1
.X
+ (line
->Thickness
+ 1) / 2);
312 MAKEMAX (box
->Y2
, line
->Point1
.Y
+ (line
->Thickness
+ 1) / 2);
313 MAKEMAX (box
->X2
, line
->Point2
.X
+ (line
->Thickness
+ 1) / 2);
314 MAKEMAX (box
->Y2
, line
->Point2
.Y
+ (line
->Thickness
+ 1) / 2);
319 SetArcBoundingBox (arc
);
320 MAKEMIN (box
->X1
, arc
->BoundingBox
.X1
);
321 MAKEMIN (box
->Y1
, arc
->BoundingBox
.Y1
);
322 MAKEMAX (box
->X2
, arc
->BoundingBox
.X2
);
323 MAKEMAX (box
->Y2
, arc
->BoundingBox
.Y2
);
329 if (Data
&& Data
->pin_tree
)
330 r_delete_entry (Data
->pin_tree
, (BoxType
*) pin
);
331 SetPinBoundingBox (pin
);
335 Data
->pin_tree
= r_create_tree (NULL
, 0, 0);
336 r_insert_entry (Data
->pin_tree
, (BoxType
*) pin
, 0);
338 MAKEMIN (box
->X1
, pin
->BoundingBox
.X1
);
339 MAKEMIN (box
->Y1
, pin
->BoundingBox
.Y1
);
340 MAKEMAX (box
->X2
, pin
->BoundingBox
.X2
);
341 MAKEMAX (box
->Y2
, pin
->BoundingBox
.Y2
);
342 MAKEMIN (vbox
->X1
, pin
->X
- pin
->Thickness
/ 2);
343 MAKEMIN (vbox
->Y1
, pin
->Y
- pin
->Thickness
/ 2);
344 MAKEMAX (vbox
->X2
, pin
->X
+ pin
->Thickness
/ 2);
345 MAKEMAX (vbox
->Y2
, pin
->Y
+ pin
->Thickness
/ 2);
350 if (Data
&& Data
->pad_tree
)
351 r_delete_entry (Data
->pad_tree
, (BoxType
*) pad
);
352 SetPadBoundingBox (pad
);
356 Data
->pad_tree
= r_create_tree (NULL
, 0, 0);
357 r_insert_entry (Data
->pad_tree
, (BoxType
*) pad
, 0);
359 MAKEMIN (box
->X1
, pad
->BoundingBox
.X1
);
360 MAKEMIN (box
->Y1
, pad
->BoundingBox
.Y1
);
361 MAKEMAX (box
->X2
, pad
->BoundingBox
.X2
);
362 MAKEMAX (box
->Y2
, pad
->BoundingBox
.Y2
);
364 MIN (pad
->Point1
.X
, pad
->Point2
.X
) - pad
->Thickness
/ 2);
366 MIN (pad
->Point1
.Y
, pad
->Point2
.Y
) - pad
->Thickness
/ 2);
368 MAX (pad
->Point1
.X
, pad
->Point2
.X
) + pad
->Thickness
/ 2);
370 MAX (pad
->Point1
.Y
, pad
->Point2
.Y
) + pad
->Thickness
/ 2);
373 /* now we set the EDGE2FLAG of the pad if Point2
374 * is closer to the outside edge than Point1
378 if (pad
->Point1
.Y
== pad
->Point2
.Y
)
381 if (box
->X2
- pad
->Point2
.X
< pad
->Point1
.X
- box
->X1
)
382 SET_FLAG (EDGE2FLAG
, pad
);
384 CLEAR_FLAG (EDGE2FLAG
, pad
);
389 if (box
->Y2
- pad
->Point2
.Y
< pad
->Point1
.Y
- box
->Y1
)
390 SET_FLAG (EDGE2FLAG
, pad
);
392 CLEAR_FLAG (EDGE2FLAG
, pad
);
397 /* mark pins with component orientation */
398 if ((box
->X2
- box
->X1
) > (box
->Y2
- box
->Y1
))
402 SET_FLAG (EDGE2FLAG
, pin
);
410 CLEAR_FLAG (EDGE2FLAG
, pin
);
416 if (Data
&& !Data
->element_tree
)
417 Data
->element_tree
= r_create_tree (NULL
, 0, 0);
419 r_insert_entry (Data
->element_tree
, box
, 0);
422 /* ---------------------------------------------------------------------------
423 * creates the bounding box of a text object
426 SetTextBoundingBox (FontTypePtr FontPtr
, TextTypePtr Text
)
428 SymbolTypePtr symbol
= FontPtr
->Symbol
;
429 unsigned char *s
= (unsigned char *) Text
->TextString
;
430 LocationType width
= 0, height
= 0;
431 BDimension maxThick
= 0;
434 /* calculate size of the bounding box */
436 if (*s
<= MAX_FONTPOSITION
&& symbol
[*s
].Valid
)
438 LineTypePtr line
= symbol
[*s
].Line
;
439 for (i
= 0; i
< symbol
[*s
].LineN
; line
++, i
++)
440 if (line
->Thickness
> maxThick
)
441 maxThick
= line
->Thickness
;
442 width
+= symbol
[*s
].Width
+ symbol
[*s
].Delta
;
443 height
= MAX (height
, (LocationType
) symbol
[*s
].Height
);
448 ((FontPtr
->DefaultSymbol
.X2
- FontPtr
->DefaultSymbol
.X1
) * 6 / 5);
449 height
= (FontPtr
->DefaultSymbol
.Y2
- FontPtr
->DefaultSymbol
.Y1
);
453 width
*= Text
->Scale
/ 100.;
454 height
*= Text
->Scale
/ 100.;
455 maxThick
*= Text
->Scale
/ 200.;
459 /* set upper-left and lower-right corner;
460 * swap coordinates if necessary (origin is already in 'swapped')
463 Text
->BoundingBox
.X1
= Text
->X
;
464 Text
->BoundingBox
.Y1
= Text
->Y
;
465 if (TEST_FLAG (ONSOLDERFLAG
, Text
))
467 Text
->BoundingBox
.X1
-= maxThick
;
468 Text
->BoundingBox
.Y1
-= SWAP_SIGN_Y (maxThick
);
469 Text
->BoundingBox
.X2
=
470 Text
->BoundingBox
.X1
+ SWAP_SIGN_X (width
+ maxThick
);
471 Text
->BoundingBox
.Y2
=
472 Text
->BoundingBox
.Y1
+ SWAP_SIGN_Y (height
+ 2 * maxThick
);
473 RotateBoxLowLevel (&Text
->BoundingBox
, Text
->X
, Text
->Y
,
474 (4 - Text
->Direction
) & 0x03);
478 Text
->BoundingBox
.X1
-= maxThick
;
479 Text
->BoundingBox
.Y1
-= maxThick
;
480 Text
->BoundingBox
.X2
= Text
->BoundingBox
.X1
+ width
+ maxThick
;
481 Text
->BoundingBox
.Y2
= Text
->BoundingBox
.Y1
+ height
+ 2 * maxThick
;
482 RotateBoxLowLevel (&Text
->BoundingBox
,
483 Text
->X
, Text
->Y
, Text
->Direction
);
486 /* the bounding box covers the extent of influence
487 * so it must include the clearance values too
489 Text
->BoundingBox
.X1
-= PCB
->Bloat
;
490 Text
->BoundingBox
.Y1
-= PCB
->Bloat
;
491 Text
->BoundingBox
.X2
+= PCB
->Bloat
;
492 Text
->BoundingBox
.Y2
+= PCB
->Bloat
;
493 close_box(&Text
->BoundingBox
);
496 /* ---------------------------------------------------------------------------
497 * returns true if data area is empty
500 IsDataEmpty (DataTypePtr Data
)
505 hasNoObjects
= (Data
->ViaN
== 0);
506 hasNoObjects
&= (Data
->ElementN
== 0);
507 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
508 hasNoObjects
= hasNoObjects
&&
509 Data
->Layer
[i
].LineN
== 0 &&
510 Data
->Layer
[i
].ArcN
== 0 &&
511 Data
->Layer
[i
].TextN
== 0 && Data
->Layer
[i
].PolygonN
== 0;
512 return (hasNoObjects
);
516 FlagIsDataEmpty (int parm
)
518 int i
= IsDataEmpty (PCB
->Data
);
519 return parm
? !i
: i
;
522 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
523 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
525 /* ---------------------------------------------------------------------------
526 * gets minimum and maximum coordinates
527 * returns NULL if layout is empty
530 GetDataBoundingBox (DataTypePtr Data
)
533 /* FIX ME: use r_search to do this much faster */
535 /* preset identifiers with highest and lowest possible values */
536 box
.X1
= box
.Y1
= MAX_COORD
;
537 box
.X2
= box
.Y2
= -MAX_COORD
;
539 /* now scan for the lowest/highest X and Y coordinate */
542 box
.X1
= MIN (box
.X1
, via
->X
- via
->Thickness
/ 2);
543 box
.Y1
= MIN (box
.Y1
, via
->Y
- via
->Thickness
/ 2);
544 box
.X2
= MAX (box
.X2
, via
->X
+ via
->Thickness
/ 2);
545 box
.Y2
= MAX (box
.Y2
, via
->Y
+ via
->Thickness
/ 2);
550 box
.X1
= MIN (box
.X1
, element
->BoundingBox
.X1
);
551 box
.Y1
= MIN (box
.Y1
, element
->BoundingBox
.Y1
);
552 box
.X2
= MAX (box
.X2
, element
->BoundingBox
.X2
);
553 box
.Y2
= MAX (box
.Y2
, element
->BoundingBox
.Y2
);
555 TextTypePtr text
= &NAMEONPCB_TEXT (element
);
556 box
.X1
= MIN (box
.X1
, text
->BoundingBox
.X1
);
557 box
.Y1
= MIN (box
.Y1
, text
->BoundingBox
.Y1
);
558 box
.X2
= MAX (box
.X2
, text
->BoundingBox
.X2
);
559 box
.Y2
= MAX (box
.Y2
, text
->BoundingBox
.Y2
);
565 box
.X1
= MIN (box
.X1
, line
->Point1
.X
- line
->Thickness
/ 2);
566 box
.Y1
= MIN (box
.Y1
, line
->Point1
.Y
- line
->Thickness
/ 2);
567 box
.X1
= MIN (box
.X1
, line
->Point2
.X
- line
->Thickness
/ 2);
568 box
.Y1
= MIN (box
.Y1
, line
->Point2
.Y
- line
->Thickness
/ 2);
569 box
.X2
= MAX (box
.X2
, line
->Point1
.X
+ line
->Thickness
/ 2);
570 box
.Y2
= MAX (box
.Y2
, line
->Point1
.Y
+ line
->Thickness
/ 2);
571 box
.X2
= MAX (box
.X2
, line
->Point2
.X
+ line
->Thickness
/ 2);
572 box
.Y2
= MAX (box
.Y2
, line
->Point2
.Y
+ line
->Thickness
/ 2);
577 box
.X1
= MIN (box
.X1
, arc
->BoundingBox
.X1
);
578 box
.Y1
= MIN (box
.Y1
, arc
->BoundingBox
.Y1
);
579 box
.X2
= MAX (box
.X2
, arc
->BoundingBox
.X2
);
580 box
.Y2
= MAX (box
.Y2
, arc
->BoundingBox
.Y2
);
585 box
.X1
= MIN (box
.X1
, text
->BoundingBox
.X1
);
586 box
.Y1
= MIN (box
.Y1
, text
->BoundingBox
.Y1
);
587 box
.X2
= MAX (box
.X2
, text
->BoundingBox
.X2
);
588 box
.Y2
= MAX (box
.Y2
, text
->BoundingBox
.Y2
);
591 ALLPOLYGON_LOOP (Data
);
593 box
.X1
= MIN (box
.X1
, polygon
->BoundingBox
.X1
);
594 box
.Y1
= MIN (box
.Y1
, polygon
->BoundingBox
.Y1
);
595 box
.X2
= MAX (box
.X2
, polygon
->BoundingBox
.X2
);
596 box
.Y2
= MAX (box
.Y2
, polygon
->BoundingBox
.Y2
);
599 return (IsDataEmpty (Data
) ? NULL
: &box
);
602 /* ---------------------------------------------------------------------------
603 * centers the displayed PCB around the specified point (X,Y)
604 * if Delta is false, X,Y are in absolute PCB coordinates
605 * if Delta is true, simply move the center by an amount X, Y in screen
609 CenterDisplay (LocationType X
, LocationType Y
, bool Delta
)
611 double save_grid
= PCB
->Grid
;
615 MoveCrosshairRelative (X
, Y
);
619 if (MoveCrosshairAbsolute (X
, Y
))
621 RestoreCrosshair(false);
624 gui
->set_crosshair (Crosshair
.X
, Crosshair
.Y
, HID_SC_WARP_POINTER
);
625 PCB
->Grid
= save_grid
;
628 /* ---------------------------------------------------------------------------
629 * transforms symbol coordinates so that the left edge of each symbol
630 * is at the zero position. The y coordinates are moved so that min(y) = 0
634 SetFontInfo (FontTypePtr Ptr
)
637 SymbolTypePtr symbol
;
639 LocationType totalminy
= MAX_COORD
;
641 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
642 * maximum cell width and height
643 * minimum x and y position of all lines
645 Ptr
->MaxWidth
= DEFAULT_CELLSIZE
;
646 Ptr
->MaxHeight
= DEFAULT_CELLSIZE
;
647 for (i
= 0, symbol
= Ptr
->Symbol
; i
<= MAX_FONTPOSITION
; i
++, symbol
++)
649 LocationType minx
, miny
, maxx
, maxy
;
651 /* next one if the index isn't used or symbol is empty (SPACE) */
652 if (!symbol
->Valid
|| !symbol
->LineN
)
655 minx
= miny
= MAX_COORD
;
657 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
659 minx
= MIN (minx
, line
->Point1
.X
);
660 miny
= MIN (miny
, line
->Point1
.Y
);
661 minx
= MIN (minx
, line
->Point2
.X
);
662 miny
= MIN (miny
, line
->Point2
.Y
);
663 maxx
= MAX (maxx
, line
->Point1
.X
);
664 maxy
= MAX (maxy
, line
->Point1
.Y
);
665 maxx
= MAX (maxx
, line
->Point2
.X
);
666 maxy
= MAX (maxy
, line
->Point2
.Y
);
669 /* move symbol to left edge */
670 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
671 MOVE_LINE_LOWLEVEL (line
, -minx
, 0);
673 /* set symbol bounding box with a minimum cell size of (1,1) */
674 symbol
->Width
= maxx
- minx
+ 1;
675 symbol
->Height
= maxy
+ 1;
677 /* check total min/max */
678 Ptr
->MaxWidth
= MAX (Ptr
->MaxWidth
, symbol
->Width
);
679 Ptr
->MaxHeight
= MAX (Ptr
->MaxHeight
, symbol
->Height
);
680 totalminy
= MIN (totalminy
, miny
);
683 /* move coordinate system to the upper edge (lowest y on screen) */
684 for (i
= 0, symbol
= Ptr
->Symbol
; i
<= MAX_FONTPOSITION
; i
++, symbol
++)
687 symbol
->Height
-= totalminy
;
688 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
689 MOVE_LINE_LOWLEVEL (line
, 0, -totalminy
);
692 /* setup the box for the default symbol */
693 Ptr
->DefaultSymbol
.X1
= Ptr
->DefaultSymbol
.Y1
= 0;
694 Ptr
->DefaultSymbol
.X2
= Ptr
->DefaultSymbol
.X1
+ Ptr
->MaxWidth
;
695 Ptr
->DefaultSymbol
.Y2
= Ptr
->DefaultSymbol
.Y1
+ Ptr
->MaxHeight
;
699 GetNum (char **s
, BDimension
* num
)
702 while (isdigit ((int) **s
))
707 /* ----------------------------------------------------------------------
708 * parses the routes definition string which is a colon separated list of
709 * comma separated Name, Dimension, Dimension, Dimension, Dimension
710 * e.g. Signal,20,40,20,10:Power,40,60,28,10:...
713 ParseRouteString (char *s
, RouteStyleTypePtr routeStyle
, int scale
)
718 memset (routeStyle
, 0, NUM_STYLES
* sizeof (RouteStyleType
));
719 for (style
= 0; style
< NUM_STYLES
; style
++, routeStyle
++)
721 while (*s
&& isspace ((int) *s
))
723 for (i
= 0; *s
&& *s
!= ','; i
++)
726 routeStyle
->Name
= strdup (Name
);
727 if (!isdigit ((int) *++s
))
729 GetNum (&s
, &routeStyle
->Thick
);
730 routeStyle
->Thick
*= scale
;
731 while (*s
&& isspace ((int) *s
))
735 while (*s
&& isspace ((int) *s
))
737 if (!isdigit ((int) *s
))
739 GetNum (&s
, &routeStyle
->Diameter
);
740 routeStyle
->Diameter
*= scale
;
741 while (*s
&& isspace ((int) *s
))
745 while (*s
&& isspace ((int) *s
))
747 if (!isdigit ((int) *s
))
749 GetNum (&s
, &routeStyle
->Hole
);
750 routeStyle
->Hole
*= scale
;
751 /* for backwards-compatibility, we use a 10-mil default
752 * for styles which omit the keepaway specification. */
754 routeStyle
->Keepaway
= 1000;
758 while (*s
&& isspace ((int) *s
))
760 if (!isdigit ((int) *s
))
762 GetNum (&s
, &routeStyle
->Keepaway
);
763 routeStyle
->Keepaway
*= scale
;
764 while (*s
&& isspace ((int) *s
))
767 if (style
< NUM_STYLES
- 1)
769 while (*s
&& isspace ((int) *s
))
778 memset (routeStyle
, 0, NUM_STYLES
* sizeof (RouteStyleType
));
782 /* ----------------------------------------------------------------------
783 * parses the group definition string which is a colon separated list of
784 * comma separated layer numbers (1,2,b:4,6,8,t)
787 ParseGroupString (char *s
, LayerGroupTypePtr LayerGroup
, int LayerN
)
789 int group
, member
, layer
;
790 bool c_set
= false, /* flags for the two special layers to */
791 s_set
= false; /* provide a default setting for old formats */
792 int groupnum
[MAX_LAYER
+ 2];
795 memset (LayerGroup
, 0, sizeof (LayerGroupType
));
797 /* Clear assignments */
798 for (layer
= 0; layer
< MAX_LAYER
+ 2; layer
++)
799 groupnum
[layer
] = -1;
801 /* loop over all groups */
802 for (group
= 0; s
&& *s
&& group
< LayerN
; group
++)
804 while (*s
&& isspace ((int) *s
))
807 /* loop over all group members */
808 for (member
= 0; *s
; s
++)
810 /* ignore white spaces and get layernumber */
811 while (*s
&& isspace ((int) *s
))
817 layer
= LayerN
+ COMPONENT_LAYER
;
823 layer
= LayerN
+ SOLDER_LAYER
;
828 if (!isdigit ((int) *s
))
830 layer
= atoi (s
) - 1;
833 if (layer
> LayerN
+ MAX (SOLDER_LAYER
, COMPONENT_LAYER
) ||
834 member
>= LayerN
+ 1)
836 groupnum
[layer
] = group
;
837 LayerGroup
->Entries
[group
][member
++] = layer
;
838 while (*++s
&& isdigit ((int) *s
));
840 /* ignore white spaces and check for separator */
841 while (*s
&& isspace ((int) *s
))
843 if (!*s
|| *s
== ':')
848 LayerGroup
->Number
[group
] = member
;
853 LayerGroup
->Entries
[SOLDER_LAYER
][LayerGroup
->Number
[SOLDER_LAYER
]++] =
854 LayerN
+ SOLDER_LAYER
;
857 Entries
[COMPONENT_LAYER
][LayerGroup
->Number
[COMPONENT_LAYER
]++] =
858 LayerN
+ COMPONENT_LAYER
;
860 for (layer
= 0; layer
< LayerN
&& group
< LayerN
; layer
++)
861 if (groupnum
[layer
] == -1)
863 LayerGroup
->Entries
[group
][0] = layer
;
864 LayerGroup
->Number
[group
] = 1;
869 /* reset structure on error */
871 memset (LayerGroup
, 0, sizeof (LayerGroupType
));
875 /* ---------------------------------------------------------------------------
879 QuitApplication (void)
882 * save data if necessary. It not needed, then don't trigger EmergencySave
883 * via our atexit() registering of EmergencySave(). We presumeably wanted to
884 * exit here and thus it is not an emergency.
886 if (PCB
->Changed
&& Settings
.SaveInTMP
)
889 DisableEmergencySave ();
891 /* Free up memory allocated to the PCB. Why bother when we're about to exit ?
892 * Because it removes some false positives from heap bug detectors such as
900 /* ---------------------------------------------------------------------------
901 * creates a filename from a template
902 * %f is replaced by the filename
903 * %p by the searchpath
906 EvaluateFilename (char *Template
, char *Path
, char *Filename
, char *Parameter
)
908 static DynamicStringType command
;
911 if (Settings
.verbose
)
913 printf ("EvaluateFilename:\n");
914 printf ("\tTemplate: \033[33m%s\033[0m\n", Template
);
915 printf ("\tPath: \033[33m%s\033[0m\n", Path
);
916 printf ("\tFilename: \033[33m%s\033[0m\n", Filename
);
917 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter
);
920 DSClearString (&command
);
922 for (p
= Template
; p
&& *p
; p
++)
924 /* copy character or add string to command */
926 && (*(p
+ 1) == 'f' || *(p
+ 1) == 'p' || *(p
+ 1) == 'a'))
930 DSAddString (&command
, Parameter
);
933 DSAddString (&command
, Filename
);
936 DSAddString (&command
, Path
);
940 DSAddCharacter (&command
, *p
);
942 DSAddCharacter (&command
, '\0');
943 if (Settings
.verbose
)
944 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command
.Data
);
946 return strdup (command
.Data
);
949 /* ---------------------------------------------------------------------------
950 * concatenates directory and filename if directory != NULL,
951 * expands them with a shell and returns the found name(s) or NULL
954 ExpandFilename (char *Dirname
, char *Filename
)
956 static DynamicStringType answer
;
961 /* allocate memory for commandline and build it */
962 DSClearString (&answer
);
965 command
= calloc (strlen (Filename
) + strlen (Dirname
) + 7,
967 sprintf (command
, "echo %s/%s", Dirname
, Filename
);
971 command
= calloc (strlen (Filename
) + 6, sizeof (char));
972 sprintf (command
, "echo %s", Filename
);
975 /* execute it with shell */
976 if ((pipe
= popen (command
, "r")) != NULL
)
978 /* discard all but the first returned line */
981 if ((c
= fgetc (pipe
)) == EOF
|| c
== '\n' || c
== '\r')
984 DSAddCharacter (&answer
, c
);
988 return (pclose (pipe
) ? NULL
: answer
.Data
);
991 /* couldn't be expanded by the shell */
992 PopenErrorMessage (command
);
998 /* ---------------------------------------------------------------------------
999 * returns the layer number for the passed pointer
1002 GetLayerNumber (DataTypePtr Data
, LayerTypePtr Layer
)
1006 for (i
= 0; i
< MAX_LAYER
+ 2; i
++)
1007 if (Layer
== &Data
->Layer
[i
])
1012 /* ---------------------------------------------------------------------------
1013 * move layer (number is passed in) to top of layerstack
1016 PushOnTopOfLayerStack (int NewTop
)
1020 /* ignore silk layers */
1021 if (NewTop
< max_copper_layer
)
1023 /* first find position of passed one */
1024 for (i
= 0; i
< max_copper_layer
; i
++)
1025 if (LayerStack
[i
] == NewTop
)
1028 /* bring this element to the top of the stack */
1030 LayerStack
[i
] = LayerStack
[i
- 1];
1031 LayerStack
[0] = NewTop
;
1036 /* ----------------------------------------------------------------------
1037 * changes the visibility of all layers in a group
1038 * returns the number of changed layers
1041 ChangeGroupVisibility (int Layer
, bool On
, bool ChangeStackOrder
)
1043 int group
, i
, changed
= 1; /* at least the current layer changes */
1045 /* Warning: these special case values must agree with what gui-top-window.c
1049 if (Settings
.verbose
)
1050 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1051 Layer
, On
, ChangeStackOrder
);
1053 /* decrement 'i' to keep stack in order of layergroup */
1054 if ((group
= GetGroupOfLayer (Layer
)) < max_group
)
1055 for (i
= PCB
->LayerGroups
.Number
[group
]; i
;)
1057 int layer
= PCB
->LayerGroups
.Entries
[group
][--i
];
1059 /* don't count the passed member of the group */
1060 if (layer
!= Layer
&& layer
< max_copper_layer
)
1062 PCB
->Data
->Layer
[layer
].On
= On
;
1064 /* push layer on top of stack if switched on */
1065 if (On
&& ChangeStackOrder
)
1066 PushOnTopOfLayerStack (layer
);
1071 /* change at least the passed layer */
1072 PCB
->Data
->Layer
[Layer
].On
= On
;
1073 if (On
&& ChangeStackOrder
)
1074 PushOnTopOfLayerStack (Layer
);
1076 /* update control panel and exit */
1077 hid_action ("LayersChanged");
1081 /* ----------------------------------------------------------------------
1082 * Given a string description of a layer stack, adjust the layer stack
1087 LayerStringToLayerStack (char *s
)
1089 static int listed_layers
= 0;
1096 args
= (char **) malloc (l
* sizeof (char *));
1119 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
1121 if (i
< max_copper_layer
)
1123 PCB
->Data
->Layer
[i
].On
= false;
1125 PCB
->ElementOn
= false;
1126 PCB
->InvisibleObjectsOn
= false;
1130 CLEAR_FLAG (SHOWMASKFLAG
, PCB
);
1131 Settings
.ShowSolderSide
= 0;
1133 for (i
=argn
-1; i
>=0; i
--)
1135 if (strcasecmp (args
[i
], "rats") == 0)
1137 else if (strcasecmp (args
[i
], "invisible") == 0)
1138 PCB
->InvisibleObjectsOn
= true;
1139 else if (strcasecmp (args
[i
], "pins") == 0)
1141 else if (strcasecmp (args
[i
], "vias") == 0)
1143 else if (strcasecmp (args
[i
], "elements") == 0
1144 || strcasecmp (args
[i
], "silk") == 0)
1145 PCB
->ElementOn
= true;
1146 else if (strcasecmp (args
[i
], "mask") == 0)
1147 SET_FLAG (SHOWMASKFLAG
, PCB
);
1148 else if (strcasecmp (args
[i
], "solderside") == 0)
1149 Settings
.ShowSolderSide
= 1;
1150 else if (isdigit ((int) args
[i
][0]))
1152 lno
= atoi (args
[i
]);
1153 ChangeGroupVisibility (lno
, true, true);
1158 for (lno
= 0; lno
< max_copper_layer
; lno
++)
1159 if (strcasecmp (args
[i
], PCB
->Data
->Layer
[lno
].Name
) == 0)
1161 ChangeGroupVisibility (lno
, true, true);
1167 fprintf(stderr
, "Warning: layer \"%s\" not known\n", args
[i
]);
1170 fprintf (stderr
, "Named layers in this board are:\n");
1172 for (lno
=0; lno
< max_copper_layer
; lno
++)
1173 fprintf(stderr
, "\t%s\n", PCB
->Data
->Layer
[lno
].Name
);
1174 fprintf(stderr
, "Also: component, solder, rats, invisible, pins, vias, elements or silk, mask, solderside.\n");
1181 /* ----------------------------------------------------------------------
1182 * lookup the group to which a layer belongs to
1183 * returns max_group if no group is found, or is
1184 * passed Layer is equal to max_copper_layer
1187 GetGroupOfLayer (int Layer
)
1191 if (Layer
== max_copper_layer
)
1193 for (group
= 0; group
< max_group
; group
++)
1194 for (i
= 0; i
< PCB
->LayerGroups
.Number
[group
]; i
++)
1195 if (PCB
->LayerGroups
.Entries
[group
][i
] == Layer
)
1201 /* ---------------------------------------------------------------------------
1202 * returns the layergroup number for the passed pointer
1205 GetLayerGroupNumberByPointer (LayerTypePtr Layer
)
1207 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB
->Data
, Layer
)));
1210 /* ---------------------------------------------------------------------------
1211 * returns the layergroup number for the passed layernumber
1214 GetLayerGroupNumberByNumber (Cardinal Layer
)
1218 for (group
= 0; group
< max_group
; group
++)
1219 for (entry
= 0; entry
< PCB
->LayerGroups
.Number
[group
]; entry
++)
1220 if (PCB
->LayerGroups
.Entries
[group
][entry
] == Layer
)
1223 /* since every layer belongs to a group it is safe to return
1224 * the value without boundary checking
1229 /* ---------------------------------------------------------------------------
1230 * returns a pointer to an objects bounding box;
1231 * data is valid until the routine is called again
1234 GetObjectBoundingBox (int Type
, void *Ptr1
, void *Ptr2
, void *Ptr3
)
1244 case ELEMENTNAME_TYPE
:
1245 return (BoxType
*)Ptr2
;
1248 return (BoxType
*)Ptr1
;
1249 case POLYGONPOINT_TYPE
:
1250 case LINEPOINT_TYPE
:
1251 return (BoxType
*)Ptr3
;
1253 Message ("Request for bounding box of unsupported type %d\n", Type
);
1254 return (BoxType
*)Ptr2
;
1258 /* ---------------------------------------------------------------------------
1259 * computes the bounding box of an arc
1262 SetArcBoundingBox (ArcTypePtr Arc
)
1264 register double ca1
, ca2
, sa1
, sa2
;
1265 double minx
, maxx
, miny
, maxy
;
1266 register LocationType ang1
, ang2
, delta
, a
;
1267 register LocationType width
;
1269 /* first put angles into standard form */
1270 if (Arc
->Delta
> 360)
1272 if (Arc
->Delta
< -360)
1277 ang1
= Arc
->StartAngle
;
1282 ang1
= Arc
->StartAngle
+ Arc
->Delta
;
1283 delta
= -Arc
->Delta
;
1286 ang1
= 360 - ((-ang1
) % 360);
1290 ang2
= ang1
+ delta
;
1292 /* calculate sines, cosines */
1293 ca1
= M180
* (double) ang1
;
1300 ca2
= M180
* (double) ang2
;
1304 minx
= MIN (minx
, ca2
);
1305 maxx
= MAX (maxx
, ca2
);
1306 miny
= MIN (miny
, sa2
);
1307 maxy
= MAX (maxy
, sa2
);
1309 for (a
= ang1
- ang1
% 90 + 90; a
< ang2
; a
+= 90)
1328 Arc
->BoundingBox
.X2
= Arc
->X
- Arc
->Width
* minx
;
1330 Arc
->BoundingBox
.X1
= Arc
->X
- Arc
->Width
* maxx
;
1332 Arc
->BoundingBox
.Y2
= Arc
->Y
+ Arc
->Height
* maxy
;
1334 Arc
->BoundingBox
.Y1
= Arc
->Y
+ Arc
->Height
* miny
;
1336 width
= (Arc
->Thickness
+ Arc
->Clearance
) / 2;
1337 Arc
->BoundingBox
.X1
-= width
;
1338 Arc
->BoundingBox
.X2
+= width
;
1339 Arc
->BoundingBox
.Y1
-= width
;
1340 Arc
->BoundingBox
.Y2
+= width
;
1341 close_box(&Arc
->BoundingBox
);
1344 /* ---------------------------------------------------------------------------
1345 * resets the layerstack setting
1348 ResetStackAndVisibility (void)
1353 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
1355 if (i
< max_copper_layer
)
1357 PCB
->Data
->Layer
[i
].On
= true;
1359 PCB
->ElementOn
= true;
1360 PCB
->InvisibleObjectsOn
= true;
1365 /* Bring the component group to the front and make it active. */
1366 comp_group
= GetLayerGroupNumberByNumber (component_silk_layer
);
1367 ChangeGroupVisibility (PCB
->LayerGroups
.Entries
[comp_group
][0], 1, 1);
1370 /* ---------------------------------------------------------------------------
1371 * saves the layerstack setting
1374 SaveStackAndVisibility (void)
1377 static bool run
= false;
1385 if (SavedStack
.cnt
!= 0)
1388 "SaveStackAndVisibility() layerstack was already saved and not"
1389 "yet restored. cnt = %d\n", SavedStack
.cnt
);
1392 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
1394 if (i
< max_copper_layer
)
1395 SavedStack
.LayerStack
[i
] = LayerStack
[i
];
1396 SavedStack
.LayerOn
[i
] = PCB
->Data
->Layer
[i
].On
;
1398 SavedStack
.ElementOn
= PCB
->ElementOn
;
1399 SavedStack
.InvisibleObjectsOn
= PCB
->InvisibleObjectsOn
;
1400 SavedStack
.PinOn
= PCB
->PinOn
;
1401 SavedStack
.ViaOn
= PCB
->ViaOn
;
1402 SavedStack
.RatOn
= PCB
->RatOn
;
1406 /* ---------------------------------------------------------------------------
1407 * restores the layerstack setting
1410 RestoreStackAndVisibility (void)
1414 if (SavedStack
.cnt
== 0)
1416 fprintf (stderr
, "RestoreStackAndVisibility() layerstack has not"
1417 " been saved. cnt = %d\n", SavedStack
.cnt
);
1420 else if (SavedStack
.cnt
!= 1)
1422 fprintf (stderr
, "RestoreStackAndVisibility() layerstack save count is"
1423 " wrong. cnt = %d\n", SavedStack
.cnt
);
1426 for (i
= 0; i
< max_copper_layer
+ 2; i
++)
1428 if (i
< max_copper_layer
)
1429 LayerStack
[i
] = SavedStack
.LayerStack
[i
];
1430 PCB
->Data
->Layer
[i
].On
= SavedStack
.LayerOn
[i
];
1432 PCB
->ElementOn
= SavedStack
.ElementOn
;
1433 PCB
->InvisibleObjectsOn
= SavedStack
.InvisibleObjectsOn
;
1434 PCB
->PinOn
= SavedStack
.PinOn
;
1435 PCB
->ViaOn
= SavedStack
.ViaOn
;
1436 PCB
->RatOn
= SavedStack
.RatOn
;
1441 /* ----------------------------------------------------------------------
1442 * returns pointer to current working directory. If 'path' is not
1443 * NULL, then the current working directory is copied to the array
1444 * pointed to by 'path'
1447 GetWorkingDirectory (char *path
)
1450 return getcwd (path
, MAXPATHLEN
);
1452 /* seems that some BSD releases lack of a prototype for getwd() */
1453 return getwd (path
);
1458 /* ---------------------------------------------------------------------------
1459 * writes a string to the passed file pointer
1460 * some special characters are quoted
1463 CreateQuotedString (DynamicStringTypePtr DS
, char *S
)
1466 DSAddCharacter (DS
, '"');
1469 if (*S
== '"' || *S
== '\\')
1470 DSAddCharacter (DS
, '\\');
1471 DSAddCharacter (DS
, *S
++);
1473 DSAddCharacter (DS
, '"');
1478 RightAngles (int Angle
, float *cosa
, float *sina
)
1480 *cosa
= (float) cos ((double) Angle
* M180
);
1481 *sina
= (float) sin ((double) Angle
* M180
);
1485 GetArcEnds (ArcTypePtr Arc
)
1490 RightAngles (Arc
->StartAngle
, &ca
, &sa
);
1491 box
.X1
= Arc
->X
- Arc
->Width
* ca
;
1492 box
.Y1
= Arc
->Y
+ Arc
->Height
* sa
;
1493 RightAngles (Arc
->StartAngle
+ Arc
->Delta
, &ca
, &sa
);
1494 box
.X2
= Arc
->X
- Arc
->Width
* ca
;
1495 box
.Y2
= Arc
->Y
+ Arc
->Height
* sa
;
1500 /* doesn't this belong in change.c ?? */
1502 ChangeArcAngles (LayerTypePtr Layer
, ArcTypePtr a
,
1503 long int new_sa
, long int new_da
)
1510 RestoreToPolygon (PCB
->Data
, ARC_TYPE
, Layer
, a
);
1511 r_delete_entry (Layer
->arc_tree
, (BoxTypePtr
) a
);
1512 AddObjectToChangeAnglesUndoList (ARC_TYPE
, a
, a
, a
);
1513 a
->StartAngle
= new_sa
;
1515 SetArcBoundingBox (a
);
1516 r_insert_entry (Layer
->arc_tree
, (BoxTypePtr
) a
, 0);
1517 ClearFromPolygon (PCB
->Data
, ARC_TYPE
, Layer
, a
);
1521 BumpName (char *Name
)
1525 static char temp
[256];
1528 /* seek end of string */
1531 /* back up to potential number */
1532 for (Name
--; isdigit ((int) *Name
); Name
--);
1535 num
= atoi (Name
) + 1;
1540 sprintf (temp
, "%s%d", start
, num
);
1541 /* if this is not our string, put back the blown character */
1548 * make a unique name for the name on board
1549 * this can alter the contents of the input string
1552 UniqueElementName (DataTypePtr Data
, char *Name
)
1555 /* null strings are ok */
1556 if (!Name
|| !*Name
)
1561 ELEMENT_LOOP (Data
);
1563 if (NAMEONPCB_NAME (element
) &&
1564 NSTRCMP (NAMEONPCB_NAME (element
), Name
) == 0)
1566 Name
= BumpName (Name
);
1579 GetGridLockCoordinates (int type
, void *ptr1
,
1580 void *ptr2
, void *ptr3
, LocationType
* x
,
1586 *x
= ((PinTypePtr
) ptr2
)->X
;
1587 *y
= ((PinTypePtr
) ptr2
)->Y
;
1590 *x
= ((LineTypePtr
) ptr2
)->Point1
.X
;
1591 *y
= ((LineTypePtr
) ptr2
)->Point1
.Y
;
1594 case ELEMENTNAME_TYPE
:
1595 *x
= ((TextTypePtr
) ptr2
)->X
;
1596 *y
= ((TextTypePtr
) ptr2
)->Y
;
1599 *x
= ((ElementTypePtr
) ptr2
)->MarkX
;
1600 *y
= ((ElementTypePtr
) ptr2
)->MarkY
;
1603 *x
= ((PolygonTypePtr
) ptr2
)->Points
[0].X
;
1604 *y
= ((PolygonTypePtr
) ptr2
)->Points
[0].Y
;
1607 case LINEPOINT_TYPE
:
1608 case POLYGONPOINT_TYPE
:
1609 *x
= ((PointTypePtr
) ptr3
)->X
;
1610 *y
= ((PointTypePtr
) ptr3
)->Y
;
1616 box
= GetArcEnds ((ArcTypePtr
) ptr2
);
1625 AttachForCopy (LocationType PlaceX
, LocationType PlaceY
)
1628 LocationType mx
= 0, my
= 0;
1630 Crosshair
.AttachedObject
.RubberbandN
= 0;
1631 if (! TEST_FLAG (SNAPPINFLAG
, PCB
))
1633 /* dither the grab point so that the mark, center, etc
1634 * will end up on a grid coordinate
1636 GetGridLockCoordinates (Crosshair
.AttachedObject
.Type
,
1637 Crosshair
.AttachedObject
.Ptr1
,
1638 Crosshair
.AttachedObject
.Ptr2
,
1639 Crosshair
.AttachedObject
.Ptr3
, &mx
, &my
);
1640 mx
= GRIDFIT_X (mx
, PCB
->Grid
) - mx
;
1641 my
= GRIDFIT_Y (my
, PCB
->Grid
) - my
;
1643 Crosshair
.AttachedObject
.X
= PlaceX
- mx
;
1644 Crosshair
.AttachedObject
.Y
= PlaceY
- my
;
1645 if (!Marked
.status
|| TEST_FLAG (LOCALREFFLAG
, PCB
))
1646 SetLocalRef (PlaceX
- mx
, PlaceY
- my
, true);
1647 Crosshair
.AttachedObject
.State
= STATE_SECOND
;
1649 /* get boundingbox of object and set cursor range */
1650 box
= GetObjectBoundingBox (Crosshair
.AttachedObject
.Type
,
1651 Crosshair
.AttachedObject
.Ptr1
,
1652 Crosshair
.AttachedObject
.Ptr2
,
1653 Crosshair
.AttachedObject
.Ptr3
);
1654 SetCrosshairRange (Crosshair
.AttachedObject
.X
- box
->X1
,
1655 Crosshair
.AttachedObject
.Y
- box
->Y1
,
1656 PCB
->MaxWidth
- (box
->X2
- Crosshair
.AttachedObject
.X
),
1657 PCB
->MaxHeight
- (box
->Y2
- Crosshair
.AttachedObject
.Y
));
1659 /* get all attached objects if necessary */
1660 if ((Settings
.Mode
!= COPY_MODE
) && TEST_FLAG (RUBBERBANDFLAG
, PCB
))
1661 LookupRubberbandLines (Crosshair
.AttachedObject
.Type
,
1662 Crosshair
.AttachedObject
.Ptr1
,
1663 Crosshair
.AttachedObject
.Ptr2
,
1664 Crosshair
.AttachedObject
.Ptr3
);
1665 if (Settings
.Mode
!= COPY_MODE
&&
1666 (Crosshair
.AttachedObject
.Type
== ELEMENT_TYPE
||
1667 Crosshair
.AttachedObject
.Type
== VIA_TYPE
||
1668 Crosshair
.AttachedObject
.Type
== LINE_TYPE
||
1669 Crosshair
.AttachedObject
.Type
== LINEPOINT_TYPE
))
1670 LookupRatLines (Crosshair
.AttachedObject
.Type
,
1671 Crosshair
.AttachedObject
.Ptr1
,
1672 Crosshair
.AttachedObject
.Ptr2
,
1673 Crosshair
.AttachedObject
.Ptr3
);
1677 * Return nonzero if the given file exists and is readable.
1680 FileExists (const char *name
)
1683 f
= fopen (name
, "r");
1693 Concat (const char *first
, ...)
1699 len
= strlen (first
);
1700 rv
= (char *) malloc (len
+ 1);
1703 va_start (a
, first
);
1706 const char *s
= va_arg (a
, const char *);
1710 rv
= (char *) realloc (rv
, len
+ 1);
1718 mem_any_set (unsigned char *ptr
, int bytes
)
1726 /* This just fills in a FlagType with current flags. */
1728 MakeFlags (unsigned int flags
)
1731 memset (&rv
, 0, sizeof (rv
));
1736 /* This converts old flag bits (from saved PCB files) to new format. */
1738 OldFlags (unsigned int flags
)
1742 memset (&rv
, 0, sizeof (rv
));
1743 /* If we move flag bits around, this is where we map old bits to them. */
1744 rv
.f
= flags
& 0xffff;
1746 for (i
= 0; i
< 8; i
++)
1748 /* use the closest thing to the old thermal style */
1750 rv
.t
[i
/ 2] |= (1 << (4 * (i
% 2)));
1757 AddFlags (FlagType flag
, unsigned int flags
)
1764 MaskFlags (FlagType flag
, unsigned int flags
)
1770 /***********************************************************************
1771 * Layer Group Functions
1775 MoveLayerToGroup (int layer
, int group
)
1779 if (layer
< 0 || layer
> max_copper_layer
+ 1)
1781 prev
= GetLayerGroupNumberByNumber (layer
);
1782 if ((layer
== solder_silk_layer
1783 && group
== GetLayerGroupNumberByNumber (component_silk_layer
))
1784 || (layer
== component_silk_layer
1785 && group
== GetLayerGroupNumberByNumber (solder_silk_layer
))
1786 || (group
< 0 || group
>= max_group
) || (prev
== group
))
1789 /* Remove layer from prev group */
1790 for (j
= i
= 0; i
< PCB
->LayerGroups
.Number
[prev
]; i
++)
1791 if (PCB
->LayerGroups
.Entries
[prev
][i
] != layer
)
1792 PCB
->LayerGroups
.Entries
[prev
][j
++] = PCB
->LayerGroups
.Entries
[prev
][i
];
1793 PCB
->LayerGroups
.Number
[prev
]--;
1795 /* Add layer to new group. */
1796 i
= PCB
->LayerGroups
.Number
[group
]++;
1797 PCB
->LayerGroups
.Entries
[group
][i
] = layer
;
1803 LayerGroupsToString (LayerGroupTypePtr lg
)
1805 #if MAX_LAYER < 9998
1806 /* Allows for layer numbers 0..9999 */
1807 static char buf
[(MAX_LAYER
+ 2) * 5 + 1];
1812 for (group
= 0; group
< max_group
; group
++)
1813 if (PCB
->LayerGroups
.Number
[group
])
1818 for (entry
= 0; entry
< PCB
->LayerGroups
.Number
[group
]; entry
++)
1820 int layer
= PCB
->LayerGroups
.Entries
[group
][entry
];
1821 if (layer
== component_silk_layer
)
1825 else if (layer
== solder_silk_layer
)
1831 sprintf (cp
, "%d", layer
+ 1);
1835 if (entry
!= PCB
->LayerGroups
.Number
[group
] - 1)
1846 #ifdef HAVE_GETPWUID
1847 static struct passwd
*pwentry
;
1848 static char *fab_author
= 0;
1852 if (Settings
.FabAuthor
&& Settings
.FabAuthor
[0])
1853 fab_author
= Settings
.FabAuthor
;
1857 char *comma
, *gecos
;
1860 pwentry
= getpwuid (getuid ());
1861 gecos
= pwentry
->pw_gecos
;
1862 comma
= strchr (gecos
, ',');
1864 len
= comma
- gecos
;
1866 len
= strlen (gecos
);
1867 fab_author
= malloc (len
+ 1);
1870 perror ("pcb: out of memory.\n");
1873 memcpy (fab_author
, gecos
, len
);
1874 fab_author
[len
] = 0;
1885 AttributeGetFromList (AttributeListType
*list
, char *name
)
1888 for (i
=0; i
<list
->Number
; i
++)
1889 if (strcmp (name
, list
->List
[i
].name
) == 0)
1890 return list
->List
[i
].value
;
1895 AttributePutToList (AttributeListType
*list
, const char *name
, const char *value
, int replace
)
1899 /* If we're allowed to replace an existing attribute, see if we
1903 for (i
=0; i
<list
->Number
; i
++)
1904 if (strcmp (name
, list
->List
[i
].name
) == 0)
1906 free (list
->List
[i
].value
);
1907 list
->List
[i
].value
= STRDUP (value
);
1912 /* At this point, we're going to need to add a new attribute to the
1913 list. See if there's room. */
1914 if (list
->Number
>= list
->Max
)
1917 list
->List
= (AttributeType
*) realloc (list
->List
,
1918 list
->Max
* sizeof (AttributeType
));
1921 /* Now add the new attribute. */
1923 list
->List
[i
].name
= STRDUP (name
);
1924 list
->List
[i
].value
= STRDUP (value
);
1930 AttributeRemoveFromList(AttributeListType
*list
, char *name
)
1933 for (i
=0; i
<list
->Number
; i
++)
1934 if (strcmp (name
, list
->List
[i
].name
) == 0)
1936 free (list
->List
[i
].name
);
1937 free (list
->List
[i
].value
);
1938 for (j
=i
; j
<list
->Number
-i
; j
++)
1939 list
->List
[j
] = list
->List
[j
+1];
1949 static char buf
[100];
1958 d
+= 0.0000005; /* rounding */
1961 sprintf (bufp
, "%d", i
);
1962 bufp
+= strlen (bufp
);
1965 f
= floor (d
* 1000000.0);
1966 sprintf (bufp
, "%06d", f
);
1971 c_strtod (const char *s
)
1977 /* leading whitespace */
1978 while (*s
&& (*s
== ' ' || *s
== '\t'))
1990 /* integer portion */
1991 while (*s
>= '0' && *s
<= '9')
1998 /* fractional portion */
2003 while (*s
>= '0' && *s
<= '9')
2005 rv
+= (*s
- '0') * scale
;
2012 if (*s
== 'E' || *s
== 'e')
2015 if (sscanf (s
+ 1, "%d", &e
) == 1)
2036 r_delete_element (DataType
* data
, ElementType
* element
)
2038 r_delete_entry (data
->element_tree
, (BoxType
*) element
);
2041 r_delete_entry (data
->pin_tree
, (BoxType
*) pin
);
2046 r_delete_entry (data
->pad_tree
, (BoxType
*) pad
);
2049 ELEMENTTEXT_LOOP (element
);
2051 r_delete_entry (data
->name_tree
[n
], (BoxType
*) text
);
2057 /* ---------------------------------------------------------------------------
2058 * Returns a string that has a bunch of information about the program.
2059 * Can be used for things like "about" dialog boxes.
2063 GetInfoString (void)
2067 static DynamicStringType info
;
2068 static int first_time
= 1;
2075 DSAddString (&info
, "This is PCB, an interactive\n");
2076 DSAddString (&info
, "printed circuit board editor\n");
2077 DSAddString (&info
, "version ");
2078 DSAddString (&info
, VERSION
);
2079 DSAddString (&info
, "\n\n");
2080 DSAddString (&info
, "Compiled on " __DATE__
" at " __TIME__
);
2081 DSAddString (&info
, "\n\n" "by harry eaton\n\n");
2083 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n");
2084 DSAddString (&info
, "Copyright (C) harry eaton 1998-2007\n");
2085 DSAddString (&info
, "Copyright (C) C. Scott Ananian 2001\n");
2087 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n");
2089 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2090 DSAddString (&info
, "It is licensed under the terms of the GNU\n");
2091 DSAddString (&info
, "General Public License version 2\n");
2092 DSAddString (&info
, "See the LICENSE file for more information\n\n");
2093 DSAddString (&info
, "For more information see:\n\n");
2094 DSAddString (&info
, "PCB homepage: http://pcb.gpleda.org\n");
2095 DSAddString (&info
, "gEDA homepage: http://www.gpleda.org\n");
2097 "gEDA Wiki: http://geda.seul.org/wiki/ \n\n");
2099 DSAddString (&info
, "----- Compile Time Options -----\n");
2100 hids
= hid_enumerate ();
2101 DSAddString (&info
, "GUI:\n");
2102 for (i
= 0; hids
[i
]; i
++)
2106 DSAddString (&info
, TAB
);
2107 DSAddString (&info
, hids
[i
]->name
);
2108 DSAddString (&info
, " : ");
2109 DSAddString (&info
, hids
[i
]->description
);
2110 DSAddString (&info
, "\n");
2114 DSAddString (&info
, "Exporters:\n");
2115 for (i
= 0; hids
[i
]; i
++)
2117 if (hids
[i
]->exporter
)
2119 DSAddString (&info
, TAB
);
2120 DSAddString (&info
, hids
[i
]->name
);
2121 DSAddString (&info
, " : ");
2122 DSAddString (&info
, hids
[i
]->description
);
2123 DSAddString (&info
, "\n");
2127 DSAddString (&info
, "Printers:\n");
2128 for (i
= 0; hids
[i
]; i
++)
2130 if (hids
[i
]->printer
)
2132 DSAddString (&info
, TAB
);
2133 DSAddString (&info
, hids
[i
]->name
);
2134 DSAddString (&info
, " : ");
2135 DSAddString (&info
, hids
[i
]->description
);
2136 DSAddString (&info
, "\n");
2145 /* ---------------------------------------------------------------------------
2146 * Returns a best guess about the orientation of an element. The
2147 * value corresponds to the rotation; a difference is the right value
2148 * to pass to RotateElementLowLevel. However, the actual value is no
2149 * indication of absolute rotation; only relative rotation is
2154 ElementOrientation (ElementType
*e
)
2156 int pin1x
, pin1y
, pin2x
, pin2y
, dx
, dy
;
2160 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2168 if (NSTRCMP (pin
->Number
, "1") == 0)
2174 else if (NSTRCMP (pin
->Number
, "2") == 0)
2185 if (NSTRCMP (pad
->Number
, "1") == 0)
2187 pin1x
= (pad
->Point1
.X
+ pad
->Point2
.X
) / 2;
2188 pin1y
= (pad
->Point1
.Y
+ pad
->Point2
.Y
) / 2;
2191 else if (NSTRCMP (pad
->Number
, "2") == 0)
2193 pin2x
= (pad
->Point1
.X
+ pad
->Point2
.X
) / 2;
2194 pin2y
= (pad
->Point1
.Y
+ pad
->Point2
.Y
) / 2;
2200 if (found_pin1
&& found_pin2
)
2205 else if (found_pin1
&& (pin1x
|| pin1y
))
2210 else if (found_pin2
&& (pin2x
|| pin2y
))
2218 if (abs(dx
) > abs(dy
))
2219 return dx
> 0 ? 0 : 2;
2220 return dy
> 0 ? 3 : 1;
2224 ActionListRotations(int argc
, char **argv
, int x
, int y
)
2226 ELEMENT_LOOP (PCB
->Data
);
2228 printf("%d %s\n", ElementOrientation(element
), NAMEONPCB_NAME(element
));
2235 HID_Action misc_action_list
[] = {
2236 {"ListRotations", 0, ActionListRotations
,
2240 REGISTER_ACTIONS (misc_action_list
)