pcb 4.1.1
An interactive printed circuit board layout editor.

line.c

Go to the documentation of this file.
00001 
00035 #ifdef HAVE_CONFIG_H
00036 #include "config.h"
00037 #endif
00038 
00039 #include <math.h>
00040 #include <setjmp.h>
00041 #include <stdlib.h>
00042 
00043 
00044 #include "global.h"
00045 #include "data.h"
00046 #include "crosshair.h"
00047 #include "find.h"
00048 #include "line.h"
00049 #include "misc.h"
00050 #include "rtree.h"
00051 
00052 #ifdef HAVE_LIBDMALLOC
00053 #include <dmalloc.h>
00054 #endif
00055 
00056 static double drc_lines (PointType *end, bool way);
00057 
00061 void
00062 AdjustAttachedLine (void)
00063 {
00064   AttachedLineType *line = &Crosshair.AttachedLine;
00065 
00066   /* I need at least one point */
00067   if (line->State == STATE_FIRST)
00068     return;
00069   /* don't draw outline when ctrl key is pressed */
00070   if (Settings.Mode == LINE_MODE && gui->control_is_pressed ())
00071     {
00072       line->draw = false;
00073       return;
00074     }
00075   else
00076     line->draw = true;
00077   /* no 45 degree lines required */
00078   if (PCB->RatDraw || TEST_FLAG (ALLDIRECTIONFLAG, PCB))
00079     {
00080       line->Point2.X = Crosshair.X;
00081       line->Point2.Y = Crosshair.Y;
00082       return;
00083     }
00084   FortyFiveLine (line);
00085 }
00086 
00099 void
00100 FortyFiveLine (AttachedLineType *Line)
00101 {
00102   Coord dx, dy, min, max;
00103   unsigned direction = 0;
00104   double m;
00105 
00106   /* first calculate direction of line */
00107   dx = Crosshair.X - Line->Point1.X;
00108   dy = Crosshair.Y - Line->Point1.Y;
00109 
00110   /* zero length line, don't draw anything */
00111   if (dx == 0 && dy == 0)
00112     return;
00113 
00114   if (dx == 0)
00115     direction = dy > 0 ? 0 : 4;
00116   else
00117     {
00118       m = (double)dy / (double)dx;
00119       direction = 2;
00120       if (m > TAN_30_DEGREE)
00121         direction = m > TAN_60_DEGREE ? 0 : 1;
00122       else if (m < -TAN_30_DEGREE)
00123         direction = m < -TAN_60_DEGREE ? 4 : 3;
00124     }
00125   if (dx < 0)
00126     direction += 4;
00127 
00128   dx = abs (dx);
00129   dy = abs (dy);
00130   min = MIN (dx, dy);
00131   max = MAX (dx, dy);
00132 
00133   /* now set up the second pair of coordinates */
00134   switch (direction)
00135     {
00136     case 0:
00137       Line->Point2.X = Line->Point1.X;
00138       Line->Point2.Y = Line->Point1.Y + max;
00139       break;
00140 
00141     case 4:
00142       Line->Point2.X = Line->Point1.X;
00143       Line->Point2.Y = Line->Point1.Y - max;
00144       break;
00145 
00146     case 2:
00147       Line->Point2.X = Line->Point1.X + max;
00148       Line->Point2.Y = Line->Point1.Y;
00149       break;
00150 
00151     case 6:
00152       Line->Point2.X = Line->Point1.X - max;
00153       Line->Point2.Y = Line->Point1.Y;
00154       break;
00155 
00156     case 1:
00157       Line->Point2.X = Line->Point1.X + min;
00158       Line->Point2.Y = Line->Point1.Y + min;
00159       break;
00160 
00161     case 3:
00162       Line->Point2.X = Line->Point1.X + min;
00163       Line->Point2.Y = Line->Point1.Y - min;
00164       break;
00165 
00166     case 5:
00167       Line->Point2.X = Line->Point1.X - min;
00168       Line->Point2.Y = Line->Point1.Y - min;
00169       break;
00170 
00171     case 7:
00172       Line->Point2.X = Line->Point1.X - min;
00173       Line->Point2.Y = Line->Point1.Y + min;
00174       break;
00175     }
00176 }
00177 
00181 void
00182 AdjustTwoLine (bool way)
00183 {
00184   Coord dx, dy;
00185   AttachedLineType *line = &Crosshair.AttachedLine;
00186 
00187   if (Crosshair.AttachedLine.State == STATE_FIRST)
00188     return;
00189   /* don't draw outline when ctrl key is pressed */
00190   if (gui->control_is_pressed ())
00191     {
00192       line->draw = false;
00193       return;
00194     }
00195   else
00196     line->draw = true;
00197   if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
00198     {
00199       line->Point2.X = Crosshair.X;
00200       line->Point2.Y = Crosshair.Y;
00201       return;
00202     }
00203   /* swap the modes if shift is held down */
00204   if (gui->shift_is_pressed ())
00205     way = !way;
00206   dx = Crosshair.X - line->Point1.X;
00207   dy = Crosshair.Y - line->Point1.Y;
00208   if (!way)
00209     {
00210       if (abs (dx) > abs (dy))
00211         {
00212           line->Point2.X = Crosshair.X - SGN (dx) * abs (dy);
00213           line->Point2.Y = line->Point1.Y;
00214         }
00215       else
00216         {
00217           line->Point2.X = line->Point1.X;
00218           line->Point2.Y = Crosshair.Y - SGN (dy) * abs (dx);
00219         }
00220     }
00221   else
00222     {
00223       if (abs (dx) > abs (dy))
00224         {
00225           line->Point2.X = line->Point1.X + SGN (dx) * abs (dy);
00226           line->Point2.Y = Crosshair.Y;
00227         }
00228       else
00229         {
00230           line->Point2.X = Crosshair.X;
00231           line->Point2.Y = line->Point1.Y + SGN (dy) * abs (dx);;
00232         }
00233     }
00234 }
00235 
00236 struct drc_info
00237 {
00238   LineType *line;
00239   bool bottom_side;
00240   bool top_side;
00241   jmp_buf env;
00242 };
00243 
00244 static int
00245 drcVia_callback (const BoxType * b, void *cl)
00246 {
00247   PinType *via = (PinType *) b;
00248   struct drc_info *i = (struct drc_info *) cl;
00249 
00250   if (!TEST_FLAG (FOUNDFLAG, via) && PinLineIntersect (via, i->line))
00251     longjmp (i->env, 1);
00252   return 1;
00253 }
00254 
00255 static int
00256 drcPad_callback (const BoxType * b, void *cl)
00257 {
00258   PadType *pad = (PadType *) b;
00259   struct drc_info *i = (struct drc_info *) cl;
00260 
00261   if (TEST_FLAG (ONSOLDERFLAG, pad) == i->bottom_side &&
00262       !TEST_FLAG (FOUNDFLAG, pad) && LinePadIntersect (i->line, pad))
00263     longjmp (i->env, 1);
00264   return 1;
00265 }
00266 
00267 static int
00268 drcLine_callback (const BoxType * b, void *cl)
00269 {
00270   LineType *line = (LineType *) b;
00271   struct drc_info *i = (struct drc_info *) cl;
00272 
00273   if (!TEST_FLAG (FOUNDFLAG, line) && LineLineIntersect (line, i->line))
00274     longjmp (i->env, 1);
00275   return 1;
00276 }
00277 
00278 static int
00279 drcArc_callback (const BoxType * b, void *cl)
00280 {
00281   ArcType *arc = (ArcType *) b;
00282   struct drc_info *i = (struct drc_info *) cl;
00283 
00284   if (!TEST_FLAG (FOUNDFLAG, arc) && LineArcIntersect (i->line, arc))
00285     longjmp (i->env, 1);
00286   return 1;
00287 }
00288 
00300 static double
00301 drc_lines (PointType *end, bool way)
00302 {
00303   double f, s, f2, s2, len, best;
00304   Coord dx, dy, temp, last, length;
00305   Coord temp2, last2, length2;
00306   LineType line1, line2;
00307   Cardinal group;
00308   struct drc_info info;
00309   bool two_lines, x_is_long, blocker;
00310   PointType ans;
00311 
00312   f = 1.0;
00313   s = 0.5;
00314   last = -1;
00315   line1.Flags = line2.Flags = NoFlags ();
00316   line1.Thickness = Settings.LineThickness + 2 * PCB->Bloat;
00317   line2.Thickness = line1.Thickness;
00318   line1.Clearance = line2.Clearance = 0;
00319   line1.Point1.X = Crosshair.AttachedLine.Point1.X;
00320   line1.Point1.Y = Crosshair.AttachedLine.Point1.Y;
00321   dy = end->Y - line1.Point1.Y;
00322   dx = end->X - line1.Point1.X;
00323   if (abs (dx) > abs (dy))
00324     {
00325       x_is_long = true;
00326       length = abs (dx);
00327     }
00328   else
00329     {
00330       x_is_long = false;
00331       length = abs (dy);
00332     }
00333 
00334   group = GetLayerGroupNumberByNumber (INDEXOFCURRENT);
00335   info.bottom_side = (GetLayerGroupNumberBySide (BOTTOM_SIDE) == group);
00336   info.top_side = (GetLayerGroupNumberBySide (TOP_SIDE) == group);
00337 
00338   temp = length;
00339   /* assume the worst */
00340   best = 0.0;
00341   ans.X = line1.Point1.X;
00342   ans.Y = line1.Point1.Y;
00343   while (length != last)
00344     {
00345       last = length;
00346       if (x_is_long)
00347         {
00348           dx = SGN (dx) * length;
00349           dy = end->Y - line1.Point1.Y;
00350           length2 = abs (dy);
00351         }
00352       else
00353         {
00354           dy = SGN (dy) * length;
00355           dx = end->X - line1.Point1.X;
00356           length2 = abs (dx);
00357         }
00358       temp2 = length2;
00359       f2 = 1.0;
00360       s2 = 0.5;
00361       last2 = -1;
00362       blocker = true;
00363       while (length2 != last2)
00364         {
00365           if (x_is_long)
00366             dy = SGN (dy) * length2;
00367           else
00368             dx = SGN (dx) * length2;
00369           two_lines = true;
00370           if (abs (dx) > abs (dy) && x_is_long)
00371             {
00372               line1.Point2.X = line1.Point1.X +
00373                 (way ? SGN (dx) * abs (dy) : dx - SGN (dx) * abs (dy));
00374               line1.Point2.Y = line1.Point1.Y + (way ? dy : 0);
00375             }
00376           else if (abs (dy) >= abs (dx) && !x_is_long)
00377             {
00378               line1.Point2.X = line1.Point1.X + (way ? dx : 0);
00379               line1.Point2.Y = line1.Point1.Y +
00380                 (way ? SGN (dy) * abs (dx) : dy - SGN (dy) * abs (dx));
00381             }
00382           else if (x_is_long)
00383             {
00384               /* we've changed which axis is long, so only do one line */
00385               line1.Point2.X = line1.Point1.X + dx;
00386               line1.Point2.Y =
00387                 line1.Point1.Y + (way ? SGN (dy) * abs (dx) : 0);
00388               two_lines = false;
00389             }
00390           else
00391             {
00392               /* we've changed which axis is long, so only do one line */
00393               line1.Point2.Y = line1.Point1.Y + dy;
00394               line1.Point2.X =
00395                 line1.Point1.X + (way ? SGN (dx) * abs (dy) : 0);
00396               two_lines = false;
00397             }
00398           line2.Point1.X = line1.Point2.X;
00399           line2.Point1.Y = line1.Point2.Y;
00400           if (!two_lines)
00401             {
00402               line2.Point2.Y = line1.Point2.Y;
00403               line2.Point2.X = line1.Point2.X;
00404             }
00405           else
00406             {
00407               line2.Point2.X = line1.Point1.X + dx;
00408               line2.Point2.Y = line1.Point1.Y + dy;
00409             }
00410           SetLineBoundingBox (&line1);
00411           SetLineBoundingBox (&line2);
00412           last2 = length2;
00413           if (setjmp (info.env) == 0)
00414             {
00415               info.line = &line1;
00416               r_search (PCB->Data->via_tree, &line1.BoundingBox, NULL,
00417                         drcVia_callback, &info);
00418               r_search (PCB->Data->pin_tree, &line1.BoundingBox, NULL,
00419                         drcVia_callback, &info);
00420               if (info.bottom_side || info.top_side)
00421                 r_search (PCB->Data->pad_tree, &line1.BoundingBox, NULL,
00422                           drcPad_callback, &info);
00423               if (two_lines)
00424                 {
00425                   info.line = &line2;
00426                   r_search (PCB->Data->via_tree, &line2.BoundingBox, NULL,
00427                             drcVia_callback, &info);
00428                   r_search (PCB->Data->pin_tree, &line2.BoundingBox, NULL,
00429                             drcVia_callback, &info);
00430                   if (info.bottom_side || info.top_side)
00431                     r_search (PCB->Data->pad_tree, &line2.BoundingBox, NULL,
00432                               drcPad_callback, &info);
00433                 }
00434               GROUP_LOOP (PCB->Data, group);
00435               {
00436                 info.line = &line1;
00437                 r_search (layer->line_tree, &line1.BoundingBox, NULL,
00438                           drcLine_callback, &info);
00439                 r_search (layer->arc_tree, &line1.BoundingBox, NULL,
00440                           drcArc_callback, &info);
00441                 if (two_lines)
00442                   {
00443                     info.line = &line2;
00444                     r_search (layer->line_tree, &line2.BoundingBox,
00445                               NULL, drcLine_callback, &info);
00446                     r_search (layer->arc_tree, &line2.BoundingBox,
00447                               NULL, drcArc_callback, &info);
00448                   }
00449               }
00450               END_LOOP;
00451               /* no intersector! */
00452               blocker = false;
00453               f2 += s2;
00454               len = (line2.Point2.X - line1.Point1.X);
00455               len *= len;
00456               len += (double) (line2.Point2.Y - line1.Point1.Y) *
00457                 (line2.Point2.Y - line1.Point1.Y);
00458               if (len > best)
00459                 {
00460                   best = len;
00461                   ans.X = line2.Point2.X;
00462                   ans.Y = line2.Point2.Y;
00463                 }
00464 #if 0
00465               if (f2 > 1.0)
00466                 f2 = 0.5;
00467 #endif
00468             }
00469           else
00470             {
00471               /* bumped into something, back off */
00472               f2 -= s2;
00473             }
00474           s2 *= 0.5;
00475           length2 = MIN (f2 * temp2, temp2);
00476         }
00477       if (!blocker && ((x_is_long && line2.Point2.X - line1.Point1.X == dx)
00478                        || (!x_is_long
00479                            && line2.Point2.Y - line1.Point1.Y == dy)))
00480         f += s;
00481       else
00482         f -= s;
00483       s *= 0.5;
00484       length = MIN (f * temp, temp);
00485     }
00486 
00487   end->X = ans.X;
00488   end->Y = ans.Y;
00489   return best;
00490 }
00491 
00492 void
00493 EnforceLineDRC (void)
00494 {
00495   PointType r45, rs;
00496   bool shift;
00497   double r1, r2;
00498 
00499   /* Silence a bogus compiler warning by storing this in a variable */
00500   int layer_idx = INDEXOFCURRENT;
00501 
00502   if ( gui->mod1_is_pressed() || gui->control_is_pressed () || PCB->RatDraw
00503       || layer_idx >= max_copper_layer)
00504     return;
00505 
00506   rs.X = r45.X = Crosshair.X;
00507   rs.Y = r45.Y = Crosshair.Y;
00508   /* first try starting straight */
00509   r1 = drc_lines (&rs, false);
00510   /* then try starting at 45 */
00511   r2 = drc_lines (&r45, true);
00512   shift = gui->shift_is_pressed ();
00513   if (XOR (r1 > r2, shift))
00514     {
00515       if (PCB->Clipping)
00516         PCB->Clipping = shift ? 2 : 1;
00517       Crosshair.X = rs.X;
00518       Crosshair.Y = rs.Y;
00519     }
00520   else
00521     {
00522       if (PCB->Clipping)
00523         PCB->Clipping = shift ? 1 : 2;
00524       Crosshair.X = r45.X;
00525       Crosshair.Y = r45.Y;
00526     }
00527 }