Fixed the "poly twin hole" bug.
[geda-pcb/pcjc2.git] / src / crosshair.c
blobc8ce6531731b9d70a9d5a6e6324e5085d8970556
1 /*!
2 * \file src/crosshair.c
4 * \brief Crosshair stuff.
6 * <hr>
8 * <h1><b>Copyright.</b></h1>\n
10 * PCB, interactive printed circuit board design
12 * Copyright (C) 1994,1995,1996 Thomas Nau
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 * Contact addresses for paper mail and Email:
29 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
30 * Thomas.Nau@rz.uni-ulm.de
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
37 #include <memory.h>
38 #include <math.h>
40 #include "global.h"
41 #include "hid_draw.h"
43 #include "crosshair.h"
44 #include "data.h"
45 #include "draw.h"
46 #include "error.h"
47 #include "line.h"
48 #include "misc.h"
49 #include "mymem.h"
50 #include "search.h"
51 #include "polygon.h"
53 #ifdef HAVE_LIBDMALLOC
54 #include <dmalloc.h>
55 #endif
57 typedef struct
59 int x, y;
60 } point;
63 /*!
64 * \brief Make a copy of the pin structure, moved to the correct
65 * position
67 static void
68 thindraw_moved_pv (hidGC gc, PinType *pv, Coord x, Coord y)
70 PinType moved_pv = *pv;
71 moved_pv.X += x;
72 moved_pv.Y += y;
74 gui->graphics->thindraw_pcb_pv (gc, gc, &moved_pv, true, false);
77 /*!
78 * \brief Draw a dashed line.
80 static void
81 draw_dashed_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
83 /*! \todo we need a real geometrical library, using double here is
84 * plain wrong. */
85 double dx = x2-x1;
86 double dy = y2-y1;
87 double len_squared = dx*dx + dy*dy;
88 int n;
89 const int segs = 11; /* must be odd */
91 if (len_squared < 1000000)
93 /*! \todo line too short, just draw it -> magic value;
94 * with a proper geo lib this would be gone anyway. */
95 gui->graphics->draw_line (gc, x1, y1, x2, y2);
96 return;
99 /* first seg is drawn from x1, y1 with no rounding error due to n-1 == 0 */
100 for (n = 1; n < segs; n += 2)
101 gui->graphics->draw_line (gc,
102 x1 + (dx * (double) (n-1) / (double) segs),
103 y1 + (dy * (double) (n-1) / (double) segs),
104 x1 + (dx * (double) n / (double) segs),
105 y1 + (dy * (double) n / (double) segs));
107 /* make sure the last segment is drawn properly to x2 and y2,
108 * don't leave room for rounding errors. */
109 gui->graphics->draw_line (gc,
110 x2 - (dx / (double) segs),
111 y2 - (dy / (double) segs),
113 y2);
117 * \brief Creates a tmp polygon with coordinates converted to screen
118 * system.
120 static void
121 XORPolygon (hidGC gc, PolygonType *polygon, Coord dx, Coord dy, int dash_last)
123 Cardinal i;
124 for (i = 0; i < polygon->PointN; i++)
126 Cardinal next = next_contour_point (polygon, i);
128 if (next == 0)
129 { /* last line: sometimes the implicit closing line */
130 if (i == 1) /* corner case: don't draw two lines on top of
131 * each other - with XOR it looks bad */
132 continue;
134 if (dash_last)
136 draw_dashed_line (gc,
137 polygon->Points[i].X + dx,
138 polygon->Points[i].Y + dy,
139 polygon->Points[next].X + dx,
140 polygon->Points[next].Y + dy);
141 break; /* skip normal line draw below */
145 /* normal contour line */
146 gui->graphics->draw_line (gc,
147 polygon->Points[i].X + dx,
148 polygon->Points[i].Y + dy,
149 polygon->Points[next].X + dx,
150 polygon->Points[next].Y + dy);
155 * \brief Draws the outline of an arc.
157 static void
158 XORDrawAttachedArc (hidGC gc, Coord thick)
160 ArcType arc;
161 BoxType *bx;
162 Coord wx, wy;
163 Angle sa, dir;
164 Coord wid = thick / 2;
166 wx = Crosshair.X - Crosshair.AttachedBox.Point1.X;
167 wy = Crosshair.Y - Crosshair.AttachedBox.Point1.Y;
168 if (wx == 0 && wy == 0)
169 return;
170 arc.X = Crosshair.AttachedBox.Point1.X;
171 arc.Y = Crosshair.AttachedBox.Point1.Y;
172 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
174 arc.X = Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
175 sa = (wx >= 0) ? 0 : 180;
176 #ifdef ARC45
177 if (abs (wy) >= 2 * abs (wx))
178 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
179 else
180 #endif
181 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
183 else
185 arc.Y = Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
186 sa = (wy >= 0) ? -90 : 90;
187 #ifdef ARC45
188 if (abs (wx) >= 2 * abs (wy))
189 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
190 else
191 #endif
192 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
193 wy = wx;
195 wy = abs (wy);
196 arc.StartAngle = sa;
197 arc.Delta = dir;
198 arc.Width = arc.Height = wy;
199 bx = GetArcEnds (&arc);
200 /* sa = sa - 180; */
201 gui->graphics->draw_arc (gc, arc.X, arc.Y, wy + wid, wy + wid, sa, dir);
202 if (wid > pixel_slop)
204 gui->graphics->draw_arc (gc, arc.X, arc.Y, wy - wid, wy - wid, sa, dir);
205 gui->graphics->draw_arc (gc, bx->X1, bx->Y1, wid, wid, sa, -180 * SGN (dir));
206 gui->graphics->draw_arc (gc, bx->X2, bx->Y2, wid, wid, sa + dir, 180 * SGN (dir));
211 * \brief Draws the outline of a line.
213 static void
214 XORDrawAttachedLine (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2, Coord thick)
216 Coord dx, dy, ox, oy;
217 double h;
219 dx = x2 - x1;
220 dy = y2 - y1;
221 if (dx != 0 || dy != 0)
222 h = 0.5 * thick / hypot (dx, dy);
223 else
224 h = 0.0;
225 ox = dy * h + 0.5 * SGN (dy);
226 oy = -(dx * h + 0.5 * SGN (dx));
227 gui->graphics->draw_line (gc, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
228 if (abs (ox) >= pixel_slop || abs (oy) >= pixel_slop)
230 Angle angle = atan2 (dx, dy) * 57.295779;
231 gui->graphics->draw_line (gc, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
232 gui->graphics->draw_arc (gc, x1, y1, thick / 2, thick / 2, angle - 180, 180);
233 gui->graphics->draw_arc (gc, x2, y2, thick / 2, thick / 2, angle, 180);
238 * \brief Draws the elements of a loaded circuit which is to be merged
239 * in.
241 static void
242 XORDrawElement (hidGC gc, ElementType *Element, Coord DX, Coord DY)
244 /* if no silkscreen, draw the bounding box */
245 if (Element->ArcN == 0 && Element->LineN == 0)
247 gui->graphics->draw_line (gc,
248 DX + Element->BoundingBox.X1,
249 DY + Element->BoundingBox.Y1,
250 DX + Element->BoundingBox.X1,
251 DY + Element->BoundingBox.Y2);
252 gui->graphics->draw_line (gc,
253 DX + Element->BoundingBox.X1,
254 DY + Element->BoundingBox.Y2,
255 DX + Element->BoundingBox.X2,
256 DY + Element->BoundingBox.Y2);
257 gui->graphics->draw_line (gc,
258 DX + Element->BoundingBox.X2,
259 DY + Element->BoundingBox.Y2,
260 DX + Element->BoundingBox.X2,
261 DY + Element->BoundingBox.Y1);
262 gui->graphics->draw_line (gc,
263 DX + Element->BoundingBox.X2,
264 DY + Element->BoundingBox.Y1,
265 DX + Element->BoundingBox.X1,
266 DY + Element->BoundingBox.Y1);
268 else
270 ELEMENTLINE_LOOP (Element);
272 gui->graphics->draw_line (gc,
273 DX + line->Point1.X,
274 DY + line->Point1.Y,
275 DX + line->Point2.X,
276 DY + line->Point2.Y);
278 END_LOOP;
280 /* arc coordinates and angles have to be converted to X11 notation */
281 ARC_LOOP (Element);
283 gui->graphics->draw_arc (gc,
284 DX + arc->X,
285 DY + arc->Y,
286 arc->Width, arc->Height, arc->StartAngle, arc->Delta);
288 END_LOOP;
290 /* pin coordinates and angles have to be converted to X11 notation */
291 PIN_LOOP (Element);
293 thindraw_moved_pv (gc, pin, DX, DY);
295 END_LOOP;
297 /* pads */
298 PAD_LOOP (Element);
300 if (PCB->InvisibleObjectsOn ||
301 (TEST_FLAG (ONSOLDERFLAG, pad) != 0) == Settings.ShowBottomSide)
303 /* Make a copy of the pad structure, moved to the correct position */
304 PadType moved_pad = *pad;
305 moved_pad.Point1.X += DX; moved_pad.Point1.Y += DY;
306 moved_pad.Point2.X += DX; moved_pad.Point2.Y += DY;
308 gui->graphics->thindraw_pcb_pad (gc, &moved_pad, false, false);
311 END_LOOP;
312 /* mark */
313 gui->graphics->draw_line (gc,
314 Element->MarkX + DX - EMARK_SIZE,
315 Element->MarkY + DY,
316 Element->MarkX + DX,
317 Element->MarkY + DY - EMARK_SIZE);
318 gui->graphics->draw_line (gc,
319 Element->MarkX + DX + EMARK_SIZE,
320 Element->MarkY + DY,
321 Element->MarkX + DX,
322 Element->MarkY + DY - EMARK_SIZE);
323 gui->graphics->draw_line (gc,
324 Element->MarkX + DX - EMARK_SIZE,
325 Element->MarkY + DY,
326 Element->MarkX + DX,
327 Element->MarkY + DY + EMARK_SIZE);
328 gui->graphics->draw_line (gc,
329 Element->MarkX + DX + EMARK_SIZE,
330 Element->MarkY + DY,
331 Element->MarkX + DX,
332 Element->MarkY + DY + EMARK_SIZE);
336 * \brief Draws all visible and attached objects of the pastebuffer.
338 static void
339 XORDrawBuffer (hidGC gc, BufferType *Buffer)
341 Cardinal i;
342 Coord x, y;
344 /* set offset */
345 x = Crosshair.X - Buffer->X;
346 y = Crosshair.Y - Buffer->Y;
348 /* draw all visible layers */
349 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
350 if (PCB->Data->Layer[i].On)
352 LayerType *layer = &Buffer->Data->Layer[i];
354 LINE_LOOP (layer);
357 XORDrawAttachedLine(x +line->Point1.X,
358 y +line->Point1.Y, x +line->Point2.X,
359 y +line->Point2.Y, line->Thickness);
361 gui->graphics->draw_line (gc,
362 x + line->Point1.X, y + line->Point1.Y,
363 x + line->Point2.X, y + line->Point2.Y);
365 END_LOOP;
366 ARC_LOOP (layer);
368 gui->graphics->draw_arc (gc,
369 x + arc->X,
370 y + arc->Y,
371 arc->Width,
372 arc->Height, arc->StartAngle, arc->Delta);
374 END_LOOP;
375 TEXT_LOOP (layer);
377 BoxType *box = &text->BoundingBox;
378 gui->graphics->draw_rect (gc,
379 x + box->X1, y + box->Y1, x + box->X2, y + box->Y2);
381 END_LOOP;
382 /* the tmp polygon has n+1 points because the first
383 * and the last one are set to the same coordinates
385 POLYGON_LOOP (layer);
387 XORPolygon (gc, polygon, x, y, 0);
389 END_LOOP;
392 /* draw elements if visible */
393 if (PCB->PinOn && PCB->ElementOn)
394 ELEMENT_LOOP (Buffer->Data);
396 if (FRONT (element) || PCB->InvisibleObjectsOn)
397 XORDrawElement (gc, element, x, y);
399 END_LOOP;
401 /* and the vias */
402 if (PCB->ViaOn)
403 VIA_LOOP (Buffer->Data);
405 thindraw_moved_pv (gc, via, x, y);
407 END_LOOP;
411 * \brief Draws the rubberband to insert points into polygons/lines/...
413 static void
414 XORDrawInsertPointObject (hidGC gc)
416 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
417 PointType *point = (PointType *) Crosshair.AttachedObject.Ptr3;
419 if (Crosshair.AttachedObject.Type != NO_TYPE)
421 gui->graphics->draw_line (gc, point->X, point->Y, line->Point1.X, line->Point1.Y);
422 gui->graphics->draw_line (gc, point->X, point->Y, line->Point2.X, line->Point2.Y);
427 * \brief Draws the attached object while in MOVE_MODE or COPY_MODE.
429 static void
430 XORDrawMoveOrCopyObject (hidGC gc)
432 RubberbandType *ptr;
433 Cardinal i;
434 Coord dx = Crosshair.X - Crosshair.AttachedObject.X,
435 dy = Crosshair.Y - Crosshair.AttachedObject.Y;
437 switch (Crosshair.AttachedObject.Type)
439 case VIA_TYPE:
441 PinType *via = (PinType *) Crosshair.AttachedObject.Ptr1;
442 thindraw_moved_pv (gc, via, dx, dy);
443 break;
446 case LINE_TYPE:
448 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
450 XORDrawAttachedLine (gc, line->Point1.X + dx, line->Point1.Y + dy,
451 line->Point2.X + dx, line->Point2.Y + dy,
452 line->Thickness);
453 break;
456 case ARC_TYPE:
458 ArcType *Arc = (ArcType *) Crosshair.AttachedObject.Ptr2;
460 gui->graphics->draw_arc (gc,
461 Arc->X + dx,
462 Arc->Y + dy,
463 Arc->Width, Arc->Height, Arc->StartAngle, Arc->Delta);
464 break;
467 case POLYGON_TYPE:
469 PolygonType *polygon =
470 (PolygonType *) Crosshair.AttachedObject.Ptr2;
472 /* the tmp polygon has n+1 points because the first
473 * and the last one are set to the same coordinates
475 XORPolygon (gc, polygon, dx, dy, 0);
476 break;
479 case LINEPOINT_TYPE:
481 LineType *line;
482 PointType *point;
484 line = (LineType *) Crosshair.AttachedObject.Ptr2;
485 point = (PointType *) Crosshair.AttachedObject.Ptr3;
486 if (point == &line->Point1)
487 XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
488 line->Point2.X, line->Point2.Y, line->Thickness);
489 else
490 XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
491 line->Point1.X, line->Point1.Y, line->Thickness);
492 break;
495 case POLYGONPOINT_TYPE:
497 PolygonType *polygon;
498 PointType *point;
499 Cardinal point_idx, prev, next;
501 polygon = (PolygonType *) Crosshair.AttachedObject.Ptr2;
502 point = (PointType *) Crosshair.AttachedObject.Ptr3;
503 point_idx = polygon_point_idx (polygon, point);
505 /* get previous and following point */
506 prev = prev_contour_point (polygon, point_idx);
507 next = next_contour_point (polygon, point_idx);
509 /* draw the two segments */
510 gui->graphics->draw_line (gc,
511 polygon->Points[prev].X, polygon->Points[prev].Y,
512 point->X + dx, point->Y + dy);
513 gui->graphics->draw_line (gc,
514 point->X + dx, point->Y + dy,
515 polygon->Points[next].X, polygon->Points[next].Y);
516 break;
519 case ELEMENTNAME_TYPE:
521 /* locate the element "mark" and draw an association line from crosshair to it */
522 ElementType *element =
523 (ElementType *) Crosshair.AttachedObject.Ptr1;
525 gui->graphics->draw_line (gc,
526 element->MarkX,
527 element->MarkY, Crosshair.X, Crosshair.Y);
528 /* fall through to move the text as a box outline */
530 case TEXT_TYPE:
532 TextType *text = (TextType *) Crosshair.AttachedObject.Ptr2;
533 BoxType *box = &text->BoundingBox;
534 gui->graphics->draw_rect (gc,
535 box->X1 + dx,
536 box->Y1 + dy, box->X2 + dx, box->Y2 + dy);
537 break;
540 /* pin/pad movements result in moving an element */
541 case PAD_TYPE:
542 case PIN_TYPE:
543 case ELEMENT_TYPE:
544 XORDrawElement (gc, (ElementType *) Crosshair.AttachedObject.Ptr2, dx, dy);
545 break;
548 /* draw the attached rubberband lines too */
549 i = Crosshair.AttachedObject.RubberbandN;
550 ptr = Crosshair.AttachedObject.Rubberband;
551 while (i)
553 PointType *point1, *point2;
555 if (TEST_FLAG (VIAFLAG, ptr->Line))
557 /* this is a rat going to a polygon. do not draw for rubberband */;
559 else if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
561 /* 'point1' is always the fix-point */
562 if (ptr->MovedPoint == &ptr->Line->Point1)
564 point1 = &ptr->Line->Point2;
565 point2 = &ptr->Line->Point1;
567 else
569 point1 = &ptr->Line->Point1;
570 point2 = &ptr->Line->Point2;
572 XORDrawAttachedLine (gc, point1->X, point1->Y,
573 point2->X + dx, point2->Y + dy,
574 ptr->Line->Thickness);
576 else if (ptr->MovedPoint == &ptr->Line->Point1)
577 XORDrawAttachedLine (gc,
578 ptr->Line->Point1.X + dx,
579 ptr->Line->Point1.Y + dy,
580 ptr->Line->Point2.X + dx,
581 ptr->Line->Point2.Y + dy, ptr->Line->Thickness);
583 ptr++;
584 i--;
589 * \brief Draws additional stuff that follows the crosshair.
591 void
592 DrawAttached (hidGC gc)
594 gui->graphics->set_color (gc, Settings.CrosshairColor);
595 gui->graphics->set_draw_xor (gc, 1);
596 gui->graphics->set_line_cap (gc, Trace_Cap);
597 gui->graphics->set_line_width (gc, 1);
599 switch (Settings.Mode)
601 case VIA_MODE:
603 /* Make a dummy via structure to draw from */
604 PinType via;
605 via.X = Crosshair.X;
606 via.Y = Crosshair.Y;
607 via.Thickness = Settings.ViaThickness;
608 via.Clearance = 2 * Settings.Keepaway;
609 via.DrillingHole = Settings.ViaDrillingHole;
610 via.Mask = 0;
611 via.Flags = NoFlags ();
613 gui->graphics->thindraw_pcb_pv (gc, gc, &via, true, false);
615 if (TEST_FLAG (SHOWDRCFLAG, PCB))
617 Coord mask_r = Settings.ViaThickness / 2 + PCB->Bloat;
618 gui->graphics->set_color (gc, Settings.CrossColor);
619 gui->graphics->set_line_cap (gc, Round_Cap);
620 gui->graphics->set_line_width (gc, 0);
621 gui->graphics->draw_arc (gc, via.X, via.Y, mask_r, mask_r, 0, 360);
622 gui->graphics->set_color (gc, Settings.CrosshairColor);
624 break;
627 /* the attached line is used by both LINEMODE, POLYGON_MODE and POLYGONHOLE_MODE*/
628 case POLYGON_MODE:
629 case POLYGONHOLE_MODE:
630 /* draw only if starting point is set */
631 if (Crosshair.AttachedLine.State != STATE_FIRST)
632 gui->graphics->draw_line (gc,
633 Crosshair.AttachedLine.Point1.X,
634 Crosshair.AttachedLine.Point1.Y,
635 Crosshair.AttachedLine.Point2.X,
636 Crosshair.AttachedLine.Point2.Y);
638 /* draw attached polygon only if in POLYGON_MODE or POLYGONHOLE_MODE */
639 if (Crosshair.AttachedPolygon.PointN > 1)
641 XORPolygon (gc, &Crosshair.AttachedPolygon, 0, 0, 1);
643 break;
645 case ARC_MODE:
646 if (Crosshair.AttachedBox.State != STATE_FIRST)
648 XORDrawAttachedArc (gc, Settings.LineThickness);
649 if (TEST_FLAG (SHOWDRCFLAG, PCB))
651 gui->graphics->set_color (gc, Settings.CrossColor);
652 XORDrawAttachedArc (gc, Settings.LineThickness + 2 * PCB->Bloat);
653 gui->graphics->set_color (gc, Settings.CrosshairColor);
657 break;
659 case LINE_MODE:
660 /* draw only if starting point exists and the line has length */
661 if (Crosshair.AttachedLine.State != STATE_FIRST &&
662 Crosshair.AttachedLine.draw)
664 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
665 Crosshair.AttachedLine.Point1.Y,
666 Crosshair.AttachedLine.Point2.X,
667 Crosshair.AttachedLine.Point2.Y,
668 PCB->RatDraw ? 10 : Settings.LineThickness);
669 /* draw two lines ? */
670 if (PCB->Clipping)
671 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
672 Crosshair.AttachedLine.Point2.Y,
673 Crosshair.X, Crosshair.Y,
674 PCB->RatDraw ? 10 : Settings.LineThickness);
675 if (TEST_FLAG (SHOWDRCFLAG, PCB))
677 gui->graphics->set_color (gc, Settings.CrossColor);
678 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
679 Crosshair.AttachedLine.Point1.Y,
680 Crosshair.AttachedLine.Point2.X,
681 Crosshair.AttachedLine.Point2.Y,
682 PCB->RatDraw ? 10 : Settings.LineThickness
683 + 2 * PCB->Bloat);
684 if (PCB->Clipping)
685 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
686 Crosshair.AttachedLine.Point2.Y,
687 Crosshair.X, Crosshair.Y,
688 PCB->RatDraw ? 10 : Settings.
689 LineThickness + 2 * PCB->Bloat);
690 gui->graphics->set_color (gc, Settings.CrosshairColor);
693 break;
695 case PASTEBUFFER_MODE:
696 XORDrawBuffer (gc, PASTEBUFFER);
697 break;
699 case COPY_MODE:
700 case MOVE_MODE:
701 XORDrawMoveOrCopyObject (gc);
702 break;
704 case INSERTPOINT_MODE:
705 XORDrawInsertPointObject (gc);
706 break;
709 /* an attached box does not depend on a special mode */
710 if (Crosshair.AttachedBox.State == STATE_SECOND ||
711 Crosshair.AttachedBox.State == STATE_THIRD)
713 Coord x1, y1, x2, y2;
715 x1 = Crosshair.AttachedBox.Point1.X;
716 y1 = Crosshair.AttachedBox.Point1.Y;
717 x2 = Crosshair.AttachedBox.Point2.X;
718 y2 = Crosshair.AttachedBox.Point2.Y;
719 gui->graphics->draw_rect (gc, x1, y1, x2, y2);
725 * \brief Draw the marker position.
727 void
728 DrawMark (hidGC gc)
730 gui->graphics->set_color (gc, Settings.CrosshairColor);
731 gui->graphics->set_draw_xor (gc, 1);
732 gui->graphics->set_line_cap (gc, Trace_Cap);
733 gui->graphics->set_line_width (gc, 1);
735 /* Mark is not drawn when it is not set */
736 if (!Marked.status)
737 return;
739 gui->graphics->draw_line (gc,
740 Marked.X - MARK_SIZE,
741 Marked.Y - MARK_SIZE,
742 Marked.X + MARK_SIZE, Marked.Y + MARK_SIZE);
743 gui->graphics->draw_line (gc,
744 Marked.X + MARK_SIZE,
745 Marked.Y - MARK_SIZE,
746 Marked.X - MARK_SIZE, Marked.Y + MARK_SIZE);
750 * \brief Returns the nearest grid-point to the given Coord.
752 Coord
753 GridFit (Coord x, Coord grid_spacing, Coord grid_offset)
755 x -= grid_offset;
756 x = grid_spacing * round ((double) x / grid_spacing);
757 x += grid_offset;
758 return x;
763 * \brief Notify the GUI that data relating to the crosshair is being
764 * changed.
766 * The argument passed is false to notify "changes are about to happen",
767 * and true to notify "changes have finished".
769 * Each call with a 'false' parameter must be matched with a following one
770 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
771 * but might be allowed in the future.
773 * GUIs should not complain if they receive extra calls with 'true' as parameter.
774 * They should initiate a redraw of the crosshair attached objects - which may
775 * (if necessary) mean repainting the whole screen if the GUI hasn't tracked the
776 * location of existing attached drawing.
778 void
779 notify_crosshair_change (bool changes_complete)
781 if (gui->notify_crosshair_change)
782 gui->notify_crosshair_change (changes_complete);
787 * \brief Notify the GUI that data relating to the mark is being changed.
789 * The argument passed is false to notify "changes are about to happen",
790 * and true to notify "changes have finished".
792 * Each call with a 'false' parameter must be matched with a following one
793 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
794 * but might be allowed in the future.
796 * GUIs should not complain if they receive extra calls with 'true' as parameter.
797 * They should initiate a redraw of the mark - which may (if necessary) mean
798 * repainting the whole screen if the GUI hasn't tracked the mark's location.
800 void
801 notify_mark_change (bool changes_complete)
803 if (gui->notify_mark_change)
804 gui->notify_mark_change (changes_complete);
809 * \brief Convenience for plugins using the old {Hide,Restore}Crosshair
810 * API.
812 * This links up to notify the GUI of the expected changes using the new APIs.
814 * Use of this old API is deprecated, as the names don't necessarily reflect
815 * what all GUIs may do in response to the notifications. Keeping these APIs
816 * is aimed at easing transition to the newer API, they will emit a harmless
817 * warning at the time of their first use.
820 void
821 HideCrosshair (void)
823 static bool warned_old_api = false;
824 if (!warned_old_api)
826 Message (_("WARNING: A plugin is using the deprecated API HideCrosshair().\n"
827 " This API may be removed in a future release of PCB.\n"));
828 warned_old_api = true;
831 notify_crosshair_change (false);
832 notify_mark_change (false);
835 void
836 RestoreCrosshair (void)
838 static bool warned_old_api = false;
839 if (!warned_old_api)
841 Message (_("WARNING: A plugin is using the deprecated API RestoreCrosshair().\n"
842 " This API may be removed in a future release of PCB.\n"));
843 warned_old_api = true;
846 notify_crosshair_change (true);
847 notify_mark_change (true);
851 * \brief Returns the square of the given number.
853 static double
854 square (double x)
856 return x * x;
859 static double
860 crosshair_sq_dist (CrosshairType *crosshair, Coord x, Coord y)
862 return square (x - crosshair->X) + square (y - crosshair->Y);
865 struct snap_data {
866 CrosshairType *crosshair;
867 double nearest_sq_dist;
868 bool nearest_is_grid;
869 Coord x, y;
873 * \brief Snap to a given location if it is the closest thing we found
874 * so far.
876 * If "prefer_to_grid" is set, the passed location will take preference
877 * over a closer grid points we already snapped to UNLESS the user is
878 * pressing the SHIFT key. If the SHIFT key is pressed, the closest object
879 * (including grid points), is always preferred.
881 static void
882 check_snap_object (struct snap_data *snap_data, Coord x, Coord y,
883 bool prefer_to_grid)
885 double sq_dist;
887 sq_dist = crosshair_sq_dist (snap_data->crosshair, x, y);
888 if (sq_dist <= snap_data->nearest_sq_dist ||
889 (prefer_to_grid && snap_data->nearest_is_grid && !gui->shift_is_pressed()))
891 snap_data->x = x;
892 snap_data->y = y;
893 snap_data->nearest_sq_dist = sq_dist;
894 snap_data->nearest_is_grid = false;
898 static void
899 check_snap_offgrid_line (struct snap_data *snap_data,
900 Coord nearest_grid_x,
901 Coord nearest_grid_y)
903 void *ptr1, *ptr2, *ptr3;
904 int ans;
905 LineType *line;
906 Coord try_x, try_y;
907 double dx, dy;
908 double dist;
910 if (!TEST_FLAG (SNAPPINFLAG, PCB))
911 return;
913 /* Code to snap at some sensible point along a line */
914 /* Pick the nearest grid-point in the x or y direction
915 * to align with, then adjust until we hit the line
917 ans = SearchObjectByLocation (LINE_TYPE, &ptr1, &ptr2, &ptr3,
918 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
921 if (ans == NO_TYPE)
922 return;
924 line = (LineType *)ptr2;
926 /* Allow snapping to off-grid lines when drawing new lines (on
927 * the same layer), and when moving a line end-point
928 * (but don't snap to the same line)
930 if ((Settings.Mode != LINE_MODE || CURRENT != ptr1) &&
931 (Settings.Mode != MOVE_MODE ||
932 Crosshair.AttachedObject.Ptr1 != ptr1 ||
933 Crosshair.AttachedObject.Type != LINEPOINT_TYPE ||
934 Crosshair.AttachedObject.Ptr2 == line))
935 return;
937 dx = line->Point2.X - line->Point1.X;
938 dy = line->Point2.Y - line->Point1.Y;
940 /* Try snapping along the X axis */
941 if (dy != 0.)
943 /* Move in the X direction until we hit the line */
944 try_x = (nearest_grid_y - line->Point1.Y) / dy * dx + line->Point1.X;
945 try_y = nearest_grid_y;
946 check_snap_object (snap_data, try_x, try_y, true);
949 /* Try snapping along the Y axis */
950 if (dx != 0.)
952 try_x = nearest_grid_x;
953 try_y = (nearest_grid_x - line->Point1.X) / dx * dy + line->Point1.Y;
954 check_snap_object (snap_data, try_x, try_y, true);
957 if (dx != dy) /* If line not parallel with dX = dY direction.. */
959 /* Try snapping diagonally towards the line in the dX = dY direction */
961 if (dy == 0)
962 dist = line->Point1.Y - nearest_grid_y;
963 else
964 dist = ((line->Point1.X - nearest_grid_x) -
965 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 - dx / dy);
967 try_x = nearest_grid_x + dist;
968 try_y = nearest_grid_y + dist;
970 check_snap_object (snap_data, try_x, try_y, true);
973 if (dx != -dy) /* If line not parallel with dX = -dY direction.. */
975 /* Try snapping diagonally towards the line in the dX = -dY direction */
977 if (dy == 0)
978 dist = nearest_grid_y - line->Point1.Y;
979 else
980 dist = ((line->Point1.X - nearest_grid_x) -
981 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 + dx / dy);
983 try_x = nearest_grid_x + dist;
984 try_y = nearest_grid_y - dist;
986 check_snap_object (snap_data, try_x, try_y, true);
991 * \brief Recalculates the passed coordinates to fit the current grid
992 * setting.
994 void
995 FitCrosshairIntoGrid (Coord X, Coord Y)
997 Coord nearest_grid_x, nearest_grid_y;
998 void *ptr1, *ptr2, *ptr3;
999 struct snap_data snap_data;
1000 int ans;
1002 Crosshair.X = CLAMP (X, Crosshair.MinX, Crosshair.MaxX);
1003 Crosshair.Y = CLAMP (Y, Crosshair.MinY, Crosshair.MaxY);
1005 if (PCB->RatDraw)
1007 nearest_grid_x = -MIL_TO_COORD (6);
1008 nearest_grid_y = -MIL_TO_COORD (6);
1010 else
1012 nearest_grid_x = GridFit (Crosshair.X, PCB->Grid, PCB->GridOffsetX);
1013 nearest_grid_y = GridFit (Crosshair.Y, PCB->Grid, PCB->GridOffsetY);
1015 if (Marked.status && TEST_FLAG (ORTHOMOVEFLAG, PCB))
1017 Coord dx = Crosshair.X - Marked.X;
1018 Coord dy = Crosshair.Y - Marked.Y;
1019 if (ABS (dx) > ABS (dy))
1020 nearest_grid_y = Marked.Y;
1021 else
1022 nearest_grid_x = Marked.X;
1027 snap_data.crosshair = &Crosshair;
1028 snap_data.nearest_sq_dist =
1029 crosshair_sq_dist (&Crosshair, nearest_grid_x, nearest_grid_y);
1030 snap_data.nearest_is_grid = true;
1031 snap_data.x = nearest_grid_x;
1032 snap_data.y = nearest_grid_y;
1034 ans = NO_TYPE;
1035 if (!PCB->RatDraw)
1036 ans = SearchObjectByLocation (ELEMENT_TYPE, &ptr1, &ptr2, &ptr3,
1037 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1039 if (ans & ELEMENT_TYPE)
1041 ElementType *el = (ElementType *) ptr1;
1042 check_snap_object (&snap_data, el->MarkX, el->MarkY, false);
1045 ans = NO_TYPE;
1046 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
1047 ans = SearchObjectByLocation (PAD_TYPE, &ptr1, &ptr2, &ptr3,
1048 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1050 /* Avoid self-snapping when moving */
1051 if (ans != NO_TYPE &&
1052 Settings.Mode == MOVE_MODE &&
1053 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
1054 ptr1 == Crosshair.AttachedObject.Ptr1)
1055 ans = NO_TYPE;
1057 if (ans != NO_TYPE &&
1058 ( Settings.Mode == LINE_MODE ||
1059 (Settings.Mode == MOVE_MODE &&
1060 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)))
1062 PadType *pad = (PadType *) ptr2;
1063 LayerType *desired_layer;
1064 Cardinal desired_group;
1065 Cardinal bottom_group, top_group;
1066 int found_our_layer = false;
1068 desired_layer = CURRENT;
1069 if (Settings.Mode == MOVE_MODE &&
1070 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)
1072 desired_layer = (LayerType *)Crosshair.AttachedObject.Ptr1;
1075 /* find layer groups of the top and bottom sides */
1076 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
1077 bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
1078 desired_group = TEST_FLAG (ONSOLDERFLAG, pad) ? bottom_group : top_group;
1080 GROUP_LOOP (PCB->Data, desired_group);
1082 if (layer == desired_layer)
1084 found_our_layer = true;
1085 break;
1088 END_LOOP;
1090 if (found_our_layer == false)
1091 ans = NO_TYPE;
1094 if (ans != NO_TYPE)
1096 PadType *pad = (PadType *)ptr2;
1097 check_snap_object (&snap_data, pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2,
1098 pad->Point1.Y + (pad->Point2.Y - pad->Point1.Y) / 2,
1099 true);
1102 ans = NO_TYPE;
1103 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
1104 ans = SearchObjectByLocation (PIN_TYPE, &ptr1, &ptr2, &ptr3,
1105 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1107 /* Avoid self-snapping when moving */
1108 if (ans != NO_TYPE &&
1109 Settings.Mode == MOVE_MODE &&
1110 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
1111 ptr1 == Crosshair.AttachedObject.Ptr1)
1112 ans = NO_TYPE;
1114 if (ans != NO_TYPE)
1116 PinType *pin = (PinType *)ptr2;
1117 check_snap_object (&snap_data, pin->X, pin->Y, true);
1120 ans = NO_TYPE;
1121 if (TEST_FLAG (SNAPPINFLAG, PCB))
1122 ans = SearchObjectByLocation (VIA_TYPE, &ptr1, &ptr2, &ptr3,
1123 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1125 /* Avoid snapping vias to any other vias */
1126 if (Settings.Mode == MOVE_MODE &&
1127 Crosshair.AttachedObject.Type == VIA_TYPE &&
1128 (ans & PIN_TYPES))
1129 ans = NO_TYPE;
1131 if (ans != NO_TYPE)
1133 PinType *pin = (PinType *)ptr2;
1134 check_snap_object (&snap_data, pin->X, pin->Y, true);
1137 ans = NO_TYPE;
1138 if (TEST_FLAG (SNAPPINFLAG, PCB))
1139 ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
1140 &ptr1, &ptr2, &ptr3,
1141 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1143 if (ans != NO_TYPE)
1145 PointType *pnt = (PointType *)ptr3;
1146 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1149 check_snap_offgrid_line (&snap_data, nearest_grid_x, nearest_grid_y);
1151 ans = NO_TYPE;
1152 if (TEST_FLAG (SNAPPINFLAG, PCB))
1153 ans = SearchObjectByLocation (POLYGONPOINT_TYPE, &ptr1, &ptr2, &ptr3,
1154 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1156 if (ans != NO_TYPE)
1158 PointType *pnt = (PointType *)ptr3;
1159 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1162 if (snap_data.x >= 0 && snap_data.y >= 0)
1164 Crosshair.X = snap_data.x;
1165 Crosshair.Y = snap_data.y;
1168 if (Settings.Mode == ARROW_MODE)
1170 ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
1171 &ptr1, &ptr2, &ptr3,
1172 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1173 if (ans == NO_TYPE)
1174 hid_action("PointCursor");
1175 else if (!TEST_FLAG(SELECTEDFLAG, (LineType *)ptr2))
1176 hid_actionl("PointCursor","True", NULL);
1179 if (Settings.Mode == LINE_MODE
1180 && Crosshair.AttachedLine.State != STATE_FIRST
1181 && TEST_FLAG (AUTODRCFLAG, PCB))
1182 EnforceLineDRC ();
1184 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_DO_NOTHING);
1188 * \brief Move crosshair absolute.
1190 * \return true if the crosshair was moved from its existing position.
1192 bool
1193 MoveCrosshairAbsolute (Coord X, Coord Y)
1195 Coord old_x = Crosshair.X;
1196 Coord old_y = Crosshair.Y;
1198 FitCrosshairIntoGrid (X, Y);
1200 if (Crosshair.X != old_x || Crosshair.Y != old_y)
1202 Coord new_x = Crosshair.X;
1203 Coord new_y = Crosshair.Y;
1205 /* back up to old position to notify the GUI
1206 * (which might want to erase the old crosshair) */
1207 Crosshair.X = old_x;
1208 Crosshair.Y = old_y;
1209 notify_crosshair_change (false); /* Our caller notifies when it has done */
1211 /* now move forward again */
1212 Crosshair.X = new_x;
1213 Crosshair.Y = new_y;
1214 return true;
1216 return false;
1220 * \brief Sets the valid range for the crosshair cursor.
1222 void
1223 SetCrosshairRange (Coord MinX, Coord MinY, Coord MaxX, Coord MaxY)
1225 Crosshair.MinX = MAX (0, MinX);
1226 Crosshair.MinY = MAX (0, MinY);
1227 Crosshair.MaxX = MIN (PCB->MaxWidth, MaxX);
1228 Crosshair.MaxY = MIN (PCB->MaxHeight, MaxY);
1230 /* force update of position */
1231 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
1235 * \brief Initializes crosshair stuff.
1237 * Clears the struct, allocates to graphical contexts.
1239 void
1240 InitCrosshair (void)
1242 /* set initial shape */
1243 Crosshair.shape = Basic_Crosshair_Shape;
1245 /* set default limits */
1246 Crosshair.MinX = Crosshair.MinY = 0;
1247 Crosshair.MaxX = PCB->MaxWidth;
1248 Crosshair.MaxY = PCB->MaxHeight;
1250 /* clear the mark */
1251 Marked.status = false;
1255 * \brief Exits crosshair routines, release GCs.
1257 void
1258 DestroyCrosshair (void)
1260 FreePolygonMemory (&Crosshair.AttachedPolygon);