Repopulate route style selector on PCBChanged action
[geda-pcb/whiteaudio.git] / src / line.c
blob22efc2bc2317a5bd300ba5b4ad704cb8d597459c
1 /* $Id$ */
3 /*
4 * COPYRIGHT
6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996 Thomas Nau
8 * Copyright (C) 2004 harry eaton
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 * Contact addresses for paper mail and Email:
25 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
26 * Thomas.Nau@rz.uni-ulm.de
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
34 #include <math.h>
35 #include <setjmp.h>
36 #include <stdlib.h>
39 #include "global.h"
40 #include "data.h"
41 #include "crosshair.h"
42 #include "find.h"
43 #include "line.h"
44 #include "misc.h"
45 #include "rtree.h"
47 #ifdef HAVE_LIBDMALLOC
48 #include <dmalloc.h>
49 #endif
51 RCSID ("$Id$");
53 static double drc_lines (PointTypePtr end, bool way);
55 /* ---------------------------------------------------------------------------
56 * Adjust the attached line to 45 degrees if necessary
58 void
59 AdjustAttachedLine (void)
61 AttachedLineTypePtr line = &Crosshair.AttachedLine;
63 /* I need at least one point */
64 if (line->State == STATE_FIRST)
65 return;
66 /* don't draw outline when ctrl key is pressed */
67 if (Settings.Mode == LINE_MODE && gui->control_is_pressed ())
69 line->draw = false;
70 return;
72 else
73 line->draw = true;
74 /* no 45 degree lines required */
75 if (PCB->RatDraw || TEST_FLAG (ALLDIRECTIONFLAG, PCB))
77 line->Point2.X = Crosshair.X;
78 line->Point2.Y = Crosshair.Y;
79 return;
81 FortyFiveLine (line);
84 /* ---------------------------------------------------------------------------
85 * makes the attached line fit into a 45 degree direction
87 * directions:
89 * 0
90 * 7 1
91 * 6 2
92 * 5 3
93 * 4
95 void
96 FortyFiveLine (AttachedLineTypePtr Line)
98 Coord dx, dy, min;
99 unsigned direction = 0;
100 double m;
102 /* first calculate direction of line */
103 dx = Crosshair.X - Line->Point1.X;
104 dy = Crosshair.Y - Line->Point1.Y;
106 if (!dx)
108 if (!dy)
109 /* zero length line, don't draw anything */
110 return;
111 else
112 direction = dy > 0 ? 0 : 4;
114 else
116 m = (double) dy / dx;
117 direction = 2;
118 if (m > TAN_30_DEGREE)
119 direction = m > TAN_60_DEGREE ? 0 : 1;
120 else if (m < -TAN_30_DEGREE)
121 direction = m < -TAN_60_DEGREE ? 0 : 3;
123 if (dx < 0)
124 direction += 4;
126 dx = abs (dx);
127 dy = abs (dy);
128 min = MIN (dx, dy);
130 /* now set up the second pair of coordinates */
131 switch (direction)
133 case 0:
134 case 4:
135 Line->Point2.X = Line->Point1.X;
136 Line->Point2.Y = Crosshair.Y;
137 break;
139 case 2:
140 case 6:
141 Line->Point2.X = Crosshair.X;
142 Line->Point2.Y = Line->Point1.Y;
143 break;
145 case 1:
146 Line->Point2.X = Line->Point1.X + min;
147 Line->Point2.Y = Line->Point1.Y + min;
148 break;
150 case 3:
151 Line->Point2.X = Line->Point1.X + min;
152 Line->Point2.Y = Line->Point1.Y - min;
153 break;
155 case 5:
156 Line->Point2.X = Line->Point1.X - min;
157 Line->Point2.Y = Line->Point1.Y - min;
158 break;
160 case 7:
161 Line->Point2.X = Line->Point1.X - min;
162 Line->Point2.Y = Line->Point1.Y + min;
163 break;
167 /* ---------------------------------------------------------------------------
168 * adjusts the insert lines to make them 45 degrees as necessary
170 void
171 AdjustTwoLine (bool way)
173 Coord dx, dy;
174 AttachedLineTypePtr line = &Crosshair.AttachedLine;
176 if (Crosshair.AttachedLine.State == STATE_FIRST)
177 return;
178 /* don't draw outline when ctrl key is pressed */
179 if (gui->control_is_pressed ())
181 line->draw = false;
182 return;
184 else
185 line->draw = true;
186 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
188 line->Point2.X = Crosshair.X;
189 line->Point2.Y = Crosshair.Y;
190 return;
192 /* swap the modes if shift is held down */
193 if (gui->shift_is_pressed ())
194 way = !way;
195 dx = Crosshair.X - line->Point1.X;
196 dy = Crosshair.Y - line->Point1.Y;
197 if (!way)
199 if (abs (dx) > abs (dy))
201 line->Point2.X = Crosshair.X - SGN (dx) * abs (dy);
202 line->Point2.Y = line->Point1.Y;
204 else
206 line->Point2.X = line->Point1.X;
207 line->Point2.Y = Crosshair.Y - SGN (dy) * abs (dx);
210 else
212 if (abs (dx) > abs (dy))
214 line->Point2.X = line->Point1.X + SGN (dx) * abs (dy);
215 line->Point2.Y = Crosshair.Y;
217 else
219 line->Point2.X = Crosshair.X;
220 line->Point2.Y = line->Point1.Y + SGN (dy) * abs (dx);;
225 struct drc_info
227 LineTypePtr line;
228 bool solder;
229 jmp_buf env;
232 static int
233 drcVia_callback (const BoxType * b, void *cl)
235 PinTypePtr via = (PinTypePtr) b;
236 struct drc_info *i = (struct drc_info *) cl;
238 if (!TEST_FLAG (FOUNDFLAG, via) && PinLineIntersect (via, i->line))
239 longjmp (i->env, 1);
240 return 1;
243 static int
244 drcPad_callback (const BoxType * b, void *cl)
246 PadTypePtr pad = (PadTypePtr) b;
247 struct drc_info *i = (struct drc_info *) cl;
249 if (TEST_FLAG (ONSOLDERFLAG, pad) == i->solder &&
250 !TEST_FLAG (FOUNDFLAG, pad) && LinePadIntersect (i->line, pad))
251 longjmp (i->env, 1);
252 return 1;
255 static int
256 drcLine_callback (const BoxType * b, void *cl)
258 LineTypePtr line = (LineTypePtr) b;
259 struct drc_info *i = (struct drc_info *) cl;
261 if (!TEST_FLAG (FOUNDFLAG, line) && LineLineIntersect (line, i->line))
262 longjmp (i->env, 1);
263 return 1;
266 static int
267 drcArc_callback (const BoxType * b, void *cl)
269 ArcTypePtr arc = (ArcTypePtr) b;
270 struct drc_info *i = (struct drc_info *) cl;
272 if (!TEST_FLAG (FOUNDFLAG, arc) && LineArcIntersect (i->line, arc))
273 longjmp (i->env, 1);
274 return 1;
277 /* drc_lines() checks for intersectors against two lines and
278 * adjusts the end point until there is no intersection or
279 * it winds up back at the start. If way is false it checks
280 * straight start, 45 end lines, otherwise it checks 45 start,
281 * straight end.
283 * It returns the straight-line length of the best answer, and
284 * changes the position of the input point to the best answer.
287 static double
288 drc_lines (PointTypePtr end, bool way)
290 double f, s, f2, s2, len, best;
291 Coord dx, dy, temp, last, length;
292 Coord temp2, last2, length2;
293 LineType line1, line2;
294 Cardinal group, comp;
295 struct drc_info info;
296 bool two_lines, x_is_long, blocker;
297 PointType ans;
299 f = 1.0;
300 s = 0.5;
301 last = -1;
302 line1.Flags = line2.Flags = NoFlags ();
303 line1.Thickness = Settings.LineThickness + 2 * (PCB->Bloat + 1);
304 line2.Thickness = line1.Thickness;
305 line1.Clearance = line2.Clearance = 0;
306 line1.Point1.X = Crosshair.AttachedLine.Point1.X;
307 line1.Point1.Y = Crosshair.AttachedLine.Point1.Y;
308 dy = end->Y - line1.Point1.Y;
309 dx = end->X - line1.Point1.X;
310 if (abs (dx) > abs (dy))
312 x_is_long = true;
313 length = abs (dx);
315 else
317 x_is_long = false;
318 length = abs (dy);
320 group = GetGroupOfLayer (INDEXOFCURRENT);
321 comp = max_group + 10; /* this out-of-range group might save a call */
322 if (GetLayerGroupNumberByNumber (solder_silk_layer) == group)
323 info.solder = true;
324 else
326 info.solder = false;
327 comp = GetLayerGroupNumberByNumber (component_silk_layer);
329 temp = length;
330 /* assume the worst */
331 best = 0.0;
332 ans.X = line1.Point1.X;
333 ans.Y = line1.Point1.Y;
334 while (length != last)
336 last = length;
337 if (x_is_long)
339 dx = SGN (dx) * length;
340 dy = end->Y - line1.Point1.Y;
341 length2 = abs (dy);
343 else
345 dy = SGN (dy) * length;
346 dx = end->X - line1.Point1.X;
347 length2 = abs (dx);
349 temp2 = length2;
350 f2 = 1.0;
351 s2 = 0.5;
352 last2 = -1;
353 blocker = true;
354 while (length2 != last2)
356 if (x_is_long)
357 dy = SGN (dy) * length2;
358 else
359 dx = SGN (dx) * length2;
360 two_lines = true;
361 if (abs (dx) > abs (dy) && x_is_long)
363 line1.Point2.X = line1.Point1.X +
364 (way ? SGN (dx) * abs (dy) : dx - SGN (dx) * abs (dy));
365 line1.Point2.Y = line1.Point1.Y + (way ? dy : 0);
367 else if (abs (dy) >= abs (dx) && !x_is_long)
369 line1.Point2.X = line1.Point1.X + (way ? dx : 0);
370 line1.Point2.Y = line1.Point1.Y +
371 (way ? SGN (dy) * abs (dx) : dy - SGN (dy) * abs (dx));
373 else if (x_is_long)
375 /* we've changed which axis is long, so only do one line */
376 line1.Point2.X = line1.Point1.X + dx;
377 line1.Point2.Y =
378 line1.Point1.Y + (way ? SGN (dy) * abs (dx) : 0);
379 two_lines = false;
381 else
383 /* we've changed which axis is long, so only do one line */
384 line1.Point2.Y = line1.Point1.Y + dy;
385 line1.Point2.X =
386 line1.Point1.X + (way ? SGN (dx) * abs (dy) : 0);
387 two_lines = false;
389 line2.Point1.X = line1.Point2.X;
390 line2.Point1.Y = line1.Point2.Y;
391 if (!two_lines)
393 line2.Point2.Y = line1.Point2.Y;
394 line2.Point2.X = line1.Point2.X;
396 else
398 line2.Point2.X = line1.Point1.X + dx;
399 line2.Point2.Y = line1.Point1.Y + dy;
401 SetLineBoundingBox (&line1);
402 SetLineBoundingBox (&line2);
403 last2 = length2;
404 if (setjmp (info.env) == 0)
406 info.line = &line1;
407 r_search (PCB->Data->via_tree, &line1.BoundingBox, NULL,
408 drcVia_callback, &info);
409 r_search (PCB->Data->pin_tree, &line1.BoundingBox, NULL,
410 drcVia_callback, &info);
411 if (info.solder || comp == group)
412 r_search (PCB->Data->pad_tree, &line1.BoundingBox, NULL,
413 drcPad_callback, &info);
414 if (two_lines)
416 info.line = &line2;
417 r_search (PCB->Data->via_tree, &line2.BoundingBox, NULL,
418 drcVia_callback, &info);
419 r_search (PCB->Data->pin_tree, &line2.BoundingBox, NULL,
420 drcVia_callback, &info);
421 if (info.solder || comp == group)
422 r_search (PCB->Data->pad_tree, &line2.BoundingBox, NULL,
423 drcPad_callback, &info);
425 GROUP_LOOP (PCB->Data, group);
427 info.line = &line1;
428 r_search (layer->line_tree, &line1.BoundingBox, NULL,
429 drcLine_callback, &info);
430 r_search (layer->arc_tree, &line1.BoundingBox, NULL,
431 drcArc_callback, &info);
432 if (two_lines)
434 info.line = &line2;
435 r_search (layer->line_tree, &line2.BoundingBox,
436 NULL, drcLine_callback, &info);
437 r_search (layer->arc_tree, &line2.BoundingBox,
438 NULL, drcArc_callback, &info);
441 END_LOOP;
442 /* no intersector! */
443 blocker = false;
444 f2 += s2;
445 len = (line2.Point2.X - line1.Point1.X);
446 len *= len;
447 len += (double) (line2.Point2.Y - line1.Point1.Y) *
448 (line2.Point2.Y - line1.Point1.Y);
449 if (len > best)
451 best = len;
452 ans.X = line2.Point2.X;
453 ans.Y = line2.Point2.Y;
455 #if 0
456 if (f2 > 1.0)
457 f2 = 0.5;
458 #endif
460 else
462 /* bumped into something, back off */
463 f2 -= s2;
465 s2 *= 0.5;
466 length2 = MIN (f2 * temp2, temp2);
468 if (!blocker && ((x_is_long && line2.Point2.X - line1.Point1.X == dx)
469 || (!x_is_long
470 && line2.Point2.Y - line1.Point1.Y == dy)))
471 f += s;
472 else
473 f -= s;
474 s *= 0.5;
475 length = MIN (f * temp, temp);
478 end->X = ans.X;
479 end->Y = ans.Y;
480 return best;
483 void
484 EnforceLineDRC (void)
486 PointType r45, rs;
487 bool shift;
488 double r1, r2;
490 /* Silence a bogus compiler warning by storing this in a variable */
491 int layer_idx = INDEXOFCURRENT;
493 if ( gui->mod1_is_pressed() || gui->control_is_pressed () || PCB->RatDraw
494 || layer_idx >= max_copper_layer)
495 return;
497 rs.X = r45.X = Crosshair.X;
498 rs.Y = r45.Y = Crosshair.Y;
499 /* first try starting straight */
500 r1 = drc_lines (&rs, false);
501 /* then try starting at 45 */
502 r2 = drc_lines (&r45, true);
503 shift = gui->shift_is_pressed ();
504 if (XOR (r1 > r2, shift))
506 if (PCB->Clipping)
508 if (shift)
509 PCB->Clipping = 2;
510 else
511 PCB->Clipping = 1;
513 Crosshair.X = rs.X;
514 Crosshair.Y = rs.Y;
516 else
518 if (PCB->Clipping)
520 if (shift)
521 PCB->Clipping = 1;
522 else
523 PCB->Clipping = 2;
525 Crosshair.X = r45.X;
526 Crosshair.Y = r45.Y;