pcb 4.1.1
An interactive printed circuit board layout editor.
|
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 }