1 /*!
2 * \file src/crosshair.c
3 *
4 * \brief Crosshair stuff.
5 *
6 * <hr>
7 *
8 * <h1><b>Copyright.</b></h1>\n
9 *
10 * PCB, interactive printed circuit board design
11 *
12 * Copyright (C) 1994,1995,1996 Thomas Nau
13 *
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.
18 *
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.
23 *
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 *
28 * Contact addresses for paper mail and Email:
29 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
30 * Thomas.Nau@rz.uni-ulm.de
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <memory.h>
38 #include <math.h>
39
40 #include "global.h"
41 #include "hid_draw.h"
42
43 #include "crosshair.h"
44
45 #include "buffer.h"
46 #include "data.h"
47 #include "draw.h"
48 #include "error.h"
49 #include "line.h"
50 #include "misc.h"
51 #include "mymem.h"
52 #include "search.h"
53 #include "polygon.h"
54
55 #ifdef HAVE_LIBDMALLOC
56 #include <dmalloc.h>
57 #endif
58
59 typedef struct
60 {
61 int x, y;
62 } point;
63
64
65 /*!
66 * \brief Make a copy of the pin structure, moved to the correct
67 * position
68 */
69 static void
thindraw_moved_pv(hidGC gc,PinType * pv,Coord x,Coord y)70 thindraw_moved_pv (hidGC gc, PinType *pv, Coord x, Coord y)
71 {
72 PinType moved_pv = *pv;
73 moved_pv.X += x;
74 moved_pv.Y += y;
75
76 gui->graphics->thindraw_pcb_pv (gc, gc, &moved_pv, true, false);
77 }
78
79 /*!
80 * \brief Draw a dashed line.
81 */
82 static void
draw_dashed_line(hidGC gc,Coord x1,Coord y1,Coord x2,Coord y2)83 draw_dashed_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
84 {
85 /*! \todo we need a real geometrical library, using double here is
86 * plain wrong. */
87 double dx = x2-x1;
88 double dy = y2-y1;
89 double len_squared = dx*dx + dy*dy;
90 int n;
91 const int segs = 11; /* must be odd */
92
93 if (len_squared < 1000000)
94 {
95 /*! \todo line too short, just draw it -> magic value;
96 * with a proper geo lib this would be gone anyway. */
97 gui->graphics->draw_line (gc, x1, y1, x2, y2);
98 return;
99 }
100
101 /* first seg is drawn from x1, y1 with no rounding error due to n-1 == 0 */
102 for (n = 1; n < segs; n += 2)
103 gui->graphics->draw_line (gc,
104 x1 + (dx * (double) (n-1) / (double) segs),
105 y1 + (dy * (double) (n-1) / (double) segs),
106 x1 + (dx * (double) n / (double) segs),
107 y1 + (dy * (double) n / (double) segs));
108
109 /* make sure the last segment is drawn properly to x2 and y2,
110 * don't leave room for rounding errors. */
111 gui->graphics->draw_line (gc,
112 x2 - (dx / (double) segs),
113 y2 - (dy / (double) segs),
114 x2,
115 y2);
116 }
117
118 /*!
119 * \brief Creates a tmp polygon with coordinates converted to screen
120 * system.
121 */
122 static void
XORPolygon(hidGC gc,PolygonType * polygon,Coord dx,Coord dy,int dash_last)123 XORPolygon (hidGC gc, PolygonType *polygon, Coord dx, Coord dy, int dash_last)
124 {
125 Cardinal i;
126 for (i = 0; i < polygon->PointN; i++)
127 {
128 Cardinal next = next_contour_point (polygon, i);
129
130 if (next == 0)
131 { /* last line: sometimes the implicit closing line */
132 if (i == 1) /* corner case: don't draw two lines on top of
133 * each other - with XOR it looks bad */
134 continue;
135
136 if (dash_last)
137 {
138 draw_dashed_line (gc,
139 polygon->Points[i].X + dx,
140 polygon->Points[i].Y + dy,
141 polygon->Points[next].X + dx,
142 polygon->Points[next].Y + dy);
143 break; /* skip normal line draw below */
144 }
145 }
146
147 /* normal contour line */
148 gui->graphics->draw_line (gc,
149 polygon->Points[i].X + dx,
150 polygon->Points[i].Y + dy,
151 polygon->Points[next].X + dx,
152 polygon->Points[next].Y + dy);
153 }
154 }
155
156 /*!
157 * \brief Draws the outline of an arc.
158 */
159 static void
XORDrawAttachedArc(hidGC gc,Coord thick)160 XORDrawAttachedArc (hidGC gc, Coord thick)
161 {
162 ArcType arc;
163 BoxType *bx;
164 Coord wx, wy;
165 Angle sa, dir;
166 Coord wid = thick / 2;
167
168 wx = Crosshair.X - Crosshair.AttachedBox.Point1.X;
169 wy = Crosshair.Y - Crosshair.AttachedBox.Point1.Y;
170 if (wx == 0 && wy == 0)
171 return;
172 arc.X = Crosshair.AttachedBox.Point1.X;
173 arc.Y = Crosshair.AttachedBox.Point1.Y;
174 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
175 {
176 arc.X = Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
177 sa = (wx >= 0) ? 0 : 180;
178 #ifdef ARC45
179 if (abs (wy) >= 2 * abs (wx))
180 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
181 else
182 #endif
183 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
184 }
185 else
186 {
187 arc.Y = Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
188 sa = (wy >= 0) ? -90 : 90;
189 #ifdef ARC45
190 if (abs (wx) >= 2 * abs (wy))
191 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
192 else
193 #endif
194 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
195 wy = wx;
196 }
197 wy = abs (wy);
198 arc.StartAngle = sa;
199 arc.Delta = dir;
200 arc.Width = arc.Height = wy;
201 bx = GetArcEnds (&arc);
202 /* sa = sa - 180; */
203 gui->graphics->draw_arc (gc, arc.X, arc.Y, wy + wid, wy + wid, sa, dir);
204 if (wid > pixel_slop)
205 {
206 gui->graphics->draw_arc (gc, arc.X, arc.Y, wy - wid, wy - wid, sa, dir);
207 gui->graphics->draw_arc (gc, bx->X1, bx->Y1, wid, wid, sa, -180 * SGN (dir));
208 gui->graphics->draw_arc (gc, bx->X2, bx->Y2, wid, wid, sa + dir, 180 * SGN (dir));
209 }
210 }
211
212 /*!
213 * \brief Draws the outline of a line.
214 */
215 static void
XORDrawAttachedLine(hidGC gc,Coord x1,Coord y1,Coord x2,Coord y2,Coord thick)216 XORDrawAttachedLine (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2, Coord thick)
217 {
218 Coord dx, dy, ox, oy;
219 double h;
220
221 dx = x2 - x1;
222 dy = y2 - y1;
223 if (dx != 0 || dy != 0)
224 h = 0.5 * thick / hypot (dx, dy);
225 else
226 h = 0.0;
227 ox = dy * h + 0.5 * SGN (dy);
228 oy = -(dx * h + 0.5 * SGN (dx));
229 gui->graphics->draw_line (gc, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
230 if (abs (ox) >= pixel_slop || abs (oy) >= pixel_slop)
231 {
232 Angle angle = atan2 (dx, dy) * 57.295779;
233 gui->graphics->draw_line (gc, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
234 gui->graphics->draw_arc (gc, x1, y1, thick / 2, thick / 2, angle - 180, 180);
235 gui->graphics->draw_arc (gc, x2, y2, thick / 2, thick / 2, angle, 180);
236 }
237 }
238
239 /*!
240 * \brief Draws the elements of a loaded circuit which is to be merged
241 * in.
242 */
243 static void
XORDrawElement(hidGC gc,ElementType * Element,Coord DX,Coord DY)244 XORDrawElement (hidGC gc, ElementType *Element, Coord DX, Coord DY)
245 {
246 /* if no silkscreen, draw the bounding box */
247 if (Element->ArcN == 0 && Element->LineN == 0)
248 {
249 gui->graphics->draw_line (gc,
250 DX + Element->BoundingBox.X1,
251 DY + Element->BoundingBox.Y1,
252 DX + Element->BoundingBox.X1,
253 DY + Element->BoundingBox.Y2);
254 gui->graphics->draw_line (gc,
255 DX + Element->BoundingBox.X1,
256 DY + Element->BoundingBox.Y2,
257 DX + Element->BoundingBox.X2,
258 DY + Element->BoundingBox.Y2);
259 gui->graphics->draw_line (gc,
260 DX + Element->BoundingBox.X2,
261 DY + Element->BoundingBox.Y2,
262 DX + Element->BoundingBox.X2,
263 DY + Element->BoundingBox.Y1);
264 gui->graphics->draw_line (gc,
265 DX + Element->BoundingBox.X2,
266 DY + Element->BoundingBox.Y1,
267 DX + Element->BoundingBox.X1,
268 DY + Element->BoundingBox.Y1);
269 }
270 else
271 {
272 ELEMENTLINE_LOOP (Element);
273 {
274 gui->graphics->draw_line (gc,
275 DX + line->Point1.X,
276 DY + line->Point1.Y,
277 DX + line->Point2.X,
278 DY + line->Point2.Y);
279 }
280 END_LOOP;
281
282 /* arc coordinates and angles have to be converted to X11 notation */
283 ARC_LOOP (Element);
284 {
285 gui->graphics->draw_arc (gc,
286 DX + arc->X,
287 DY + arc->Y,
288 arc->Width, arc->Height, arc->StartAngle, arc->Delta);
289 }
290 END_LOOP;
291 }
292 /* pin coordinates and angles have to be converted to X11 notation */
293 PIN_LOOP (Element);
294 {
295 thindraw_moved_pv (gc, pin, DX, DY);
296 }
297 END_LOOP;
298
299 /* pads */
300 PAD_LOOP (Element);
301 {
302 if (PCB->InvisibleObjectsOn ||
303 (TEST_FLAG (ONSOLDERFLAG, pad) != 0) == Settings.ShowBottomSide)
304 {
305 /* Make a copy of the pad structure, moved to the correct position */
306 PadType moved_pad = *pad;
307 moved_pad.Point1.X += DX; moved_pad.Point1.Y += DY;
308 moved_pad.Point2.X += DX; moved_pad.Point2.Y += DY;
309
310 gui->graphics->thindraw_pcb_pad (gc, &moved_pad, false, false);
311 }
312 }
313 END_LOOP;
314 /* mark */
315 gui->graphics->draw_line (gc,
316 Element->MarkX + DX - EMARK_SIZE,
317 Element->MarkY + DY,
318 Element->MarkX + DX,
319 Element->MarkY + DY - EMARK_SIZE);
320 gui->graphics->draw_line (gc,
321 Element->MarkX + DX + EMARK_SIZE,
322 Element->MarkY + DY,
323 Element->MarkX + DX,
324 Element->MarkY + DY - EMARK_SIZE);
325 gui->graphics->draw_line (gc,
326 Element->MarkX + DX - EMARK_SIZE,
327 Element->MarkY + DY,
328 Element->MarkX + DX,
329 Element->MarkY + DY + EMARK_SIZE);
330 gui->graphics->draw_line (gc,
331 Element->MarkX + DX + EMARK_SIZE,
332 Element->MarkY + DY,
333 Element->MarkX + DX,
334 Element->MarkY + DY + EMARK_SIZE);
335 }
336
337 /*!
338 * \brief Draws all visible and attached objects of the pastebuffer.
339 */
340 static void
XORDrawBuffer(hidGC gc,BufferType * Buffer)341 XORDrawBuffer (hidGC gc, BufferType *Buffer)
342 {
343 Cardinal i;
344 Coord x, y;
345
346 /* set offset */
347 x = Crosshair.X - Buffer->X;
348 y = Crosshair.Y - Buffer->Y;
349
350 /* draw all visible layers */
351 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
352 if (PCB->Data->Layer[i].On)
353 {
354 LayerType *layer = &Buffer->Data->Layer[i];
355
356 LINE_LOOP (layer);
357 {
358 /*
359 XORDrawAttachedLine(x +line->Point1.X,
360 y +line->Point1.Y, x +line->Point2.X,
361 y +line->Point2.Y, line->Thickness);
362 */
363 gui->graphics->draw_line (gc,
364 x + line->Point1.X, y + line->Point1.Y,
365 x + line->Point2.X, y + line->Point2.Y);
366 }
367 END_LOOP;
368 ARC_LOOP (layer);
369 {
370 gui->graphics->draw_arc (gc,
371 x + arc->X,
372 y + arc->Y,
373 arc->Width,
374 arc->Height, arc->StartAngle, arc->Delta);
375 }
376 END_LOOP;
377 TEXT_LOOP (layer);
378 {
379 BoxType *box = &text->BoundingBox;
380 gui->graphics->draw_rect (gc,
381 x + box->X1, y + box->Y1, x + box->X2, y + box->Y2);
382 }
383 END_LOOP;
384 /* the tmp polygon has n+1 points because the first
385 * and the last one are set to the same coordinates
386 */
387 POLYGON_LOOP (layer);
388 {
389 XORPolygon (gc, polygon, x, y, 0);
390 }
391 END_LOOP;
392 }
393
394 /* draw elements if visible */
395 if (PCB->PinOn && PCB->ElementOn)
396 ELEMENT_LOOP (Buffer->Data);
397 {
398 if (FRONT (element) || PCB->InvisibleObjectsOn)
399 XORDrawElement (gc, element, x, y);
400 }
401 END_LOOP;
402
403 /* and the vias */
404 if (PCB->ViaOn)
405 VIA_LOOP (Buffer->Data);
406 {
407 thindraw_moved_pv (gc, via, x, y);
408 }
409 END_LOOP;
410 }
411
412 /*!
413 * \brief Draws the rubberband to insert points into polygons/lines/...
414 */
415 static void
XORDrawInsertPointObject(hidGC gc)416 XORDrawInsertPointObject (hidGC gc)
417 {
418 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
419 PointType *point = (PointType *) Crosshair.AttachedObject.Ptr3;
420
421 if (Crosshair.AttachedObject.Type != NO_TYPE)
422 {
423 gui->graphics->draw_line (gc, point->X, point->Y, line->Point1.X, line->Point1.Y);
424 gui->graphics->draw_line (gc, point->X, point->Y, line->Point2.X, line->Point2.Y);
425 }
426 }
427
428 /*!
429 * \brief Draws the attached object while in MOVE_MODE or COPY_MODE.
430 */
431 static void
XORDrawMoveOrCopyObject(hidGC gc)432 XORDrawMoveOrCopyObject (hidGC gc)
433 {
434 RubberbandType *ptr;
435 Cardinal i;
436 Coord dx = Crosshair.X - Crosshair.AttachedObject.X,
437 dy = Crosshair.Y - Crosshair.AttachedObject.Y;
438
439 switch (Crosshair.AttachedObject.Type)
440 {
441 case VIA_TYPE:
442 {
443 PinType *via = (PinType *) Crosshair.AttachedObject.Ptr1;
444 thindraw_moved_pv (gc, via, dx, dy);
445 break;
446 }
447
448 case LINE_TYPE:
449 {
450 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
451
452 XORDrawAttachedLine (gc, line->Point1.X + dx, line->Point1.Y + dy,
453 line->Point2.X + dx, line->Point2.Y + dy,
454 line->Thickness);
455 break;
456 }
457
458 case ARC_TYPE:
459 {
460 ArcType *Arc = (ArcType *) Crosshair.AttachedObject.Ptr2;
461
462 gui->graphics->draw_arc (gc,
463 Arc->X + dx,
464 Arc->Y + dy,
465 Arc->Width, Arc->Height, Arc->StartAngle, Arc->Delta);
466 break;
467 }
468
469 case POLYGON_TYPE:
470 {
471 PolygonType *polygon =
472 (PolygonType *) Crosshair.AttachedObject.Ptr2;
473
474 /* the tmp polygon has n+1 points because the first
475 * and the last one are set to the same coordinates
476 */
477 XORPolygon (gc, polygon, dx, dy, 0);
478 break;
479 }
480
481 case LINEPOINT_TYPE:
482 {
483 LineType *line;
484 PointType *point;
485
486 line = (LineType *) Crosshair.AttachedObject.Ptr2;
487 point = (PointType *) Crosshair.AttachedObject.Ptr3;
488 if (point == &line->Point1)
489 XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
490 line->Point2.X, line->Point2.Y, line->Thickness);
491 else
492 XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
493 line->Point1.X, line->Point1.Y, line->Thickness);
494 break;
495 }
496
497 case POLYGONPOINT_TYPE:
498 {
499 PolygonType *polygon;
500 PointType *point;
501 Cardinal point_idx, prev, next;
502
503 polygon = (PolygonType *) Crosshair.AttachedObject.Ptr2;
504 point = (PointType *) Crosshair.AttachedObject.Ptr3;
505 point_idx = polygon_point_idx (polygon, point);
506
507 /* get previous and following point */
508 prev = prev_contour_point (polygon, point_idx);
509 next = next_contour_point (polygon, point_idx);
510
511 /* draw the two segments */
512 gui->graphics->draw_line (gc,
513 polygon->Points[prev].X, polygon->Points[prev].Y,
514 point->X + dx, point->Y + dy);
515 gui->graphics->draw_line (gc,
516 point->X + dx, point->Y + dy,
517 polygon->Points[next].X, polygon->Points[next].Y);
518 break;
519 }
520
521 case ELEMENTNAME_TYPE:
522 {
523 /* locate the element "mark" and draw an association line from crosshair to it */
524 ElementType *element =
525 (ElementType *) Crosshair.AttachedObject.Ptr1;
526
527 gui->graphics->draw_line (gc,
528 element->MarkX,
529 element->MarkY, Crosshair.X, Crosshair.Y);
530 /* fall through to move the text as a box outline */
531 }
532 case TEXT_TYPE:
533 {
534 TextType *text = (TextType *) Crosshair.AttachedObject.Ptr2;
535 BoxType *box = &text->BoundingBox;
536 gui->graphics->draw_rect (gc,
537 box->X1 + dx,
538 box->Y1 + dy, box->X2 + dx, box->Y2 + dy);
539 break;
540 }
541
542 /* pin/pad movements result in moving an element */
543 case PAD_TYPE:
544 case PIN_TYPE:
545 case ELEMENT_TYPE:
546 XORDrawElement (gc, (ElementType *) Crosshair.AttachedObject.Ptr2, dx, dy);
547 break;
548 }
549
550 /* draw the attached rubberband lines too */
551 i = Crosshair.AttachedObject.RubberbandN;
552 ptr = Crosshair.AttachedObject.Rubberband;
553 while (i)
554 {
555 PointType *point1, *point2;
556
557 if (TEST_FLAG (VIAFLAG, ptr->Line))
558 {
559 /* this is a rat going to a polygon. do not draw for rubberband */;
560 }
561 else if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
562 {
563 /* 'point1' is always the fix-point */
564 if (ptr->MovedPoint == &ptr->Line->Point1)
565 {
566 point1 = &ptr->Line->Point2;
567 point2 = &ptr->Line->Point1;
568 }
569 else
570 {
571 point1 = &ptr->Line->Point1;
572 point2 = &ptr->Line->Point2;
573 }
574 XORDrawAttachedLine (gc, point1->X, point1->Y,
575 point2->X + dx, point2->Y + dy,
576 ptr->Line->Thickness);
577 }
578 else if (ptr->MovedPoint == &ptr->Line->Point1)
579 XORDrawAttachedLine (gc,
580 ptr->Line->Point1.X + dx,
581 ptr->Line->Point1.Y + dy,
582 ptr->Line->Point2.X + dx,
583 ptr->Line->Point2.Y + dy, ptr->Line->Thickness);
584
585 ptr++;
586 i--;
587 }
588 }
589
590 /*!
591 * \brief Draws additional stuff that follows the crosshair.
592 */
593 void
DrawAttached(hidGC gc)594 DrawAttached (hidGC gc)
595 {
596 gui->graphics->set_color (gc, Settings.CrosshairColor);
597 gui->graphics->set_draw_xor (gc, 1);
598 gui->graphics->set_line_cap (gc, Trace_Cap);
599 gui->graphics->set_line_width (gc, 1);
600
601 switch (Settings.Mode)
602 {
603 case VIA_MODE:
604 {
605 /* Make a dummy via structure to draw from */
606 PinType via;
607 via.X = Crosshair.X;
608 via.Y = Crosshair.Y;
609 via.Thickness = Settings.ViaThickness;
610 via.Clearance = 2 * Settings.Keepaway;
611 via.DrillingHole = Settings.ViaDrillingHole;
612 via.Mask = 0;
613 via.Flags = NoFlags ();
614
615 gui->graphics->thindraw_pcb_pv (gc, gc, &via, true, false);
616
617 if (TEST_FLAG (SHOWDRCFLAG, PCB))
618 {
619 Coord mask_r = Settings.ViaThickness / 2 + PCB->Bloat;
620 gui->graphics->set_color (gc, Settings.CrossColor);
621 gui->graphics->set_line_cap (gc, Round_Cap);
622 gui->graphics->set_line_width (gc, 0);
623 gui->graphics->draw_arc (gc, via.X, via.Y, mask_r, mask_r, 0, 360);
624 gui->graphics->set_color (gc, Settings.CrosshairColor);
625 }
626 break;
627 }
628
629 /* the attached line is used by both LINEMODE, POLYGON_MODE and POLYGONHOLE_MODE*/
630 case POLYGON_MODE:
631 case POLYGONHOLE_MODE:
632 /* draw only if starting point is set */
633 if (Crosshair.AttachedLine.State != STATE_FIRST)
634 gui->graphics->draw_line (gc,
635 Crosshair.AttachedLine.Point1.X,
636 Crosshair.AttachedLine.Point1.Y,
637 Crosshair.AttachedLine.Point2.X,
638 Crosshair.AttachedLine.Point2.Y);
639
640 /* draw attached polygon only if in POLYGON_MODE or POLYGONHOLE_MODE */
641 if (Crosshair.AttachedPolygon.PointN > 1)
642 {
643 XORPolygon (gc, &Crosshair.AttachedPolygon, 0, 0, 1);
644 }
645 break;
646
647 case ARC_MODE:
648 if (Crosshair.AttachedBox.State != STATE_FIRST)
649 {
650 XORDrawAttachedArc (gc, Settings.LineThickness);
651 if (TEST_FLAG (SHOWDRCFLAG, PCB))
652 {
653 gui->graphics->set_color (gc, Settings.CrossColor);
654 XORDrawAttachedArc (gc, Settings.LineThickness + 2 * PCB->Bloat);
655 gui->graphics->set_color (gc, Settings.CrosshairColor);
656 }
657
658 }
659 break;
660
661 case LINE_MODE:
662 /* draw only if starting point exists and the line has length */
663 if (Crosshair.AttachedLine.State != STATE_FIRST &&
664 Crosshair.AttachedLine.draw)
665 {
666 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
667 Crosshair.AttachedLine.Point1.Y,
668 Crosshair.AttachedLine.Point2.X,
669 Crosshair.AttachedLine.Point2.Y,
670 PCB->RatDraw ? 10 : Settings.LineThickness);
671 /* draw two lines ? */
672 if (PCB->Clipping)
673 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
674 Crosshair.AttachedLine.Point2.Y,
675 Crosshair.X, Crosshair.Y,
676 PCB->RatDraw ? 10 : Settings.LineThickness);
677 if (TEST_FLAG (SHOWDRCFLAG, PCB))
678 {
679 gui->graphics->set_color (gc, Settings.CrossColor);
680 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
681 Crosshair.AttachedLine.Point1.Y,
682 Crosshair.AttachedLine.Point2.X,
683 Crosshair.AttachedLine.Point2.Y,
684 PCB->RatDraw ? 10 : Settings.LineThickness
685 + 2 * PCB->Bloat);
686 if (PCB->Clipping)
687 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
688 Crosshair.AttachedLine.Point2.Y,
689 Crosshair.X, Crosshair.Y,
690 PCB->RatDraw ? 10 : Settings.
691 LineThickness + 2 * PCB->Bloat);
692 gui->graphics->set_color (gc, Settings.CrosshairColor);
693 }
694 }
695 break;
696
697 case PASTEBUFFER_MODE:
698 XORDrawBuffer (gc, PASTEBUFFER);
699 break;
700
701 case COPY_MODE:
702 case MOVE_MODE:
703 XORDrawMoveOrCopyObject (gc);
704 break;
705
706 case INSERTPOINT_MODE:
707 XORDrawInsertPointObject (gc);
708 break;
709 }
710
711 /* an attached box does not depend on a special mode */
712 if (Crosshair.AttachedBox.State == STATE_SECOND ||
713 Crosshair.AttachedBox.State == STATE_THIRD)
714 {
715 Coord x1, y1, x2, y2;
716
717 x1 = Crosshair.AttachedBox.Point1.X;
718 y1 = Crosshair.AttachedBox.Point1.Y;
719 x2 = Crosshair.AttachedBox.Point2.X;
720 y2 = Crosshair.AttachedBox.Point2.Y;
721 gui->graphics->draw_rect (gc, x1, y1, x2, y2);
722 }
723 }
724
725
726 /*!
727 * \brief Draw the marker position.
728 */
729 void
DrawMark(hidGC gc)730 DrawMark (hidGC gc)
731 {
732 gui->graphics->set_color (gc, Settings.CrosshairColor);
733 gui->graphics->set_draw_xor (gc, 1);
734 gui->graphics->set_line_cap (gc, Trace_Cap);
735 gui->graphics->set_line_width (gc, 1);
736
737 /* Mark is not drawn when it is not set */
738 if (!Marked.status)
739 return;
740
741 gui->graphics->draw_line (gc,
742 Marked.X - MARK_SIZE,
743 Marked.Y - MARK_SIZE,
744 Marked.X + MARK_SIZE, Marked.Y + MARK_SIZE);
745 gui->graphics->draw_line (gc,
746 Marked.X + MARK_SIZE,
747 Marked.Y - MARK_SIZE,
748 Marked.X - MARK_SIZE, Marked.Y + MARK_SIZE);
749 }
750
751 /*!
752 * \brief Returns the nearest grid-point to the given Coord.
753 */
754 Coord
GridFit(Coord x,Coord grid_spacing,Coord grid_offset)755 GridFit (Coord x, Coord grid_spacing, Coord grid_offset)
756 {
757 x -= grid_offset;
758 x = grid_spacing * round ((double) x / grid_spacing);
759 x += grid_offset;
760 return x;
761 }
762
763
764 /*!
765 * \brief Notify the GUI that data relating to the crosshair is being
766 * changed.
767 *
768 * The argument passed is false to notify "changes are about to happen",
769 * and true to notify "changes have finished".
770 *
771 * Each call with a 'false' parameter must be matched with a following one
772 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
773 * but might be allowed in the future.
774 *
775 * GUIs should not complain if they receive extra calls with 'true' as parameter.
776 * They should initiate a redraw of the crosshair attached objects - which may
777 * (if necessary) mean repainting the whole screen if the GUI hasn't tracked the
778 * location of existing attached drawing.
779 */
780 void
notify_crosshair_change(bool changes_complete)781 notify_crosshair_change (bool changes_complete)
782 {
783 if (gui->notify_crosshair_change)
784 gui->notify_crosshair_change (changes_complete);
785 }
786
787
788 /*!
789 * \brief Notify the GUI that data relating to the mark is being changed.
790 *
791 * The argument passed is false to notify "changes are about to happen",
792 * and true to notify "changes have finished".
793 *
794 * Each call with a 'false' parameter must be matched with a following one
795 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
796 * but might be allowed in the future.
797 *
798 * GUIs should not complain if they receive extra calls with 'true' as parameter.
799 * They should initiate a redraw of the mark - which may (if necessary) mean
800 * repainting the whole screen if the GUI hasn't tracked the mark's location.
801 */
802 void
notify_mark_change(bool changes_complete)803 notify_mark_change (bool changes_complete)
804 {
805 if (gui->notify_mark_change)
806 gui->notify_mark_change (changes_complete);
807 }
808
809
810 /*!
811 * \brief Convenience for plugins using the old {Hide,Restore}Crosshair
812 * API.
813 *
814 * This links up to notify the GUI of the expected changes using the new APIs.
815 *
816 * Use of this old API is deprecated, as the names don't necessarily reflect
817 * what all GUIs may do in response to the notifications. Keeping these APIs
818 * is aimed at easing transition to the newer API, they will emit a harmless
819 * warning at the time of their first use.
820 *
821 */
822 void
HideCrosshair(void)823 HideCrosshair (void)
824 {
825 static bool warned_old_api = false;
826 if (!warned_old_api)
827 {
828 Message (_("WARNING: A plugin is using the deprecated API HideCrosshair().\n"
829 " This API may be removed in a future release of PCB.\n"));
830 warned_old_api = true;
831 }
832
833 notify_crosshair_change (false);
834 notify_mark_change (false);
835 }
836
837 void
RestoreCrosshair(void)838 RestoreCrosshair (void)
839 {
840 static bool warned_old_api = false;
841 if (!warned_old_api)
842 {
843 Message (_("WARNING: A plugin is using the deprecated API RestoreCrosshair().\n"
844 " This API may be removed in a future release of PCB.\n"));
845 warned_old_api = true;
846 }
847
848 notify_crosshair_change (true);
849 notify_mark_change (true);
850 }
851
852 /*!
853 * \brief Returns the square of the given number.
854 */
855 static double
square(double x)856 square (double x)
857 {
858 return x * x;
859 }
860
861 static double
crosshair_sq_dist(CrosshairType * crosshair,Coord x,Coord y)862 crosshair_sq_dist (CrosshairType *crosshair, Coord x, Coord y)
863 {
864 return square (x - crosshair->X) + square (y - crosshair->Y);
865 }
866
867 struct snap_data {
868 CrosshairType *crosshair;
869 double nearest_sq_dist;
870 bool nearest_is_grid;
871 Coord x, y;
872 };
873
874 /*!
875 * \brief Snap to a given location if it is the closest thing we found
876 * so far.
877 *
878 * If "prefer_to_grid" is set, the passed location will take preference
879 * over a closer grid points we already snapped to UNLESS the user is
880 * pressing the SHIFT key. If the SHIFT key is pressed, the closest object
881 * (including grid points), is always preferred.
882 */
883 static void
check_snap_object(struct snap_data * snap_data,Coord x,Coord y,bool prefer_to_grid)884 check_snap_object (struct snap_data *snap_data, Coord x, Coord y,
885 bool prefer_to_grid)
886 {
887 double sq_dist;
888
889 sq_dist = crosshair_sq_dist (snap_data->crosshair, x, y);
890 if (sq_dist <= snap_data->nearest_sq_dist ||
891 (prefer_to_grid && snap_data->nearest_is_grid && !gui->shift_is_pressed()))
892 {
893 snap_data->x = x;
894 snap_data->y = y;
895 snap_data->nearest_sq_dist = sq_dist;
896 snap_data->nearest_is_grid = false;
897 }
898 }
899
900 static void
check_snap_offgrid_line(struct snap_data * snap_data,Coord nearest_grid_x,Coord nearest_grid_y)901 check_snap_offgrid_line (struct snap_data *snap_data,
902 Coord nearest_grid_x,
903 Coord nearest_grid_y)
904 {
905 void *ptr1, *ptr2, *ptr3;
906 int ans;
907 LineType *line;
908 Coord try_x, try_y;
909 double dx, dy;
910 double dist;
911
912 if (!TEST_FLAG (SNAPPINFLAG, PCB))
913 return;
914
915 /* Code to snap at some sensible point along a line */
916 /* Pick the nearest grid-point in the x or y direction
917 * to align with, then adjust until we hit the line
918 */
919 ans = SearchObjectByLocation (LINE_TYPE, &ptr1, &ptr2, &ptr3,
920 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
921
922
923 if (ans == NO_TYPE)
924 return;
925
926 line = (LineType *)ptr2;
927
928 /* Allow snapping to off-grid lines when drawing new lines (on
929 * the same layer), and when moving a line end-point
930 * (but don't snap to the same line)
931 */
932 if ((Settings.Mode != LINE_MODE || CURRENT != ptr1) &&
933 (Settings.Mode != MOVE_MODE ||
934 Crosshair.AttachedObject.Ptr1 != ptr1 ||
935 Crosshair.AttachedObject.Type != LINEPOINT_TYPE ||
936 Crosshair.AttachedObject.Ptr2 == line))
937 return;
938
939 dx = line->Point2.X - line->Point1.X;
940 dy = line->Point2.Y - line->Point1.Y;
941
942 /* Try snapping along the X axis */
943 if (dy != 0.)
944 {
945 /* Move in the X direction until we hit the line */
946 try_x = (nearest_grid_y - line->Point1.Y) / dy * dx + line->Point1.X;
947 try_y = nearest_grid_y;
948 check_snap_object (snap_data, try_x, try_y, true);
949 }
950
951 /* Try snapping along the Y axis */
952 if (dx != 0.)
953 {
954 try_x = nearest_grid_x;
955 try_y = (nearest_grid_x - line->Point1.X) / dx * dy + line->Point1.Y;
956 check_snap_object (snap_data, try_x, try_y, true);
957 }
958
959 if (dx != dy) /* If line not parallel with dX = dY direction.. */
960 {
961 /* Try snapping diagonally towards the line in the dX = dY direction */
962
963 if (dy == 0)
964 dist = line->Point1.Y - nearest_grid_y;
965 else
966 dist = ((line->Point1.X - nearest_grid_x) -
967 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 - dx / dy);
968
969 try_x = nearest_grid_x + dist;
970 try_y = nearest_grid_y + dist;
971
972 check_snap_object (snap_data, try_x, try_y, true);
973 }
974
975 if (dx != -dy) /* If line not parallel with dX = -dY direction.. */
976 {
977 /* Try snapping diagonally towards the line in the dX = -dY direction */
978
979 if (dy == 0)
980 dist = nearest_grid_y - line->Point1.Y;
981 else
982 dist = ((line->Point1.X - nearest_grid_x) -
983 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 + dx / dy);
984
985 try_x = nearest_grid_x + dist;
986 try_y = nearest_grid_y - dist;
987
988 check_snap_object (snap_data, try_x, try_y, true);
989 }
990 }
991
992 /*!
993 * \brief Recalculates the passed coordinates to fit the current grid
994 * setting.
995 */
996 void
FitCrosshairIntoGrid(Coord X,Coord Y)997 FitCrosshairIntoGrid (Coord X, Coord Y)
998 {
999 Coord nearest_grid_x, nearest_grid_y;
1000 void *ptr1, *ptr2, *ptr3;
1001 struct snap_data snap_data;
1002 int ans;
1003
1004 /* limit the crosshair location to the board area */
1005 Crosshair.X = CLAMP (X, Crosshair.MinX, Crosshair.MaxX);
1006 Crosshair.Y = CLAMP (Y, Crosshair.MinY, Crosshair.MaxY);
1007
1008 /*
1009 * GRID SNAPPING
1010 *
1011 * First figure out where the nearest grid point that would snap to is. Then
1012 * if we don't find an object that we would prefer, snap to the grid point.
1013 *
1014 */
1015
1016 /* if we're drawing rats, don't snap to the grid */
1017 if (PCB->RatDraw)
1018 {
1019 /* set the "nearest grid" somewhere off the board so that everything else
1020 is closer */
1021 nearest_grid_x = -MIL_TO_COORD (6);
1022 nearest_grid_y = -MIL_TO_COORD (6);
1023 }
1024 else
1025 {
1026 /* not drawing rats */
1027 /* find the closest grid point */
1028 nearest_grid_x = GridFit (Crosshair.X, PCB->Grid, PCB->GridOffsetX);
1029 nearest_grid_y = GridFit (Crosshair.Y, PCB->Grid, PCB->GridOffsetY);
1030
1031 /* if something is marked and we're forcing orthogonal moves... */
1032 if (Marked.status && TEST_FLAG (ORTHOMOVEFLAG, PCB))
1033 {
1034 Coord dx = Crosshair.X - Marked.X;
1035 Coord dy = Crosshair.Y - Marked.Y;
1036 /* restrict motion along the x or y axis
1037 this assumes we're always snapped to a grid point... */
1038 if (ABS (dx) > ABS (dy))
1039 nearest_grid_y = Marked.Y;
1040 else
1041 nearest_grid_x = Marked.X;
1042 }
1043
1044 }
1045
1046 /* The snap_data structure contains all of the information about where we're
1047 * going to snap to. */
1048 snap_data.crosshair = &Crosshair;
1049 snap_data.nearest_sq_dist =
1050 crosshair_sq_dist (&Crosshair, nearest_grid_x, nearest_grid_y);
1051 snap_data.nearest_is_grid = true;
1052 snap_data.x = nearest_grid_x;
1053 snap_data.y = nearest_grid_y;
1054
1055 ans = NO_TYPE;
1056 /* if we're not drawing rats, check for elements first */
1057 if (!PCB->RatDraw)
1058 ans = SearchObjectByLocation (ELEMENT_TYPE, &ptr1, &ptr2, &ptr3,
1059 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1060
1061 if (ans & ELEMENT_TYPE)
1062 {
1063 /* if we found an element, check to see if we should snap to it */
1064 ElementType *el = (ElementType *) ptr1;
1065 check_snap_object (&snap_data, el->MarkX, el->MarkY, false);
1066 }
1067
1068 /* try snapping to a pad if we're drawing rats, or pad snapping is turned on */
1069 ans = NO_TYPE;
1070 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
1071 ans = SearchObjectByLocation (PAD_TYPE, &ptr1, &ptr2, &ptr3,
1072 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1073
1074 /* Avoid self-snapping when moving */
1075 if (ans != NO_TYPE &&
1076 Settings.Mode == MOVE_MODE &&
1077 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
1078 ptr1 == Crosshair.AttachedObject.Ptr1)
1079 ans = NO_TYPE;
1080
1081 if (ans != NO_TYPE && /* we found a pad */
1082 ( Settings.Mode == LINE_MODE || /* we're in line drawing mode, or... */
1083 Settings.Mode == ARC_MODE || /* we're in arc drawing mode, or... */
1084 (Settings.Mode == MOVE_MODE && /* we're moving something and ... */
1085 ((Crosshair.AttachedObject.Type == LINEPOINT_TYPE) || /* it's a line */
1086 (Crosshair.AttachedObject.Type == ARCPOINT_TYPE))))) /* it's an arc */
1087 {
1088 /* we found a pad and we're drawing or moving a line */
1089 PadType *pad = (PadType *) ptr2;
1090 LayerType *desired_layer;
1091 Cardinal desired_group;
1092 Cardinal bottom_group, top_group;
1093 int found_our_layer = false;
1094
1095 /* the layer we'd like to snap to, start with the current layer */
1096 desired_layer = CURRENT;
1097 if (Settings.Mode == MOVE_MODE &&
1098 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)
1099 {
1100 /* if we're moving something, the desired layer is the layer it's on */
1101 desired_layer = (LayerType *)Crosshair.AttachedObject.Ptr1;
1102 }
1103
1104 /* pads must be on the top or bottom sides.
1105 * find layer groups of the top and bottom sides */
1106 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
1107 bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
1108 desired_group = TEST_FLAG (ONSOLDERFLAG, pad) ? bottom_group : top_group;
1109
1110 GROUP_LOOP (PCB->Data, desired_group);
1111 {
1112 if (layer == desired_layer)
1113 {
1114 /* the layer the pad is on is the same as the layer of the line
1115 * that we're moving. */
1116 found_our_layer = true;
1117 break;
1118 }
1119 }
1120 END_LOOP;
1121
1122 if (found_our_layer == false)
1123 /* different layers, so don't snap */
1124 ans = NO_TYPE;
1125 }
1126
1127 if (ans != NO_TYPE)
1128 {
1129 /* we found a pad we want to snap to, check to see if we should */
1130 PadType *pad = (PadType *)ptr2;
1131 check_snap_object (&snap_data, pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2,
1132 pad->Point1.Y + (pad->Point2.Y - pad->Point1.Y) / 2,
1133 true);
1134 }
1135
1136 /* try snapping to a pin
1137 * similar to snapping to a pad, but without the layer restriction */
1138 ans = NO_TYPE;
1139 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
1140 ans = SearchObjectByLocation (PIN_TYPE, &ptr1, &ptr2, &ptr3,
1141 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1142
1143 /* Avoid self-snapping when moving */
1144 if (ans != NO_TYPE &&
1145 Settings.Mode == MOVE_MODE &&
1146 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
1147 ptr1 == Crosshair.AttachedObject.Ptr1)
1148 ans = NO_TYPE;
1149
1150 if (ans != NO_TYPE)
1151 {
1152 /* we found a pin, try to snap to it */
1153 PinType *pin = (PinType *)ptr2;
1154 check_snap_object (&snap_data, pin->X, pin->Y, true);
1155 }
1156
1157 /* if snapping to pins and pads is turned on, try snapping to vias */
1158 ans = NO_TYPE;
1159 if (TEST_FLAG (SNAPPINFLAG, PCB))
1160 ans = SearchObjectByLocation (VIA_TYPE, &ptr1, &ptr2, &ptr3,
1161 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1162
1163 /* Avoid snapping vias to any other vias */
1164 if (Settings.Mode == MOVE_MODE &&
1165 Crosshair.AttachedObject.Type == VIA_TYPE &&
1166 (ans & PIN_TYPES))
1167 ans = NO_TYPE;
1168
1169 if (ans != NO_TYPE)
1170 {
1171 /* found a via, try snapping to it */
1172 PinType *pin = (PinType *)ptr2;
1173 check_snap_object (&snap_data, pin->X, pin->Y, true);
1174 }
1175
1176 /* try snapping to the end points of lines and arcs */
1177 ans = NO_TYPE;
1178 if (TEST_FLAG (SNAPPINFLAG, PCB))
1179 ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
1180 &ptr1, &ptr2, &ptr3,
1181 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1182
1183 if (ans != NO_TYPE)
1184 {
1185 /* found an end point, try to snap to it */
1186 PointType *pnt = (PointType *)ptr3;
1187 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1188 }
1189
1190 /* try snapping to a point on a line that's not on the grid */
1191 check_snap_offgrid_line (&snap_data, nearest_grid_x, nearest_grid_y);
1192
1193 /* try snapping to a point defining a polygon */
1194 ans = NO_TYPE;
1195 if (TEST_FLAG (SNAPPINFLAG, PCB))
1196 ans = SearchObjectByLocation (POLYGONPOINT_TYPE, &ptr1, &ptr2, &ptr3,
1197 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1198
1199 if (ans != NO_TYPE)
1200 {
1201 /* found a polygon point, try snapping to it */
1202 PointType *pnt = (PointType *)ptr3;
1203 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1204 }
1205
1206 /* if snap_data.[x,y] are >= 0, then we found something,
1207 * so move the crosshair */
1208 if (snap_data.x >= 0 && snap_data.y >= 0)
1209 {
1210 Crosshair.X = snap_data.x;
1211 Crosshair.Y = snap_data.y;
1212 }
1213
1214 /* If we're in arrow mode and we're over the end point of a line,
1215 * change the cursor to the "draped box" to indicate that clicking would
1216 * grab the line endpoint */
1217 if (Settings.Mode == ARROW_MODE)
1218 {
1219 ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
1220 &ptr1, &ptr2, &ptr3,
1221 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1222 if (ans == NO_TYPE)
1223 hid_action("PointCursor");
1224 else if (!TEST_FLAG(SELECTEDFLAG, (LineType *)ptr2))
1225 hid_actionl("PointCursor","True", NULL);
1226 }
1227
1228 if (Settings.Mode == LINE_MODE
1229 && Crosshair.AttachedLine.State != STATE_FIRST
1230 && TEST_FLAG (AUTODRCFLAG, PCB))
1231 EnforceLineDRC ();
1232
1233 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_DO_NOTHING);
1234 }
1235
1236 /*!
1237 * \brief Move crosshair absolute.
1238 *
1239 * \return true if the crosshair was moved from its existing position.
1240 */
1241 bool
MoveCrosshairAbsolute(Coord X,Coord Y)1242 MoveCrosshairAbsolute (Coord X, Coord Y)
1243 {
1244 Coord old_x = Crosshair.X;
1245 Coord old_y = Crosshair.Y;
1246
1247 FitCrosshairIntoGrid (X, Y);
1248
1249 if (Crosshair.X != old_x || Crosshair.Y != old_y)
1250 {
1251 Coord new_x = Crosshair.X;
1252 Coord new_y = Crosshair.Y;
1253
1254 /* back up to old position to notify the GUI
1255 * (which might want to erase the old crosshair) */
1256 Crosshair.X = old_x;
1257 Crosshair.Y = old_y;
1258 notify_crosshair_change (false); /* Our caller notifies when it has done */
1259
1260 /* now move forward again */
1261 Crosshair.X = new_x;
1262 Crosshair.Y = new_y;
1263 return true;
1264 }
1265 return false;
1266 }
1267
1268 /*!
1269 * \brief Update the valid range for the crosshair cursor
1270 */
1271
1272 void
crosshair_update_range(void)1273 crosshair_update_range(void)
1274 {
1275 Coord xmin, xmax, ymin, ymax;
1276 BoxType *box;
1277
1278 /* limit the crosshair to being on the working area */
1279 xmin = 0; xmax = PCB->MaxWidth;
1280 ymin = 0; ymax = PCB->MaxHeight;
1281
1282 /* limit the crosshair so attached objects stay on the working area */
1283 /* note: There are presently two ways this is done in the code, the first uses
1284 * the pastebuffer bounding box, the second uses the attached object
1285 * bounding box.
1286 */
1287 if (Settings.Mode == PASTEBUFFER_MODE) {
1288 SetBufferBoundingBox(PASTEBUFFER);
1289 xmin = MAX(xmin, PASTEBUFFER->X - PASTEBUFFER->BoundingBox.X1);
1290 ymin = MAX(ymin, PASTEBUFFER->Y - PASTEBUFFER->BoundingBox.Y1);
1291 xmax = MIN(xmax, PCB->MaxWidth - (PASTEBUFFER->BoundingBox.X2 - PASTEBUFFER->X));
1292 ymax = MIN(ymax, PCB->MaxHeight - (PASTEBUFFER->BoundingBox.Y2 - PASTEBUFFER->Y));
1293 }
1294
1295 /* if there's an attached object, */
1296 if (Crosshair.AttachedObject.Type != NO_TYPE) {
1297 box = GetObjectBoundingBox (Crosshair.AttachedObject.Type,
1298 Crosshair.AttachedObject.Ptr1,
1299 Crosshair.AttachedObject.Ptr2,
1300 Crosshair.AttachedObject.Ptr3);
1301 xmin = MAX(xmin, Crosshair.AttachedObject.X - box->X1);
1302 ymin = MAX(ymin, Crosshair.AttachedObject.Y - box->Y1);
1303 xmax = MIN(xmax, PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X));
1304 ymax = MIN(ymax, PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
1305 }
1306
1307 SetCrosshairRange(xmin, ymin, xmax, ymax);
1308
1309 }
1310
1311 /*!
1312 * \brief Sets the valid range for the crosshair cursor.
1313 */
1314 void
SetCrosshairRange(Coord MinX,Coord MinY,Coord MaxX,Coord MaxY)1315 SetCrosshairRange (Coord MinX, Coord MinY, Coord MaxX, Coord MaxY)
1316 {
1317 Crosshair.MinX = MAX (0, MinX);
1318 Crosshair.MinY = MAX (0, MinY);
1319 Crosshair.MaxX = MIN (PCB->MaxWidth, MaxX);
1320 Crosshair.MaxY = MIN (PCB->MaxHeight, MaxY);
1321
1322 /* force update of position */
1323 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
1324 }
1325
1326 /*!
1327 * \brief Initializes crosshair stuff.
1328 *
1329 * Clears the struct, allocates to graphical contexts.
1330 */
1331 void
InitCrosshair(void)1332 InitCrosshair (void)
1333 {
1334 /* set initial shape */
1335 Crosshair.shape = Basic_Crosshair_Shape;
1336
1337 /* set default limits */
1338 Crosshair.MinX = Crosshair.MinY = 0;
1339 Crosshair.MaxX = PCB->MaxWidth;
1340 Crosshair.MaxY = PCB->MaxHeight;
1341
1342 /* clear the mark */
1343 Marked.status = false;
1344 }
1345
1346 /*!
1347 * \brief Exits crosshair routines, release GCs.
1348 */
1349 void
DestroyCrosshair(void)1350 DestroyCrosshair (void)
1351 {
1352 FreePolygonMemory (&Crosshair.AttachedPolygon);
1353 }
1354