(no commit message)
[geda-pcb/pcjc2.git] / src / crosshair.c
blob348afdd90e8419d7c2495658c136cbef27919563
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
28 /* crosshair stuff
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include <memory.h>
36 #include <math.h>
38 #include "global.h"
39 #include "hid_draw.h"
41 #include "crosshair.h"
42 #include "data.h"
43 #include "draw.h"
44 #include "error.h"
45 #include "line.h"
46 #include "misc.h"
47 #include "mymem.h"
48 #include "search.h"
49 #include "polygon.h"
51 #ifdef HAVE_LIBDMALLOC
52 #include <dmalloc.h>
53 #endif
55 typedef struct
57 int x, y;
58 } point;
60 /* ---------------------------------------------------------------------------
61 * some local prototypes
63 static void XORPolygon (PolygonType *, Coord, Coord);
64 static void XORDrawElement (ElementType *, Coord, Coord);
65 static void XORDrawBuffer (BufferType *);
66 static void XORDrawInsertPointObject (void);
67 static void XORDrawMoveOrCopyObject (void);
68 static void XORDrawAttachedLine (Coord, Coord, Coord, Coord, Coord);
69 static void XORDrawAttachedArc (Coord);
71 static void
72 thindraw_moved_pv (PinType *pv, Coord x, Coord y)
74 /* Make a copy of the pin structure, moved to the correct position */
75 PinType moved_pv = *pv;
76 moved_pv.X += x;
77 moved_pv.Y += y;
79 gui->graphics->thindraw_pcb_pv (Crosshair.GC, Crosshair.GC, &moved_pv, true, false);
82 /* ---------------------------------------------------------------------------
83 * creates a tmp polygon with coordinates converted to screen system
85 static void
86 XORPolygon (PolygonType *polygon, Coord dx, Coord dy)
88 Cardinal i;
89 for (i = 0; i < polygon->PointN; i++)
91 Cardinal next = next_contour_point (polygon, i);
92 gui->graphics->draw_line (Crosshair.GC,
93 polygon->Points[i].X + dx,
94 polygon->Points[i].Y + dy,
95 polygon->Points[next].X + dx,
96 polygon->Points[next].Y + dy);
100 /*-----------------------------------------------------------
101 * Draws the outline of an arc
103 static void
104 XORDrawAttachedArc (Coord thick)
106 ArcType arc;
107 BoxType *bx;
108 Coord wx, wy;
109 Angle sa, dir;
110 Coord wid = thick / 2;
112 wx = Crosshair.X - Crosshair.AttachedBox.Point1.X;
113 wy = Crosshair.Y - Crosshair.AttachedBox.Point1.Y;
114 if (wx == 0 && wy == 0)
115 return;
116 arc.X = Crosshair.AttachedBox.Point1.X;
117 arc.Y = Crosshair.AttachedBox.Point1.Y;
118 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
120 arc.X = Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
121 sa = (wx >= 0) ? 0 : 180;
122 #ifdef ARC45
123 if (abs (wy) >= 2 * abs (wx))
124 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
125 else
126 #endif
127 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
129 else
131 arc.Y = Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
132 sa = (wy >= 0) ? -90 : 90;
133 #ifdef ARC45
134 if (abs (wx) >= 2 * abs (wy))
135 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
136 else
137 #endif
138 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
139 wy = wx;
141 wy = abs (wy);
142 arc.StartAngle = sa;
143 arc.Delta = dir;
144 arc.Width = arc.Height = wy;
145 bx = GetArcEnds (&arc);
146 /* sa = sa - 180; */
147 gui->graphics->draw_arc (Crosshair.GC, arc.X, arc.Y, wy + wid, wy + wid, sa, dir);
148 if (wid > pixel_slop)
150 gui->graphics->draw_arc (Crosshair.GC, arc.X, arc.Y, wy - wid, wy - wid, sa, dir);
151 gui->graphics->draw_arc (Crosshair.GC, bx->X1, bx->Y1, wid, wid, sa, -180 * SGN (dir));
152 gui->graphics->draw_arc (Crosshair.GC, bx->X2, bx->Y2, wid, wid, sa + dir, 180 * SGN (dir));
156 /*-----------------------------------------------------------
157 * Draws the outline of a line
159 static void
160 XORDrawAttachedLine (Coord x1, Coord y1, Coord x2, Coord y2, Coord thick)
162 Coord dx, dy, ox, oy;
163 double h;
165 dx = x2 - x1;
166 dy = y2 - y1;
167 if (dx != 0 || dy != 0)
168 h = 0.5 * thick / sqrt (SQUARE (dx) + SQUARE (dy));
169 else
170 h = 0.0;
171 ox = dy * h + 0.5 * SGN (dy);
172 oy = -(dx * h + 0.5 * SGN (dx));
173 gui->graphics->draw_line (Crosshair.GC, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
174 if (abs (ox) >= pixel_slop || abs (oy) >= pixel_slop)
176 Angle angle = atan2 (dx, dy) * 57.295779;
177 gui->graphics->draw_line (Crosshair.GC, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
178 gui->graphics->draw_arc (Crosshair.GC, x1, y1, thick / 2, thick / 2, angle - 180, 180);
179 gui->graphics->draw_arc (Crosshair.GC, x2, y2, thick / 2, thick / 2, angle, 180);
183 /* ---------------------------------------------------------------------------
184 * draws the elements of a loaded circuit which is to be merged in
186 static void
187 XORDrawElement (ElementType *Element, Coord DX, Coord DY)
189 /* if no silkscreen, draw the bounding box */
190 if (Element->ArcN == 0 && Element->LineN == 0)
192 gui->graphics->draw_line (Crosshair.GC,
193 DX + Element->BoundingBox.X1,
194 DY + Element->BoundingBox.Y1,
195 DX + Element->BoundingBox.X1,
196 DY + Element->BoundingBox.Y2);
197 gui->graphics->draw_line (Crosshair.GC,
198 DX + Element->BoundingBox.X1,
199 DY + Element->BoundingBox.Y2,
200 DX + Element->BoundingBox.X2,
201 DY + Element->BoundingBox.Y2);
202 gui->graphics->draw_line (Crosshair.GC,
203 DX + Element->BoundingBox.X2,
204 DY + Element->BoundingBox.Y2,
205 DX + Element->BoundingBox.X2,
206 DY + Element->BoundingBox.Y1);
207 gui->graphics->draw_line (Crosshair.GC,
208 DX + Element->BoundingBox.X2,
209 DY + Element->BoundingBox.Y1,
210 DX + Element->BoundingBox.X1,
211 DY + Element->BoundingBox.Y1);
213 else
215 ELEMENTLINE_LOOP (Element);
217 gui->graphics->draw_line (Crosshair.GC,
218 DX + line->Point1.X,
219 DY + line->Point1.Y,
220 DX + line->Point2.X,
221 DY + line->Point2.Y);
223 END_LOOP;
225 /* arc coordinates and angles have to be converted to X11 notation */
226 ARC_LOOP (Element);
228 gui->graphics->draw_arc (Crosshair.GC,
229 DX + arc->X,
230 DY + arc->Y,
231 arc->Width, arc->Height, arc->StartAngle, arc->Delta);
233 END_LOOP;
235 /* pin coordinates and angles have to be converted to X11 notation */
236 PIN_LOOP (Element);
238 thindraw_moved_pv (pin, DX, DY);
240 END_LOOP;
242 /* pads */
243 PAD_LOOP (Element);
245 if (PCB->InvisibleObjectsOn ||
246 (TEST_FLAG (ONSOLDERFLAG, pad) != 0) == Settings.ShowBottomSide)
248 /* Make a copy of the pad structure, moved to the correct position */
249 PadType moved_pad = *pad;
250 moved_pad.Point1.X += DX; moved_pad.Point1.Y += DY;
251 moved_pad.Point2.X += DX; moved_pad.Point2.Y += DY;
253 gui->graphics->thindraw_pcb_pad (Crosshair.GC, &moved_pad, false, false);
256 END_LOOP;
257 /* mark */
258 gui->graphics->draw_line (Crosshair.GC,
259 Element->MarkX + DX - EMARK_SIZE,
260 Element->MarkY + DY,
261 Element->MarkX + DX,
262 Element->MarkY + DY - EMARK_SIZE);
263 gui->graphics->draw_line (Crosshair.GC,
264 Element->MarkX + DX + EMARK_SIZE,
265 Element->MarkY + DY,
266 Element->MarkX + DX,
267 Element->MarkY + DY - EMARK_SIZE);
268 gui->graphics->draw_line (Crosshair.GC,
269 Element->MarkX + DX - EMARK_SIZE,
270 Element->MarkY + DY,
271 Element->MarkX + DX,
272 Element->MarkY + DY + EMARK_SIZE);
273 gui->graphics->draw_line (Crosshair.GC,
274 Element->MarkX + DX + EMARK_SIZE,
275 Element->MarkY + DY,
276 Element->MarkX + DX,
277 Element->MarkY + DY + EMARK_SIZE);
280 /* ---------------------------------------------------------------------------
281 * draws all visible and attached objects of the pastebuffer
283 static void
284 XORDrawBuffer (BufferType *Buffer)
286 Cardinal i;
287 Coord x, y;
289 /* set offset */
290 x = Crosshair.X - Buffer->X;
291 y = Crosshair.Y - Buffer->Y;
293 /* draw all visible layers */
294 for (i = 0; i < max_copper_layer + 2; i++)
295 if (PCB->Data->Layer[i].On)
297 LayerType *layer = &Buffer->Data->Layer[i];
299 LINE_LOOP (layer);
302 XORDrawAttachedLine(x +line->Point1.X,
303 y +line->Point1.Y, x +line->Point2.X,
304 y +line->Point2.Y, line->Thickness);
306 gui->graphics->draw_line (Crosshair.GC,
307 x + line->Point1.X, y + line->Point1.Y,
308 x + line->Point2.X, y + line->Point2.Y);
310 END_LOOP;
311 ARC_LOOP (layer);
313 gui->graphics->draw_arc (Crosshair.GC,
314 x + arc->X,
315 y + arc->Y,
316 arc->Width,
317 arc->Height, arc->StartAngle, arc->Delta);
319 END_LOOP;
320 TEXT_LOOP (layer);
322 BoxType *box = &text->BoundingBox;
323 gui->graphics->draw_rect (Crosshair.GC,
324 x + box->X1, y + box->Y1, x + box->X2, y + box->Y2);
326 END_LOOP;
327 /* the tmp polygon has n+1 points because the first
328 * and the last one are set to the same coordinates
330 POLYGON_LOOP (layer);
332 XORPolygon (polygon, x, y);
334 END_LOOP;
337 /* draw elements if visible */
338 if (PCB->PinOn && PCB->ElementOn)
339 ELEMENT_LOOP (Buffer->Data);
341 if (FRONT (element) || PCB->InvisibleObjectsOn)
342 XORDrawElement (element, x, y);
344 END_LOOP;
346 /* and the vias */
347 if (PCB->ViaOn)
348 VIA_LOOP (Buffer->Data);
350 thindraw_moved_pv (via, x, y);
352 END_LOOP;
355 /* ---------------------------------------------------------------------------
356 * draws the rubberband to insert points into polygons/lines/...
358 static void
359 XORDrawInsertPointObject (void)
361 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
362 PointType *point = (PointType *) Crosshair.AttachedObject.Ptr3;
364 if (Crosshair.AttachedObject.Type != NO_TYPE)
366 gui->graphics->draw_line (Crosshair.GC, point->X, point->Y, line->Point1.X, line->Point1.Y);
367 gui->graphics->draw_line (Crosshair.GC, point->X, point->Y, line->Point2.X, line->Point2.Y);
371 /* ---------------------------------------------------------------------------
372 * draws the attached object while in MOVE_MODE or COPY_MODE
374 static void
375 XORDrawMoveOrCopyObject (void)
377 RubberbandType *ptr;
378 Cardinal i;
379 Coord dx = Crosshair.X - Crosshair.AttachedObject.X,
380 dy = Crosshair.Y - Crosshair.AttachedObject.Y;
382 switch (Crosshair.AttachedObject.Type)
384 case VIA_TYPE:
386 PinType *via = (PinType *) Crosshair.AttachedObject.Ptr1;
387 thindraw_moved_pv (via, dx, dy);
388 break;
391 case LINE_TYPE:
393 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
395 XORDrawAttachedLine (line->Point1.X + dx, line->Point1.Y + dy,
396 line->Point2.X + dx, line->Point2.Y + dy,
397 line->Thickness);
398 break;
401 case ARC_TYPE:
403 ArcType *Arc = (ArcType *) Crosshair.AttachedObject.Ptr2;
405 gui->graphics->draw_arc (Crosshair.GC,
406 Arc->X + dx,
407 Arc->Y + dy,
408 Arc->Width, Arc->Height, Arc->StartAngle, Arc->Delta);
409 break;
412 case POLYGON_TYPE:
414 PolygonType *polygon =
415 (PolygonType *) Crosshair.AttachedObject.Ptr2;
417 /* the tmp polygon has n+1 points because the first
418 * and the last one are set to the same coordinates
420 XORPolygon (polygon, dx, dy);
421 break;
424 case LINEPOINT_TYPE:
426 LineType *line;
427 PointType *point;
429 line = (LineType *) Crosshair.AttachedObject.Ptr2;
430 point = (PointType *) Crosshair.AttachedObject.Ptr3;
431 if (point == &line->Point1)
432 XORDrawAttachedLine (point->X + dx,
433 point->Y + dy, line->Point2.X,
434 line->Point2.Y, line->Thickness);
435 else
436 XORDrawAttachedLine (point->X + dx,
437 point->Y + dy, line->Point1.X,
438 line->Point1.Y, line->Thickness);
439 break;
442 case POLYGONPOINT_TYPE:
444 PolygonType *polygon;
445 PointType *point;
446 Cardinal point_idx, prev, next;
448 polygon = (PolygonType *) Crosshair.AttachedObject.Ptr2;
449 point = (PointType *) Crosshair.AttachedObject.Ptr3;
450 point_idx = polygon_point_idx (polygon, point);
452 /* get previous and following point */
453 prev = prev_contour_point (polygon, point_idx);
454 next = next_contour_point (polygon, point_idx);
456 /* draw the two segments */
457 gui->graphics->draw_line (Crosshair.GC,
458 polygon->Points[prev].X, polygon->Points[prev].Y,
459 point->X + dx, point->Y + dy);
460 gui->graphics->draw_line (Crosshair.GC,
461 point->X + dx, point->Y + dy,
462 polygon->Points[next].X, polygon->Points[next].Y);
463 break;
466 case ELEMENTNAME_TYPE:
468 /* locate the element "mark" and draw an association line from crosshair to it */
469 ElementType *element =
470 (ElementType *) Crosshair.AttachedObject.Ptr1;
472 gui->graphics->draw_line (Crosshair.GC,
473 element->MarkX,
474 element->MarkY, Crosshair.X, Crosshair.Y);
475 /* fall through to move the text as a box outline */
477 case TEXT_TYPE:
479 TextType *text = (TextType *) Crosshair.AttachedObject.Ptr2;
480 BoxType *box = &text->BoundingBox;
481 gui->graphics->draw_rect (Crosshair.GC,
482 box->X1 + dx,
483 box->Y1 + dy, box->X2 + dx, box->Y2 + dy);
484 break;
487 /* pin/pad movements result in moving an element */
488 case PAD_TYPE:
489 case PIN_TYPE:
490 case ELEMENT_TYPE:
491 XORDrawElement ((ElementType *) Crosshair.AttachedObject.Ptr2, dx, dy);
492 break;
495 /* draw the attached rubberband lines too */
496 i = Crosshair.AttachedObject.RubberbandN;
497 ptr = Crosshair.AttachedObject.Rubberband;
498 while (i)
500 PointType *point1, *point2;
502 if (TEST_FLAG (VIAFLAG, ptr->Line))
504 /* this is a rat going to a polygon. do not draw for rubberband */;
506 else if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
508 /* 'point1' is always the fix-point */
509 if (ptr->MovedPoint == &ptr->Line->Point1)
511 point1 = &ptr->Line->Point2;
512 point2 = &ptr->Line->Point1;
514 else
516 point1 = &ptr->Line->Point1;
517 point2 = &ptr->Line->Point2;
519 XORDrawAttachedLine (point1->X,
520 point1->Y, point2->X + dx,
521 point2->Y + dy, ptr->Line->Thickness);
523 else if (ptr->MovedPoint == &ptr->Line->Point1)
524 XORDrawAttachedLine (ptr->Line->Point1.X + dx,
525 ptr->Line->Point1.Y + dy,
526 ptr->Line->Point2.X + dx,
527 ptr->Line->Point2.Y + dy, ptr->Line->Thickness);
529 ptr++;
530 i--;
534 /* ---------------------------------------------------------------------------
535 * draws additional stuff that follows the crosshair
537 void
538 DrawAttached (void)
540 switch (Settings.Mode)
542 case VIA_MODE:
544 /* Make a dummy via structure to draw from */
545 PinType via;
546 via.X = Crosshair.X;
547 via.Y = Crosshair.Y;
548 via.Thickness = Settings.ViaThickness;
549 via.Clearance = 2 * Settings.Keepaway;
550 via.DrillingHole = Settings.ViaDrillingHole;
551 via.Mask = 0;
552 via.Flags = NoFlags ();
554 gui->graphics->thindraw_pcb_pv (Crosshair.GC, Crosshair.GC, &via, true, false);
556 if (TEST_FLAG (SHOWDRCFLAG, PCB))
558 /* XXX: Naughty cheat - use the mask to draw DRC clearance! */
559 via.Mask = Settings.ViaThickness + PCB->Bloat * 2;
560 gui->graphics->set_color (Crosshair.GC, Settings.CrossColor);
561 gui->graphics->thindraw_pcb_pv (Crosshair.GC, Crosshair.GC, &via, false, true);
562 gui->graphics->set_color (Crosshair.GC, Settings.CrosshairColor);
564 break;
567 /* the attached line is used by both LINEMODE, POLYGON_MODE and POLYGONHOLE_MODE*/
568 case POLYGON_MODE:
569 case POLYGONHOLE_MODE:
570 /* draw only if starting point is set */
571 if (Crosshair.AttachedLine.State != STATE_FIRST)
572 gui->graphics->draw_line (Crosshair.GC,
573 Crosshair.AttachedLine.Point1.X,
574 Crosshair.AttachedLine.Point1.Y,
575 Crosshair.AttachedLine.Point2.X,
576 Crosshair.AttachedLine.Point2.Y);
578 /* draw attached polygon only if in POLYGON_MODE or POLYGONHOLE_MODE */
579 if (Crosshair.AttachedPolygon.PointN > 1)
581 XORPolygon (&Crosshair.AttachedPolygon, 0, 0);
583 break;
585 case ARC_MODE:
586 if (Crosshair.AttachedBox.State != STATE_FIRST)
588 XORDrawAttachedArc (Settings.LineThickness);
589 if (TEST_FLAG (SHOWDRCFLAG, PCB))
591 gui->graphics->set_color (Crosshair.GC, Settings.CrossColor);
592 XORDrawAttachedArc (Settings.LineThickness +
593 2 * (PCB->Bloat + 1));
594 gui->graphics->set_color (Crosshair.GC, Settings.CrosshairColor);
598 break;
600 case LINE_MODE:
601 /* draw only if starting point exists and the line has length */
602 if (Crosshair.AttachedLine.State != STATE_FIRST &&
603 Crosshair.AttachedLine.draw)
605 XORDrawAttachedLine (Crosshair.AttachedLine.Point1.X,
606 Crosshair.AttachedLine.Point1.Y,
607 Crosshair.AttachedLine.Point2.X,
608 Crosshair.AttachedLine.Point2.Y,
609 PCB->RatDraw ? 10 : Settings.LineThickness);
610 /* draw two lines ? */
611 if (PCB->Clipping)
612 XORDrawAttachedLine (Crosshair.AttachedLine.Point2.X,
613 Crosshair.AttachedLine.Point2.Y,
614 Crosshair.X, Crosshair.Y,
615 PCB->RatDraw ? 10 : Settings.LineThickness);
616 if (TEST_FLAG (SHOWDRCFLAG, PCB))
618 gui->graphics->set_color (Crosshair.GC, Settings.CrossColor);
619 XORDrawAttachedLine (Crosshair.AttachedLine.Point1.X,
620 Crosshair.AttachedLine.Point1.Y,
621 Crosshair.AttachedLine.Point2.X,
622 Crosshair.AttachedLine.Point2.Y,
623 PCB->RatDraw ? 10 : Settings.LineThickness
624 + 2 * (PCB->Bloat + 1));
625 if (PCB->Clipping)
626 XORDrawAttachedLine (Crosshair.AttachedLine.Point2.X,
627 Crosshair.AttachedLine.Point2.Y,
628 Crosshair.X, Crosshair.Y,
629 PCB->RatDraw ? 10 : Settings.
630 LineThickness + 2 * (PCB->Bloat + 1));
631 gui->graphics->set_color (Crosshair.GC, Settings.CrosshairColor);
634 break;
636 case PASTEBUFFER_MODE:
637 XORDrawBuffer (PASTEBUFFER);
638 break;
640 case COPY_MODE:
641 case MOVE_MODE:
642 XORDrawMoveOrCopyObject ();
643 break;
645 case INSERTPOINT_MODE:
646 XORDrawInsertPointObject ();
647 break;
650 /* an attached box does not depend on a special mode */
651 if (Crosshair.AttachedBox.State == STATE_SECOND ||
652 Crosshair.AttachedBox.State == STATE_THIRD)
654 Coord x1, y1, x2, y2;
656 x1 = Crosshair.AttachedBox.Point1.X;
657 y1 = Crosshair.AttachedBox.Point1.Y;
658 x2 = Crosshair.AttachedBox.Point2.X;
659 y2 = Crosshair.AttachedBox.Point2.Y;
660 gui->graphics->draw_rect (Crosshair.GC, x1, y1, x2, y2);
665 /* --------------------------------------------------------------------------
666 * draw the marker position
668 void
669 DrawMark (void)
671 /* Mark is not drawn when it is not set */
672 if (!Marked.status)
673 return;
675 gui->graphics->draw_line (Crosshair.GC,
676 Marked.X - MARK_SIZE,
677 Marked.Y - MARK_SIZE,
678 Marked.X + MARK_SIZE, Marked.Y + MARK_SIZE);
679 gui->graphics->draw_line (Crosshair.GC,
680 Marked.X + MARK_SIZE,
681 Marked.Y - MARK_SIZE,
682 Marked.X - MARK_SIZE, Marked.Y + MARK_SIZE);
685 /* ---------------------------------------------------------------------------
686 * Returns the nearest grid-point to the given Coord
688 Coord
689 GridFit (Coord x, Coord grid_spacing, Coord grid_offset)
691 x -= grid_offset;
692 x = grid_spacing * round ((double) x / grid_spacing);
693 x += grid_offset;
694 return x;
698 /* ---------------------------------------------------------------------------
699 * notify the GUI that data relating to the crosshair is being changed.
701 * The argument passed is false to notify "changes are about to happen",
702 * and true to notify "changes have finished".
704 * Each call with a 'false' parameter must be matched with a following one
705 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
706 * but might be allowed in the future.
708 * GUIs should not complain if they receive extra calls with 'true' as parameter.
709 * They should initiate a redraw of the crosshair attached objects - which may
710 * (if necessary) mean repainting the whole screen if the GUI hasn't tracked the
711 * location of existing attached drawing.
713 void
714 notify_crosshair_change (bool changes_complete)
716 if (gui->notify_crosshair_change)
717 gui->notify_crosshair_change (changes_complete);
721 /* ---------------------------------------------------------------------------
722 * notify the GUI that data relating to the mark is being changed.
724 * The argument passed is false to notify "changes are about to happen",
725 * and true to notify "changes have finished".
727 * Each call with a 'false' parameter must be matched with a following one
728 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
729 * but might be allowed in the future.
731 * GUIs should not complain if they receive extra calls with 'true' as parameter.
732 * They should initiate a redraw of the mark - which may (if necessary) mean
733 * repainting the whole screen if the GUI hasn't tracked the mark's location.
735 void
736 notify_mark_change (bool changes_complete)
738 if (gui->notify_mark_change)
739 gui->notify_mark_change (changes_complete);
743 /* ---------------------------------------------------------------------------
744 * Convenience for plugins using the old {Hide,Restore}Crosshair API.
745 * This links up to notify the GUI of the expected changes using the new APIs.
747 * Use of this old API is deprecated, as the names don't necessarily reflect
748 * what all GUIs may do in response to the notifications. Keeping these APIs
749 * is aimed at easing transition to the newer API, they will emit a harmless
750 * warning at the time of their first use.
753 void
754 HideCrosshair (void)
756 static bool warned_old_api = false;
757 if (!warned_old_api)
759 Message (_("WARNING: A plugin is using the deprecated API HideCrosshair().\n"
760 " This API may be removed in a future release of PCB.\n"));
761 warned_old_api = true;
764 notify_crosshair_change (false);
765 notify_mark_change (false);
768 void
769 RestoreCrosshair (void)
771 static bool warned_old_api = false;
772 if (!warned_old_api)
774 Message (_("WARNING: A plugin is using the deprecated API RestoreCrosshair().\n"
775 " This API may be removed in a future release of PCB.\n"));
776 warned_old_api = true;
779 notify_crosshair_change (true);
780 notify_mark_change (true);
783 /* ---------------------------------------------------------------------------
784 * Returns the square of the given number
786 static double
787 square (double x)
789 return x * x;
792 static double
793 crosshair_sq_dist (CrosshairType *crosshair, Coord x, Coord y)
795 return square (x - crosshair->X) + square (y - crosshair->Y);
798 struct snap_data {
799 CrosshairType *crosshair;
800 double nearest_sq_dist;
801 bool nearest_is_grid;
802 Coord x, y;
805 /* Snap to a given location if it is the closest thing we found so far.
806 * If "prefer_to_grid" is set, the passed location will take preference
807 * over a closer grid points we already snapped to UNLESS the user is
808 * pressing the SHIFT key. If the SHIFT key is pressed, the closest object
809 * (including grid points), is always preferred.
811 static void
812 check_snap_object (struct snap_data *snap_data, Coord x, Coord y,
813 bool prefer_to_grid)
815 double sq_dist;
817 sq_dist = crosshair_sq_dist (snap_data->crosshair, x, y);
818 if (sq_dist <= snap_data->nearest_sq_dist ||
819 (prefer_to_grid && snap_data->nearest_is_grid && !gui->shift_is_pressed()))
821 snap_data->x = x;
822 snap_data->y = y;
823 snap_data->nearest_sq_dist = sq_dist;
824 snap_data->nearest_is_grid = false;
828 static void
829 check_snap_offgrid_line (struct snap_data *snap_data,
830 Coord nearest_grid_x,
831 Coord nearest_grid_y)
833 void *ptr1, *ptr2, *ptr3;
834 int ans;
835 LineType *line;
836 Coord try_x, try_y;
837 double dx, dy;
838 double dist;
840 if (!TEST_FLAG (SNAPPINFLAG, PCB))
841 return;
843 /* Code to snap at some sensible point along a line */
844 /* Pick the nearest grid-point in the x or y direction
845 * to align with, then adjust until we hit the line
847 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
848 LINE_TYPE, &ptr1, &ptr2, &ptr3);
850 if (ans == NO_TYPE)
851 return;
853 line = (LineType *)ptr2;
855 /* Allow snapping to off-grid lines when drawing new lines (on
856 * the same layer), and when moving a line end-point
857 * (but don't snap to the same line)
859 if ((Settings.Mode != LINE_MODE || CURRENT != ptr1) &&
860 (Settings.Mode != MOVE_MODE ||
861 Crosshair.AttachedObject.Ptr1 != ptr1 ||
862 Crosshair.AttachedObject.Type != LINEPOINT_TYPE ||
863 Crosshair.AttachedObject.Ptr2 == line))
864 return;
866 dx = line->Point2.X - line->Point1.X;
867 dy = line->Point2.Y - line->Point1.Y;
869 /* Try snapping along the X axis */
870 if (dy != 0.)
872 /* Move in the X direction until we hit the line */
873 try_x = (nearest_grid_y - line->Point1.Y) / dy * dx + line->Point1.X;
874 try_y = nearest_grid_y;
875 check_snap_object (snap_data, try_x, try_y, true);
878 /* Try snapping along the Y axis */
879 if (dx != 0.)
881 try_x = nearest_grid_x;
882 try_y = (nearest_grid_x - line->Point1.X) / dx * dy + line->Point1.Y;
883 check_snap_object (snap_data, try_x, try_y, true);
886 if (dx != dy) /* If line not parallel with dX = dY direction.. */
888 /* Try snapping diagonally towards the line in the dX = dY direction */
890 if (dy == 0)
891 dist = line->Point1.Y - nearest_grid_y;
892 else
893 dist = ((line->Point1.X - nearest_grid_x) -
894 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 - dx / dy);
896 try_x = nearest_grid_x + dist;
897 try_y = nearest_grid_y + dist;
899 check_snap_object (snap_data, try_x, try_y, true);
902 if (dx != -dy) /* If line not parallel with dX = -dY direction.. */
904 /* Try snapping diagonally towards the line in the dX = -dY direction */
906 if (dy == 0)
907 dist = nearest_grid_y - line->Point1.Y;
908 else
909 dist = ((line->Point1.X - nearest_grid_x) -
910 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 + dx / dy);
912 try_x = nearest_grid_x + dist;
913 try_y = nearest_grid_y - dist;
915 check_snap_object (snap_data, try_x, try_y, true);
919 /* ---------------------------------------------------------------------------
920 * recalculates the passed coordinates to fit the current grid setting
922 void
923 FitCrosshairIntoGrid (Coord X, Coord Y)
925 Coord nearest_grid_x, nearest_grid_y;
926 void *ptr1, *ptr2, *ptr3;
927 struct snap_data snap_data;
928 int ans;
930 Crosshair.X = CLAMP (X, Crosshair.MinX, Crosshair.MaxX);
931 Crosshair.Y = CLAMP (Y, Crosshair.MinY, Crosshair.MaxY);
933 if (PCB->RatDraw)
935 nearest_grid_x = -MIL_TO_COORD (6);
936 nearest_grid_y = -MIL_TO_COORD (6);
938 else
940 nearest_grid_x = GridFit (Crosshair.X, PCB->Grid, PCB->GridOffsetX);
941 nearest_grid_y = GridFit (Crosshair.Y, PCB->Grid, PCB->GridOffsetY);
943 if (Marked.status && TEST_FLAG (ORTHOMOVEFLAG, PCB))
945 Coord dx = Crosshair.X - Marked.X;
946 Coord dy = Crosshair.Y - Marked.Y;
947 if (ABS (dx) > ABS (dy))
948 nearest_grid_y = Marked.Y;
949 else
950 nearest_grid_x = Marked.X;
955 snap_data.crosshair = &Crosshair;
956 snap_data.nearest_sq_dist =
957 crosshair_sq_dist (&Crosshair, nearest_grid_x, nearest_grid_y);
958 snap_data.nearest_is_grid = true;
959 snap_data.x = nearest_grid_x;
960 snap_data.y = nearest_grid_y;
962 ans = NO_TYPE;
963 if (!PCB->RatDraw)
964 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
965 ELEMENT_TYPE, &ptr1, &ptr2, &ptr3);
967 if (ans & ELEMENT_TYPE)
969 ElementType *el = (ElementType *) ptr1;
970 check_snap_object (&snap_data, el->MarkX, el->MarkY, false);
973 ans = NO_TYPE;
974 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
975 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
976 PAD_TYPE, &ptr1, &ptr2, &ptr3);
978 /* Avoid self-snapping when moving */
979 if (ans != NO_TYPE &&
980 Settings.Mode == MOVE_MODE &&
981 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
982 ptr1 == Crosshair.AttachedObject.Ptr1)
983 ans = NO_TYPE;
985 if (ans != NO_TYPE &&
986 ( Settings.Mode == LINE_MODE ||
987 (Settings.Mode == MOVE_MODE &&
988 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)))
990 PadType *pad = (PadType *) ptr2;
991 LayerType *desired_layer;
992 Cardinal desired_group;
993 Cardinal bottom_group, top_group;
994 int found_our_layer = false;
996 desired_layer = CURRENT;
997 if (Settings.Mode == MOVE_MODE &&
998 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)
1000 desired_layer = (LayerType *)Crosshair.AttachedObject.Ptr1;
1003 /* find layer groups of the top and bottom sides */
1004 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
1005 bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
1006 desired_group = TEST_FLAG (ONSOLDERFLAG, pad) ? bottom_group : top_group;
1008 GROUP_LOOP (PCB->Data, desired_group);
1010 if (layer == desired_layer)
1012 found_our_layer = true;
1013 break;
1016 END_LOOP;
1018 if (found_our_layer == false)
1019 ans = NO_TYPE;
1022 if (ans != NO_TYPE)
1024 PadType *pad = (PadType *)ptr2;
1025 check_snap_object (&snap_data, pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2,
1026 pad->Point1.Y + (pad->Point2.Y - pad->Point1.Y) / 2,
1027 true);
1030 ans = NO_TYPE;
1031 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
1032 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1033 PIN_TYPE, &ptr1, &ptr2, &ptr3);
1035 /* Avoid self-snapping when moving */
1036 if (ans != NO_TYPE &&
1037 Settings.Mode == MOVE_MODE &&
1038 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
1039 ptr1 == Crosshair.AttachedObject.Ptr1)
1040 ans = NO_TYPE;
1042 if (ans != NO_TYPE)
1044 PinType *pin = (PinType *)ptr2;
1045 check_snap_object (&snap_data, pin->X, pin->Y, true);
1048 ans = NO_TYPE;
1049 if (TEST_FLAG (SNAPPINFLAG, PCB))
1050 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1051 VIA_TYPE, &ptr1, &ptr2, &ptr3);
1053 /* Avoid snapping vias to any other vias */
1054 if (Settings.Mode == MOVE_MODE &&
1055 Crosshair.AttachedObject.Type == VIA_TYPE &&
1056 (ans & PIN_TYPES))
1057 ans = NO_TYPE;
1059 if (ans != NO_TYPE)
1061 PinType *pin = (PinType *)ptr2;
1062 check_snap_object (&snap_data, pin->X, pin->Y, true);
1065 ans = NO_TYPE;
1066 if (TEST_FLAG (SNAPPINFLAG, PCB))
1067 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1068 LINEPOINT_TYPE | ARCPOINT_TYPE,
1069 &ptr1, &ptr2, &ptr3);
1071 if (ans != NO_TYPE)
1073 PointType *pnt = (PointType *)ptr3;
1074 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1077 check_snap_offgrid_line (&snap_data, nearest_grid_x, nearest_grid_y);
1079 ans = NO_TYPE;
1080 if (TEST_FLAG (SNAPPINFLAG, PCB))
1081 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1082 POLYGONPOINT_TYPE, &ptr1, &ptr2, &ptr3);
1084 if (ans != NO_TYPE)
1086 PointType *pnt = (PointType *)ptr3;
1087 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1090 if (snap_data.x >= 0 && snap_data.y >= 0)
1092 Crosshair.X = snap_data.x;
1093 Crosshair.Y = snap_data.y;
1096 if (Settings.Mode == ARROW_MODE)
1098 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1099 LINEPOINT_TYPE | ARCPOINT_TYPE,
1100 &ptr1, &ptr2, &ptr3);
1101 if (ans == NO_TYPE)
1102 hid_action("PointCursor");
1103 else if (!TEST_FLAG(SELECTEDFLAG, (LineType *)ptr2))
1104 hid_actionl("PointCursor","True", NULL);
1107 if (Settings.Mode == LINE_MODE
1108 && Crosshair.AttachedLine.State != STATE_FIRST
1109 && TEST_FLAG (AUTODRCFLAG, PCB))
1110 EnforceLineDRC ();
1112 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_DO_NOTHING);
1115 /* ---------------------------------------------------------------------------
1116 * move crosshair absolute
1117 * return true if the crosshair was moved from its existing position
1119 bool
1120 MoveCrosshairAbsolute (Coord X, Coord Y)
1122 Coord old_x = Crosshair.X;
1123 Coord old_y = Crosshair.Y;
1125 FitCrosshairIntoGrid (X, Y);
1127 if (Crosshair.X != old_x || Crosshair.Y != old_y)
1129 Coord new_x = Crosshair.X;
1130 Coord new_y = Crosshair.Y;
1132 /* back up to old position to notify the GUI
1133 * (which might want to erase the old crosshair) */
1134 Crosshair.X = old_x;
1135 Crosshair.Y = old_y;
1136 notify_crosshair_change (false); /* Our caller notifies when it has done */
1138 /* now move forward again */
1139 Crosshair.X = new_x;
1140 Crosshair.Y = new_y;
1141 return true;
1143 return false;
1146 /* ---------------------------------------------------------------------------
1147 * sets the valid range for the crosshair cursor
1149 void
1150 SetCrosshairRange (Coord MinX, Coord MinY, Coord MaxX, Coord MaxY)
1152 Crosshair.MinX = MAX (0, MinX);
1153 Crosshair.MinY = MAX (0, MinY);
1154 Crosshair.MaxX = MIN (PCB->MaxWidth, MaxX);
1155 Crosshair.MaxY = MIN (PCB->MaxHeight, MaxY);
1157 /* force update of position */
1158 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
1161 /* ---------------------------------------------------------------------------
1162 * initializes crosshair stuff
1163 * clears the struct, allocates to graphical contexts
1165 void
1166 InitCrosshair (void)
1168 Crosshair.GC = gui->graphics->make_gc ();
1170 gui->graphics->set_color (Crosshair.GC, Settings.CrosshairColor);
1171 gui->graphics->set_draw_xor (Crosshair.GC, 1);
1172 gui->graphics->set_line_cap (Crosshair.GC, Trace_Cap);
1173 gui->graphics->set_line_width (Crosshair.GC, 1);
1175 /* set initial shape */
1176 Crosshair.shape = Basic_Crosshair_Shape;
1178 /* set default limits */
1179 Crosshair.MinX = Crosshair.MinY = 0;
1180 Crosshair.MaxX = PCB->MaxWidth;
1181 Crosshair.MaxY = PCB->MaxHeight;
1183 /* clear the mark */
1184 Marked.status = false;
1187 /* ---------------------------------------------------------------------------
1188 * exits crosshair routines, release GCs
1190 void
1191 DestroyCrosshair (void)
1193 FreePolygonMemory (&Crosshair.AttachedPolygon);
1194 gui->graphics->destroy_gc (Crosshair.GC);