Play with grid snapping heuristics a little
[geda-pcb/pcjc2.git] / src / crosshair.c
blob09a9fb94d8b8a1865ba21f4ae0dcf2941e0b8e13
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;
61 static void
62 thindraw_moved_pv (hidGC gc, PinType *pv, Coord x, Coord y)
64 /* Make a copy of the pin structure, moved to the correct position */
65 PinType moved_pv = *pv;
66 moved_pv.X += x;
67 moved_pv.Y += y;
69 hid_draw_thin_pcb_pv (gc, gc, &moved_pv, true, false);
72 /* ---------------------------------------------------------------------------
73 * creates a tmp polygon with coordinates converted to screen system
75 static void
76 XORPolygon (hidGC gc, PolygonType *polygon, Coord dx, Coord dy)
78 Cardinal i;
79 for (i = 0; i < polygon->PointN; i++)
81 Cardinal next = next_contour_point (polygon, i);
82 hid_draw_line (gc, polygon->Points[i].X + dx,
83 polygon->Points[i].Y + dy,
84 polygon->Points[next].X + dx,
85 polygon->Points[next].Y + dy);
89 /*-----------------------------------------------------------
90 * Draws the outline of an arc
92 static void
93 XORDrawAttachedArc (hidGC gc, Coord thick)
95 ArcType arc;
96 BoxType *bx;
97 Coord wx, wy;
98 Angle sa, dir;
99 Coord wid = thick / 2;
101 wx = Crosshair.X - Crosshair.AttachedBox.Point1.X;
102 wy = Crosshair.Y - Crosshair.AttachedBox.Point1.Y;
103 if (wx == 0 && wy == 0)
104 return;
105 arc.X = Crosshair.AttachedBox.Point1.X;
106 arc.Y = Crosshair.AttachedBox.Point1.Y;
107 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
109 arc.X = Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
110 sa = (wx >= 0) ? 0 : 180;
111 #ifdef ARC45
112 if (abs (wy) >= 2 * abs (wx))
113 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
114 else
115 #endif
116 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
118 else
120 arc.Y = Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
121 sa = (wy >= 0) ? -90 : 90;
122 #ifdef ARC45
123 if (abs (wx) >= 2 * abs (wy))
124 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
125 else
126 #endif
127 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
128 wy = wx;
130 wy = abs (wy);
131 arc.StartAngle = sa;
132 arc.Delta = dir;
133 arc.Width = arc.Height = wy;
134 bx = GetArcEnds (&arc);
135 /* sa = sa - 180; */
136 hid_draw_arc (gc, arc.X, arc.Y, wy + wid, wy + wid, sa, dir);
137 if (wid > pixel_slop)
139 hid_draw_arc (gc, arc.X, arc.Y, wy - wid, wy - wid, sa, dir);
140 hid_draw_arc (gc, bx->X1, bx->Y1, wid, wid, sa, -180 * SGN (dir));
141 hid_draw_arc (gc, bx->X2, bx->Y2, wid, wid, sa + dir, 180 * SGN (dir));
145 /*-----------------------------------------------------------
146 * Draws the outline of a line
148 static void
149 XORDrawAttachedLine (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2, Coord thick)
151 Coord dx, dy, ox, oy;
152 double h;
154 dx = x2 - x1;
155 dy = y2 - y1;
156 if (dx != 0 || dy != 0)
157 h = 0.5 * thick / sqrt (SQUARE (dx) + SQUARE (dy));
158 else
159 h = 0.0;
160 ox = dy * h + 0.5 * SGN (dy);
161 oy = -(dx * h + 0.5 * SGN (dx));
162 hid_draw_line (gc, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
163 if (abs (ox) >= pixel_slop || abs (oy) >= pixel_slop)
165 Angle angle = atan2 (dx, dy) * 57.295779;
166 hid_draw_line (gc, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
167 hid_draw_arc (gc, x1, y1, thick / 2, thick / 2, angle - 180, 180);
168 hid_draw_arc (gc, x2, y2, thick / 2, thick / 2, angle, 180);
172 /* ---------------------------------------------------------------------------
173 * draws the elements of a loaded circuit which is to be merged in
175 static void
176 XORDrawElement (hidGC gc, ElementType *Element, Coord DX, Coord DY)
178 /* if no silkscreen, draw the bounding box */
179 if (Element->ArcN == 0 && Element->LineN == 0)
181 hid_draw_line (gc, DX + Element->BoundingBox.X1,
182 DY + Element->BoundingBox.Y1,
183 DX + Element->BoundingBox.X1,
184 DY + Element->BoundingBox.Y2);
185 hid_draw_line (gc, DX + Element->BoundingBox.X1,
186 DY + Element->BoundingBox.Y2,
187 DX + Element->BoundingBox.X2,
188 DY + Element->BoundingBox.Y2);
189 hid_draw_line (gc, DX + Element->BoundingBox.X2,
190 DY + Element->BoundingBox.Y2,
191 DX + Element->BoundingBox.X2,
192 DY + Element->BoundingBox.Y1);
193 hid_draw_line (gc, DX + Element->BoundingBox.X2,
194 DY + Element->BoundingBox.Y1,
195 DX + Element->BoundingBox.X1,
196 DY + Element->BoundingBox.Y1);
198 else
200 ELEMENTLINE_LOOP (Element);
202 hid_draw_line (gc, DX + line->Point1.X,
203 DY + line->Point1.Y,
204 DX + line->Point2.X,
205 DY + line->Point2.Y);
207 END_LOOP;
209 /* arc coordinates and angles have to be converted to X11 notation */
210 ARC_LOOP (Element);
212 hid_draw_arc (gc, DX + arc->X,
213 DY + arc->Y,
214 arc->Width, arc->Height, arc->StartAngle, arc->Delta);
216 END_LOOP;
218 /* pin coordinates and angles have to be converted to X11 notation */
219 PIN_LOOP (Element);
221 thindraw_moved_pv (gc, pin, DX, DY);
223 END_LOOP;
225 /* pads */
226 PAD_LOOP (Element);
228 if (PCB->InvisibleObjectsOn ||
229 (TEST_FLAG (ONSOLDERFLAG, pad) != 0) == Settings.ShowBottomSide)
231 /* Make a copy of the pad structure, moved to the correct position */
232 PadType moved_pad = *pad;
233 moved_pad.Point1.X += DX; moved_pad.Point1.Y += DY;
234 moved_pad.Point2.X += DX; moved_pad.Point2.Y += DY;
236 hid_draw_thin_pcb_pad (gc, &moved_pad, false, false);
239 END_LOOP;
240 /* mark */
241 hid_draw_line (gc, Element->MarkX + DX - EMARK_SIZE,
242 Element->MarkY + DY,
243 Element->MarkX + DX,
244 Element->MarkY + DY - EMARK_SIZE);
245 hid_draw_line (gc, Element->MarkX + DX + EMARK_SIZE,
246 Element->MarkY + DY,
247 Element->MarkX + DX,
248 Element->MarkY + DY - EMARK_SIZE);
249 hid_draw_line (gc, Element->MarkX + DX - EMARK_SIZE,
250 Element->MarkY + DY,
251 Element->MarkX + DX,
252 Element->MarkY + DY + EMARK_SIZE);
253 hid_draw_line (gc, Element->MarkX + DX + EMARK_SIZE,
254 Element->MarkY + DY,
255 Element->MarkX + DX,
256 Element->MarkY + DY + EMARK_SIZE);
259 /* ---------------------------------------------------------------------------
260 * draws all visible and attached objects of the pastebuffer
262 static void
263 XORDrawBuffer (hidGC gc, BufferType *Buffer)
265 Cardinal i;
266 Coord x, y;
268 /* set offset */
269 x = Crosshair.X - Buffer->X;
270 y = Crosshair.Y - Buffer->Y;
272 /* draw all visible layers */
273 for (i = 0; i < max_copper_layer + 2; i++)
274 if (PCB->Data->Layer[i].On)
276 LayerType *layer = &Buffer->Data->Layer[i];
278 LINE_LOOP (layer);
281 XORDrawAttachedLine(x +line->Point1.X,
282 y +line->Point1.Y, x +line->Point2.X,
283 y +line->Point2.Y, line->Thickness);
285 hid_draw_line (gc, x + line->Point1.X, y + line->Point1.Y,
286 x + line->Point2.X, y + line->Point2.Y);
288 END_LOOP;
289 ARC_LOOP (layer);
291 hid_draw_arc (gc, x + arc->X,
292 y + arc->Y,
293 arc->Width, arc->Height, arc->StartAngle, arc->Delta);
295 END_LOOP;
296 TEXT_LOOP (layer);
298 BoxType *box = &text->BoundingBox;
299 hid_draw_rect (gc, x + box->X1, y + box->Y1, x + box->X2, y + box->Y2);
301 END_LOOP;
302 /* the tmp polygon has n+1 points because the first
303 * and the last one are set to the same coordinates
305 POLYGON_LOOP (layer);
307 XORPolygon (gc, polygon, x, y);
309 END_LOOP;
312 /* draw elements if visible */
313 if (PCB->PinOn && PCB->ElementOn)
314 ELEMENT_LOOP (Buffer->Data);
316 if (FRONT (element) || PCB->InvisibleObjectsOn)
317 XORDrawElement (gc, element, x, y);
319 END_LOOP;
321 /* and the vias */
322 if (PCB->ViaOn)
323 VIA_LOOP (Buffer->Data);
325 thindraw_moved_pv (gc, via, x, y);
327 END_LOOP;
330 /* ---------------------------------------------------------------------------
331 * draws the rubberband to insert points into polygons/lines/...
333 static void
334 XORDrawInsertPointObject (hidGC gc)
336 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
337 PointType *point = (PointType *) Crosshair.AttachedObject.Ptr3;
339 if (Crosshair.AttachedObject.Type != NO_TYPE)
341 hid_draw_line (gc, point->X, point->Y, line->Point1.X, line->Point1.Y);
342 hid_draw_line (gc, point->X, point->Y, line->Point2.X, line->Point2.Y);
346 /* ---------------------------------------------------------------------------
347 * draws the attached object while in MOVE_MODE or COPY_MODE
349 static void
350 XORDrawMoveOrCopyObject (hidGC gc)
352 RubberbandType *ptr;
353 Cardinal i;
354 Coord dx = Crosshair.X - Crosshair.AttachedObject.X,
355 dy = Crosshair.Y - Crosshair.AttachedObject.Y;
357 switch (Crosshair.AttachedObject.Type)
359 case VIA_TYPE:
361 PinType *via = (PinType *) Crosshair.AttachedObject.Ptr1;
362 thindraw_moved_pv (gc, via, dx, dy);
363 break;
366 case LINE_TYPE:
368 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
370 XORDrawAttachedLine (gc, line->Point1.X + dx, line->Point1.Y + dy,
371 line->Point2.X + dx, line->Point2.Y + dy,
372 line->Thickness);
373 break;
376 case ARC_TYPE:
378 ArcType *Arc = (ArcType *) Crosshair.AttachedObject.Ptr2;
380 hid_draw_arc (gc, Arc->X + dx,
381 Arc->Y + dy,
382 Arc->Width, Arc->Height, Arc->StartAngle, Arc->Delta);
383 break;
386 case POLYGON_TYPE:
388 PolygonType *polygon =
389 (PolygonType *) Crosshair.AttachedObject.Ptr2;
391 /* the tmp polygon has n+1 points because the first
392 * and the last one are set to the same coordinates
394 XORPolygon (gc, polygon, dx, dy);
395 break;
398 case LINEPOINT_TYPE:
400 LineType *line;
401 PointType *point;
403 line = (LineType *) Crosshair.AttachedObject.Ptr2;
404 point = (PointType *) Crosshair.AttachedObject.Ptr3;
405 if (point == &line->Point1)
406 XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
407 line->Point2.X, line->Point2.Y, line->Thickness);
408 else
409 XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
410 line->Point1.X, line->Point1.Y, line->Thickness);
411 break;
414 case POLYGONPOINT_TYPE:
416 PolygonType *polygon;
417 PointType *point;
418 Cardinal point_idx, prev, next;
420 polygon = (PolygonType *) Crosshair.AttachedObject.Ptr2;
421 point = (PointType *) Crosshair.AttachedObject.Ptr3;
422 point_idx = polygon_point_idx (polygon, point);
424 /* get previous and following point */
425 prev = prev_contour_point (polygon, point_idx);
426 next = next_contour_point (polygon, point_idx);
428 /* draw the two segments */
429 hid_draw_line (gc, polygon->Points[prev].X, polygon->Points[prev].Y,
430 point->X + dx, point->Y + dy);
431 hid_draw_line (gc, point->X + dx, point->Y + dy,
432 polygon->Points[next].X, polygon->Points[next].Y);
433 break;
436 case ELEMENTNAME_TYPE:
438 /* locate the element "mark" and draw an association line from crosshair to it */
439 ElementType *element =
440 (ElementType *) Crosshair.AttachedObject.Ptr1;
442 hid_draw_line (gc, element->MarkX, element->MarkY,
443 Crosshair.X, Crosshair.Y);
444 /* fall through to move the text as a box outline */
446 case TEXT_TYPE:
448 TextType *text = (TextType *) Crosshair.AttachedObject.Ptr2;
449 BoxType *box = &text->BoundingBox;
450 hid_draw_rect (gc, box->X1 + dx, box->Y1 + dy, box->X2 + dx, box->Y2 + dy);
451 break;
454 /* pin/pad movements result in moving an element */
455 case PAD_TYPE:
456 case PIN_TYPE:
457 case ELEMENT_TYPE:
458 XORDrawElement (gc, (ElementType *) Crosshair.AttachedObject.Ptr2, dx, dy);
459 break;
462 /* draw the attached rubberband lines too */
463 i = Crosshair.AttachedObject.RubberbandN;
464 ptr = Crosshair.AttachedObject.Rubberband;
465 while (i)
467 PointType *point1, *point2;
469 if (TEST_FLAG (VIAFLAG, ptr->Line))
471 /* this is a rat going to a polygon. do not draw for rubberband */;
473 else if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
475 /* 'point1' is always the fix-point */
476 if (ptr->MovedPoint == &ptr->Line->Point1)
478 point1 = &ptr->Line->Point2;
479 point2 = &ptr->Line->Point1;
481 else
483 point1 = &ptr->Line->Point1;
484 point2 = &ptr->Line->Point2;
486 XORDrawAttachedLine (gc, point1->X, point1->Y,
487 point2->X + dx, point2->Y + dy,
488 ptr->Line->Thickness);
490 else if (ptr->MovedPoint == &ptr->Line->Point1)
491 XORDrawAttachedLine (gc,
492 ptr->Line->Point1.X + dx,
493 ptr->Line->Point1.Y + dy,
494 ptr->Line->Point2.X + dx,
495 ptr->Line->Point2.Y + dy, ptr->Line->Thickness);
497 ptr++;
498 i--;
502 /* ---------------------------------------------------------------------------
503 * draws additional stuff that follows the crosshair
505 void
506 DrawAttached (hidGC gc)
508 hid_draw_set_color (gc, Settings.CrosshairColor);
509 hid_draw_set_draw_xor (gc, 1);
510 hid_draw_set_line_cap (gc, Trace_Cap);
511 hid_draw_set_line_width (gc, 1);
513 switch (Settings.Mode)
515 case VIA_MODE:
517 /* Make a dummy via structure to draw from */
518 PinType via;
519 via.X = Crosshair.X;
520 via.Y = Crosshair.Y;
521 via.Thickness = Settings.ViaThickness;
522 via.Clearance = 2 * Settings.Keepaway;
523 via.DrillingHole = Settings.ViaDrillingHole;
524 via.Mask = 0;
525 via.Flags = NoFlags ();
527 hid_draw_thin_pcb_pv (gc, gc, &via, true, false);
529 if (TEST_FLAG (SHOWDRCFLAG, PCB))
531 Coord mask_r = Settings.ViaThickness / 2 + PCB->Bloat;
532 hid_draw_set_color (gc, Settings.CrossColor);
533 hid_draw_set_line_cap (gc, Round_Cap);
534 hid_draw_set_line_width (gc, 0);
535 hid_draw_arc (gc, via.X, via.Y, mask_r, mask_r, 0, 360);
536 hid_draw_set_color (gc, Settings.CrosshairColor);
538 break;
541 /* the attached line is used by both LINEMODE, POLYGON_MODE and POLYGONHOLE_MODE*/
542 case POLYGON_MODE:
543 case POLYGONHOLE_MODE:
544 /* draw only if starting point is set */
545 if (Crosshair.AttachedLine.State != STATE_FIRST)
546 hid_draw_line (gc, Crosshair.AttachedLine.Point1.X,
547 Crosshair.AttachedLine.Point1.Y,
548 Crosshair.AttachedLine.Point2.X,
549 Crosshair.AttachedLine.Point2.Y);
551 /* draw attached polygon only if in POLYGON_MODE or POLYGONHOLE_MODE */
552 if (Crosshair.AttachedPolygon.PointN > 1)
554 XORPolygon (gc, &Crosshair.AttachedPolygon, 0, 0);
556 break;
558 case ARC_MODE:
559 if (Crosshair.AttachedBox.State != STATE_FIRST)
561 XORDrawAttachedArc (gc, Settings.LineThickness);
562 if (TEST_FLAG (SHOWDRCFLAG, PCB))
564 hid_draw_set_color (gc, Settings.CrossColor);
565 XORDrawAttachedArc (gc, Settings.LineThickness + 2 * (PCB->Bloat + 1));
566 hid_draw_set_color (gc, Settings.CrosshairColor);
570 break;
572 case LINE_MODE:
573 /* draw only if starting point exists and the line has length */
574 if (Crosshair.AttachedLine.State != STATE_FIRST &&
575 Crosshair.AttachedLine.draw)
577 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
578 Crosshair.AttachedLine.Point1.Y,
579 Crosshair.AttachedLine.Point2.X,
580 Crosshair.AttachedLine.Point2.Y,
581 PCB->RatDraw ? 10 : Settings.LineThickness);
582 /* draw two lines ? */
583 if (PCB->Clipping)
584 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
585 Crosshair.AttachedLine.Point2.Y,
586 Crosshair.X, Crosshair.Y,
587 PCB->RatDraw ? 10 : Settings.LineThickness);
588 if (TEST_FLAG (SHOWDRCFLAG, PCB))
590 hid_draw_set_color (gc, Settings.CrossColor);
591 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
592 Crosshair.AttachedLine.Point1.Y,
593 Crosshair.AttachedLine.Point2.X,
594 Crosshair.AttachedLine.Point2.Y,
595 PCB->RatDraw ? 10 : Settings.LineThickness
596 + 2 * (PCB->Bloat + 1));
597 if (PCB->Clipping)
598 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
599 Crosshair.AttachedLine.Point2.Y,
600 Crosshair.X, Crosshair.Y,
601 PCB->RatDraw ? 10 : Settings.
602 LineThickness + 2 * (PCB->Bloat + 1));
603 hid_draw_set_color (gc, Settings.CrosshairColor);
606 break;
608 case PASTEBUFFER_MODE:
609 XORDrawBuffer (gc, PASTEBUFFER);
610 break;
612 case COPY_MODE:
613 case MOVE_MODE:
614 XORDrawMoveOrCopyObject (gc);
615 break;
617 case INSERTPOINT_MODE:
618 XORDrawInsertPointObject (gc);
619 break;
622 /* an attached box does not depend on a special mode */
623 if (Crosshair.AttachedBox.State == STATE_SECOND ||
624 Crosshair.AttachedBox.State == STATE_THIRD)
626 Coord x1, y1, x2, y2;
628 x1 = Crosshair.AttachedBox.Point1.X;
629 y1 = Crosshair.AttachedBox.Point1.Y;
630 x2 = Crosshair.AttachedBox.Point2.X;
631 y2 = Crosshair.AttachedBox.Point2.Y;
632 hid_draw_rect (gc, x1, y1, x2, y2);
637 /* --------------------------------------------------------------------------
638 * draw the marker position
640 void
641 DrawMark (hidGC gc)
643 hid_draw_set_color (gc, Settings.CrosshairColor);
644 hid_draw_set_draw_xor (gc, 1);
645 hid_draw_set_line_cap (gc, Trace_Cap);
646 hid_draw_set_line_width (gc, 1);
648 /* Mark is not drawn when it is not set */
649 if (!Marked.status)
650 return;
652 hid_draw_line (gc, Marked.X - MARK_SIZE, Marked.Y - MARK_SIZE,
653 Marked.X + MARK_SIZE, Marked.Y + MARK_SIZE);
654 hid_draw_line (gc, Marked.X + MARK_SIZE, Marked.Y - MARK_SIZE,
655 Marked.X - MARK_SIZE, Marked.Y + MARK_SIZE);
658 /* ---------------------------------------------------------------------------
659 * Returns the nearest grid-point to the given Coord
661 Coord
662 GridFit (Coord x, Coord grid_spacing, Coord grid_offset)
664 x -= grid_offset;
665 x = grid_spacing * round ((double) x / grid_spacing);
666 x += grid_offset;
667 return x;
671 /* ---------------------------------------------------------------------------
672 * notify the GUI that data relating to the crosshair is being changed.
674 * The argument passed is false to notify "changes are about to happen",
675 * and true to notify "changes have finished".
677 * Each call with a 'false' parameter must be matched with a following one
678 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
679 * but might be allowed in the future.
681 * GUIs should not complain if they receive extra calls with 'true' as parameter.
682 * They should initiate a redraw of the crosshair attached objects - which may
683 * (if necessary) mean repainting the whole screen if the GUI hasn't tracked the
684 * location of existing attached drawing.
686 void
687 notify_crosshair_change (bool changes_complete)
689 if (gui->notify_crosshair_change)
690 gui->notify_crosshair_change (changes_complete);
694 /* ---------------------------------------------------------------------------
695 * notify the GUI that data relating to the mark is being changed.
697 * The argument passed is false to notify "changes are about to happen",
698 * and true to notify "changes have finished".
700 * Each call with a 'false' parameter must be matched with a following one
701 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
702 * but might be allowed in the future.
704 * GUIs should not complain if they receive extra calls with 'true' as parameter.
705 * They should initiate a redraw of the mark - which may (if necessary) mean
706 * repainting the whole screen if the GUI hasn't tracked the mark's location.
708 void
709 notify_mark_change (bool changes_complete)
711 if (gui->notify_mark_change)
712 gui->notify_mark_change (changes_complete);
716 /* ---------------------------------------------------------------------------
717 * Convenience for plugins using the old {Hide,Restore}Crosshair API.
718 * This links up to notify the GUI of the expected changes using the new APIs.
720 * Use of this old API is deprecated, as the names don't necessarily reflect
721 * what all GUIs may do in response to the notifications. Keeping these APIs
722 * is aimed at easing transition to the newer API, they will emit a harmless
723 * warning at the time of their first use.
726 void
727 HideCrosshair (void)
729 static bool warned_old_api = false;
730 if (!warned_old_api)
732 Message (_("WARNING: A plugin is using the deprecated API HideCrosshair().\n"
733 " This API may be removed in a future release of PCB.\n"));
734 warned_old_api = true;
737 notify_crosshair_change (false);
738 notify_mark_change (false);
741 void
742 RestoreCrosshair (void)
744 static bool warned_old_api = false;
745 if (!warned_old_api)
747 Message (_("WARNING: A plugin is using the deprecated API RestoreCrosshair().\n"
748 " This API may be removed in a future release of PCB.\n"));
749 warned_old_api = true;
752 notify_crosshair_change (true);
753 notify_mark_change (true);
756 /* ---------------------------------------------------------------------------
757 * Returns the square of the given number
759 static double
760 square (double x)
762 return x * x;
765 static double
766 crosshair_sq_dist (CrosshairType *crosshair, Coord x, Coord y)
768 return square (x - crosshair->X) + square (y - crosshair->Y);
771 struct snap_data {
772 CrosshairType *crosshair;
773 double nearest_sq_dist;
774 bool nearest_is_grid;
775 Coord x, y;
778 /* Snap to a given location if it is the closest thing we found so far.
779 * If "prefer_to_grid" is set, the passed location will take preference
780 * over a closer grid points we already snapped to UNLESS the user is
781 * pressing the SHIFT key. If the SHIFT key is pressed, the closest object
782 * (including grid points), is always preferred.
784 static void
785 check_snap_object (struct snap_data *snap_data, Coord x, Coord y,
786 bool prefer_to_grid)
788 double sq_dist;
790 sq_dist = crosshair_sq_dist (snap_data->crosshair, x, y);
791 if (sq_dist <= snap_data->nearest_sq_dist ||
792 (prefer_to_grid && snap_data->nearest_is_grid && !gui->shift_is_pressed()))
794 snap_data->x = x;
795 snap_data->y = y;
796 snap_data->nearest_sq_dist = sq_dist;
797 snap_data->nearest_is_grid = false;
801 static void
802 check_snap_offgrid_line (struct snap_data *snap_data,
803 Coord nearest_grid_x,
804 Coord nearest_grid_y)
806 void *ptr1, *ptr2, *ptr3;
807 int ans;
808 LineType *line;
809 Coord try_x, try_y;
810 double dx, dy;
811 double dist;
813 if (!TEST_FLAG (SNAPPINFLAG, PCB))
814 return;
816 /* Code to snap at some sensible point along a line */
817 /* Pick the nearest grid-point in the x or y direction
818 * to align with, then adjust until we hit the line
820 ans = SearchObjectByLocation (LINE_TYPE, &ptr1, &ptr2, &ptr3,
821 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
824 if (ans == NO_TYPE)
825 return;
827 line = (LineType *)ptr2;
829 /* Allow snapping to off-grid lines when drawing new lines (on
830 * the same layer), and when moving a line end-point
831 * (but don't snap to the same line)
833 if ((Settings.Mode != LINE_MODE || CURRENT != ptr1) &&
834 (Settings.Mode != MOVE_MODE ||
835 Crosshair.AttachedObject.Ptr1 != ptr1 ||
836 Crosshair.AttachedObject.Type != LINEPOINT_TYPE ||
837 Crosshair.AttachedObject.Ptr2 == line))
838 return;
840 dx = line->Point2.X - line->Point1.X;
841 dy = line->Point2.Y - line->Point1.Y;
843 /* Try snapping along the X axis */
844 if (dy != 0.)
846 /* Move in the X direction until we hit the line */
847 try_x = (nearest_grid_y - line->Point1.Y) / dy * dx + line->Point1.X;
848 try_y = nearest_grid_y;
849 check_snap_object (snap_data, try_x, try_y, true);
852 /* Try snapping along the Y axis */
853 if (dx != 0.)
855 try_x = nearest_grid_x;
856 try_y = (nearest_grid_x - line->Point1.X) / dx * dy + line->Point1.Y;
857 check_snap_object (snap_data, try_x, try_y, true);
860 if (dx != dy) /* If line not parallel with dX = dY direction.. */
862 /* Try snapping diagonally towards the line in the dX = dY direction */
864 if (dy == 0)
865 dist = line->Point1.Y - nearest_grid_y;
866 else
867 dist = ((line->Point1.X - nearest_grid_x) -
868 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 - dx / dy);
870 try_x = nearest_grid_x + dist;
871 try_y = nearest_grid_y + dist;
873 check_snap_object (snap_data, try_x, try_y, true);
876 if (dx != -dy) /* If line not parallel with dX = -dY direction.. */
878 /* Try snapping diagonally towards the line in the dX = -dY direction */
880 if (dy == 0)
881 dist = nearest_grid_y - line->Point1.Y;
882 else
883 dist = ((line->Point1.X - nearest_grid_x) -
884 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 + dx / dy);
886 try_x = nearest_grid_x + dist;
887 try_y = nearest_grid_y - dist;
889 check_snap_object (snap_data, try_x, try_y, true);
893 /* ---------------------------------------------------------------------------
894 * recalculates the passed coordinates to fit the current grid setting
896 void
897 FitCrosshairIntoGrid (Coord X, Coord Y)
899 Coord nearest_grid_x, nearest_grid_y;
900 void *ptr1, *ptr2, *ptr3;
901 struct snap_data snap_data;
902 int ans;
904 Coord old_x, old_y;
906 old_x = Crosshair.X;
907 old_y = Crosshair.Y;
909 Crosshair.X = CLAMP (X, Crosshair.MinX, Crosshair.MaxX);
910 Crosshair.Y = CLAMP (Y, Crosshair.MinY, Crosshair.MaxY);
912 if (PCB->RatDraw)
914 nearest_grid_x = -MIL_TO_COORD (6);
915 nearest_grid_y = -MIL_TO_COORD (6);
917 else
919 nearest_grid_x = GridFit (Crosshair.X, PCB->Grid, PCB->GridOffsetX);
920 nearest_grid_y = GridFit (Crosshair.Y, PCB->Grid, PCB->GridOffsetY);
922 if (Marked.status && TEST_FLAG (ORTHOMOVEFLAG, PCB))
924 Coord dx = Crosshair.X - Marked.X;
925 Coord dy = Crosshair.Y - Marked.Y;
926 if (ABS (dx) > ABS (dy))
927 nearest_grid_y = Marked.Y;
928 else
929 nearest_grid_x = Marked.X;
934 snap_data.crosshair = &Crosshair;
935 snap_data.nearest_is_grid = true;
936 snap_data.x = nearest_grid_x;
937 snap_data.y = nearest_grid_y;
938 #if 0
939 snap_data.nearest_sq_dist = crosshair_sq_dist (&Crosshair, snap_data.x, snap_data.y);
941 if (snap_data.nearest_sq_dist > PCB->Grid / 3 * PCB->Grid / 3)
943 snap_data.x = old_x;
944 snap_data.y = old_y;
946 #endif
948 if (labs (nearest_grid_x - Crosshair.X) > PCB->Grid / 3)
949 snap_data.x = old_x;
951 if (labs (nearest_grid_y - Crosshair.Y) > PCB->Grid / 3)
952 snap_data.y = old_y;
954 snap_data.nearest_sq_dist = crosshair_sq_dist (&Crosshair, snap_data.x, snap_data.y);
956 ans = NO_TYPE;
957 if (!PCB->RatDraw)
958 ans = SearchObjectByLocation (ELEMENT_TYPE, &ptr1, &ptr2, &ptr3,
959 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
961 if (ans & ELEMENT_TYPE)
963 ElementType *el = (ElementType *) ptr1;
964 check_snap_object (&snap_data, el->MarkX, el->MarkY, false);
967 ans = NO_TYPE;
968 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
969 ans = SearchObjectByLocation (PAD_TYPE, &ptr1, &ptr2, &ptr3,
970 Crosshair.X, Crosshair.Y, 0);
972 /* Avoid self-snapping when moving */
973 if (ans != NO_TYPE &&
974 Settings.Mode == MOVE_MODE &&
975 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
976 ptr1 == Crosshair.AttachedObject.Ptr1)
977 ans = NO_TYPE;
979 if (ans != NO_TYPE &&
980 ( Settings.Mode == LINE_MODE ||
981 (Settings.Mode == MOVE_MODE &&
982 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)))
984 PadType *pad = (PadType *) ptr2;
985 LayerType *desired_layer;
986 Cardinal desired_group;
987 Cardinal bottom_group, top_group;
988 int found_our_layer = false;
990 desired_layer = CURRENT;
991 if (Settings.Mode == MOVE_MODE &&
992 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)
994 desired_layer = (LayerType *)Crosshair.AttachedObject.Ptr1;
997 /* find layer groups of the top and bottom sides */
998 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
999 bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
1000 desired_group = TEST_FLAG (ONSOLDERFLAG, pad) ? bottom_group : top_group;
1002 GROUP_LOOP (PCB->Data, desired_group);
1004 if (layer == desired_layer)
1006 found_our_layer = true;
1007 break;
1010 END_LOOP;
1012 if (found_our_layer == false)
1013 ans = NO_TYPE;
1016 if (ans != NO_TYPE)
1018 PadType *pad = (PadType *)ptr2;
1019 check_snap_object (&snap_data, pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2,
1020 pad->Point1.Y + (pad->Point2.Y - pad->Point1.Y) / 2,
1021 true);
1024 ans = NO_TYPE;
1025 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
1026 ans = SearchObjectByLocation (PIN_TYPE, &ptr1, &ptr2, &ptr3,
1027 Crosshair.X, Crosshair.Y, 0);
1029 /* Avoid self-snapping when moving */
1030 if (ans != NO_TYPE &&
1031 Settings.Mode == MOVE_MODE &&
1032 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
1033 ptr1 == Crosshair.AttachedObject.Ptr1)
1034 ans = NO_TYPE;
1036 if (ans != NO_TYPE)
1038 PinType *pin = (PinType *)ptr2;
1039 check_snap_object (&snap_data, pin->X, pin->Y, true);
1042 ans = NO_TYPE;
1043 if (TEST_FLAG (SNAPPINFLAG, PCB))
1044 ans = SearchObjectByLocation (VIA_TYPE, &ptr1, &ptr2, &ptr3,
1045 Crosshair.X, Crosshair.Y, 0);
1047 /* Avoid snapping vias to any other vias */
1048 if (Settings.Mode == MOVE_MODE &&
1049 Crosshair.AttachedObject.Type == VIA_TYPE &&
1050 (ans & PIN_TYPES))
1051 ans = NO_TYPE;
1053 if (ans != NO_TYPE)
1055 PinType *pin = (PinType *)ptr2;
1056 check_snap_object (&snap_data, pin->X, pin->Y, true);
1059 ans = NO_TYPE;
1060 if (TEST_FLAG (SNAPPINFLAG, PCB))
1061 ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
1062 &ptr1, &ptr2, &ptr3,
1063 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1065 if (ans != NO_TYPE)
1067 PointType *pnt = (PointType *)ptr3;
1068 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1071 check_snap_offgrid_line (&snap_data, nearest_grid_x, nearest_grid_y);
1073 ans = NO_TYPE;
1074 if (TEST_FLAG (SNAPPINFLAG, PCB))
1075 ans = SearchObjectByLocation (POLYGONPOINT_TYPE, &ptr1, &ptr2, &ptr3,
1076 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1078 if (ans != NO_TYPE)
1080 PointType *pnt = (PointType *)ptr3;
1081 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1084 if (snap_data.x >= 0 && snap_data.y >= 0)
1086 Crosshair.X = snap_data.x;
1087 Crosshair.Y = snap_data.y;
1090 if (Settings.Mode == ARROW_MODE)
1092 ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
1093 &ptr1, &ptr2, &ptr3,
1094 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1095 if (ans == NO_TYPE)
1096 hid_action("PointCursor");
1097 else if (!TEST_FLAG(SELECTEDFLAG, (LineType *)ptr2))
1098 hid_actionl("PointCursor","True", NULL);
1101 if (Settings.Mode == LINE_MODE
1102 && Crosshair.AttachedLine.State != STATE_FIRST
1103 && TEST_FLAG (AUTODRCFLAG, PCB))
1104 EnforceLineDRC ();
1106 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_DO_NOTHING);
1109 /* ---------------------------------------------------------------------------
1110 * move crosshair absolute
1111 * return true if the crosshair was moved from its existing position
1113 bool
1114 MoveCrosshairAbsolute (Coord X, Coord Y)
1116 Coord old_x = Crosshair.X;
1117 Coord old_y = Crosshair.Y;
1119 FitCrosshairIntoGrid (X, Y);
1121 if (Crosshair.X != old_x || Crosshair.Y != old_y)
1123 Coord new_x = Crosshair.X;
1124 Coord new_y = Crosshair.Y;
1126 /* back up to old position to notify the GUI
1127 * (which might want to erase the old crosshair) */
1128 Crosshair.X = old_x;
1129 Crosshair.Y = old_y;
1130 notify_crosshair_change (false); /* Our caller notifies when it has done */
1132 /* now move forward again */
1133 Crosshair.X = new_x;
1134 Crosshair.Y = new_y;
1135 return true;
1137 return false;
1140 /* ---------------------------------------------------------------------------
1141 * sets the valid range for the crosshair cursor
1143 void
1144 SetCrosshairRange (Coord MinX, Coord MinY, Coord MaxX, Coord MaxY)
1146 Crosshair.MinX = MAX (0, MinX);
1147 Crosshair.MinY = MAX (0, MinY);
1148 Crosshair.MaxX = MIN (PCB->MaxWidth, MaxX);
1149 Crosshair.MaxY = MIN (PCB->MaxHeight, MaxY);
1151 /* force update of position */
1152 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
1155 /* ---------------------------------------------------------------------------
1156 * initializes crosshair stuff
1157 * clears the struct, allocates to graphical contexts
1159 void
1160 InitCrosshair (void)
1162 /* set initial shape */
1163 Crosshair.shape = Basic_Crosshair_Shape;
1165 /* set default limits */
1166 Crosshair.MinX = Crosshair.MinY = 0;
1167 Crosshair.MaxX = PCB->MaxWidth;
1168 Crosshair.MaxY = PCB->MaxHeight;
1170 /* clear the mark */
1171 Marked.status = false;
1174 /* ---------------------------------------------------------------------------
1175 * exits crosshair routines, release GCs
1177 void
1178 DestroyCrosshair (void)
1180 FreePolygonMemory (&Crosshair.AttachedPolygon);