pcb 4.1.1
An interactive printed circuit board layout editor.

crosshair.c

Go to the documentation of this file.
00001 
00033 #ifdef HAVE_CONFIG_H
00034 #include "config.h"
00035 #endif
00036 
00037 #include <memory.h>
00038 #include <math.h>
00039 
00040 #include "global.h"
00041 #include "hid_draw.h"
00042 
00043 #include "crosshair.h"
00044 
00045 #include "buffer.h"
00046 #include "data.h"
00047 #include "draw.h"
00048 #include "error.h"
00049 #include "line.h"
00050 #include "misc.h"
00051 #include "mymem.h"
00052 #include "search.h"
00053 #include "polygon.h"
00054 
00055 #ifdef HAVE_LIBDMALLOC
00056 #include <dmalloc.h>
00057 #endif
00058 
00059 typedef struct
00060 {
00061   int x, y;
00062 } point;
00063 
00064 
00069 static void
00070 thindraw_moved_pv (hidGC gc, PinType *pv, Coord x, Coord y)
00071 {
00072   PinType moved_pv = *pv;
00073   moved_pv.X += x;
00074   moved_pv.Y += y;
00075 
00076   gui->graphics->thindraw_pcb_pv (gc, gc, &moved_pv, true, false);
00077 }
00078 
00082 static void
00083 draw_dashed_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
00084 {
00087   double dx = x2-x1;
00088   double dy = y2-y1;
00089   double len_squared = dx*dx + dy*dy;
00090   int n;
00091   const int segs = 11; /* must be odd */
00092 
00093   if (len_squared < 1000000)
00094   {
00097     gui->graphics->draw_line (gc, x1, y1, x2, y2);
00098     return;
00099   }
00100 
00101   /* first seg is drawn from x1, y1 with no rounding error due to n-1 == 0 */
00102   for (n = 1; n < segs; n += 2)
00103     gui->graphics->draw_line (gc,
00104                               x1 + (dx * (double) (n-1) / (double) segs),
00105                               y1 + (dy * (double) (n-1) / (double) segs),
00106                               x1 + (dx * (double) n / (double) segs),
00107                               y1 + (dy * (double) n / (double) segs));
00108 
00109   /* make sure the last segment is drawn properly to x2 and y2,
00110    * don't leave room for rounding errors. */
00111   gui->graphics->draw_line (gc,
00112                             x2 - (dx / (double) segs),
00113                             y2 - (dy / (double) segs),
00114                             x2,
00115                             y2);
00116 }
00117 
00122 static void
00123 XORPolygon (hidGC gc, PolygonType *polygon, Coord dx, Coord dy, int dash_last)
00124 {
00125   Cardinal i;
00126   for (i = 0; i < polygon->PointN; i++)
00127     {
00128       Cardinal next = next_contour_point (polygon, i);
00129 
00130       if (next == 0)
00131         { /* last line: sometimes the implicit closing line */
00132           if (i == 1) /* corner case: don't draw two lines on top of
00133                        * each other - with XOR it looks bad */
00134             continue;
00135 
00136         if (dash_last)
00137           {
00138             draw_dashed_line (gc,
00139                               polygon->Points[i].X + dx,
00140                               polygon->Points[i].Y + dy,
00141                               polygon->Points[next].X + dx,
00142                               polygon->Points[next].Y + dy);
00143             break; /* skip normal line draw below */
00144           }
00145         }
00146 
00147       /* normal contour line */
00148       gui->graphics->draw_line (gc,
00149                                 polygon->Points[i].X + dx,
00150                                 polygon->Points[i].Y + dy,
00151                                 polygon->Points[next].X + dx,
00152                                 polygon->Points[next].Y + dy);
00153     }
00154 }
00155 
00159 static void
00160 XORDrawAttachedArc (hidGC gc, Coord thick)
00161 {
00162   ArcType arc;
00163   BoxType *bx;
00164   Coord wx, wy;
00165   Angle sa, dir;
00166   Coord wid = thick / 2;
00167 
00168   wx = Crosshair.X - Crosshair.AttachedBox.Point1.X;
00169   wy = Crosshair.Y - Crosshair.AttachedBox.Point1.Y;
00170   if (wx == 0 && wy == 0)
00171     return;
00172   arc.X = Crosshair.AttachedBox.Point1.X;
00173   arc.Y = Crosshair.AttachedBox.Point1.Y;
00174   if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
00175     {
00176       arc.X = Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
00177       sa = (wx >= 0) ? 0 : 180;
00178 #ifdef ARC45
00179       if (abs (wy) >= 2 * abs (wx))
00180         dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
00181       else
00182 #endif
00183         dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
00184     }
00185   else
00186     {
00187       arc.Y = Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
00188       sa = (wy >= 0) ? -90 : 90;
00189 #ifdef ARC45
00190       if (abs (wx) >= 2 * abs (wy))
00191         dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
00192       else
00193 #endif
00194         dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
00195       wy = wx;
00196     }
00197   wy = abs (wy);
00198   arc.StartAngle = sa;
00199   arc.Delta = dir;
00200   arc.Width = arc.Height = wy;
00201   bx = GetArcEnds (&arc);
00202   /*  sa = sa - 180; */
00203   gui->graphics->draw_arc (gc, arc.X, arc.Y, wy + wid, wy + wid, sa, dir);
00204   if (wid > pixel_slop)
00205     {
00206       gui->graphics->draw_arc (gc, arc.X, arc.Y, wy - wid, wy - wid, sa, dir);
00207       gui->graphics->draw_arc (gc, bx->X1, bx->Y1, wid, wid, sa,      -180 * SGN (dir));
00208       gui->graphics->draw_arc (gc, bx->X2, bx->Y2, wid, wid, sa + dir, 180 * SGN (dir));
00209     }
00210 }
00211 
00215 static void
00216 XORDrawAttachedLine (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2, Coord thick)
00217 {
00218   Coord dx, dy, ox, oy;
00219   double h;
00220 
00221   dx = x2 - x1;
00222   dy = y2 - y1;
00223   if (dx != 0 || dy != 0)
00224     h = 0.5 * thick / hypot (dx, dy);
00225   else
00226     h = 0.0;
00227   ox = dy * h + 0.5 * SGN (dy);
00228   oy = -(dx * h + 0.5 * SGN (dx));
00229   gui->graphics->draw_line (gc, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
00230   if (abs (ox) >= pixel_slop || abs (oy) >= pixel_slop)
00231     {
00232       Angle angle = atan2 (dx, dy) * 57.295779;
00233       gui->graphics->draw_line (gc, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
00234       gui->graphics->draw_arc (gc, x1, y1, thick / 2, thick / 2, angle - 180, 180);
00235       gui->graphics->draw_arc (gc, x2, y2, thick / 2, thick / 2, angle, 180);
00236     }
00237 }
00238 
00243 static void
00244 XORDrawElement (hidGC gc, ElementType *Element, Coord DX, Coord DY)
00245 {
00246   /* if no silkscreen, draw the bounding box */
00247   if (Element->ArcN == 0 && Element->LineN == 0)
00248     {
00249       gui->graphics->draw_line (gc,
00250                                 DX + Element->BoundingBox.X1,
00251                                 DY + Element->BoundingBox.Y1,
00252                                 DX + Element->BoundingBox.X1,
00253                                 DY + Element->BoundingBox.Y2);
00254       gui->graphics->draw_line (gc,
00255                                 DX + Element->BoundingBox.X1,
00256                                 DY + Element->BoundingBox.Y2,
00257                                 DX + Element->BoundingBox.X2,
00258                                 DY + Element->BoundingBox.Y2);
00259       gui->graphics->draw_line (gc,
00260                                 DX + Element->BoundingBox.X2,
00261                                 DY + Element->BoundingBox.Y2,
00262                                 DX + Element->BoundingBox.X2,
00263                                 DY + Element->BoundingBox.Y1);
00264       gui->graphics->draw_line (gc,
00265                                 DX + Element->BoundingBox.X2,
00266                                 DY + Element->BoundingBox.Y1,
00267                                 DX + Element->BoundingBox.X1,
00268                                 DY + Element->BoundingBox.Y1);
00269     }
00270   else
00271     {
00272       ELEMENTLINE_LOOP (Element);
00273       {
00274         gui->graphics->draw_line (gc,
00275                                   DX + line->Point1.X,
00276                                   DY + line->Point1.Y,
00277                                   DX + line->Point2.X,
00278                                   DY + line->Point2.Y);
00279       }
00280       END_LOOP;
00281 
00282       /* arc coordinates and angles have to be converted to X11 notation */
00283       ARC_LOOP (Element);
00284       {
00285         gui->graphics->draw_arc (gc,
00286                                  DX + arc->X,
00287                                  DY + arc->Y,
00288                                  arc->Width, arc->Height, arc->StartAngle, arc->Delta);
00289       }
00290       END_LOOP;
00291     }
00292   /* pin coordinates and angles have to be converted to X11 notation */
00293   PIN_LOOP (Element);
00294   {
00295     thindraw_moved_pv (gc, pin, DX, DY);
00296   }
00297   END_LOOP;
00298 
00299   /* pads */
00300   PAD_LOOP (Element);
00301   {
00302     if (PCB->InvisibleObjectsOn ||
00303         (TEST_FLAG (ONSOLDERFLAG, pad) != 0) == Settings.ShowBottomSide)
00304       {
00305         /* Make a copy of the pad structure, moved to the correct position */
00306         PadType moved_pad = *pad;
00307         moved_pad.Point1.X += DX; moved_pad.Point1.Y += DY;
00308         moved_pad.Point2.X += DX; moved_pad.Point2.Y += DY;
00309 
00310         gui->graphics->thindraw_pcb_pad (gc, &moved_pad, false, false);
00311       }
00312   }
00313   END_LOOP;
00314   /* mark */
00315   gui->graphics->draw_line (gc,
00316                             Element->MarkX + DX - EMARK_SIZE,
00317                             Element->MarkY + DY,
00318                             Element->MarkX + DX,
00319                             Element->MarkY + DY - EMARK_SIZE);
00320   gui->graphics->draw_line (gc,
00321                             Element->MarkX + DX + EMARK_SIZE,
00322                             Element->MarkY + DY,
00323                             Element->MarkX + DX,
00324                             Element->MarkY + DY - EMARK_SIZE);
00325   gui->graphics->draw_line (gc,
00326                             Element->MarkX + DX - EMARK_SIZE,
00327                             Element->MarkY + DY,
00328                             Element->MarkX + DX,
00329                             Element->MarkY + DY + EMARK_SIZE);
00330   gui->graphics->draw_line (gc,
00331                             Element->MarkX + DX + EMARK_SIZE,
00332                             Element->MarkY + DY,
00333                             Element->MarkX + DX,
00334                             Element->MarkY + DY + EMARK_SIZE);
00335 }
00336 
00340 static void
00341 XORDrawBuffer (hidGC gc, BufferType *Buffer)
00342 {
00343   Cardinal i;
00344   Coord x, y;
00345 
00346   /* set offset */
00347   x = Crosshair.X - Buffer->X;
00348   y = Crosshair.Y - Buffer->Y;
00349 
00350   /* draw all visible layers */
00351   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
00352     if (PCB->Data->Layer[i].On)
00353       {
00354         LayerType *layer = &Buffer->Data->Layer[i];
00355 
00356         LINE_LOOP (layer);
00357         {
00358 /*
00359                                 XORDrawAttachedLine(x +line->Point1.X,
00360                                         y +line->Point1.Y, x +line->Point2.X,
00361                                         y +line->Point2.Y, line->Thickness);
00362 */
00363         gui->graphics->draw_line (gc,
00364                                   x + line->Point1.X, y + line->Point1.Y,
00365                                   x + line->Point2.X, y + line->Point2.Y);
00366         }
00367         END_LOOP;
00368         ARC_LOOP (layer);
00369         {
00370           gui->graphics->draw_arc (gc,
00371                                    x + arc->X,
00372                                    y + arc->Y,
00373                                    arc->Width,
00374                                    arc->Height, arc->StartAngle, arc->Delta);
00375         }
00376         END_LOOP;
00377         TEXT_LOOP (layer);
00378         {
00379           BoxType *box = &text->BoundingBox;
00380           gui->graphics->draw_rect (gc,
00381                                     x + box->X1, y + box->Y1, x + box->X2, y + box->Y2);
00382         }
00383         END_LOOP;
00384         /* the tmp polygon has n+1 points because the first
00385          * and the last one are set to the same coordinates
00386          */
00387         POLYGON_LOOP (layer);
00388         {
00389           XORPolygon (gc, polygon, x, y, 0);
00390         }
00391         END_LOOP;
00392       }
00393 
00394   /* draw elements if visible */
00395   if (PCB->PinOn && PCB->ElementOn)
00396     ELEMENT_LOOP (Buffer->Data);
00397   {
00398     if (FRONT (element) || PCB->InvisibleObjectsOn)
00399       XORDrawElement (gc, element, x, y);
00400   }
00401   END_LOOP;
00402 
00403   /* and the vias */
00404   if (PCB->ViaOn)
00405     VIA_LOOP (Buffer->Data);
00406   {
00407     thindraw_moved_pv (gc, via, x, y);
00408   }
00409   END_LOOP;
00410 }
00411 
00415 static void
00416 XORDrawInsertPointObject (hidGC gc)
00417 {
00418   LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
00419   PointType *point = (PointType *) Crosshair.AttachedObject.Ptr3;
00420 
00421   if (Crosshair.AttachedObject.Type != NO_TYPE)
00422     {
00423       gui->graphics->draw_line (gc, point->X, point->Y, line->Point1.X, line->Point1.Y);
00424       gui->graphics->draw_line (gc, point->X, point->Y, line->Point2.X, line->Point2.Y);
00425     }
00426 }
00427 
00431 static void
00432 XORDrawMoveOrCopyObject (hidGC gc)
00433 {
00434   RubberbandType *ptr;
00435   Cardinal i;
00436   Coord dx = Crosshair.X - Crosshair.AttachedObject.X,
00437     dy = Crosshair.Y - Crosshair.AttachedObject.Y;
00438 
00439   switch (Crosshair.AttachedObject.Type)
00440     {
00441     case VIA_TYPE:
00442       {
00443         PinType *via = (PinType *) Crosshair.AttachedObject.Ptr1;
00444         thindraw_moved_pv (gc, via, dx, dy);
00445         break;
00446       }
00447 
00448     case LINE_TYPE:
00449       {
00450         LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
00451 
00452         XORDrawAttachedLine (gc, line->Point1.X + dx, line->Point1.Y + dy,
00453                                  line->Point2.X + dx, line->Point2.Y + dy,
00454                              line->Thickness);
00455         break;
00456       }
00457 
00458     case ARC_TYPE:
00459       {
00460         ArcType *Arc = (ArcType *) Crosshair.AttachedObject.Ptr2;
00461 
00462         gui->graphics->draw_arc (gc,
00463                                  Arc->X + dx,
00464                                  Arc->Y + dy,
00465                                  Arc->Width, Arc->Height, Arc->StartAngle, Arc->Delta);
00466         break;
00467       }
00468 
00469     case POLYGON_TYPE:
00470       {
00471         PolygonType *polygon =
00472           (PolygonType *) Crosshair.AttachedObject.Ptr2;
00473 
00474         /* the tmp polygon has n+1 points because the first
00475          * and the last one are set to the same coordinates
00476          */
00477         XORPolygon (gc, polygon, dx, dy, 0);
00478         break;
00479       }
00480 
00481     case LINEPOINT_TYPE:
00482       {
00483         LineType *line;
00484         PointType *point;
00485 
00486         line = (LineType *) Crosshair.AttachedObject.Ptr2;
00487         point = (PointType *) Crosshair.AttachedObject.Ptr3;
00488         if (point == &line->Point1)
00489           XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
00490                                line->Point2.X, line->Point2.Y, line->Thickness);
00491         else
00492           XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
00493                                line->Point1.X, line->Point1.Y, line->Thickness);
00494         break;
00495       }
00496 
00497     case POLYGONPOINT_TYPE:
00498       {
00499         PolygonType *polygon;
00500         PointType *point;
00501         Cardinal point_idx, prev, next;
00502 
00503         polygon = (PolygonType *) Crosshair.AttachedObject.Ptr2;
00504         point = (PointType *) Crosshair.AttachedObject.Ptr3;
00505         point_idx = polygon_point_idx (polygon, point);
00506 
00507         /* get previous and following point */
00508         prev = prev_contour_point (polygon, point_idx);
00509         next = next_contour_point (polygon, point_idx);
00510 
00511         /* draw the two segments */
00512         gui->graphics->draw_line (gc,
00513                                   polygon->Points[prev].X, polygon->Points[prev].Y,
00514                                   point->X + dx, point->Y + dy);
00515         gui->graphics->draw_line (gc,
00516                                   point->X + dx, point->Y + dy,
00517                                   polygon->Points[next].X, polygon->Points[next].Y);
00518         break;
00519       }
00520 
00521     case ELEMENTNAME_TYPE:
00522       {
00523         /* locate the element "mark" and draw an association line from crosshair to it */
00524         ElementType *element =
00525           (ElementType *) Crosshair.AttachedObject.Ptr1;
00526 
00527         gui->graphics->draw_line (gc,
00528                                   element->MarkX,
00529                                   element->MarkY, Crosshair.X, Crosshair.Y);
00530         /* fall through to move the text as a box outline */
00531       }
00532     case TEXT_TYPE:
00533       {
00534         TextType *text = (TextType *) Crosshair.AttachedObject.Ptr2;
00535         BoxType *box = &text->BoundingBox;
00536         gui->graphics->draw_rect (gc,
00537                                   box->X1 + dx,
00538                                   box->Y1 + dy, box->X2 + dx, box->Y2 + dy);
00539         break;
00540       }
00541 
00542       /* pin/pad movements result in moving an element */
00543     case PAD_TYPE:
00544     case PIN_TYPE:
00545     case ELEMENT_TYPE:
00546       XORDrawElement (gc, (ElementType *) Crosshair.AttachedObject.Ptr2, dx, dy);
00547       break;
00548     }
00549 
00550   /* draw the attached rubberband lines too */
00551   i = Crosshair.AttachedObject.RubberbandN;
00552   ptr = Crosshair.AttachedObject.Rubberband;
00553   while (i)
00554     {
00555       PointType *point1, *point2;
00556 
00557       if (TEST_FLAG (VIAFLAG, ptr->Line))
00558         {
00559           /* this is a rat going to a polygon.  do not draw for rubberband */;
00560         }
00561       else if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
00562         {
00563           /* 'point1' is always the fix-point */
00564           if (ptr->MovedPoint == &ptr->Line->Point1)
00565             {
00566               point1 = &ptr->Line->Point2;
00567               point2 = &ptr->Line->Point1;
00568             }
00569           else
00570             {
00571               point1 = &ptr->Line->Point1;
00572               point2 = &ptr->Line->Point2;
00573             }
00574           XORDrawAttachedLine (gc, point1->X, point1->Y,
00575                                point2->X + dx, point2->Y + dy,
00576                                ptr->Line->Thickness);
00577         }
00578       else if (ptr->MovedPoint == &ptr->Line->Point1)
00579         XORDrawAttachedLine (gc,
00580                              ptr->Line->Point1.X + dx,
00581                              ptr->Line->Point1.Y + dy,
00582                              ptr->Line->Point2.X + dx,
00583                              ptr->Line->Point2.Y + dy, ptr->Line->Thickness);
00584 
00585       ptr++;
00586       i--;
00587     }
00588 }
00589 
00593 void
00594 DrawAttached (hidGC gc)
00595 {
00596   gui->graphics->set_color (gc, Settings.CrosshairColor);
00597   gui->graphics->set_draw_xor (gc, 1);
00598   gui->graphics->set_line_cap (gc, Trace_Cap);
00599   gui->graphics->set_line_width (gc, 1);
00600 
00601   switch (Settings.Mode)
00602     {
00603     case VIA_MODE:
00604       {
00605         /* Make a dummy via structure to draw from */
00606         PinType via;
00607         via.X = Crosshair.X;
00608         via.Y = Crosshair.Y;
00609         via.Thickness = Settings.ViaThickness;
00610         via.Clearance = 2 * Settings.Keepaway;
00611         via.DrillingHole = Settings.ViaDrillingHole;
00612         via.Mask = 0;
00613         via.Flags = NoFlags ();
00614 
00615         gui->graphics->thindraw_pcb_pv (gc, gc, &via, true, false);
00616 
00617         if (TEST_FLAG (SHOWDRCFLAG, PCB))
00618           {
00619             Coord mask_r = Settings.ViaThickness / 2 + PCB->Bloat;
00620             gui->graphics->set_color (gc, Settings.CrossColor);
00621             gui->graphics->set_line_cap (gc, Round_Cap);
00622             gui->graphics->set_line_width (gc, 0);
00623             gui->graphics->draw_arc (gc, via.X, via.Y, mask_r, mask_r, 0, 360);
00624             gui->graphics->set_color (gc, Settings.CrosshairColor);
00625           }
00626         break;
00627       }
00628 
00629       /* the attached line is used by both LINEMODE, POLYGON_MODE and POLYGONHOLE_MODE*/
00630     case POLYGON_MODE:
00631     case POLYGONHOLE_MODE:
00632       /* draw only if starting point is set */
00633       if (Crosshair.AttachedLine.State != STATE_FIRST)
00634         gui->graphics->draw_line (gc,
00635                                   Crosshair.AttachedLine.Point1.X,
00636                                   Crosshair.AttachedLine.Point1.Y,
00637                                   Crosshair.AttachedLine.Point2.X,
00638                                   Crosshair.AttachedLine.Point2.Y);
00639 
00640       /* draw attached polygon only if in POLYGON_MODE or POLYGONHOLE_MODE */
00641       if (Crosshair.AttachedPolygon.PointN > 1)
00642         {
00643           XORPolygon (gc, &Crosshair.AttachedPolygon, 0, 0, 1);
00644         }
00645       break;
00646 
00647     case ARC_MODE:
00648       if (Crosshair.AttachedBox.State != STATE_FIRST)
00649         {
00650           XORDrawAttachedArc (gc, Settings.LineThickness);
00651           if (TEST_FLAG (SHOWDRCFLAG, PCB))
00652             {
00653               gui->graphics->set_color (gc, Settings.CrossColor);
00654               XORDrawAttachedArc (gc, Settings.LineThickness + 2 * PCB->Bloat);
00655               gui->graphics->set_color (gc, Settings.CrosshairColor);
00656             }
00657 
00658         }
00659       break;
00660 
00661     case LINE_MODE:
00662       /* draw only if starting point exists and the line has length */
00663       if (Crosshair.AttachedLine.State != STATE_FIRST &&
00664           Crosshair.AttachedLine.draw)
00665         {
00666           XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
00667                                    Crosshair.AttachedLine.Point1.Y,
00668                                    Crosshair.AttachedLine.Point2.X,
00669                                    Crosshair.AttachedLine.Point2.Y,
00670                                    PCB->RatDraw ? 10 : Settings.LineThickness);
00671           /* draw two lines ? */
00672           if (PCB->Clipping)
00673             XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
00674                                      Crosshair.AttachedLine.Point2.Y,
00675                                  Crosshair.X, Crosshair.Y,
00676                                  PCB->RatDraw ? 10 : Settings.LineThickness);
00677           if (TEST_FLAG (SHOWDRCFLAG, PCB))
00678             {
00679               gui->graphics->set_color (gc, Settings.CrossColor);
00680               XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
00681                                        Crosshair.AttachedLine.Point1.Y,
00682                                    Crosshair.AttachedLine.Point2.X,
00683                                    Crosshair.AttachedLine.Point2.Y,
00684                                    PCB->RatDraw ? 10 : Settings.LineThickness
00685                                    + 2 * PCB->Bloat);
00686               if (PCB->Clipping)
00687                 XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
00688                                          Crosshair.AttachedLine.Point2.Y,
00689                                      Crosshair.X, Crosshair.Y,
00690                                      PCB->RatDraw ? 10 : Settings.
00691                                      LineThickness + 2 * PCB->Bloat);
00692               gui->graphics->set_color (gc, Settings.CrosshairColor);
00693             }
00694         }
00695       break;
00696 
00697     case PASTEBUFFER_MODE:
00698       XORDrawBuffer (gc, PASTEBUFFER);
00699       break;
00700 
00701     case COPY_MODE:
00702     case MOVE_MODE:
00703       XORDrawMoveOrCopyObject (gc);
00704       break;
00705 
00706     case INSERTPOINT_MODE:
00707       XORDrawInsertPointObject (gc);
00708       break;
00709     }
00710 
00711   /* an attached box does not depend on a special mode */
00712   if (Crosshair.AttachedBox.State == STATE_SECOND ||
00713       Crosshair.AttachedBox.State == STATE_THIRD)
00714     {
00715       Coord x1, y1, x2, y2;
00716 
00717       x1 = Crosshair.AttachedBox.Point1.X;
00718       y1 = Crosshair.AttachedBox.Point1.Y;
00719       x2 = Crosshair.AttachedBox.Point2.X;
00720       y2 = Crosshair.AttachedBox.Point2.Y;
00721       gui->graphics->draw_rect (gc, x1, y1, x2, y2);
00722     }
00723 }
00724 
00725 
00729 void
00730 DrawMark (hidGC gc)
00731 {
00732   gui->graphics->set_color (gc, Settings.CrosshairColor);
00733   gui->graphics->set_draw_xor (gc, 1);
00734   gui->graphics->set_line_cap (gc, Trace_Cap);
00735   gui->graphics->set_line_width (gc, 1);
00736 
00737   /* Mark is not drawn when it is not set */
00738   if (!Marked.status)
00739     return;
00740 
00741   gui->graphics->draw_line (gc,
00742                   Marked.X - MARK_SIZE,
00743                   Marked.Y - MARK_SIZE,
00744                   Marked.X + MARK_SIZE, Marked.Y + MARK_SIZE);
00745   gui->graphics->draw_line (gc,
00746                   Marked.X + MARK_SIZE,
00747                   Marked.Y - MARK_SIZE,
00748                   Marked.X - MARK_SIZE, Marked.Y + MARK_SIZE);
00749 }
00750 
00754 Coord
00755 GridFit (Coord x, Coord grid_spacing, Coord grid_offset)
00756 {
00757   x -= grid_offset;
00758   x = grid_spacing * round ((double) x / grid_spacing);
00759   x += grid_offset;
00760   return x;
00761 }
00762 
00763 
00780 void
00781 notify_crosshair_change (bool changes_complete)
00782 {
00783   if (gui->notify_crosshair_change)
00784     gui->notify_crosshair_change (changes_complete);
00785 }
00786 
00787 
00802 void
00803 notify_mark_change (bool changes_complete)
00804 {
00805   if (gui->notify_mark_change)
00806     gui->notify_mark_change (changes_complete);
00807 }
00808 
00809 
00822 void
00823 HideCrosshair (void)
00824 {
00825   static bool warned_old_api = false;
00826   if (!warned_old_api)
00827     {
00828       Message (_("WARNING: A plugin is using the deprecated API HideCrosshair().\n"
00829                  "         This API may be removed in a future release of PCB.\n"));
00830       warned_old_api = true;
00831     }
00832 
00833   notify_crosshair_change (false);
00834   notify_mark_change (false);
00835 }
00836 
00837 void
00838 RestoreCrosshair (void)
00839 {
00840   static bool warned_old_api = false;
00841   if (!warned_old_api)
00842     {
00843       Message (_("WARNING: A plugin is using the deprecated API RestoreCrosshair().\n"
00844                  "         This API may be removed in a future release of PCB.\n"));
00845       warned_old_api = true;
00846     }
00847 
00848   notify_crosshair_change (true);
00849   notify_mark_change (true);
00850 }
00851 
00855 static double
00856 square (double x)
00857 {
00858   return x * x;
00859 }
00860 
00861 static double
00862 crosshair_sq_dist (CrosshairType *crosshair, Coord x, Coord y)
00863 {
00864   return square (x - crosshair->X) + square (y - crosshair->Y);
00865 }
00866 
00867 struct snap_data {
00868   CrosshairType *crosshair;
00869   double nearest_sq_dist;
00870   bool nearest_is_grid;
00871   Coord x, y;
00872 };
00873 
00883 static void
00884 check_snap_object (struct snap_data *snap_data, Coord x, Coord y,
00885                    bool prefer_to_grid)
00886 {
00887   double sq_dist;
00888 
00889   sq_dist = crosshair_sq_dist (snap_data->crosshair, x, y);
00890   if (sq_dist <= snap_data->nearest_sq_dist ||
00891       (prefer_to_grid && snap_data->nearest_is_grid && !gui->shift_is_pressed()))
00892     {
00893       snap_data->x = x;
00894       snap_data->y = y;
00895       snap_data->nearest_sq_dist = sq_dist;
00896       snap_data->nearest_is_grid = false;
00897     }
00898 }
00899 
00900 static void
00901 check_snap_offgrid_line (struct snap_data *snap_data,
00902                          Coord nearest_grid_x,
00903                          Coord nearest_grid_y)
00904 {
00905   void *ptr1, *ptr2, *ptr3;
00906   int ans;
00907   LineType *line;
00908   Coord try_x, try_y;
00909   double dx, dy;
00910   double dist;
00911 
00912   if (!TEST_FLAG (SNAPPINFLAG, PCB))
00913     return;
00914 
00915   /* Code to snap at some sensible point along a line */
00916   /* Pick the nearest grid-point in the x or y direction
00917    * to align with, then adjust until we hit the line
00918    */
00919   ans = SearchObjectByLocation (LINE_TYPE, &ptr1, &ptr2, &ptr3,
00920                                 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
00921 
00922 
00923   if (ans == NO_TYPE)
00924     return;
00925 
00926   line = (LineType *)ptr2;
00927 
00928   /* Allow snapping to off-grid lines when drawing new lines (on
00929    * the same layer), and when moving a line end-point
00930    * (but don't snap to the same line)
00931    */
00932   if ((Settings.Mode != LINE_MODE || CURRENT != ptr1) &&
00933       (Settings.Mode != MOVE_MODE ||
00934        Crosshair.AttachedObject.Ptr1 != ptr1 ||
00935        Crosshair.AttachedObject.Type != LINEPOINT_TYPE ||
00936        Crosshair.AttachedObject.Ptr2 == line))
00937     return;
00938 
00939   dx = line->Point2.X - line->Point1.X;
00940   dy = line->Point2.Y - line->Point1.Y;
00941 
00942   /* Try snapping along the X axis */
00943   if (dy != 0.)
00944     {
00945       /* Move in the X direction until we hit the line */
00946       try_x = (nearest_grid_y - line->Point1.Y) / dy * dx + line->Point1.X;
00947       try_y = nearest_grid_y;
00948       check_snap_object (snap_data, try_x, try_y, true);
00949     }
00950 
00951   /* Try snapping along the Y axis */
00952   if (dx != 0.)
00953     {
00954       try_x = nearest_grid_x;
00955       try_y = (nearest_grid_x - line->Point1.X) / dx * dy + line->Point1.Y;
00956       check_snap_object (snap_data, try_x, try_y, true);
00957     }
00958 
00959   if (dx != dy) /* If line not parallel with dX = dY direction.. */
00960     {
00961       /* Try snapping diagonally towards the line in the dX = dY direction */
00962 
00963       if (dy == 0)
00964         dist = line->Point1.Y - nearest_grid_y;
00965       else
00966         dist = ((line->Point1.X - nearest_grid_x) -
00967                 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 - dx / dy);
00968 
00969       try_x = nearest_grid_x + dist;
00970       try_y = nearest_grid_y + dist;
00971 
00972       check_snap_object (snap_data, try_x, try_y, true);
00973     }
00974 
00975   if (dx != -dy) /* If line not parallel with dX = -dY direction.. */
00976     {
00977       /* Try snapping diagonally towards the line in the dX = -dY direction */
00978 
00979       if (dy == 0)
00980         dist = nearest_grid_y - line->Point1.Y;
00981       else
00982         dist = ((line->Point1.X - nearest_grid_x) -
00983                 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 + dx / dy);
00984 
00985       try_x = nearest_grid_x + dist;
00986       try_y = nearest_grid_y - dist;
00987 
00988       check_snap_object (snap_data, try_x, try_y, true);
00989     }
00990 }
00991 
00996 void
00997 FitCrosshairIntoGrid (Coord X, Coord Y)
00998 {
00999   Coord nearest_grid_x, nearest_grid_y;
01000   void *ptr1, *ptr2, *ptr3;
01001   struct snap_data snap_data;
01002   int ans;
01003 
01004   /* limit the crosshair location to the board area */
01005   Crosshair.X = CLAMP (X, Crosshair.MinX, Crosshair.MaxX);
01006   Crosshair.Y = CLAMP (Y, Crosshair.MinY, Crosshair.MaxY);
01007 
01008   /*
01009    * GRID SNAPPING
01010    *
01011    * First figure out where the nearest grid point that would snap to is. Then
01012    * if we don't find an object that we would prefer, snap to the grid point.
01013    *
01014    */
01015   
01016   /* if we're drawing rats, don't snap to the grid */
01017   if (PCB->RatDraw)
01018     {
01019     /* set the "nearest grid" somewhere off the board so that everything else
01020      is closer */
01021       nearest_grid_x = -MIL_TO_COORD (6);
01022       nearest_grid_y = -MIL_TO_COORD (6);
01023     }
01024   else
01025     {
01026     /* not drawing rats */
01027     /* find the closest grid point */
01028       nearest_grid_x = GridFit (Crosshair.X, PCB->Grid, PCB->GridOffsetX);
01029       nearest_grid_y = GridFit (Crosshair.Y, PCB->Grid, PCB->GridOffsetY);
01030 
01031     /* if something is marked and we're forcing orthogonal moves... */
01032       if (Marked.status && TEST_FLAG (ORTHOMOVEFLAG, PCB))
01033         {
01034           Coord dx = Crosshair.X - Marked.X;
01035           Coord dy = Crosshair.Y - Marked.Y;
01036       /* restrict motion along the x or y axis
01037        this assumes we're always snapped to a grid point... */
01038           if (ABS (dx) > ABS (dy))
01039             nearest_grid_y = Marked.Y;
01040           else
01041             nearest_grid_x = Marked.X;
01042         }
01043 
01044     }
01045 
01046   /* The snap_data structure contains all of the information about where we're
01047    * going to snap to. */
01048   snap_data.crosshair = &Crosshair;
01049   snap_data.nearest_sq_dist =
01050     crosshair_sq_dist (&Crosshair, nearest_grid_x, nearest_grid_y);
01051   snap_data.nearest_is_grid = true;
01052   snap_data.x = nearest_grid_x;
01053   snap_data.y = nearest_grid_y;
01054 
01055   ans = NO_TYPE;
01056   /* if we're not drawing rats, check for elements first */
01057   if (!PCB->RatDraw)
01058     ans = SearchObjectByLocation (ELEMENT_TYPE, &ptr1, &ptr2, &ptr3,
01059                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
01060 
01061   if (ans & ELEMENT_TYPE)
01062     {
01063     /* if we found an element, check to see if we should snap to it */
01064       ElementType *el = (ElementType *) ptr1;
01065       check_snap_object (&snap_data, el->MarkX, el->MarkY, false);
01066     }
01067 
01068   /* try snapping to a pad if we're drawing rats, or pad snapping is turned on */
01069   ans = NO_TYPE;
01070   if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
01071     ans = SearchObjectByLocation (PAD_TYPE, &ptr1, &ptr2, &ptr3,
01072                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
01073 
01074   /* Avoid self-snapping when moving */
01075   if (ans != NO_TYPE &&
01076       Settings.Mode == MOVE_MODE &&
01077       Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
01078       ptr1 == Crosshair.AttachedObject.Ptr1)
01079     ans = NO_TYPE;
01080 
01081   if (ans != NO_TYPE &&                  /* we found a pad */
01082       ( Settings.Mode == LINE_MODE ||    /* we're in line drawing mode, or... */
01083         Settings.Mode == ARC_MODE  ||    /* we're in arc drawing mode, or... */
01084        (Settings.Mode == MOVE_MODE &&    /* we're moving something and ... */
01085         ((Crosshair.AttachedObject.Type == LINEPOINT_TYPE) || /* it's a line */
01086          (Crosshair.AttachedObject.Type == ARCPOINT_TYPE))))) /* it's an arc */
01087     {
01088     /* we found a pad and we're drawing or moving a line */
01089       PadType *pad = (PadType *) ptr2;
01090       LayerType *desired_layer;
01091       Cardinal desired_group;
01092       Cardinal bottom_group, top_group;
01093       int found_our_layer = false;
01094 
01095     /* the layer we'd like to snap to, start with the current layer */
01096       desired_layer = CURRENT;
01097       if (Settings.Mode == MOVE_MODE &&
01098           Crosshair.AttachedObject.Type == LINEPOINT_TYPE)
01099         {
01100       /* if we're moving something, the desired layer is the layer it's on */
01101           desired_layer = (LayerType *)Crosshair.AttachedObject.Ptr1;
01102         }
01103 
01104     /* pads must be on the top or bottom sides.
01105      * find layer groups of the top and bottom sides */
01106       top_group = GetLayerGroupNumberBySide (TOP_SIDE);
01107       bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
01108       desired_group = TEST_FLAG (ONSOLDERFLAG, pad) ? bottom_group : top_group;
01109 
01110       GROUP_LOOP (PCB->Data, desired_group);
01111       {
01112         if (layer == desired_layer)
01113           {
01114         /* the layer the pad is on is the same as the layer of the line
01115          * that we're moving. */
01116             found_our_layer = true;
01117             break;
01118           }
01119       }
01120       END_LOOP;
01121 
01122       if (found_our_layer == false)
01123     /* different layers, so don't snap */
01124         ans = NO_TYPE;
01125     }
01126 
01127   if (ans != NO_TYPE)
01128     {
01129     /* we found a pad we want to snap to, check to see if we should */
01130       PadType *pad = (PadType *)ptr2;
01131       check_snap_object (&snap_data, pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2,
01132                                      pad->Point1.Y + (pad->Point2.Y - pad->Point1.Y) / 2,
01133                          true);
01134     }
01135 
01136   /* try snapping to a pin
01137    * similar to snapping to a pad, but without the layer restriction */
01138   ans = NO_TYPE;
01139   if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
01140     ans = SearchObjectByLocation (PIN_TYPE, &ptr1, &ptr2, &ptr3,
01141                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
01142 
01143   /* Avoid self-snapping when moving */
01144   if (ans != NO_TYPE &&
01145       Settings.Mode == MOVE_MODE &&
01146       Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
01147       ptr1 == Crosshair.AttachedObject.Ptr1)
01148     ans = NO_TYPE;
01149 
01150   if (ans != NO_TYPE)
01151     {
01152     /* we found a pin, try to snap to it */
01153       PinType *pin = (PinType *)ptr2;
01154       check_snap_object (&snap_data, pin->X, pin->Y, true);
01155     }
01156 
01157   /* if snapping to pins and pads is turned on, try snapping to vias */
01158   ans = NO_TYPE;
01159   if (TEST_FLAG (SNAPPINFLAG, PCB))
01160     ans = SearchObjectByLocation (VIA_TYPE, &ptr1, &ptr2, &ptr3,
01161                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
01162 
01163   /* Avoid snapping vias to any other vias */
01164   if (Settings.Mode == MOVE_MODE &&
01165       Crosshair.AttachedObject.Type == VIA_TYPE &&
01166       (ans & PIN_TYPES))
01167     ans = NO_TYPE;
01168 
01169   if (ans != NO_TYPE)
01170     {
01171     /* found a via, try snapping to it */
01172       PinType *pin = (PinType *)ptr2;
01173       check_snap_object (&snap_data, pin->X, pin->Y, true);
01174     }
01175 
01176   /* try snapping to the end points of lines and arcs */
01177   ans = NO_TYPE;
01178   if (TEST_FLAG (SNAPPINFLAG, PCB))
01179     ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
01180                                   &ptr1, &ptr2, &ptr3,
01181                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
01182 
01183   if (ans != NO_TYPE)
01184     {
01185     /* found an end point, try to snap to it */
01186       PointType *pnt = (PointType *)ptr3;
01187       check_snap_object (&snap_data, pnt->X, pnt->Y, true);
01188     }
01189 
01190   /* try snapping to a point on a line that's not on the grid */
01191   check_snap_offgrid_line (&snap_data, nearest_grid_x, nearest_grid_y);
01192 
01193   /* try snapping to a point defining a polygon */
01194   ans = NO_TYPE;
01195   if (TEST_FLAG (SNAPPINFLAG, PCB))
01196     ans = SearchObjectByLocation (POLYGONPOINT_TYPE, &ptr1, &ptr2, &ptr3,
01197                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
01198 
01199   if (ans != NO_TYPE)
01200     {
01201     /* found a polygon point, try snapping to it */
01202       PointType *pnt = (PointType *)ptr3;
01203       check_snap_object (&snap_data, pnt->X, pnt->Y, true);
01204     }
01205 
01206   /* if snap_data.[x,y] are >= 0, then we found something,
01207    * so move the crosshair */
01208   if (snap_data.x >= 0 && snap_data.y >= 0)
01209     {
01210       Crosshair.X = snap_data.x;
01211       Crosshair.Y = snap_data.y;
01212     }
01213 
01214   /* If we're in arrow mode and we're over the end point of a line,
01215    * change the cursor to the "draped box" to indicate that clicking would
01216    * grab the line endpoint */
01217   if (Settings.Mode == ARROW_MODE)
01218     {
01219       ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
01220                                     &ptr1, &ptr2, &ptr3,
01221                                     Crosshair.X, Crosshair.Y, PCB->Grid / 2);
01222       if (ans == NO_TYPE)
01223         hid_action("PointCursor");
01224       else if (!TEST_FLAG(SELECTEDFLAG, (LineType *)ptr2))
01225         hid_actionl("PointCursor","True", NULL);
01226     }
01227 
01228   if (Settings.Mode == LINE_MODE
01229       && Crosshair.AttachedLine.State != STATE_FIRST
01230       && TEST_FLAG (AUTODRCFLAG, PCB))
01231     EnforceLineDRC ();
01232 
01233   gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_DO_NOTHING);
01234 }
01235 
01241 bool
01242 MoveCrosshairAbsolute (Coord X, Coord Y)
01243 {
01244   Coord old_x = Crosshair.X;
01245   Coord old_y = Crosshair.Y;
01246 
01247   FitCrosshairIntoGrid (X, Y);
01248 
01249   if (Crosshair.X != old_x || Crosshair.Y != old_y)
01250     {
01251       Coord new_x = Crosshair.X;
01252       Coord new_y = Crosshair.Y;
01253 
01254       /* back up to old position to notify the GUI
01255        * (which might want to erase the old crosshair) */
01256       Crosshair.X = old_x;
01257       Crosshair.Y = old_y;
01258       notify_crosshair_change (false); /* Our caller notifies when it has done */
01259 
01260       /* now move forward again */
01261       Crosshair.X = new_x;
01262       Crosshair.Y = new_y;
01263       return true;
01264     }
01265   return false;
01266 }
01267 
01272 void
01273 crosshair_update_range(void)
01274 {
01275   Coord xmin, xmax, ymin, ymax;
01276   BoxType *box;
01277   
01278   /* limit the crosshair to being on the working area */
01279   xmin = 0;  xmax = PCB->MaxWidth;
01280   ymin = 0;  ymax = PCB->MaxHeight;
01281   
01282   /* limit the crosshair so attached objects stay on the working area */
01283   /* note: There are presently two ways this is done in the code, the first uses
01284    *       the pastebuffer bounding box, the second uses the attached object
01285    *       bounding box.
01286    */
01287   if (Settings.Mode == PASTEBUFFER_MODE) {
01288         SetBufferBoundingBox(PASTEBUFFER);
01289     xmin = MAX(xmin, PASTEBUFFER->X - PASTEBUFFER->BoundingBox.X1);
01290     ymin = MAX(ymin, PASTEBUFFER->Y - PASTEBUFFER->BoundingBox.Y1);
01291     xmax = MIN(xmax, PCB->MaxWidth - (PASTEBUFFER->BoundingBox.X2 - PASTEBUFFER->X));
01292     ymax = MIN(ymax, PCB->MaxHeight - (PASTEBUFFER->BoundingBox.Y2 - PASTEBUFFER->Y));
01293   }
01294   
01295   /* if there's an attached object, */
01296   if (Crosshair.AttachedObject.Type != NO_TYPE) {
01297     box = GetObjectBoundingBox (Crosshair.AttachedObject.Type,
01298                                 Crosshair.AttachedObject.Ptr1,
01299                                 Crosshair.AttachedObject.Ptr2,
01300                                 Crosshair.AttachedObject.Ptr3);
01301     xmin = MAX(xmin, Crosshair.AttachedObject.X - box->X1);
01302     ymin = MAX(ymin, Crosshair.AttachedObject.Y - box->Y1);
01303     xmax = MIN(xmax, PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X));
01304     ymax = MIN(ymax, PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
01305   }
01306   
01307   SetCrosshairRange(xmin, ymin, xmax, ymax);
01308   
01309 }
01310 
01314 void
01315 SetCrosshairRange (Coord MinX, Coord MinY, Coord MaxX, Coord MaxY)
01316 {
01317   Crosshair.MinX = MAX (0, MinX);
01318   Crosshair.MinY = MAX (0, MinY);
01319   Crosshair.MaxX = MIN (PCB->MaxWidth, MaxX);
01320   Crosshair.MaxY = MIN (PCB->MaxHeight, MaxY);
01321 
01322   /* force update of position */
01323   FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
01324 }
01325 
01331 void
01332 InitCrosshair (void)
01333 {
01334   /* set initial shape */
01335   Crosshair.shape = Basic_Crosshair_Shape;
01336 
01337   /* set default limits */
01338   Crosshair.MinX = Crosshair.MinY = 0;
01339   Crosshair.MaxX = PCB->MaxWidth;
01340   Crosshair.MaxY = PCB->MaxHeight;
01341 
01342   /* clear the mark */
01343   Marked.status = false;
01344 }
01345 
01349 void
01350 DestroyCrosshair (void)
01351 {
01352   FreePolygonMemory (&Crosshair.AttachedPolygon);
01353 }