Fix math error in IsPointOnLine()
[geda-pcb.git] / src / search.c
blob60e8dde96a00d66e54e41389e35d3cab991aca72
1 /* $Id$ */
3 /*
4 * COPYRIGHT
6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996 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 /* search routines
31 * some of the functions use dummy parameters
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
38 #include <math.h>
39 #include <setjmp.h>
41 #include "global.h"
43 #include "box.h"
44 #include "data.h"
45 #include "draw.h"
46 #include "error.h"
47 #include "find.h"
48 #include "misc.h"
49 #include "polygon.h"
50 #include "rtree.h"
51 #include "search.h"
53 #ifdef HAVE_LIBDMALLOC
54 #include <dmalloc.h>
55 #endif
57 RCSID ("$Id$");
60 /* ---------------------------------------------------------------------------
61 * some local identifiers
63 static double PosX, PosY; /* search position for subroutines */
64 static Coord SearchRadius;
65 static BoxType SearchBox;
66 static LayerTypePtr SearchLayer;
68 /* ---------------------------------------------------------------------------
69 * some local prototypes. The first parameter includes LOCKED_TYPE if we
70 * want to include locked types in the search.
72 static bool SearchLineByLocation (int, LayerTypePtr *, LineTypePtr *,
73 LineTypePtr *);
74 static bool SearchArcByLocation (int, LayerTypePtr *, ArcTypePtr *,
75 ArcTypePtr *);
76 static bool SearchRatLineByLocation (int, RatTypePtr *, RatTypePtr *,
77 RatTypePtr *);
78 static bool SearchTextByLocation (int, LayerTypePtr *, TextTypePtr *,
79 TextTypePtr *);
80 static bool SearchPolygonByLocation (int, LayerTypePtr *, PolygonTypePtr *,
81 PolygonTypePtr *);
82 static bool SearchPinByLocation (int, ElementTypePtr *, PinTypePtr *,
83 PinTypePtr *);
84 static bool SearchPadByLocation (int, ElementTypePtr *, PadTypePtr *,
85 PadTypePtr *, bool);
86 static bool SearchViaByLocation (int, PinTypePtr *, PinTypePtr *,
87 PinTypePtr *);
88 static bool SearchElementNameByLocation (int, ElementTypePtr *,
89 TextTypePtr *, TextTypePtr *,
90 bool);
91 static bool SearchLinePointByLocation (int, LayerTypePtr *, LineTypePtr *,
92 PointTypePtr *);
93 static bool SearchPointByLocation (int, LayerTypePtr *, PolygonTypePtr *,
94 PointTypePtr *);
95 static bool SearchElementByLocation (int, ElementTypePtr *,
96 ElementTypePtr *, ElementTypePtr *,
97 bool);
99 /* ---------------------------------------------------------------------------
100 * searches a via
102 struct ans_info
104 void **ptr1, **ptr2, **ptr3;
105 bool BackToo;
106 double area;
107 jmp_buf env;
108 int locked; /* This will be zero or LOCKFLAG */
111 static int
112 pinorvia_callback (const BoxType * box, void *cl)
114 struct ans_info *i = (struct ans_info *) cl;
115 PinTypePtr pin = (PinTypePtr) box;
116 AnyObjectType *ptr1 = pin->Element ? pin->Element : pin;
118 if (TEST_FLAG (i->locked, ptr1))
119 return 0;
121 if (!IsPointOnPin (PosX, PosY, SearchRadius, pin))
122 return 0;
123 *i->ptr1 = ptr1;
124 *i->ptr2 = *i->ptr3 = pin;
125 longjmp (i->env, 1);
126 return 1; /* never reached */
129 static bool
130 SearchViaByLocation (int locked, PinTypePtr * Via, PinTypePtr * Dummy1,
131 PinTypePtr * Dummy2)
133 struct ans_info info;
135 /* search only if via-layer is visible */
136 if (!PCB->ViaOn)
137 return false;
139 info.ptr1 = (void **) Via;
140 info.ptr2 = (void **) Dummy1;
141 info.ptr3 = (void **) Dummy2;
142 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
144 if (setjmp (info.env) == 0)
146 r_search (PCB->Data->via_tree, &SearchBox, NULL, pinorvia_callback,
147 &info);
148 return false;
150 return true;
153 /* ---------------------------------------------------------------------------
154 * searches a pin
155 * starts with the newest element
157 static bool
158 SearchPinByLocation (int locked, ElementTypePtr * Element, PinTypePtr * Pin,
159 PinTypePtr * Dummy)
161 struct ans_info info;
163 /* search only if pin-layer is visible */
164 if (!PCB->PinOn)
165 return false;
166 info.ptr1 = (void **) Element;
167 info.ptr2 = (void **) Pin;
168 info.ptr3 = (void **) Dummy;
169 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
171 if (setjmp (info.env) == 0)
172 r_search (PCB->Data->pin_tree, &SearchBox, NULL, pinorvia_callback,
173 &info);
174 else
175 return true;
176 return false;
179 static int
180 pad_callback (const BoxType * b, void *cl)
182 PadTypePtr pad = (PadTypePtr) b;
183 struct ans_info *i = (struct ans_info *) cl;
184 AnyObjectType *ptr1 = pad->Element;
186 if (TEST_FLAG (i->locked, ptr1))
187 return 0;
189 if (FRONT (pad) || i->BackToo)
191 if (IsPointInPad (PosX, PosY, SearchRadius, pad))
193 *i->ptr1 = ptr1;
194 *i->ptr2 = *i->ptr3 = pad;
195 longjmp (i->env, 1);
198 return 0;
201 /* ---------------------------------------------------------------------------
202 * searches a pad
203 * starts with the newest element
205 static bool
206 SearchPadByLocation (int locked, ElementTypePtr * Element, PadTypePtr * Pad,
207 PadTypePtr * Dummy, bool BackToo)
209 struct ans_info info;
211 /* search only if pin-layer is visible */
212 if (!PCB->PinOn)
213 return (false);
214 info.ptr1 = (void **) Element;
215 info.ptr2 = (void **) Pad;
216 info.ptr3 = (void **) Dummy;
217 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
218 info.BackToo = (BackToo && PCB->InvisibleObjectsOn);
219 if (setjmp (info.env) == 0)
220 r_search (PCB->Data->pad_tree, &SearchBox, NULL, pad_callback, &info);
221 else
222 return true;
223 return false;
226 /* ---------------------------------------------------------------------------
227 * searches ordinary line on the SearchLayer
230 struct line_info
232 LineTypePtr *Line;
233 PointTypePtr *Point;
234 double least;
235 jmp_buf env;
236 int locked;
239 static int
240 line_callback (const BoxType * box, void *cl)
242 struct line_info *i = (struct line_info *) cl;
243 LineTypePtr l = (LineTypePtr) box;
245 if (TEST_FLAG (i->locked, l))
246 return 0;
248 if (!IsPointInPad (PosX, PosY, SearchRadius, (PadTypePtr)l))
249 return 0;
250 *i->Line = l;
251 *i->Point = (PointTypePtr) l;
252 longjmp (i->env, 1);
253 return 1; /* never reached */
257 static bool
258 SearchLineByLocation (int locked, LayerTypePtr * Layer, LineTypePtr * Line,
259 LineTypePtr * Dummy)
261 struct line_info info;
263 info.Line = Line;
264 info.Point = (PointTypePtr *) Dummy;
265 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
267 *Layer = SearchLayer;
268 if (setjmp (info.env) == 0)
270 r_search (SearchLayer->line_tree, &SearchBox, NULL, line_callback,
271 &info);
272 return false;
274 return (true);
277 static int
278 rat_callback (const BoxType * box, void *cl)
280 LineTypePtr line = (LineTypePtr) box;
281 struct ans_info *i = (struct ans_info *) cl;
283 if (TEST_FLAG (i->locked, line))
284 return 0;
286 if (TEST_FLAG (VIAFLAG, line) ?
287 (Distance (line->Point1.X, line->Point1.Y, PosX, PosY) <=
288 line->Thickness * 2 + SearchRadius) :
289 IsPointOnLine (PosX, PosY, SearchRadius, line))
291 *i->ptr1 = *i->ptr2 = *i->ptr3 = line;
292 longjmp (i->env, 1);
294 return 0;
297 /* ---------------------------------------------------------------------------
298 * searches rat lines if they are visible
300 static bool
301 SearchRatLineByLocation (int locked, RatTypePtr * Line, RatTypePtr * Dummy1,
302 RatTypePtr * Dummy2)
304 struct ans_info info;
306 info.ptr1 = (void **) Line;
307 info.ptr2 = (void **) Dummy1;
308 info.ptr3 = (void **) Dummy2;
309 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
311 if (setjmp (info.env) == 0)
313 r_search (PCB->Data->rat_tree, &SearchBox, NULL, rat_callback, &info);
314 return false;
316 return (true);
319 /* ---------------------------------------------------------------------------
320 * searches arc on the SearchLayer
322 struct arc_info
324 ArcTypePtr *Arc, *Dummy;
325 jmp_buf env;
326 int locked;
329 static int
330 arc_callback (const BoxType * box, void *cl)
332 struct arc_info *i = (struct arc_info *) cl;
333 ArcTypePtr a = (ArcTypePtr) box;
335 if (TEST_FLAG (i->locked, a))
336 return 0;
338 if (!IsPointOnArc (PosX, PosY, SearchRadius, a))
339 return 0;
340 *i->Arc = a;
341 *i->Dummy = a;
342 longjmp (i->env, 1);
343 return 1; /* never reached */
347 static bool
348 SearchArcByLocation (int locked, LayerTypePtr * Layer, ArcTypePtr * Arc,
349 ArcTypePtr * Dummy)
351 struct arc_info info;
353 info.Arc = Arc;
354 info.Dummy = Dummy;
355 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
357 *Layer = SearchLayer;
358 if (setjmp (info.env) == 0)
360 r_search (SearchLayer->arc_tree, &SearchBox, NULL, arc_callback, &info);
361 return false;
363 return (true);
366 static int
367 text_callback (const BoxType * box, void *cl)
369 TextTypePtr text = (TextTypePtr) box;
370 struct ans_info *i = (struct ans_info *) cl;
372 if (TEST_FLAG (i->locked, text))
373 return 0;
375 if (POINT_IN_BOX (PosX, PosY, &text->BoundingBox))
377 *i->ptr2 = *i->ptr3 = text;
378 longjmp (i->env, 1);
380 return 0;
383 /* ---------------------------------------------------------------------------
384 * searches text on the SearchLayer
386 static bool
387 SearchTextByLocation (int locked, LayerTypePtr * Layer, TextTypePtr * Text,
388 TextTypePtr * Dummy)
390 struct ans_info info;
392 *Layer = SearchLayer;
393 info.ptr2 = (void **) Text;
394 info.ptr3 = (void **) Dummy;
395 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
397 if (setjmp (info.env) == 0)
399 r_search (SearchLayer->text_tree, &SearchBox, NULL, text_callback,
400 &info);
401 return false;
403 return (true);
406 static int
407 polygon_callback (const BoxType * box, void *cl)
409 PolygonTypePtr polygon = (PolygonTypePtr) box;
410 struct ans_info *i = (struct ans_info *) cl;
412 if (TEST_FLAG (i->locked, polygon))
413 return 0;
415 if (IsPointInPolygon (PosX, PosY, SearchRadius, polygon))
417 *i->ptr2 = *i->ptr3 = polygon;
418 longjmp (i->env, 1);
420 return 0;
424 /* ---------------------------------------------------------------------------
425 * searches a polygon on the SearchLayer
427 static bool
428 SearchPolygonByLocation (int locked, LayerTypePtr * Layer,
429 PolygonTypePtr * Polygon, PolygonTypePtr * Dummy)
431 struct ans_info info;
433 *Layer = SearchLayer;
434 info.ptr2 = (void **) Polygon;
435 info.ptr3 = (void **) Dummy;
436 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
438 if (setjmp (info.env) == 0)
440 r_search (SearchLayer->polygon_tree, &SearchBox, NULL, polygon_callback,
441 &info);
442 return false;
444 return (true);
447 static int
448 linepoint_callback (const BoxType * b, void *cl)
450 LineTypePtr line = (LineTypePtr) b;
451 struct line_info *i = (struct line_info *) cl;
452 int ret_val = 0;
453 double d;
455 if (TEST_FLAG (i->locked, line))
456 return 0;
458 /* some stupid code to check both points */
459 d = Distance (PosX, PosY, line->Point1.X, line->Point1.Y);
460 if (d < i->least)
462 i->least = d;
463 *i->Line = line;
464 *i->Point = &line->Point1;
465 ret_val = 1;
468 d = Distance (PosX, PosY, line->Point2.X, line->Point2.Y);
469 if (d < i->least)
471 i->least = d;
472 *i->Line = line;
473 *i->Point = &line->Point2;
474 ret_val = 1;
476 return ret_val;
479 /* ---------------------------------------------------------------------------
480 * searches a line-point on all the search layer
482 static bool
483 SearchLinePointByLocation (int locked, LayerTypePtr * Layer,
484 LineTypePtr * Line, PointTypePtr * Point)
486 struct line_info info;
487 *Layer = SearchLayer;
488 info.Line = Line;
489 info.Point = Point;
490 *Point = NULL;
491 info.least = MAX_LINE_POINT_DISTANCE + SearchRadius;
492 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
493 if (r_search
494 (SearchLayer->line_tree, &SearchBox, NULL, linepoint_callback, &info))
495 return true;
496 return false;
499 /* ---------------------------------------------------------------------------
500 * searches a polygon-point on all layers that are switched on
501 * in layerstack order
503 static bool
504 SearchPointByLocation (int locked, LayerTypePtr * Layer,
505 PolygonTypePtr * Polygon, PointTypePtr * Point)
507 double d, least;
508 bool found = false;
510 least = SearchRadius + MAX_POLYGON_POINT_DISTANCE;
511 *Layer = SearchLayer;
512 POLYGON_LOOP (*Layer);
514 POLYGONPOINT_LOOP (polygon);
516 d = Distance (point->X, point->Y, PosX, PosY);
517 if (d < least)
519 least = d;
520 *Polygon = polygon;
521 *Point = point;
522 found = true;
525 END_LOOP;
527 END_LOOP;
528 if (found)
529 return (true);
530 return (false);
533 static int
534 name_callback (const BoxType * box, void *cl)
536 TextTypePtr text = (TextTypePtr) box;
537 struct ans_info *i = (struct ans_info *) cl;
538 ElementTypePtr element = (ElementTypePtr) text->Element;
539 double newarea;
541 if (TEST_FLAG (i->locked, text))
542 return 0;
544 if ((FRONT (element) || i->BackToo) && !TEST_FLAG (HIDENAMEFLAG, element) &&
545 POINT_IN_BOX (PosX, PosY, &text->BoundingBox))
547 /* use the text with the smallest bounding box */
548 newarea = (text->BoundingBox.X2 - text->BoundingBox.X1) *
549 (double) (text->BoundingBox.Y2 - text->BoundingBox.Y1);
550 if (newarea < i->area)
552 i->area = newarea;
553 *i->ptr1 = element;
554 *i->ptr2 = *i->ptr3 = text;
556 return 1;
558 return 0;
561 /* ---------------------------------------------------------------------------
562 * searches the name of an element
563 * the search starts with the last element and goes back to the beginning
565 static bool
566 SearchElementNameByLocation (int locked, ElementTypePtr * Element,
567 TextTypePtr * Text, TextTypePtr * Dummy,
568 bool BackToo)
570 struct ans_info info;
572 /* package layer have to be switched on */
573 if (PCB->ElementOn)
575 info.ptr1 = (void **) Element;
576 info.ptr2 = (void **) Text;
577 info.ptr3 = (void **) Dummy;
578 info.area = SQUARE (MAX_COORD);
579 info.BackToo = (BackToo && PCB->InvisibleObjectsOn);
580 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
581 if (r_search (PCB->Data->name_tree[NAME_INDEX (PCB)], &SearchBox, NULL,
582 name_callback, &info))
583 return true;
585 return (false);
588 static int
589 element_callback (const BoxType * box, void *cl)
591 ElementTypePtr element = (ElementTypePtr) box;
592 struct ans_info *i = (struct ans_info *) cl;
593 double newarea;
595 if (TEST_FLAG (i->locked, element))
596 return 0;
598 if ((FRONT (element) || i->BackToo) &&
599 POINT_IN_BOX (PosX, PosY, &element->VBox))
601 /* use the element with the smallest bounding box */
602 newarea = (element->VBox.X2 - element->VBox.X1) *
603 (double) (element->VBox.Y2 - element->VBox.Y1);
604 if (newarea < i->area)
606 i->area = newarea;
607 *i->ptr1 = *i->ptr2 = *i->ptr3 = element;
608 return 1;
611 return 0;
614 /* ---------------------------------------------------------------------------
615 * searches an element
616 * the search starts with the last element and goes back to the beginning
617 * if more than one element matches, the smallest one is taken
619 static bool
620 SearchElementByLocation (int locked,
621 ElementTypePtr * Element,
622 ElementTypePtr * Dummy1, ElementTypePtr * Dummy2,
623 bool BackToo)
625 struct ans_info info;
627 /* Both package layers have to be switched on */
628 if (PCB->ElementOn && PCB->PinOn)
630 info.ptr1 = (void **) Element;
631 info.ptr2 = (void **) Dummy1;
632 info.ptr3 = (void **) Dummy2;
633 info.area = SQUARE (MAX_COORD);
634 info.BackToo = (BackToo && PCB->InvisibleObjectsOn);
635 info.locked = (locked & LOCKED_TYPE) ? 0 : LOCKFLAG;
636 if (r_search
637 (PCB->Data->element_tree, &SearchBox, NULL, element_callback,
638 &info))
639 return true;
641 return false;
644 /* ---------------------------------------------------------------------------
645 * checks if a point is on a pin
647 bool
648 IsPointOnPin (Coord X, Coord Y, Coord Radius, PinTypePtr pin)
650 Coord t = PIN_SIZE (pin) / 2;
651 if (TEST_FLAG (SQUAREFLAG, pin))
653 BoxType b;
655 b.X1 = pin->X - t;
656 b.X2 = pin->X + t;
657 b.Y1 = pin->Y - t;
658 b.Y2 = pin->Y + t;
659 if (IsPointInBox (X, Y, &b, Radius))
660 return true;
662 else if (Distance (pin->X, pin->Y, X, Y) <= Radius + t)
663 return true;
664 return false;
667 /* ---------------------------------------------------------------------------
668 * checks if a rat-line end is on a PV
670 bool
671 IsPointOnLineEnd (Coord X, Coord Y, RatTypePtr Line)
673 if (((X == Line->Point1.X) && (Y == Line->Point1.Y)) ||
674 ((X == Line->Point2.X) && (Y == Line->Point2.Y)))
675 return (true);
676 return (false);
679 /* ---------------------------------------------------------------------------
680 * checks if a line intersects with a PV
682 * let the point be (X,Y) and the line (X1,Y1)(X2,Y2)
683 * the length of the line is
685 * L = ((X2-X1)^2 + (Y2-Y1)^2)^0.5
687 * let Q be the point of perpendicular projection of (X,Y) onto the line
689 * QX = X1 + D1*(X2-X1) / L
690 * QY = Y1 + D1*(Y2-Y1) / L
692 * with (from vector geometry)
694 * (Y1-Y)(Y1-Y2)+(X1-X)(X1-X2)
695 * D1 = ---------------------------
698 * D1 < 0 Q is on backward extension of the line
699 * D1 > L Q is on forward extension of the line
700 * else Q is on the line
702 * the signed distance from (X,Y) to Q is
704 * (Y2-Y1)(X-X1)-(X2-X1)(Y-Y1)
705 * D2 = ----------------------------
708 * Finally, D1 and D2 are orthogonal, so we can sum them easily
709 * by pythagorean theorem.
711 bool
712 IsPointOnLine (Coord X, Coord Y, Coord Radius, LineTypePtr Line)
714 double D1, D2, L;
716 /* Get length of segment */
717 L = Distance (Line->Point1.X, Line->Point1.Y, Line->Point2.X, Line->Point2.Y);
718 if (L < 0.1)
719 return Distance (X, Y, Line->Point1.X, Line->Point1.Y) < Radius + Line->Thickness / 2;
721 /* Get distance from (X1, Y1) to Q (on the line) */
722 D1 = ((double) (Y - Line->Point1.Y) * (Line->Point2.Y - Line->Point1.Y)
723 + (double) (X - Line->Point1.X) * (Line->Point2.X - Line->Point1.X)) / L;
724 /* Translate this into distance to Q from segment */
725 if (D1 < 0) D1 = -D1;
726 else if (D1 > L) D1 -= L;
727 else D1 = 0;
728 /* Get distance from (X, Y) to Q */
729 D2 = ((double) (X - Line->Point1.X) * (Line->Point2.Y - Line->Point1.Y)
730 - (double) (Y - Line->Point1.Y) * (Line->Point2.X - Line->Point1.X)) / L;
731 /* Total distance is then the pythagorean sum of these */
732 return sqrt (D1*D1 + D2*D2) <= Radius + Line->Thickness / 2;
735 /* ---------------------------------------------------------------------------
736 * checks if a line crosses a rectangle
738 bool
739 IsLineInRectangle (Coord X1, Coord Y1, Coord X2, Coord Y2, LineTypePtr Line)
741 LineType line;
743 /* first, see if point 1 is inside the rectangle */
744 /* in case the whole line is inside the rectangle */
745 if (X1 < Line->Point1.X && X2 > Line->Point1.X &&
746 Y1 < Line->Point1.Y && Y2 > Line->Point1.Y)
747 return (true);
748 /* construct a set of dummy lines and check each of them */
749 line.Thickness = 0;
750 line.Flags = NoFlags ();
752 /* upper-left to upper-right corner */
753 line.Point1.Y = line.Point2.Y = Y1;
754 line.Point1.X = X1;
755 line.Point2.X = X2;
756 if (LineLineIntersect (&line, Line))
757 return (true);
759 /* upper-right to lower-right corner */
760 line.Point1.X = X2;
761 line.Point1.Y = Y1;
762 line.Point2.Y = Y2;
763 if (LineLineIntersect (&line, Line))
764 return (true);
766 /* lower-right to lower-left corner */
767 line.Point1.Y = Y2;
768 line.Point1.X = X1;
769 line.Point2.X = X2;
770 if (LineLineIntersect (&line, Line))
771 return (true);
773 /* lower-left to upper-left corner */
774 line.Point2.X = X1;
775 line.Point1.Y = Y1;
776 line.Point2.Y = Y2;
777 if (LineLineIntersect (&line, Line))
778 return (true);
780 return (false);
783 static int /*checks if a point (of null radius) is in a slanted rectangle*/
784 IsPointInQuadrangle(PointType p[4], PointTypePtr l)
786 Coord dx, dy, x, y;
787 double prod0, prod1;
789 dx = p[1].X - p[0].X;
790 dy = p[1].Y - p[0].Y;
791 x = l->X - p[0].X;
792 y = l->Y - p[0].Y;
793 prod0 = (double) x * dx + (double) y * dy;
794 x = l->X - p[1].X;
795 y = l->Y - p[1].Y;
796 prod1 = (double) x * dx + (double) y * dy;
797 if (prod0 * prod1 <= 0)
799 dx = p[1].X - p[2].X;
800 dy = p[1].Y - p[2].Y;
801 prod0 = (double) x * dx + (double) y * dy;
802 x = l->X - p[2].X;
803 y = l->Y - p[2].Y;
804 prod1 = (double) x * dx + (double) y * dy;
805 if (prod0 * prod1 <= 0)
806 return true;
808 return false;
810 /* ---------------------------------------------------------------------------
811 * checks if a line crosses a quadrangle: almost copied from IsLineInRectangle()
812 * Note: actually this quadrangle is a slanted rectangle
814 bool
815 IsLineInQuadrangle (PointType p[4], LineTypePtr Line)
817 LineType line;
819 /* first, see if point 1 is inside the rectangle */
820 /* in case the whole line is inside the rectangle */
821 if (IsPointInQuadrangle(p,&(Line->Point1)))
822 return true;
823 if (IsPointInQuadrangle(p,&(Line->Point2)))
824 return true;
825 /* construct a set of dummy lines and check each of them */
826 line.Thickness = 0;
827 line.Flags = NoFlags ();
829 /* upper-left to upper-right corner */
830 line.Point1.X = p[0].X; line.Point1.Y = p[0].Y;
831 line.Point2.X = p[1].X; line.Point2.Y = p[1].Y;
832 if (LineLineIntersect (&line, Line))
833 return (true);
835 /* upper-right to lower-right corner */
836 line.Point1.X = p[2].X; line.Point1.Y = p[2].Y;
837 if (LineLineIntersect (&line, Line))
838 return (true);
840 /* lower-right to lower-left corner */
841 line.Point2.X = p[3].X; line.Point2.Y = p[3].Y;
842 if (LineLineIntersect (&line, Line))
843 return (true);
845 /* lower-left to upper-left corner */
846 line.Point1.X = p[0].X; line.Point1.Y = p[0].Y;
847 if (LineLineIntersect (&line, Line))
848 return (true);
850 return (false);
852 /* ---------------------------------------------------------------------------
853 * checks if an arc crosses a square
855 bool
856 IsArcInRectangle (Coord X1, Coord Y1, Coord X2, Coord Y2, ArcTypePtr Arc)
858 LineType line;
860 /* construct a set of dummy lines and check each of them */
861 line.Thickness = 0;
862 line.Flags = NoFlags ();
864 /* upper-left to upper-right corner */
865 line.Point1.Y = line.Point2.Y = Y1;
866 line.Point1.X = X1;
867 line.Point2.X = X2;
868 if (LineArcIntersect (&line, Arc))
869 return (true);
871 /* upper-right to lower-right corner */
872 line.Point1.X = line.Point2.X = X2;
873 line.Point1.Y = Y1;
874 line.Point2.Y = Y2;
875 if (LineArcIntersect (&line, Arc))
876 return (true);
878 /* lower-right to lower-left corner */
879 line.Point1.Y = line.Point2.Y = Y2;
880 line.Point1.X = X1;
881 line.Point2.X = X2;
882 if (LineArcIntersect (&line, Arc))
883 return (true);
885 /* lower-left to upper-left corner */
886 line.Point1.X = line.Point2.X = X1;
887 line.Point1.Y = Y1;
888 line.Point2.Y = Y2;
889 if (LineArcIntersect (&line, Arc))
890 return (true);
892 return (false);
895 /* ---------------------------------------------------------------------------
896 * Check if a circle of Radius with center at (X, Y) intersects a Pad.
897 * Written to enable arbitrary pad directions; for rounded pads, too.
899 bool
900 IsPointInPad (Coord X, Coord Y, Coord Radius, PadTypePtr Pad)
902 double r, Sin, Cos;
903 Coord x;
904 Coord t2 = (Pad->Thickness + 1) / 2, range;
905 PadType pad = *Pad;
907 /* series of transforms saving range */
908 /* move Point1 to the origin */
909 X -= pad.Point1.X;
910 Y -= pad.Point1.Y;
912 pad.Point2.X -= pad.Point1.X;
913 pad.Point2.Y -= pad.Point1.Y;
914 /* so, pad.Point1.X = pad.Point1.Y = 0; */
916 /* rotate round (0, 0) so that Point2 coordinates be (r, 0) */
917 r = Distance (0, 0, pad.Point2.X, pad.Point2.Y);
918 if (r < .1)
920 Cos = 1;
921 Sin = 0;
923 else
925 Sin = pad.Point2.Y / r;
926 Cos = pad.Point2.X / r;
928 x = X;
929 X = X * Cos + Y * Sin;
930 Y = Y * Cos - x * Sin;
931 /* now pad.Point2.X = r; pad.Point2.Y = 0; */
933 /* take into account the ends */
934 if (TEST_FLAG (SQUAREFLAG, Pad))
936 r += Pad->Thickness;
937 X += t2;
939 if (Y < 0)
940 Y = -Y; /* range value is evident now*/
942 if (TEST_FLAG (SQUAREFLAG, Pad))
944 if (X <= 0)
946 if (Y <= t2)
947 range = -X;
948 else
949 return Radius > Distance (0, t2, X, Y);
951 else if (X >= r)
953 if (Y <= t2)
954 range = X - r;
955 else
956 return Radius > Distance (r, t2, X, Y);
958 else
959 range = Y - t2;
961 else/*Rounded pad: even more simple*/
963 if (X <= 0)
964 return (Radius + t2) > Distance (0, 0, X, Y);
965 else if (X >= r)
966 return (Radius + t2) > Distance (r, 0, X, Y);
967 else
968 range = Y - t2;
970 return range < Radius;
973 bool
974 IsPointInBox (Coord X, Coord Y, BoxTypePtr box, Coord Radius)
976 Coord width, height, range;
978 /* NB: Assumes box has point1 with numerically lower X and Y coordinates */
980 /* Compute coordinates relative to Point1 */
981 X -= box->X1;
982 Y -= box->Y1;
984 width = box->X2 - box->X1;
985 height = box->Y2 - box->Y1;
987 if (X <= 0)
989 if (Y < 0)
990 return Radius > Distance (0, 0, X, Y);
991 else if (Y > height)
992 return Radius > Distance (0, height, X, Y);
993 else
994 range = -X;
996 else if (X >= width)
998 if (Y < 0)
999 return Radius > Distance (width, 0, X, Y);
1000 else if (Y > height)
1001 return Radius > Distance (width, height, X, Y);
1002 else
1003 range = X - width;
1005 else
1007 if (Y < 0)
1008 range = -Y;
1009 else if (Y > height)
1010 range = Y - height;
1011 else
1012 return true;
1015 return range < Radius;
1018 /* TODO: this code is BROKEN in the case of non-circular arcs,
1019 * and in the case that the arc thickness is greater than
1020 * the radius.
1022 bool
1023 IsPointOnArc (Coord X, Coord Y, Coord Radius, ArcTypePtr Arc)
1025 /* Calculate angle of point from arc center */
1026 double p_dist = Distance (X, Y, Arc->X, Arc->Y);
1027 double p_cos = (X - Arc->X) / p_dist;
1028 Angle p_ang = acos (p_cos) * RAD_TO_DEG;
1029 Angle ang1, ang2;
1031 /* Convert StartAngle, Delta into bounding angles in [0, 720) */
1032 if (Arc->Delta > 0)
1034 ang1 = NormalizeAngle (Arc->StartAngle);
1035 ang2 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1037 else
1039 ang1 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1040 ang2 = NormalizeAngle (Arc->StartAngle);
1042 if (ang1 > ang2)
1043 ang2 += 360;
1044 /* Make sure full circles aren't treated as zero-length arcs */
1045 if (Arc->Delta == 360 || Arc->Delta == -360)
1046 ang2 = ang1 + 360;
1048 if (Y > Arc->Y)
1049 p_ang = -p_ang;
1050 p_ang += 180;
1052 /* Check point is outside arc range, check distance from endpoints */
1053 if (ang1 >= p_ang || ang2 <= p_ang)
1055 Coord ArcX, ArcY;
1057 ArcX = Arc->X + Arc->Width *
1058 cos ((Arc->StartAngle + 180) / RAD_TO_DEG);
1059 ArcY = Arc->Y - Arc->Width *
1060 sin ((Arc->StartAngle + 180) / RAD_TO_DEG);
1061 if (Distance (X, Y, ArcX, ArcY) < Radius + Arc->Thickness / 2)
1062 return true;
1064 ArcX = Arc->X + Arc->Width *
1065 cos ((Arc->StartAngle + Arc->Delta + 180) / RAD_TO_DEG);
1066 ArcY = Arc->Y - Arc->Width *
1067 sin ((Arc->StartAngle + Arc->Delta + 180) / RAD_TO_DEG);
1068 if (Distance (X, Y, ArcX, ArcY) < Radius + Arc->Thickness / 2)
1069 return true;
1070 return false;
1072 /* If point is inside the arc range, just compare it to the arc */
1073 return fabs (Distance (X, Y, Arc->X, Arc->Y) - Arc->Width) < Radius + Arc->Thickness / 2;
1076 /* ---------------------------------------------------------------------------
1077 * searches for any kind of object or for a set of object types
1078 * the calling routine passes two pointers to allocated memory for storing
1079 * the results.
1080 * A type value is returned too which is NO_TYPE if no objects has been found.
1081 * A set of object types is passed in.
1082 * The object is located by it's position.
1084 * The layout is checked in the following order:
1085 * polygon-point, pin, via, line, text, elementname, polygon, element
1087 * Note that if Type includes LOCKED_TYPE, then the search includes
1088 * locked items. Otherwise, locked items are ignored.
1091 SearchObjectByLocation (unsigned Type,
1092 void **Result1, void **Result2, void **Result3,
1093 Coord X, Coord Y, Coord Radius)
1095 void *r1, *r2, *r3;
1096 void **pr1 = &r1, **pr2 = &r2, **pr3 = &r3;
1097 int i;
1098 double HigherBound = 0;
1099 int HigherAvail = NO_TYPE;
1100 int locked = Type & LOCKED_TYPE;
1101 /* setup variables used by local functions */
1102 PosX = X;
1103 PosY = Y;
1104 SearchRadius = Radius;
1105 if (Radius)
1107 SearchBox.X1 = X - Radius;
1108 SearchBox.Y1 = Y - Radius;
1109 SearchBox.X2 = X + Radius;
1110 SearchBox.Y2 = Y + Radius;
1112 else
1114 SearchBox = point_box (X, Y);
1117 if (TEST_FLAG (LOCKNAMESFLAG, PCB))
1119 Type &= ~ (ELEMENTNAME_TYPE | TEXT_TYPE);
1121 if (TEST_FLAG (HIDENAMESFLAG, PCB))
1123 Type &= ~ELEMENTNAME_TYPE;
1125 if (TEST_FLAG (ONLYNAMESFLAG, PCB))
1127 Type &= (ELEMENTNAME_TYPE | TEXT_TYPE);
1129 if (TEST_FLAG (THINDRAWFLAG, PCB) || TEST_FLAG (THINDRAWPOLYFLAG, PCB))
1131 Type &= ~POLYGON_TYPE;
1134 if (Type & RATLINE_TYPE && PCB->RatOn &&
1135 SearchRatLineByLocation (locked,
1136 (RatTypePtr *) Result1,
1137 (RatTypePtr *) Result2,
1138 (RatTypePtr *) Result3))
1139 return (RATLINE_TYPE);
1141 if (Type & VIA_TYPE &&
1142 SearchViaByLocation (locked,
1143 (PinTypePtr *) Result1,
1144 (PinTypePtr *) Result2, (PinTypePtr *) Result3))
1145 return (VIA_TYPE);
1147 if (Type & PIN_TYPE &&
1148 SearchPinByLocation (locked,
1149 (ElementTypePtr *) pr1,
1150 (PinTypePtr *) pr2, (PinTypePtr *) pr3))
1151 HigherAvail = PIN_TYPE;
1153 if (!HigherAvail && Type & PAD_TYPE &&
1154 SearchPadByLocation (locked,
1155 (ElementTypePtr *) pr1,
1156 (PadTypePtr *) pr2, (PadTypePtr *) pr3, false))
1157 HigherAvail = PAD_TYPE;
1159 if (!HigherAvail && Type & ELEMENTNAME_TYPE &&
1160 SearchElementNameByLocation (locked,
1161 (ElementTypePtr *) pr1,
1162 (TextTypePtr *) pr2, (TextTypePtr *) pr3,
1163 false))
1165 BoxTypePtr box = &((TextTypePtr) r2)->BoundingBox;
1166 HigherBound = (double) (box->X2 - box->X1) * (double) (box->Y2 - box->Y1);
1167 HigherAvail = ELEMENTNAME_TYPE;
1170 if (!HigherAvail && Type & ELEMENT_TYPE &&
1171 SearchElementByLocation (locked,
1172 (ElementTypePtr *) pr1,
1173 (ElementTypePtr *) pr2,
1174 (ElementTypePtr *) pr3, false))
1176 BoxTypePtr box = &((ElementTypePtr) r1)->BoundingBox;
1177 HigherBound = (double) (box->X2 - box->X1) * (double) (box->Y2 - box->Y1);
1178 HigherAvail = ELEMENT_TYPE;
1181 for (i = -1; i < max_copper_layer + 1; i++)
1183 if (i < 0)
1184 SearchLayer = &PCB->Data->SILKLAYER;
1185 else if (i < max_copper_layer)
1186 SearchLayer = LAYER_ON_STACK (i);
1187 else
1189 SearchLayer = &PCB->Data->BACKSILKLAYER;
1190 if (!PCB->InvisibleObjectsOn)
1191 continue;
1193 if (SearchLayer->On)
1195 if ((HigherAvail & (PIN_TYPE | PAD_TYPE)) == 0 &&
1196 Type & POLYGONPOINT_TYPE &&
1197 SearchPointByLocation (locked,
1198 (LayerTypePtr *) Result1,
1199 (PolygonTypePtr *) Result2,
1200 (PointTypePtr *) Result3))
1201 return (POLYGONPOINT_TYPE);
1203 if ((HigherAvail & (PIN_TYPE | PAD_TYPE)) == 0 &&
1204 Type & LINEPOINT_TYPE &&
1205 SearchLinePointByLocation (locked,
1206 (LayerTypePtr *) Result1,
1207 (LineTypePtr *) Result2,
1208 (PointTypePtr *) Result3))
1209 return (LINEPOINT_TYPE);
1211 if ((HigherAvail & (PIN_TYPE | PAD_TYPE)) == 0 && Type & LINE_TYPE
1212 && SearchLineByLocation (locked,
1213 (LayerTypePtr *) Result1,
1214 (LineTypePtr *) Result2,
1215 (LineTypePtr *) Result3))
1216 return (LINE_TYPE);
1218 if ((HigherAvail & (PIN_TYPE | PAD_TYPE)) == 0 && Type & ARC_TYPE &&
1219 SearchArcByLocation (locked,
1220 (LayerTypePtr *) Result1,
1221 (ArcTypePtr *) Result2,
1222 (ArcTypePtr *) Result3))
1223 return (ARC_TYPE);
1225 if ((HigherAvail & (PIN_TYPE | PAD_TYPE)) == 0 && Type & TEXT_TYPE
1226 && SearchTextByLocation (locked,
1227 (LayerTypePtr *) Result1,
1228 (TextTypePtr *) Result2,
1229 (TextTypePtr *) Result3))
1230 return (TEXT_TYPE);
1232 if (Type & POLYGON_TYPE &&
1233 SearchPolygonByLocation (locked,
1234 (LayerTypePtr *) Result1,
1235 (PolygonTypePtr *) Result2,
1236 (PolygonTypePtr *) Result3))
1238 if (HigherAvail)
1240 BoxTypePtr box =
1241 &(*(PolygonTypePtr *) Result2)->BoundingBox;
1242 double area =
1243 (double) (box->X2 - box->X1) * (double) (box->X2 - box->X1);
1244 if (HigherBound < area)
1245 break;
1246 else
1247 return (POLYGON_TYPE);
1249 else
1250 return (POLYGON_TYPE);
1254 /* return any previously found objects */
1255 if (HigherAvail & PIN_TYPE)
1257 *Result1 = r1;
1258 *Result2 = r2;
1259 *Result3 = r3;
1260 return (PIN_TYPE);
1263 if (HigherAvail & PAD_TYPE)
1265 *Result1 = r1;
1266 *Result2 = r2;
1267 *Result3 = r3;
1268 return (PAD_TYPE);
1271 if (HigherAvail & ELEMENTNAME_TYPE)
1273 *Result1 = r1;
1274 *Result2 = r2;
1275 *Result3 = r3;
1276 return (ELEMENTNAME_TYPE);
1279 if (HigherAvail & ELEMENT_TYPE)
1281 *Result1 = r1;
1282 *Result2 = r2;
1283 *Result3 = r3;
1284 return (ELEMENT_TYPE);
1287 /* search the 'invisible objects' last */
1288 if (!PCB->InvisibleObjectsOn)
1289 return (NO_TYPE);
1291 if (Type & PAD_TYPE &&
1292 SearchPadByLocation (locked,
1293 (ElementTypePtr *) Result1,
1294 (PadTypePtr *) Result2, (PadTypePtr *) Result3,
1295 true))
1296 return (PAD_TYPE);
1298 if (Type & ELEMENTNAME_TYPE &&
1299 SearchElementNameByLocation (locked,
1300 (ElementTypePtr *) Result1,
1301 (TextTypePtr *) Result2,
1302 (TextTypePtr *) Result3, true))
1303 return (ELEMENTNAME_TYPE);
1305 if (Type & ELEMENT_TYPE &&
1306 SearchElementByLocation (locked,
1307 (ElementTypePtr *) Result1,
1308 (ElementTypePtr *) Result2,
1309 (ElementTypePtr *) Result3, true))
1310 return (ELEMENT_TYPE);
1312 return (NO_TYPE);
1315 /* ---------------------------------------------------------------------------
1316 * searches for a object by it's unique ID. It doesn't matter if
1317 * the object is visible or not. The search is performed on a PCB, a
1318 * buffer or on the remove list.
1319 * The calling routine passes two pointers to allocated memory for storing
1320 * the results.
1321 * A type value is returned too which is NO_TYPE if no objects has been found.
1324 SearchObjectByID (DataTypePtr Base,
1325 void **Result1, void **Result2, void **Result3, int ID,
1326 int type)
1328 if (type == LINE_TYPE || type == LINEPOINT_TYPE)
1330 ALLLINE_LOOP (Base);
1332 if (line->ID == ID)
1334 *Result1 = (void *) layer;
1335 *Result2 = *Result3 = (void *) line;
1336 return (LINE_TYPE);
1338 if (line->Point1.ID == ID)
1340 *Result1 = (void *) layer;
1341 *Result2 = (void *) line;
1342 *Result3 = (void *) &line->Point1;
1343 return (LINEPOINT_TYPE);
1345 if (line->Point2.ID == ID)
1347 *Result1 = (void *) layer;
1348 *Result2 = (void *) line;
1349 *Result3 = (void *) &line->Point2;
1350 return (LINEPOINT_TYPE);
1353 ENDALL_LOOP;
1355 if (type == ARC_TYPE)
1357 ALLARC_LOOP (Base);
1359 if (arc->ID == ID)
1361 *Result1 = (void *) layer;
1362 *Result2 = *Result3 = (void *) arc;
1363 return (ARC_TYPE);
1366 ENDALL_LOOP;
1369 if (type == TEXT_TYPE)
1371 ALLTEXT_LOOP (Base);
1373 if (text->ID == ID)
1375 *Result1 = (void *) layer;
1376 *Result2 = *Result3 = (void *) text;
1377 return (TEXT_TYPE);
1380 ENDALL_LOOP;
1383 if (type == POLYGON_TYPE || type == POLYGONPOINT_TYPE)
1385 ALLPOLYGON_LOOP (Base);
1387 if (polygon->ID == ID)
1389 *Result1 = (void *) layer;
1390 *Result2 = *Result3 = (void *) polygon;
1391 return (POLYGON_TYPE);
1393 if (type == POLYGONPOINT_TYPE)
1394 POLYGONPOINT_LOOP (polygon);
1396 if (point->ID == ID)
1398 *Result1 = (void *) layer;
1399 *Result2 = (void *) polygon;
1400 *Result3 = (void *) point;
1401 return (POLYGONPOINT_TYPE);
1404 END_LOOP;
1406 ENDALL_LOOP;
1408 if (type == VIA_TYPE)
1410 VIA_LOOP (Base);
1412 if (via->ID == ID)
1414 *Result1 = *Result2 = *Result3 = (void *) via;
1415 return (VIA_TYPE);
1418 END_LOOP;
1421 if (type == RATLINE_TYPE || type == LINEPOINT_TYPE)
1423 RAT_LOOP (Base);
1425 if (line->ID == ID)
1427 *Result1 = *Result2 = *Result3 = (void *) line;
1428 return (RATLINE_TYPE);
1430 if (line->Point1.ID == ID)
1432 *Result1 = (void *) NULL;
1433 *Result2 = (void *) line;
1434 *Result3 = (void *) &line->Point1;
1435 return (LINEPOINT_TYPE);
1437 if (line->Point2.ID == ID)
1439 *Result1 = (void *) NULL;
1440 *Result2 = (void *) line;
1441 *Result3 = (void *) &line->Point2;
1442 return (LINEPOINT_TYPE);
1445 END_LOOP;
1448 if (type == ELEMENT_TYPE || type == PAD_TYPE || type == PIN_TYPE
1449 || type == ELEMENTLINE_TYPE || type == ELEMENTNAME_TYPE
1450 || type == ELEMENTARC_TYPE)
1451 /* check pins and elementnames too */
1452 ELEMENT_LOOP (Base);
1454 if (element->ID == ID)
1456 *Result1 = *Result2 = *Result3 = (void *) element;
1457 return (ELEMENT_TYPE);
1459 if (type == ELEMENTLINE_TYPE)
1460 ELEMENTLINE_LOOP (element);
1462 if (line->ID == ID)
1464 *Result1 = (void *) element;
1465 *Result2 = *Result3 = (void *) line;
1466 return (ELEMENTLINE_TYPE);
1469 END_LOOP;
1470 if (type == ELEMENTARC_TYPE)
1471 ARC_LOOP (element);
1473 if (arc->ID == ID)
1475 *Result1 = (void *) element;
1476 *Result2 = *Result3 = (void *) arc;
1477 return (ELEMENTARC_TYPE);
1480 END_LOOP;
1481 if (type == ELEMENTNAME_TYPE)
1482 ELEMENTTEXT_LOOP (element);
1484 if (text->ID == ID)
1486 *Result1 = (void *) element;
1487 *Result2 = *Result3 = (void *) text;
1488 return (ELEMENTNAME_TYPE);
1491 END_LOOP;
1492 if (type == PIN_TYPE)
1493 PIN_LOOP (element);
1495 if (pin->ID == ID)
1497 *Result1 = (void *) element;
1498 *Result2 = *Result3 = (void *) pin;
1499 return (PIN_TYPE);
1502 END_LOOP;
1503 if (type == PAD_TYPE)
1504 PAD_LOOP (element);
1506 if (pad->ID == ID)
1508 *Result1 = (void *) element;
1509 *Result2 = *Result3 = (void *) pad;
1510 return (PAD_TYPE);
1513 END_LOOP;
1515 END_LOOP;
1517 Message ("hace: Internal error, search for ID %d failed\n", ID);
1518 return (NO_TYPE);
1521 /* ---------------------------------------------------------------------------
1522 * searches for an element by its board name.
1523 * The function returns a pointer to the element, NULL if not found
1525 ElementTypePtr
1526 SearchElementByName (DataTypePtr Base, char *Name)
1528 ElementTypePtr result = NULL;
1530 ELEMENT_LOOP (Base);
1532 if (element->Name[1].TextString &&
1533 NSTRCMP (element->Name[1].TextString, Name) == 0)
1535 result = element;
1536 return (result);
1539 END_LOOP;
1540 return result;
1543 /* ---------------------------------------------------------------------------
1544 * searches the cursor position for the type
1547 SearchScreen (Coord X, Coord Y, int Type, void **Result1,
1548 void **Result2, void **Result3)
1550 int ans;
1552 ans = SearchObjectByLocation (Type, Result1, Result2, Result3,
1553 X, Y, SLOP * pixel_slop);
1554 return (ans);
1557 /* ---------------------------------------------------------------------------
1558 * searches the cursor position for the type
1561 SearchScreenGridSlop (Coord X, Coord Y, int Type, void **Result1,
1562 void **Result2, void **Result3)
1564 int ans;
1566 ans = SearchObjectByLocation (Type, Result1, Result2, Result3,
1567 X, Y, PCB->Grid / 2);
1568 return (ans);