Fix distribution of gnet-pcbfwd.scm in the dist tarball
[geda-pcb/gde.git] / src / line.c
blobff2c8848a4d18d4ecbddc72e339e38d90b668bda
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 #define NUDGE (TO_PCB (6))
36 #include <math.h>
37 #include <setjmp.h>
38 #include <stdlib.h>
41 #include "global.h"
42 #include "data.h"
43 #include "crosshair.h"
44 #include "find.h"
45 #include "line.h"
46 #include "misc.h"
47 #include "rtree.h"
49 #ifdef HAVE_LIBDMALLOC
50 #include <dmalloc.h>
51 #endif
53 RCSID ("$Id$");
55 static float drc_lines (PointTypePtr end, Boolean way);
57 /* ---------------------------------------------------------------------------
58 * Adjust the attached line to 45 degrees if necessary
60 void
61 AdjustAttachedLine (void)
63 AttachedLineTypePtr line = &Crosshair.AttachedLine;
65 /* I need at least one point */
66 if (line->State == STATE_FIRST)
67 return;
68 /* don't draw outline when ctrl key is pressed */
69 if (Settings.Mode == LINE_MODE && gui->control_is_pressed ())
71 line->draw = False;
72 return;
74 else
75 line->draw = True;
76 /* no 45 degree lines required */
77 if (PCB->RatDraw || TEST_FLAG (ALLDIRECTIONFLAG, PCB))
79 line->Point2.X = Crosshair.X;
80 line->Point2.Y = Crosshair.Y;
81 return;
83 FortyFiveLine (line);
86 /* ---------------------------------------------------------------------------
87 * makes the attached line fit into a 45 degree direction
89 * directions:
91 * 0
92 * 7 1
93 * 6 2
94 * 5 3
95 * 4
97 void
98 FortyFiveLine (AttachedLineTypePtr Line)
100 LocationType dx, dy, min;
101 BYTE direction = 0;
102 float m;
104 /* first calculate direction of line */
105 dx = Crosshair.X - Line->Point1.X;
106 dy = Crosshair.Y - Line->Point1.Y;
108 if (!dx)
110 if (!dy)
111 /* zero length line, don't draw anything */
112 return;
113 else
114 direction = dy > 0 ? 0 : 4;
116 else
118 m = (float) dy / (float) dx;
119 direction = 2;
120 if (m > TAN_30_DEGREE)
121 direction = m > TAN_60_DEGREE ? 0 : 1;
122 else if (m < -TAN_30_DEGREE)
123 direction = m < -TAN_60_DEGREE ? 0 : 3;
125 if (dx < 0)
126 direction += 4;
128 dx = abs (dx);
129 dy = abs (dy);
130 min = MIN (dx, dy);
132 /* now set up the second pair of coordinates */
133 switch (direction)
135 case 0:
136 case 4:
137 Line->Point2.X = Line->Point1.X;
138 Line->Point2.Y = Crosshair.Y;
139 break;
141 case 2:
142 case 6:
143 Line->Point2.X = Crosshair.X;
144 Line->Point2.Y = Line->Point1.Y;
145 break;
147 case 1:
148 Line->Point2.X = Line->Point1.X + min;
149 Line->Point2.Y = Line->Point1.Y + min;
150 break;
152 case 3:
153 Line->Point2.X = Line->Point1.X + min;
154 Line->Point2.Y = Line->Point1.Y - min;
155 break;
157 case 5:
158 Line->Point2.X = Line->Point1.X - min;
159 Line->Point2.Y = Line->Point1.Y - min;
160 break;
162 case 7:
163 Line->Point2.X = Line->Point1.X - min;
164 Line->Point2.Y = Line->Point1.Y + min;
165 break;
169 /* ---------------------------------------------------------------------------
170 * adjusts the insert lines to make them 45 degrees as necessary
172 void
173 AdjustTwoLine (int way)
175 LocationType dx, dy;
176 AttachedLineTypePtr line = &Crosshair.AttachedLine;
178 if (Crosshair.AttachedLine.State == STATE_FIRST)
179 return;
180 /* don't draw outline when ctrl key is pressed */
181 if (gui->control_is_pressed ())
183 line->draw = False;
184 return;
186 else
187 line->draw = True;
188 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
190 line->Point2.X = Crosshair.X;
191 line->Point2.Y = Crosshair.Y;
192 return;
194 /* swap the modes if shift is held down */
195 if (gui->shift_is_pressed ())
196 way = !way;
197 dx = Crosshair.X - line->Point1.X;
198 dy = Crosshair.Y - line->Point1.Y;
199 if (!way)
201 if (abs (dx) > abs (dy))
203 line->Point2.X = Crosshair.X - SGN (dx) * abs (dy);
204 line->Point2.Y = line->Point1.Y;
206 else
208 line->Point2.X = line->Point1.X;
209 line->Point2.Y = Crosshair.Y - SGN (dy) * abs (dx);
212 else
214 if (abs (dx) > abs (dy))
216 line->Point2.X = line->Point1.X + SGN (dx) * abs (dy);
217 line->Point2.Y = Crosshair.Y;
219 else
221 line->Point2.X = Crosshair.X;
222 line->Point2.Y = line->Point1.Y + SGN (dy) * abs (dx);;
227 struct drc_info
229 LineTypePtr line;
230 Boolean solder;
231 jmp_buf env;
234 static int
235 drcVia_callback (const BoxType * b, void *cl)
237 PinTypePtr via = (PinTypePtr) b;
238 struct drc_info *i = (struct drc_info *) cl;
240 if (!TEST_FLAG (FOUNDFLAG, via) && PinLineIntersect (via, i->line))
241 longjmp (i->env, 1);
242 return 1;
245 static int
246 drcPad_callback (const BoxType * b, void *cl)
248 PadTypePtr pad = (PadTypePtr) b;
249 struct drc_info *i = (struct drc_info *) cl;
251 if (TEST_FLAG (ONSOLDERFLAG, pad) == i->solder &&
252 !TEST_FLAG (FOUNDFLAG, pad) && LinePadIntersect (i->line, pad))
253 longjmp (i->env, 1);
254 return 1;
257 static int
258 drcLine_callback (const BoxType * b, void *cl)
260 LineTypePtr line = (LineTypePtr) b;
261 struct drc_info *i = (struct drc_info *) cl;
263 if (!TEST_FLAG (FOUNDFLAG, line) && LineLineIntersect (line, i->line))
264 longjmp (i->env, 1);
265 return 1;
268 static int
269 drcArc_callback (const BoxType * b, void *cl)
271 ArcTypePtr arc = (ArcTypePtr) b;
272 struct drc_info *i = (struct drc_info *) cl;
274 if (!TEST_FLAG (FOUNDFLAG, arc) && LineArcIntersect (i->line, arc))
275 longjmp (i->env, 1);
276 return 1;
279 /* drc_lines() checks for intersectors against two lines and
280 * adjusts the end point until there is no intersection or
281 * it winds up back at the start. If way is false it checks
282 * straight start, 45 end lines, otherwise it checks 45 start,
283 * straight end.
285 * It returns the straight-line length of the best answer, and
286 * changes the position of the input point to the best answer.
289 static float
290 drc_lines (PointTypePtr end, Boolean way)
292 float f, s, f2, s2, len, best;
293 LocationType dx, dy, temp, last, length;
294 LocationType temp2, last2, length2;
295 LineType line1, line2;
296 Cardinal group, comp;
297 struct drc_info info;
298 Boolean two_lines, x_is_long, blocker;
299 PointType ans;
301 f = 1.0;
302 s = 0.5;
303 last = -1;
304 line1.Flags = line2.Flags = NoFlags ();
305 line1.Thickness = Settings.LineThickness + 2 * (PCB->Bloat + 1);
306 line2.Thickness = line1.Thickness;
307 line1.Clearance = line2.Clearance = 0;
308 line1.Point1.X = Crosshair.AttachedLine.Point1.X;
309 line1.Point1.Y = Crosshair.AttachedLine.Point1.Y;
310 dy = end->Y - line1.Point1.Y;
311 dx = end->X - line1.Point1.X;
312 if (abs (dx) > abs (dy))
314 x_is_long = True;
315 length = abs (dx);
317 else
319 x_is_long = False;
320 length = abs (dy);
322 group = GetGroupOfLayer (INDEXOFCURRENT);
323 comp = max_layer + 10; /* this out-of-range group might save a call */
324 if (GetLayerGroupNumberByNumber (max_layer + SOLDER_LAYER) == group)
325 info.solder = True;
326 else
328 info.solder = False;
329 comp = GetLayerGroupNumberByNumber (max_layer + COMPONENT_LAYER);
331 temp = length;
332 /* assume the worst */
333 best = 0.0;
334 ans.X = line1.Point1.X;
335 ans.Y = line1.Point1.Y;
336 while (length != last)
338 last = length;
339 if (x_is_long)
341 dx = SGN (dx) * length;
342 dy = end->Y - line1.Point1.Y;
343 length2 = abs (dy);
345 else
347 dy = SGN (dy) * length;
348 dx = end->X - line1.Point1.X;
349 length2 = abs (dx);
351 temp2 = length2;
352 f2 = 1.0;
353 s2 = 0.5;
354 last2 = -1;
355 blocker = True;
356 while (length2 != last2)
358 if (x_is_long)
359 dy = SGN (dy) * length2;
360 else
361 dx = SGN (dx) * length2;
362 two_lines = True;
363 if (abs (dx) > abs (dy) && x_is_long)
365 line1.Point2.X = line1.Point1.X +
366 (way ? SGN (dx) * abs (dy) : dx - SGN (dx) * abs (dy));
367 line1.Point2.Y = line1.Point1.Y + (way ? dy : 0);
369 else if (abs (dy) >= abs (dx) && !x_is_long)
371 line1.Point2.X = line1.Point1.X + (way ? dx : 0);
372 line1.Point2.Y = line1.Point1.Y +
373 (way ? SGN (dy) * abs (dx) : dy - SGN (dy) * abs (dx));
375 else if (x_is_long)
377 /* we've changed which axis is long, so only do one line */
378 line1.Point2.X = line1.Point1.X + dx;
379 line1.Point2.Y =
380 line1.Point1.Y + (way ? SGN (dy) * abs (dx) : 0);
381 two_lines = False;
383 else
385 /* we've changed which axis is long, so only do one line */
386 line1.Point2.Y = line1.Point1.Y + dy;
387 line1.Point2.X =
388 line1.Point1.X + (way ? SGN (dx) * abs (dy) : 0);
389 two_lines = False;
391 line2.Point1.X = line1.Point2.X;
392 line2.Point1.Y = line1.Point2.Y;
393 if (!two_lines)
395 line2.Point2.Y = line1.Point2.Y;
396 line2.Point2.X = line1.Point2.X;
398 else
400 line2.Point2.X = line1.Point1.X + dx;
401 line2.Point2.Y = line1.Point1.Y + dy;
403 SetLineBoundingBox (&line1);
404 SetLineBoundingBox (&line2);
405 last2 = length2;
406 if (setjmp (info.env) == 0)
408 info.line = &line1;
409 r_search (PCB->Data->via_tree, &line1.BoundingBox, NULL,
410 drcVia_callback, &info);
411 r_search (PCB->Data->pin_tree, &line1.BoundingBox, NULL,
412 drcVia_callback, &info);
413 if (info.solder || comp == group)
414 r_search (PCB->Data->pad_tree, &line1.BoundingBox, NULL,
415 drcPad_callback, &info);
416 if (two_lines)
418 info.line = &line2;
419 r_search (PCB->Data->via_tree, &line2.BoundingBox, NULL,
420 drcVia_callback, &info);
421 r_search (PCB->Data->pin_tree, &line2.BoundingBox, NULL,
422 drcVia_callback, &info);
423 if (info.solder || comp == group)
424 r_search (PCB->Data->pad_tree, &line2.BoundingBox, NULL,
425 drcPad_callback, &info);
427 GROUP_LOOP (PCB->Data, group);
429 info.line = &line1;
430 r_search (layer->line_tree, &line1.BoundingBox, NULL,
431 drcLine_callback, &info);
432 r_search (layer->arc_tree, &line1.BoundingBox, NULL,
433 drcArc_callback, &info);
434 if (two_lines)
436 info.line = &line2;
437 r_search (layer->line_tree, &line2.BoundingBox,
438 NULL, drcLine_callback, &info);
439 r_search (layer->arc_tree, &line2.BoundingBox,
440 NULL, drcArc_callback, &info);
443 END_LOOP;
444 /* no intersector! */
445 blocker = False;
446 f2 += s2;
447 len = (line2.Point2.X - line1.Point1.X);
448 len *= len;
449 len += (float) (line2.Point2.Y - line1.Point1.Y) *
450 (line2.Point2.Y - line1.Point1.Y);
451 if (len > best)
453 best = len;
454 ans.X = line2.Point2.X;
455 ans.Y = line2.Point2.Y;
457 #if 0
458 if (f2 > 1.0)
459 f2 = 0.5;
460 #endif
462 else
464 /* bumped into something, back off */
465 f2 -= s2;
467 s2 *= 0.5;
468 length2 = MIN (f2 * temp2, temp2);
470 if (!blocker && ((x_is_long && line2.Point2.X - line1.Point1.X == dx)
471 || (!x_is_long
472 && line2.Point2.Y - line1.Point1.Y == dy)))
473 f += s;
474 else
475 f -= s;
476 s *= 0.5;
477 length = MIN (f * temp, temp);
480 end->X = ans.X;
481 end->Y = ans.Y;
482 return best;
485 void
486 EnforceLineDRC (void)
488 PointType r45, rs;
489 Boolean shift;
490 float r1, r2;
492 if ( gui->mod1_is_pressed() || gui->control_is_pressed () || PCB->RatDraw
493 || INDEXOFCURRENT >= max_layer)
494 return;
495 rs.X = r45.X = Crosshair.X;
496 rs.Y = r45.Y = Crosshair.Y;
497 /* first try starting straight */
498 r1 = drc_lines (&rs, False);
499 /* then try starting at 45 */
500 r2 = drc_lines (&r45, True);
501 shift = gui->shift_is_pressed ();
502 if (XOR (r1 > r2, shift))
504 if (PCB->Clipping)
506 if (shift)
507 PCB->Clipping = 2;
508 else
509 PCB->Clipping = 1;
511 Crosshair.X = rs.X;
512 Crosshair.Y = rs.Y;
514 else
516 if (PCB->Clipping)
518 if (shift)
519 PCB->Clipping = 1;
520 else
521 PCB->Clipping = 2;
523 Crosshair.X = r45.X;
524 Crosshair.Y = r45.Y;