Introduce POLYGONHOLE_MODE for creating holes in polygons
[geda-pcb/gde.git] / src / misc.c
blobdfbb1d717cb05cb96d78904bfae7a02af7c5d11f
1 /* $Id$ */
3 /*
4 * COPYRIGHT
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
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #ifdef HAVE_STRING_H
40 #include <string.h>
41 #endif
42 #include <memory.h>
43 #include <ctype.h>
44 #include <signal.h>
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <math.h>
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 #ifdef HAVE_PWD_H
53 #include <pwd.h>
54 #endif
56 #include "global.h"
58 #include "box.h"
59 #include "crosshair.h"
60 #include "create.h"
61 #include "data.h"
62 #include "draw.h"
63 #include "file.h"
64 #include "error.h"
65 #include "mymem.h"
66 #include "misc.h"
67 #include "move.h"
68 #include "polygon.h"
69 #include "remove.h"
70 #include "rtree.h"
71 #include "rotate.h"
72 #include "rubberband.h"
73 #include "search.h"
74 #include "set.h"
75 #include "undo.h"
76 #include "action.h"
78 #ifdef HAVE_LIBDMALLOC
79 #include <dmalloc.h>
80 #endif
82 RCSID ("$Id$");
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 *);
92 /* Local variables */
94 /*
95 * Used by SaveStackAndVisibility() and
96 * RestoreStackAndVisibility()
99 static struct
101 bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
102 int LayerStack[MAX_LAYER];
103 bool LayerOn[MAX_LAYER];
104 int cnt;
105 } SavedStack;
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
110 float
111 GetValue (char *val, char *units, bool * absolute)
113 float value;
115 /* if the first character is a sign we have to add the
116 * value to the current one
118 if (*val == '=')
120 *absolute = true;
121 value = atof (val + 1);
123 else
125 if (isdigit ((int) *val))
126 *absolute = true;
127 else
128 *absolute = false;
129 value = atof (val);
131 if (units && *units)
133 if (strncasecmp (units, "mm", 2) == 0)
134 value *= MM_TO_COOR;
135 else if (strncasecmp (units, "mil", 3) == 0)
136 value *= 100;
138 return value;
141 /* ---------------------------------------------------------------------------
142 * sets the bounding box of a point (which is silly)
144 void
145 SetPointBoundingBox (PointTypePtr Pnt)
147 Pnt->X2 = Pnt->X + 1;
148 Pnt->Y2 = Pnt->Y + 1;
151 /* ---------------------------------------------------------------------------
152 * sets the bounding box of a pin or via
154 void
155 SetPinBoundingBox (PinTypePtr Pin)
157 BDimension width;
159 /* the bounding box covers the extent of influence
160 * so it must include the clearance values too
162 width = (Pin->Clearance + Pin->Thickness + 1) / 2;
163 width = MAX (width, (Pin->Mask + 1) / 2);
164 Pin->BoundingBox.X1 = Pin->X - width;
165 Pin->BoundingBox.Y1 = Pin->Y - width;
166 Pin->BoundingBox.X2 = Pin->X + width;
167 Pin->BoundingBox.Y2 = Pin->Y + width;
168 close_box(&Pin->BoundingBox);
171 /* ---------------------------------------------------------------------------
172 * sets the bounding box of a pad
174 void
175 SetPadBoundingBox (PadTypePtr Pad)
177 BDimension width;
178 BDimension deltax;
179 BDimension deltay;
181 /* the bounding box covers the extent of influence
182 * so it must include the clearance values too
184 width = (Pad->Thickness + Pad->Clearance + 1) / 2;
185 width = MAX (width, (Pad->Mask + 1) / 2);
186 deltax = Pad->Point2.X - Pad->Point1.X;
187 deltay = Pad->Point2.Y - Pad->Point1.Y;
189 if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0)
191 /* slanted square pad */
192 float tx, ty, theta;
193 BDimension btx, bty;
195 theta = atan2 (deltay, deltax);
197 /* T is a vector half a thickness long, in the direction of
198 one of the corners. */
199 tx = width * cos (theta + M_PI/4) * sqrt(2.0);
200 ty = width * sin (theta + M_PI/4) * sqrt(2.0);
202 /* cast back to this integer type */
203 btx = tx;
204 bty = ty;
206 Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty),
207 MIN (Pad->Point2.X + btx, Pad->Point2.X + bty));
208 Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty),
209 MAX (Pad->Point2.X + btx, Pad->Point2.X + bty));
210 Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
211 MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
212 Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
213 MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
215 else
217 Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width;
218 Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width;
219 Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width;
220 Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width;
222 close_box(&Pad->BoundingBox);
225 /* ---------------------------------------------------------------------------
226 * sets the bounding box of a line
228 void
229 SetLineBoundingBox (LineTypePtr Line)
231 BDimension width;
233 width = (Line->Thickness + Line->Clearance + 1) / 2;
235 Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width;
236 Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width;
237 Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width;
238 Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width;
239 close_box(&Line->BoundingBox);
240 SetPointBoundingBox (&Line->Point1);
241 SetPointBoundingBox (&Line->Point2);
244 /* ---------------------------------------------------------------------------
245 * sets the bounding box of a polygons
247 void
248 SetPolygonBoundingBox (PolygonTypePtr Polygon)
250 Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
251 Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
252 POLYGONPOINT_LOOP (Polygon);
254 MAKEMIN (Polygon->BoundingBox.X1, point->X);
255 MAKEMIN (Polygon->BoundingBox.Y1, point->Y);
256 MAKEMAX (Polygon->BoundingBox.X2, point->X);
257 MAKEMAX (Polygon->BoundingBox.Y2, point->Y);
259 /* boxes don't include the lower right corner */
260 close_box(&Polygon->BoundingBox);
261 END_LOOP;
264 /* ---------------------------------------------------------------------------
265 * sets the bounding box of an elements
267 void
268 SetElementBoundingBox (DataTypePtr Data, ElementTypePtr Element,
269 FontTypePtr Font)
271 BoxTypePtr box, vbox;
273 if (Data && Data->element_tree)
274 r_delete_entry (Data->element_tree, (BoxType *) Element);
275 /* first update the text objects */
276 ELEMENTTEXT_LOOP (Element);
278 if (Data && Data->name_tree[n])
279 r_delete_entry (Data->name_tree[n], (BoxType *) text);
280 SetTextBoundingBox (Font, text);
281 if (Data && !Data->name_tree[n])
282 Data->name_tree[n] = r_create_tree (NULL, 0, 0);
283 if (Data)
284 r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
286 END_LOOP;
288 /* do not include the elementnames bounding box which
289 * is handled separately
291 box = &Element->BoundingBox;
292 vbox = &Element->VBox;
293 box->X1 = box->Y1 = MAX_COORD;
294 box->X2 = box->Y2 = 0;
295 ELEMENTLINE_LOOP (Element);
297 SetLineBoundingBox (line);
298 MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2);
299 MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
300 MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2);
301 MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
302 MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2);
303 MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
304 MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2);
305 MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
307 END_LOOP;
308 ARC_LOOP (Element);
310 SetArcBoundingBox (arc);
311 MAKEMIN (box->X1, arc->BoundingBox.X1);
312 MAKEMIN (box->Y1, arc->BoundingBox.Y1);
313 MAKEMAX (box->X2, arc->BoundingBox.X2);
314 MAKEMAX (box->Y2, arc->BoundingBox.Y2);
316 END_LOOP;
317 *vbox = *box;
318 PIN_LOOP (Element);
320 if (Data && Data->pin_tree)
321 r_delete_entry (Data->pin_tree, (BoxType *) pin);
322 SetPinBoundingBox (pin);
323 if (Data)
325 if (!Data->pin_tree)
326 Data->pin_tree = r_create_tree (NULL, 0, 0);
327 r_insert_entry (Data->pin_tree, (BoxType *) pin, 0);
329 MAKEMIN (box->X1, pin->BoundingBox.X1);
330 MAKEMIN (box->Y1, pin->BoundingBox.Y1);
331 MAKEMAX (box->X2, pin->BoundingBox.X2);
332 MAKEMAX (box->Y2, pin->BoundingBox.Y2);
333 MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2);
334 MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2);
335 MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2);
336 MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2);
338 END_LOOP;
339 PAD_LOOP (Element);
341 if (Data && Data->pad_tree)
342 r_delete_entry (Data->pad_tree, (BoxType *) pad);
343 SetPadBoundingBox (pad);
344 if (Data)
346 if (!Data->pad_tree)
347 Data->pad_tree = r_create_tree (NULL, 0, 0);
348 r_insert_entry (Data->pad_tree, (BoxType *) pad, 0);
350 MAKEMIN (box->X1, pad->BoundingBox.X1);
351 MAKEMIN (box->Y1, pad->BoundingBox.Y1);
352 MAKEMAX (box->X2, pad->BoundingBox.X2);
353 MAKEMAX (box->Y2, pad->BoundingBox.Y2);
354 MAKEMIN (vbox->X1,
355 MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
356 MAKEMIN (vbox->Y1,
357 MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
358 MAKEMAX (vbox->X2,
359 MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
360 MAKEMAX (vbox->Y2,
361 MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
363 END_LOOP;
364 /* now we set the EDGE2FLAG of the pad if Point2
365 * is closer to the outside edge than Point1
367 PAD_LOOP (Element);
369 if (pad->Point1.Y == pad->Point2.Y)
371 /* horizontal pad */
372 if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
373 SET_FLAG (EDGE2FLAG, pad);
374 else
375 CLEAR_FLAG (EDGE2FLAG, pad);
377 else
379 /* vertical pad */
380 if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
381 SET_FLAG (EDGE2FLAG, pad);
382 else
383 CLEAR_FLAG (EDGE2FLAG, pad);
386 END_LOOP;
388 /* mark pins with component orientation */
389 if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
391 PIN_LOOP (Element);
393 SET_FLAG (EDGE2FLAG, pin);
395 END_LOOP;
397 else
399 PIN_LOOP (Element);
401 CLEAR_FLAG (EDGE2FLAG, pin);
403 END_LOOP;
405 close_box(box);
406 close_box(vbox);
407 if (Data && !Data->element_tree)
408 Data->element_tree = r_create_tree (NULL, 0, 0);
409 if (Data)
410 r_insert_entry (Data->element_tree, box, 0);
413 /* ---------------------------------------------------------------------------
414 * creates the bounding box of a text object
416 void
417 SetTextBoundingBox (FontTypePtr FontPtr, TextTypePtr Text)
419 SymbolTypePtr symbol = FontPtr->Symbol;
420 unsigned char *s = (unsigned char *) Text->TextString;
421 LocationType width = 0, height = 0;
422 BDimension maxThick = 0;
423 int i;
425 /* calculate size of the bounding box */
426 for (; s && *s; s++)
427 if (*s <= MAX_FONTPOSITION && symbol[*s].Valid)
429 LineTypePtr line = symbol[*s].Line;
430 for (i = 0; i < symbol[*s].LineN; line++, i++)
431 if (line->Thickness > maxThick)
432 maxThick = line->Thickness;
433 width += symbol[*s].Width + symbol[*s].Delta;
434 height = MAX (height, (LocationType) symbol[*s].Height);
436 else
438 width +=
439 ((FontPtr->DefaultSymbol.X2 - FontPtr->DefaultSymbol.X1) * 6 / 5);
440 height = (FontPtr->DefaultSymbol.Y2 - FontPtr->DefaultSymbol.Y1);
443 /* scale values */
444 width *= Text->Scale / 100.;
445 height *= Text->Scale / 100.;
446 maxThick *= Text->Scale / 200.;
447 if (maxThick < 400)
448 maxThick = 400;
450 /* set upper-left and lower-right corner;
451 * swap coordinates if necessary (origin is already in 'swapped')
452 * and rotate box
454 Text->BoundingBox.X1 = Text->X;
455 Text->BoundingBox.Y1 = Text->Y;
456 if (TEST_FLAG (ONSOLDERFLAG, Text))
458 Text->BoundingBox.X1 -= maxThick;
459 Text->BoundingBox.Y1 -= SWAP_SIGN_Y (maxThick);
460 Text->BoundingBox.X2 =
461 Text->BoundingBox.X1 + SWAP_SIGN_X (width + maxThick);
462 Text->BoundingBox.Y2 =
463 Text->BoundingBox.Y1 + SWAP_SIGN_Y (height + 2 * maxThick);
464 RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y,
465 (4 - Text->Direction) & 0x03);
467 else
469 Text->BoundingBox.X1 -= maxThick;
470 Text->BoundingBox.Y1 -= maxThick;
471 Text->BoundingBox.X2 = Text->BoundingBox.X1 + width + maxThick;
472 Text->BoundingBox.Y2 = Text->BoundingBox.Y1 + height + 2 * maxThick;
473 RotateBoxLowLevel (&Text->BoundingBox,
474 Text->X, Text->Y, Text->Direction);
477 /* the bounding box covers the extent of influence
478 * so it must include the clearance values too
480 Text->BoundingBox.X1 -= PCB->Bloat;
481 Text->BoundingBox.Y1 -= PCB->Bloat;
482 Text->BoundingBox.X2 += PCB->Bloat;
483 Text->BoundingBox.Y2 += PCB->Bloat;
484 close_box(&Text->BoundingBox);
487 /* ---------------------------------------------------------------------------
488 * returns true if data area is empty
490 bool
491 IsDataEmpty (DataTypePtr Data)
493 bool hasNoObjects;
494 Cardinal i;
496 hasNoObjects = (Data->ViaN == 0);
497 hasNoObjects &= (Data->ElementN == 0);
498 for (i = 0; i < max_layer + 2; i++)
499 hasNoObjects = hasNoObjects &&
500 Data->Layer[i].LineN == 0 &&
501 Data->Layer[i].ArcN == 0 &&
502 Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0;
503 return (hasNoObjects);
507 FlagIsDataEmpty (int parm)
509 int i = IsDataEmpty (PCB->Data);
510 return parm ? !i : i;
513 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
514 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
516 /* ---------------------------------------------------------------------------
517 * gets minimum and maximum coordinates
518 * returns NULL if layout is empty
520 BoxTypePtr
521 GetDataBoundingBox (DataTypePtr Data)
523 static BoxType box;
524 /* FIX ME: use r_search to do this much faster */
526 /* preset identifiers with highest and lowest possible values */
527 box.X1 = box.Y1 = MAX_COORD;
528 box.X2 = box.Y2 = -MAX_COORD;
530 /* now scan for the lowest/highest X and Y coordinate */
531 VIA_LOOP (Data);
533 box.X1 = MIN (box.X1, via->X - via->Thickness / 2);
534 box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2);
535 box.X2 = MAX (box.X2, via->X + via->Thickness / 2);
536 box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2);
538 END_LOOP;
539 ELEMENT_LOOP (Data);
541 box.X1 = MIN (box.X1, element->BoundingBox.X1);
542 box.Y1 = MIN (box.Y1, element->BoundingBox.Y1);
543 box.X2 = MAX (box.X2, element->BoundingBox.X2);
544 box.Y2 = MAX (box.Y2, element->BoundingBox.Y2);
546 TextTypePtr text = &NAMEONPCB_TEXT (element);
547 box.X1 = MIN (box.X1, text->BoundingBox.X1);
548 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
549 box.X2 = MAX (box.X2, text->BoundingBox.X2);
550 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
553 END_LOOP;
554 ALLLINE_LOOP (Data);
556 box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2);
557 box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2);
558 box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2);
559 box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2);
560 box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2);
561 box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2);
562 box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2);
563 box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2);
565 ENDALL_LOOP;
566 ALLARC_LOOP (Data);
568 box.X1 = MIN (box.X1, arc->BoundingBox.X1);
569 box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1);
570 box.X2 = MAX (box.X2, arc->BoundingBox.X2);
571 box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2);
573 ENDALL_LOOP;
574 ALLTEXT_LOOP (Data);
576 box.X1 = MIN (box.X1, text->BoundingBox.X1);
577 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
578 box.X2 = MAX (box.X2, text->BoundingBox.X2);
579 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
581 ENDALL_LOOP;
582 ALLPOLYGON_LOOP (Data);
584 box.X1 = MIN (box.X1, polygon->BoundingBox.X1);
585 box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1);
586 box.X2 = MAX (box.X2, polygon->BoundingBox.X2);
587 box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2);
589 ENDALL_LOOP;
590 return (IsDataEmpty (Data) ? NULL : &box);
593 /* ---------------------------------------------------------------------------
594 * centers the displayed PCB around the specified point (X,Y)
595 * if Delta is false, X,Y are in absolute PCB coordinates
596 * if Delta is true, simply move the center by an amount X, Y in screen
597 * coordinates
599 void
600 CenterDisplay (LocationType X, LocationType Y, bool Delta)
602 double save_grid = PCB->Grid;
603 PCB->Grid = 1;
604 if (Delta)
606 MoveCrosshairRelative (X, Y);
608 else
610 if (MoveCrosshairAbsolute (X, Y))
612 RestoreCrosshair(false);
615 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_WARP_POINTER);
616 PCB->Grid = save_grid;
619 /* ---------------------------------------------------------------------------
620 * transforms symbol coordinates so that the left edge of each symbol
621 * is at the zero position. The y coordinates are moved so that min(y) = 0
624 void
625 SetFontInfo (FontTypePtr Ptr)
627 Cardinal i, j;
628 SymbolTypePtr symbol;
629 LineTypePtr line;
630 LocationType totalminy = MAX_COORD;
632 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
633 * maximum cell width and height
634 * minimum x and y position of all lines
636 Ptr->MaxWidth = DEFAULT_CELLSIZE;
637 Ptr->MaxHeight = DEFAULT_CELLSIZE;
638 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
640 LocationType minx, miny, maxx, maxy;
642 /* next one if the index isn't used or symbol is empty (SPACE) */
643 if (!symbol->Valid || !symbol->LineN)
644 continue;
646 minx = miny = MAX_COORD;
647 maxx = maxy = 0;
648 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
650 minx = MIN (minx, line->Point1.X);
651 miny = MIN (miny, line->Point1.Y);
652 minx = MIN (minx, line->Point2.X);
653 miny = MIN (miny, line->Point2.Y);
654 maxx = MAX (maxx, line->Point1.X);
655 maxy = MAX (maxy, line->Point1.Y);
656 maxx = MAX (maxx, line->Point2.X);
657 maxy = MAX (maxy, line->Point2.Y);
660 /* move symbol to left edge */
661 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
662 MOVE_LINE_LOWLEVEL (line, -minx, 0);
664 /* set symbol bounding box with a minimum cell size of (1,1) */
665 symbol->Width = maxx - minx + 1;
666 symbol->Height = maxy + 1;
668 /* check total min/max */
669 Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width);
670 Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height);
671 totalminy = MIN (totalminy, miny);
674 /* move coordinate system to the upper edge (lowest y on screen) */
675 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
676 if (symbol->Valid)
678 symbol->Height -= totalminy;
679 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
680 MOVE_LINE_LOWLEVEL (line, 0, -totalminy);
683 /* setup the box for the default symbol */
684 Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
685 Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
686 Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
689 static void
690 GetNum (char **s, BDimension * num)
692 *num = atoi (*s);
693 while (isdigit ((int) **s))
694 (*s)++;
698 /* ----------------------------------------------------------------------
699 * parses the routes definition string which is a colon separated list of
700 * comma separated Name, Dimension, Dimension, Dimension, Dimension
701 * e.g. Signal,20,40,20,10:Power,40,60,28,10:...
704 ParseRouteString (char *s, RouteStyleTypePtr routeStyle, int scale)
706 int i, style;
707 char Name[256];
709 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
710 for (style = 0; style < NUM_STYLES; style++, routeStyle++)
712 while (*s && isspace ((int) *s))
713 s++;
714 for (i = 0; *s && *s != ','; i++)
715 Name[i] = *s++;
716 Name[i] = '\0';
717 routeStyle->Name = MyStrdup (Name, "ParseRouteString()");
718 if (!isdigit ((int) *++s))
719 goto error;
720 GetNum (&s, &routeStyle->Thick);
721 routeStyle->Thick *= scale;
722 while (*s && isspace ((int) *s))
723 s++;
724 if (*s++ != ',')
725 goto error;
726 while (*s && isspace ((int) *s))
727 s++;
728 if (!isdigit ((int) *s))
729 goto error;
730 GetNum (&s, &routeStyle->Diameter);
731 routeStyle->Diameter *= scale;
732 while (*s && isspace ((int) *s))
733 s++;
734 if (*s++ != ',')
735 goto error;
736 while (*s && isspace ((int) *s))
737 s++;
738 if (!isdigit ((int) *s))
739 goto error;
740 GetNum (&s, &routeStyle->Hole);
741 routeStyle->Hole *= scale;
742 /* for backwards-compatibility, we use a 10-mil default
743 * for styles which omit the keepaway specification. */
744 if (*s != ',')
745 routeStyle->Keepaway = 1000;
746 else
748 s++;
749 while (*s && isspace ((int) *s))
750 s++;
751 if (!isdigit ((int) *s))
752 goto error;
753 GetNum (&s, &routeStyle->Keepaway);
754 routeStyle->Keepaway *= scale;
755 while (*s && isspace ((int) *s))
756 s++;
758 if (style < NUM_STYLES - 1)
760 while (*s && isspace ((int) *s))
761 s++;
762 if (*s++ != ':')
763 goto error;
766 return (0);
768 error:
769 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
770 return (1);
773 /* ----------------------------------------------------------------------
774 * parses the group definition string which is a colon separated list of
775 * comma separated layer numbers (1,2,b:4,6,8,t)
778 ParseGroupString (char *s, LayerGroupTypePtr LayerGroup, int LayerN)
780 int group, member, layer;
781 bool c_set = false, /* flags for the two special layers to */
782 s_set = false; /* provide a default setting for old formats */
783 int groupnum[MAX_LAYER + 2];
785 /* clear struct */
786 memset (LayerGroup, 0, sizeof (LayerGroupType));
788 /* Clear assignments */
789 for (layer = 0; layer < MAX_LAYER + 2; layer++)
790 groupnum[layer] = -1;
792 /* loop over all groups */
793 for (group = 0; s && *s && group < LayerN; group++)
795 while (*s && isspace ((int) *s))
796 s++;
798 /* loop over all group members */
799 for (member = 0; *s; s++)
801 /* ignore white spaces and get layernumber */
802 while (*s && isspace ((int) *s))
803 s++;
804 switch (*s)
806 case 'c':
807 case 'C':
808 layer = LayerN + COMPONENT_LAYER;
809 c_set = true;
810 break;
812 case 's':
813 case 'S':
814 layer = LayerN + SOLDER_LAYER;
815 s_set = true;
816 break;
818 default:
819 if (!isdigit ((int) *s))
820 goto error;
821 layer = atoi (s) - 1;
822 break;
824 if (layer > LayerN + MAX (SOLDER_LAYER, COMPONENT_LAYER) ||
825 member >= LayerN + 1)
826 goto error;
827 groupnum[layer] = group;
828 LayerGroup->Entries[group][member++] = layer;
829 while (*++s && isdigit ((int) *s));
831 /* ignore white spaces and check for separator */
832 while (*s && isspace ((int) *s))
833 s++;
834 if (!*s || *s == ':')
835 break;
836 if (*s != ',')
837 goto error;
839 LayerGroup->Number[group] = member;
840 if (*s == ':')
841 s++;
843 if (!s_set)
844 LayerGroup->Entries[SOLDER_LAYER][LayerGroup->Number[SOLDER_LAYER]++] =
845 LayerN + SOLDER_LAYER;
846 if (!c_set)
847 LayerGroup->
848 Entries[COMPONENT_LAYER][LayerGroup->Number[COMPONENT_LAYER]++] =
849 LayerN + COMPONENT_LAYER;
851 for (layer = 0; layer < LayerN && group < LayerN; layer++)
852 if (groupnum[layer] == -1)
854 LayerGroup->Entries[group][0] = layer;
855 LayerGroup->Number[group] = 1;
856 group++;
858 return (0);
860 /* reset structure on error */
861 error:
862 memset (LayerGroup, 0, sizeof (LayerGroupType));
863 return (1);
866 /* ---------------------------------------------------------------------------
867 * quits application
869 void
870 QuitApplication (void)
873 * save data if necessary. It not needed, then don't trigger EmergencySave
874 * via our atexit() registering of EmergencySave(). We presumeably wanted to
875 * exit here and thus it is not an emergency.
877 if (PCB->Changed && Settings.SaveInTMP)
878 EmergencySave ();
879 else
880 DisableEmergencySave ();
882 exit (0);
885 /* ---------------------------------------------------------------------------
886 * creates a filename from a template
887 * %f is replaced by the filename
888 * %p by the searchpath
890 char *
891 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
893 static DynamicStringType command;
894 char *p;
896 if (Settings.verbose)
898 printf ("EvaluateFilename:\n");
899 printf ("\tTemplate: \033[33m%s\033[0m\n", Template);
900 printf ("\tPath: \033[33m%s\033[0m\n", Path);
901 printf ("\tFilename: \033[33m%s\033[0m\n", Filename);
902 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter);
905 DSClearString (&command);
907 for (p = Template; p && *p; p++)
909 /* copy character or add string to command */
910 if (*p == '%'
911 && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
912 switch (*(++p))
914 case 'a':
915 DSAddString (&command, Parameter);
916 break;
917 case 'f':
918 DSAddString (&command, Filename);
919 break;
920 case 'p':
921 DSAddString (&command, Path);
922 break;
924 else
925 DSAddCharacter (&command, *p);
927 DSAddCharacter (&command, '\0');
928 if (Settings.verbose)
929 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data);
931 return (MyStrdup (command.Data, "EvaluateFilename()"));
934 /* ---------------------------------------------------------------------------
935 * concatenates directory and filename if directory != NULL,
936 * expands them with a shell and returns the found name(s) or NULL
938 char *
939 ExpandFilename (char *Dirname, char *Filename)
941 static DynamicStringType answer;
942 char *command;
943 FILE *pipe;
944 int c;
946 /* allocate memory for commandline and build it */
947 DSClearString (&answer);
948 if (Dirname)
950 command = MyCalloc (strlen (Filename) + strlen (Dirname) + 7,
951 sizeof (char), "ExpandFilename()");
952 sprintf (command, "echo %s/%s", Dirname, Filename);
954 else
956 command = MyCalloc (strlen (Filename) + 6, sizeof (char), "Expand()");
957 sprintf (command, "echo %s", Filename);
960 /* execute it with shell */
961 if ((pipe = popen (command, "r")) != NULL)
963 /* discard all but the first returned line */
964 for (;;)
966 if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
967 break;
968 else
969 DSAddCharacter (&answer, c);
972 SaveFree (command);
973 return (pclose (pipe) ? NULL : answer.Data);
976 /* couldn't be expanded by the shell */
977 PopenErrorMessage (command);
978 SaveFree (command);
979 return (NULL);
983 /* ---------------------------------------------------------------------------
984 * returns the layer number for the passed pointer
987 GetLayerNumber (DataTypePtr Data, LayerTypePtr Layer)
989 int i;
991 for (i = 0; i < MAX_LAYER + 2; i++)
992 if (Layer == &Data->Layer[i])
993 break;
994 return (i);
997 /* ---------------------------------------------------------------------------
998 * move layer (number is passed in) to top of layerstack
1000 static void
1001 PushOnTopOfLayerStack (int NewTop)
1003 int i;
1005 /* ignore COMPONENT_LAYER and SOLDER_LAYER */
1006 if (NewTop < max_layer)
1008 /* first find position of passed one */
1009 for (i = 0; i < max_layer; i++)
1010 if (LayerStack[i] == NewTop)
1011 break;
1013 /* bring this element to the top of the stack */
1014 for (; i; i--)
1015 LayerStack[i] = LayerStack[i - 1];
1016 LayerStack[0] = NewTop;
1021 /* ----------------------------------------------------------------------
1022 * changes the visibility of all layers in a group
1023 * returns the number of changed layers
1026 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder)
1028 int group, i, changed = 1; /* at least the current layer changes */
1030 /* Warning: these special case values must agree with what gui-top-window.c
1031 | thinks the are.
1034 if (Settings.verbose)
1035 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1036 Layer, On, ChangeStackOrder);
1038 /* decrement 'i' to keep stack in order of layergroup */
1039 if ((group = GetGroupOfLayer (Layer)) < max_layer)
1040 for (i = PCB->LayerGroups.Number[group]; i;)
1042 int layer = PCB->LayerGroups.Entries[group][--i];
1044 /* don't count the passed member of the group */
1045 if (layer != Layer && layer < max_layer)
1047 PCB->Data->Layer[layer].On = On;
1049 /* push layer on top of stack if switched on */
1050 if (On && ChangeStackOrder)
1051 PushOnTopOfLayerStack (layer);
1052 changed++;
1056 /* change at least the passed layer */
1057 PCB->Data->Layer[Layer].On = On;
1058 if (On && ChangeStackOrder)
1059 PushOnTopOfLayerStack (Layer);
1061 /* update control panel and exit */
1062 hid_action ("LayersChanged");
1063 return (changed);
1066 /* ----------------------------------------------------------------------
1067 * Given a string description of a layer stack, adjust the layer stack
1068 * to correspond.
1071 void
1072 LayerStringToLayerStack (char *s)
1074 static int listed_layers = 0;
1075 int l = strlen (s);
1076 char **args;
1077 int i, argn, lno;
1078 int prev_sep = 1;
1080 s = strdup (s);
1081 args = (char **) malloc (l * sizeof (char *));
1082 argn = 0;
1084 for (i=0; i<l; i++)
1086 switch (s[i])
1088 case ' ':
1089 case '\t':
1090 case ',':
1091 case ';':
1092 case ':':
1093 prev_sep = 1;
1094 s[i] = '\0';
1095 break;
1096 default:
1097 if (prev_sep)
1098 args[argn++] = s+i;
1099 prev_sep = 0;
1100 break;
1104 for (i = 0; i < max_layer + 2; i++)
1106 if (i < max_layer)
1107 LayerStack[i] = i;
1108 PCB->Data->Layer[i].On = false;
1110 PCB->ElementOn = false;
1111 PCB->InvisibleObjectsOn = false;
1112 PCB->PinOn = false;
1113 PCB->ViaOn = false;
1114 PCB->RatOn = false;
1115 CLEAR_FLAG (SHOWMASKFLAG, PCB);
1116 Settings.ShowSolderSide = 0;
1118 for (i=argn-1; i>=0; i--)
1120 if (strcasecmp (args[i], "rats") == 0)
1121 PCB->RatOn = true;
1122 else if (strcasecmp (args[i], "invisible") == 0)
1123 PCB->InvisibleObjectsOn = true;
1124 else if (strcasecmp (args[i], "pins") == 0)
1125 PCB->PinOn = true;
1126 else if (strcasecmp (args[i], "vias") == 0)
1127 PCB->ViaOn = true;
1128 else if (strcasecmp (args[i], "elements") == 0
1129 || strcasecmp (args[i], "silk") == 0)
1130 PCB->ElementOn = true;
1131 else if (strcasecmp (args[i], "mask") == 0)
1132 SET_FLAG (SHOWMASKFLAG, PCB);
1133 else if (strcasecmp (args[i], "solderside") == 0)
1134 Settings.ShowSolderSide = 1;
1135 else if (isdigit ((int) args[i][0]))
1137 lno = atoi (args[i]);
1138 ChangeGroupVisibility (lno, true, true);
1140 else
1142 int found = 0;
1143 for (lno = 0; lno < max_layer; lno++)
1144 if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
1146 ChangeGroupVisibility (lno, true, true);
1147 found = 1;
1148 break;
1150 if (!found)
1152 fprintf(stderr, "Warning: layer \"%s\" not known\n", args[i]);
1153 if (!listed_layers)
1155 fprintf (stderr, "Named layers in this board are:\n");
1156 listed_layers = 1;
1157 for (lno=0; lno < max_layer; lno ++)
1158 fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
1159 fprintf(stderr, "Also: component, solder, rats, invisible, pins, vias, elements or silk, mask, solderside.\n");
1166 /* ----------------------------------------------------------------------
1167 * lookup the group to which a layer belongs to
1168 * returns max_layer if no group is found
1171 GetGroupOfLayer (int Layer)
1173 int group, i;
1175 if (Layer == max_layer)
1176 return (Layer);
1177 for (group = 0; group < max_layer; group++)
1178 for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
1179 if (PCB->LayerGroups.Entries[group][i] == Layer)
1180 return (group);
1181 return (max_layer);
1185 /* ---------------------------------------------------------------------------
1186 * returns the layergroup number for the passed pointer
1189 GetLayerGroupNumberByPointer (LayerTypePtr Layer)
1191 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer)));
1194 /* ---------------------------------------------------------------------------
1195 * returns the layergroup number for the passed layernumber
1198 GetLayerGroupNumberByNumber (Cardinal Layer)
1200 int group, entry;
1202 for (group = 0; group < max_layer; group++)
1203 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1204 if (PCB->LayerGroups.Entries[group][entry] == Layer)
1205 return (group);
1207 /* since every layer belongs to a group it is safe to return
1208 * the value without boundary checking
1210 return (group);
1213 /* ---------------------------------------------------------------------------
1214 * returns a pointer to an objects bounding box;
1215 * data is valid until the routine is called again
1217 BoxTypePtr
1218 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1220 switch (Type)
1222 case LINE_TYPE:
1223 case ARC_TYPE:
1224 case TEXT_TYPE:
1225 case POLYGON_TYPE:
1226 case PAD_TYPE:
1227 case PIN_TYPE:
1228 case ELEMENTNAME_TYPE:
1229 return (BoxType *)Ptr2;
1230 case VIA_TYPE:
1231 case ELEMENT_TYPE:
1232 return (BoxType *)Ptr1;
1233 case POLYGONPOINT_TYPE:
1234 case LINEPOINT_TYPE:
1235 return (BoxType *)Ptr3;
1236 default:
1237 Message ("Request for bounding box of unsupported type %d\n", Type);
1238 return (BoxType *)Ptr2;
1242 /* ---------------------------------------------------------------------------
1243 * computes the bounding box of an arc
1245 void
1246 SetArcBoundingBox (ArcTypePtr Arc)
1248 register double ca1, ca2, sa1, sa2;
1249 register LocationType ang1, ang2;
1250 register LocationType width;
1252 /* first put angles into standard form */
1253 if (Arc->Delta > 360)
1254 Arc->Delta = 360;
1255 ang1 = Arc->StartAngle;
1256 ang2 = Arc->StartAngle + Arc->Delta;
1257 if (Arc->Delta < 0)
1259 LocationType temp;
1260 temp = ang1;
1261 ang1 = ang2;
1262 ang2 = temp;
1264 if (ang1 < 0)
1266 ang1 += 360;
1267 ang2 += 360;
1269 /* calculate sines, cosines */
1270 switch (ang1)
1272 case 0:
1273 ca1 = 1.0;
1274 sa1 = 0;
1275 break;
1276 case 90:
1277 ca1 = 0;
1278 sa1 = 1.0;
1279 break;
1280 case 180:
1281 ca1 = -1.0;
1282 sa1 = 0;
1283 break;
1284 case 270:
1285 ca1 = 0;
1286 sa1 = -1.0;
1287 break;
1288 default:
1289 ca1 = M180 * (double) ang1;
1290 sa1 = sin (ca1);
1291 ca1 = cos (ca1);
1293 switch (ang2)
1295 case 0:
1296 ca2 = 1.0;
1297 sa2 = 0;
1298 break;
1299 case 90:
1300 ca2 = 0;
1301 sa2 = 1.0;
1302 break;
1303 case 180:
1304 ca2 = -1.0;
1305 sa2 = 0;
1306 break;
1307 case 270:
1308 ca2 = 0;
1309 sa2 = -1.0;
1310 break;
1311 default:
1312 ca2 = M180 * (double) ang2;
1313 sa2 = sin (ca2);
1314 ca2 = cos (ca2);
1317 Arc->BoundingBox.X2 = Arc->X - Arc->Width *
1318 ((ang1 < 180 && ang2 > 180) ? -1 : MIN (ca1, ca2));
1320 Arc->BoundingBox.X1 = Arc->X - Arc->Width *
1321 ((ang1 < 360 && ang2 > 360) ? 1 : MAX (ca1, ca2));
1323 Arc->BoundingBox.Y2 = Arc->Y + Arc->Height *
1324 ((ang1 < 90 && ang2 > 90) ? 1 : MAX (sa1, sa2));
1326 Arc->BoundingBox.Y1 = Arc->Y + Arc->Height *
1327 ((ang1 < 270 && ang2 > 270) ? -1 : MIN (sa1, sa2));
1329 width = (Arc->Thickness + Arc->Clearance) / 2;
1330 Arc->BoundingBox.X1 -= width;
1331 Arc->BoundingBox.X2 += width;
1332 Arc->BoundingBox.Y1 -= width;
1333 Arc->BoundingBox.Y2 += width;
1334 close_box(&Arc->BoundingBox);
1337 /* ---------------------------------------------------------------------------
1338 * resets the layerstack setting
1340 void
1341 ResetStackAndVisibility (void)
1343 int comp_group;
1344 Cardinal i;
1346 for (i = 0; i < max_layer + 2; i++)
1348 if (i < max_layer)
1349 LayerStack[i] = i;
1350 PCB->Data->Layer[i].On = true;
1352 PCB->ElementOn = true;
1353 PCB->InvisibleObjectsOn = true;
1354 PCB->PinOn = true;
1355 PCB->ViaOn = true;
1356 PCB->RatOn = true;
1358 /* Bring the component group to the front and make it active. */
1359 comp_group = GetLayerGroupNumberByNumber (max_layer + COMPONENT_LAYER);
1360 ChangeGroupVisibility (PCB->LayerGroups.Entries[comp_group][0], 1, 1);
1363 /* ---------------------------------------------------------------------------
1364 * saves the layerstack setting
1366 void
1367 SaveStackAndVisibility (void)
1369 Cardinal i;
1370 static bool run = false;
1372 if (run == false)
1374 SavedStack.cnt = 0;
1375 run = true;
1378 if (SavedStack.cnt != 0)
1380 fprintf (stderr,
1381 "SaveStackAndVisibility() layerstack was already saved and not"
1382 "yet restored. cnt = %d\n", SavedStack.cnt);
1385 for (i = 0; i < max_layer + 2; i++)
1387 if (i < max_layer)
1388 SavedStack.LayerStack[i] = LayerStack[i];
1389 SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
1391 SavedStack.ElementOn = PCB->ElementOn;
1392 SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
1393 SavedStack.PinOn = PCB->PinOn;
1394 SavedStack.ViaOn = PCB->ViaOn;
1395 SavedStack.RatOn = PCB->RatOn;
1396 SavedStack.cnt++;
1399 /* ---------------------------------------------------------------------------
1400 * restores the layerstack setting
1402 void
1403 RestoreStackAndVisibility (void)
1405 Cardinal i;
1407 if (SavedStack.cnt == 0)
1409 fprintf (stderr, "RestoreStackAndVisibility() layerstack has not"
1410 " been saved. cnt = %d\n", SavedStack.cnt);
1411 return;
1413 else if (SavedStack.cnt != 1)
1415 fprintf (stderr, "RestoreStackAndVisibility() layerstack save count is"
1416 " wrong. cnt = %d\n", SavedStack.cnt);
1419 for (i = 0; i < max_layer + 2; i++)
1421 if (i < max_layer)
1422 LayerStack[i] = SavedStack.LayerStack[i];
1423 PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
1425 PCB->ElementOn = SavedStack.ElementOn;
1426 PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
1427 PCB->PinOn = SavedStack.PinOn;
1428 PCB->ViaOn = SavedStack.ViaOn;
1429 PCB->RatOn = SavedStack.RatOn;
1431 SavedStack.cnt--;
1434 /* ----------------------------------------------------------------------
1435 * returns pointer to current working directory. If 'path' is not
1436 * NULL, then the current working directory is copied to the array
1437 * pointed to by 'path'
1439 char *
1440 GetWorkingDirectory (char *path)
1442 #ifdef HAVE_GETCWD
1443 return getcwd (path, MAXPATHLEN);
1444 #else
1445 /* seems that some BSD releases lack of a prototype for getwd() */
1446 return getwd (path);
1447 #endif
1451 /* ---------------------------------------------------------------------------
1452 * writes a string to the passed file pointer
1453 * some special characters are quoted
1455 void
1456 CreateQuotedString (DynamicStringTypePtr DS, char *S)
1458 DSClearString (DS);
1459 DSAddCharacter (DS, '"');
1460 while (*S)
1462 if (*S == '"' || *S == '\\')
1463 DSAddCharacter (DS, '\\');
1464 DSAddCharacter (DS, *S++);
1466 DSAddCharacter (DS, '"');
1470 static void
1471 RightAngles (int Angle, float *cosa, float *sina)
1473 *cosa = (float) cos ((double) Angle * M180);
1474 *sina = (float) sin ((double) Angle * M180);
1477 BoxTypePtr
1478 GetArcEnds (ArcTypePtr Arc)
1480 static BoxType box;
1481 float ca, sa;
1483 RightAngles (Arc->StartAngle, &ca, &sa);
1484 box.X1 = Arc->X - Arc->Width * ca;
1485 box.Y1 = Arc->Y + Arc->Height * sa;
1486 RightAngles (Arc->StartAngle + Arc->Delta, &ca, &sa);
1487 box.X2 = Arc->X - Arc->Width * ca;
1488 box.Y2 = Arc->Y + Arc->Height * sa;
1489 return (&box);
1493 /* doesn't this belong in change.c ?? */
1494 void
1495 ChangeArcAngles (LayerTypePtr Layer, ArcTypePtr a,
1496 long int new_sa, long int new_da)
1498 if (new_da >= 360)
1500 new_da = 360;
1501 new_sa = 0;
1503 RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a);
1504 r_delete_entry (Layer->arc_tree, (BoxTypePtr) a);
1505 AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a);
1506 a->StartAngle = new_sa;
1507 a->Delta = new_da;
1508 SetArcBoundingBox (a);
1509 r_insert_entry (Layer->arc_tree, (BoxTypePtr) a, 0);
1510 ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
1513 static char *
1514 BumpName (char *Name)
1516 int num;
1517 char c, *start;
1518 static char temp[256];
1520 start = Name;
1521 /* seek end of string */
1522 while (*Name != 0)
1523 Name++;
1524 /* back up to potential number */
1525 for (Name--; isdigit ((int) *Name); Name--);
1526 Name++;
1527 if (*Name)
1528 num = atoi (Name) + 1;
1529 else
1530 num = 1;
1531 c = *Name;
1532 *Name = 0;
1533 sprintf (temp, "%s%d", start, num);
1534 /* if this is not our string, put back the blown character */
1535 if (start != temp)
1536 *Name = c;
1537 return (temp);
1541 * make a unique name for the name on board
1542 * this can alter the contents of the input string
1544 char *
1545 UniqueElementName (DataTypePtr Data, char *Name)
1547 bool unique = true;
1548 /* null strings are ok */
1549 if (!Name || !*Name)
1550 return (Name);
1552 for (;;)
1554 ELEMENT_LOOP (Data);
1556 if (NAMEONPCB_NAME (element) &&
1557 NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
1559 Name = BumpName (Name);
1560 unique = false;
1561 break;
1564 END_LOOP;
1565 if (unique)
1566 return (Name);
1567 unique = true;
1571 static void
1572 GetGridLockCoordinates (int type, void *ptr1,
1573 void *ptr2, void *ptr3, LocationType * x,
1574 LocationType * y)
1576 switch (type)
1578 case VIA_TYPE:
1579 *x = ((PinTypePtr) ptr2)->X;
1580 *y = ((PinTypePtr) ptr2)->Y;
1581 break;
1582 case LINE_TYPE:
1583 *x = ((LineTypePtr) ptr2)->Point1.X;
1584 *y = ((LineTypePtr) ptr2)->Point1.Y;
1585 break;
1586 case TEXT_TYPE:
1587 case ELEMENTNAME_TYPE:
1588 *x = ((TextTypePtr) ptr2)->X;
1589 *y = ((TextTypePtr) ptr2)->Y;
1590 break;
1591 case ELEMENT_TYPE:
1592 *x = ((ElementTypePtr) ptr2)->MarkX;
1593 *y = ((ElementTypePtr) ptr2)->MarkY;
1594 break;
1595 case POLYGON_TYPE:
1596 *x = ((PolygonTypePtr) ptr2)->Points[0].X;
1597 *y = ((PolygonTypePtr) ptr2)->Points[0].Y;
1598 break;
1600 case LINEPOINT_TYPE:
1601 case POLYGONPOINT_TYPE:
1602 *x = ((PointTypePtr) ptr3)->X;
1603 *y = ((PointTypePtr) ptr3)->Y;
1604 break;
1605 case ARC_TYPE:
1607 BoxTypePtr box;
1609 box = GetArcEnds ((ArcTypePtr) ptr2);
1610 *x = box->X1;
1611 *y = box->Y1;
1612 break;
1617 void
1618 AttachForCopy (LocationType PlaceX, LocationType PlaceY)
1620 BoxTypePtr box;
1621 LocationType mx = 0, my = 0;
1623 Crosshair.AttachedObject.RubberbandN = 0;
1624 if (! TEST_FLAG (SNAPPINFLAG, PCB))
1626 /* dither the grab point so that the mark, center, etc
1627 * will end up on a grid coordinate
1629 GetGridLockCoordinates (Crosshair.AttachedObject.Type,
1630 Crosshair.AttachedObject.Ptr1,
1631 Crosshair.AttachedObject.Ptr2,
1632 Crosshair.AttachedObject.Ptr3, &mx, &my);
1633 mx = GRIDFIT_X (mx, PCB->Grid) - mx;
1634 my = GRIDFIT_Y (my, PCB->Grid) - my;
1636 Crosshair.AttachedObject.X = PlaceX - mx;
1637 Crosshair.AttachedObject.Y = PlaceY - my;
1638 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
1639 SetLocalRef (PlaceX - mx, PlaceY - my, true);
1640 Crosshair.AttachedObject.State = STATE_SECOND;
1642 /* get boundingbox of object and set cursor range */
1643 box = GetObjectBoundingBox (Crosshair.AttachedObject.Type,
1644 Crosshair.AttachedObject.Ptr1,
1645 Crosshair.AttachedObject.Ptr2,
1646 Crosshair.AttachedObject.Ptr3);
1647 SetCrosshairRange (Crosshair.AttachedObject.X - box->X1,
1648 Crosshair.AttachedObject.Y - box->Y1,
1649 PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X),
1650 PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
1652 /* get all attached objects if necessary */
1653 if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB))
1654 LookupRubberbandLines (Crosshair.AttachedObject.Type,
1655 Crosshair.AttachedObject.Ptr1,
1656 Crosshair.AttachedObject.Ptr2,
1657 Crosshair.AttachedObject.Ptr3);
1658 if (Settings.Mode != COPY_MODE &&
1659 (Crosshair.AttachedObject.Type == ELEMENT_TYPE ||
1660 Crosshair.AttachedObject.Type == VIA_TYPE ||
1661 Crosshair.AttachedObject.Type == LINE_TYPE ||
1662 Crosshair.AttachedObject.Type == LINEPOINT_TYPE))
1663 LookupRatLines (Crosshair.AttachedObject.Type,
1664 Crosshair.AttachedObject.Ptr1,
1665 Crosshair.AttachedObject.Ptr2,
1666 Crosshair.AttachedObject.Ptr3);
1670 * Return nonzero if the given file exists and is readable.
1673 FileExists (const char *name)
1675 FILE *f;
1676 f = fopen (name, "r");
1677 if (f)
1679 fclose (f);
1680 return 1;
1682 return 0;
1685 char *
1686 Concat (const char *first, ...)
1688 char *rv;
1689 int len;
1690 va_list a;
1692 len = strlen (first);
1693 rv = (char *) malloc (len + 1);
1694 strcpy (rv, first);
1696 va_start (a, first);
1697 while (1)
1699 const char *s = va_arg (a, const char *);
1700 if (!s)
1701 break;
1702 len += strlen (s);
1703 rv = (char *) realloc (rv, len + 1);
1704 strcat (rv, s);
1706 va_end (a);
1707 return rv;
1711 mem_any_set (unsigned char *ptr, int bytes)
1713 while (bytes--)
1714 if (*ptr++)
1715 return 1;
1716 return 0;
1719 /* This just fills in a FlagType with current flags. */
1720 FlagType
1721 MakeFlags (unsigned int flags)
1723 FlagType rv;
1724 memset (&rv, 0, sizeof (rv));
1725 rv.f = flags;
1726 return rv;
1729 /* This converts old flag bits (from saved PCB files) to new format. */
1730 FlagType
1731 OldFlags (unsigned int flags)
1733 FlagType rv;
1734 int i, f;
1735 memset (&rv, 0, sizeof (rv));
1736 /* If we move flag bits around, this is where we map old bits to them. */
1737 rv.f = flags & 0xffff;
1738 f = 0x10000;
1739 for (i = 0; i < 8; i++)
1741 /* use the closest thing to the old thermal style */
1742 if (flags & f)
1743 rv.t[i / 2] |= (1 << (4 * (i % 2)));
1744 f <<= 1;
1746 return rv;
1749 FlagType
1750 AddFlags (FlagType flag, unsigned int flags)
1752 flag.f |= flags;
1753 return flag;
1756 FlagType
1757 MaskFlags (FlagType flag, unsigned int flags)
1759 flag.f &= ~flags;
1760 return flag;
1763 /***********************************************************************
1764 * Layer Group Functions
1768 MoveLayerToGroup (int layer, int group)
1770 int prev, i, j;
1772 if (layer < 0 || layer > max_layer + 1)
1773 return -1;
1774 prev = GetLayerGroupNumberByNumber (layer);
1775 if ((layer == max_layer
1776 && group == GetLayerGroupNumberByNumber (max_layer + 1))
1777 || (layer == max_layer + 1
1778 && group == GetLayerGroupNumberByNumber (max_layer))
1779 || (group < 0 || group >= max_layer) || (prev == group))
1780 return prev;
1782 /* Remove layer from prev group */
1783 for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
1784 if (PCB->LayerGroups.Entries[prev][i] != layer)
1785 PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
1786 PCB->LayerGroups.Number[prev]--;
1788 /* Add layer to new group. */
1789 i = PCB->LayerGroups.Number[group]++;
1790 PCB->LayerGroups.Entries[group][i] = layer;
1792 return group;
1795 char *
1796 LayerGroupsToString (LayerGroupTypePtr lg)
1798 #if MAX_LAYER < 9998
1799 /* Allows for layer numbers 0..9999 */
1800 static char buf[(MAX_LAYER + 2) * 5 + 1];
1801 #endif
1802 char *cp = buf;
1803 char sep = 0;
1804 int group, entry;
1805 for (group = 0; group < max_layer; group++)
1806 if (PCB->LayerGroups.Number[group])
1808 if (sep)
1809 *cp++ = ':';
1810 sep = 1;
1811 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1813 int layer = PCB->LayerGroups.Entries[group][entry];
1814 if (layer == max_layer + COMPONENT_LAYER)
1816 *cp++ = 'c';
1818 else if (layer == max_layer + SOLDER_LAYER)
1820 *cp++ = 's';
1822 else
1824 sprintf (cp, "%d", layer + 1);
1825 while (*++cp)
1828 if (entry != PCB->LayerGroups.Number[group] - 1)
1829 *cp++ = ',';
1832 *cp++ = 0;
1833 return buf;
1836 char *
1837 pcb_author (void)
1839 #ifdef HAVE_GETPWUID
1840 static struct passwd *pwentry;
1841 static char *fab_author = 0;
1843 if (!fab_author)
1845 if (Settings.FabAuthor && Settings.FabAuthor[0])
1846 fab_author = Settings.FabAuthor;
1847 else
1849 int len;
1850 char *comma, *gecos;
1852 /* ID the user. */
1853 pwentry = getpwuid (getuid ());
1854 gecos = pwentry->pw_gecos;
1855 comma = strchr (gecos, ',');
1856 if (comma)
1857 len = comma - gecos;
1858 else
1859 len = strlen (gecos);
1860 fab_author = malloc (len + 1);
1861 if (!fab_author)
1863 perror ("pcb: out of memory.\n");
1864 exit (-1);
1866 memcpy (fab_author, gecos, len);
1867 fab_author[len] = 0;
1870 return fab_author;
1871 #else
1872 return "Unknown";
1873 #endif
1877 char *
1878 AttributeGetFromList (AttributeListType *list, char *name)
1880 int i;
1881 for (i=0; i<list->Number; i++)
1882 if (strcmp (name, list->List[i].name) == 0)
1883 return list->List[i].value;
1884 return NULL;
1888 AttributePutToList (AttributeListType *list, char *name, char *value, int replace)
1890 int i;
1892 /* If we're allowed to replace an existing attribute, see if we
1893 can. */
1894 if (replace)
1896 for (i=0; i<list->Number; i++)
1897 if (strcmp (name, list->List[i].name) == 0)
1899 free (list->List[i].value);
1900 list->List[i].value = MyStrdup (value, "AttributePutToList");
1901 return 1;
1905 /* At this point, we're going to need to add a new attribute to the
1906 list. See if there's room. */
1907 if (list->Number >= list->Max)
1909 list->Max += 10;
1910 list->List = (AttributeType *) realloc (list->List,
1911 list->Max * sizeof (AttributeType));
1914 /* Now add the new attribute. */
1915 i = list->Number;
1916 list->List[i].name = MyStrdup (name, "AttributePutToList");
1917 list->List[i].value = MyStrdup (value, "AttributePutToList");
1918 list->Number ++;
1919 return 0;
1924 const char *
1925 c_dtostr (double d)
1927 static char buf[100];
1928 int i, f;
1929 char *bufp = buf;
1931 if (d < 0)
1933 *bufp++ = '-';
1934 d = -d;
1936 d += 0.0000005; /* rounding */
1937 i = floor (d);
1938 d -= i;
1939 sprintf (bufp, "%d", i);
1940 bufp += strlen (bufp);
1941 *bufp++ = '.';
1943 f = floor (d * 1000000.0);
1944 sprintf (bufp, "%06d", f);
1945 return buf;
1948 double
1949 c_strtod (const char *s)
1951 double rv = 0;
1952 double sign = 1.0;
1953 double scale;
1955 /* leading whitespace */
1956 while (*s && (*s == ' ' || *s == '\t'))
1957 s++;
1959 /* optional sign */
1960 if (*s == '-')
1962 sign = -1.0;
1963 s++;
1965 else if (*s == '+')
1966 s++;
1968 /* integer portion */
1969 while (*s >= '0' && *s <= '9')
1971 rv *= 10.0;
1972 rv += *s - '0';
1973 s++;
1976 /* fractional portion */
1977 if (*s == '.')
1979 s++;
1980 scale = 0.1;
1981 while (*s >= '0' && *s <= '9')
1983 rv += (*s - '0') * scale;
1984 scale *= 0.1;
1985 s++;
1989 /* exponent */
1990 if (*s == 'E' || *s == 'e')
1992 int e;
1993 if (sscanf (s + 1, "%d", &e) == 1)
1995 scale = 1.0;
1996 while (e > 0)
1998 scale *= 10.0;
1999 e--;
2001 while (e < 0)
2003 scale *= 0.1;
2004 e++;
2006 rv *= scale;
2010 return rv * sign;
2013 void
2014 r_delete_element (DataType * data, ElementType * element)
2016 r_delete_entry (data->element_tree, (BoxType *) element);
2017 PIN_LOOP (element);
2019 r_delete_entry (data->pin_tree, (BoxType *) pin);
2021 END_LOOP;
2022 PAD_LOOP (element);
2024 r_delete_entry (data->pad_tree, (BoxType *) pad);
2026 END_LOOP;
2027 ELEMENTTEXT_LOOP (element);
2029 r_delete_entry (data->name_tree[n], (BoxType *) text);
2031 END_LOOP;
2035 /* ---------------------------------------------------------------------------
2036 * Returns a string that has a bunch of information about the program.
2037 * Can be used for things like "about" dialog boxes.
2040 char *
2041 GetInfoString (void)
2043 HID **hids;
2044 int i;
2045 static DynamicStringType info;
2046 static int first_time = 1;
2048 #define TAB " "
2050 if (first_time)
2052 first_time = 0;
2053 DSAddString (&info, "This is PCB, an interactive\n");
2054 DSAddString (&info, "printed circuit board editor\n");
2055 DSAddString (&info, "version ");
2056 DSAddString (&info, VERSION);
2057 DSAddString (&info, "\n\n");
2058 DSAddString (&info, "Compiled on " __DATE__ " at " __TIME__);
2059 DSAddString (&info, "\n\n" "by harry eaton\n\n");
2060 DSAddString (&info,
2061 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n");
2062 DSAddString (&info, "Copyright (C) harry eaton 1998-2007\n");
2063 DSAddString (&info, "Copyright (C) C. Scott Ananian 2001\n");
2064 DSAddString (&info,
2065 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n");
2066 DSAddString (&info,
2067 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2068 DSAddString (&info, "It is licensed under the terms of the GNU\n");
2069 DSAddString (&info, "General Public License version 2\n");
2070 DSAddString (&info, "See the LICENSE file for more information\n\n");
2071 DSAddString (&info, "For more information see:\n\n");
2072 DSAddString (&info, "PCB homepage: http://pcb.gpleda.org\n");
2073 DSAddString (&info, "gEDA homepage: http://www.gpleda.org\n");
2074 DSAddString (&info,
2075 "gEDA Wiki: http://geda.seul.org/wiki/ \n\n");
2077 DSAddString (&info, "----- Compile Time Options -----\n");
2078 hids = hid_enumerate ();
2079 DSAddString (&info, "GUI:\n");
2080 for (i = 0; hids[i]; i++)
2082 if (hids[i]->gui)
2084 DSAddString (&info, TAB);
2085 DSAddString (&info, hids[i]->name);
2086 DSAddString (&info, " : ");
2087 DSAddString (&info, hids[i]->description);
2088 DSAddString (&info, "\n");
2092 DSAddString (&info, "Exporters:\n");
2093 for (i = 0; hids[i]; i++)
2095 if (hids[i]->exporter)
2097 DSAddString (&info, TAB);
2098 DSAddString (&info, hids[i]->name);
2099 DSAddString (&info, " : ");
2100 DSAddString (&info, hids[i]->description);
2101 DSAddString (&info, "\n");
2105 DSAddString (&info, "Printers:\n");
2106 for (i = 0; hids[i]; i++)
2108 if (hids[i]->printer)
2110 DSAddString (&info, TAB);
2111 DSAddString (&info, hids[i]->name);
2112 DSAddString (&info, " : ");
2113 DSAddString (&info, hids[i]->description);
2114 DSAddString (&info, "\n");
2118 #undef TAB
2120 return info.Data;
2123 /* ---------------------------------------------------------------------------
2124 * Returns a best guess about the orientation of an element. The
2125 * value corresponds to the rotation; a difference is the right value
2126 * to pass to RotateElementLowLevel. However, the actual value is no
2127 * indication of absolute rotation; only relative rotation is
2128 * meaningful.
2131 int
2132 ElementOrientation (ElementType *e)
2134 int pin1x, pin1y, pin2x, pin2y, dx, dy;
2135 int found_pin1 = 0;
2136 int found_pin2 = 0;
2138 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2139 pin1x = 0;
2140 pin1y = 0;
2141 pin2x = 0;
2142 pin2y = 0;
2144 PIN_LOOP (e);
2146 if (NSTRCMP (pin->Number, "1") == 0)
2148 pin1x = pin->X;
2149 pin1y = pin->Y;
2150 found_pin1 = 1;
2152 else if (NSTRCMP (pin->Number, "2") == 0)
2154 pin2x = pin->X;
2155 pin2y = pin->Y;
2156 found_pin2 = 1;
2159 END_LOOP;
2161 PAD_LOOP (e);
2163 if (NSTRCMP (pad->Number, "1") == 0)
2165 pin1x = (pad->Point1.X + pad->Point2.X) / 2;
2166 pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
2167 found_pin1 = 1;
2169 else if (NSTRCMP (pad->Number, "2") == 0)
2171 pin2x = (pad->Point1.X + pad->Point2.X) / 2;
2172 pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
2173 found_pin2 = 1;
2176 END_LOOP;
2178 if (found_pin1 && found_pin2)
2180 dx = pin2x - pin1x;
2181 dy = pin2y - pin1y;
2183 else if (found_pin1 && (pin1x || pin1y))
2185 dx = pin1x;
2186 dy = pin1y;
2188 else if (found_pin2 && (pin2x || pin2y))
2190 dx = pin2x;
2191 dy = pin2y;
2193 else
2194 return 0;
2196 if (abs(dx) > abs(dy))
2197 return dx > 0 ? 0 : 2;
2198 return dy > 0 ? 3 : 1;
2202 ActionListRotations(int argc, char **argv, int x, int y)
2204 ELEMENT_LOOP (PCB->Data);
2206 printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
2208 END_LOOP;
2210 return 0;
2213 HID_Action misc_action_list[] = {
2214 {"ListRotations", 0, ActionListRotations,
2215 0,0},
2218 REGISTER_ACTIONS (misc_action_list)