From 0490b695f5eb125d2980ffc4501cd32ad85210ca Mon Sep 17 00:00:00 2001 From: Ineiev Date: Sat, 22 Sep 2012 21:27:11 +0200 Subject: [PATCH 1/1] Currently PCB user can draw arcs at limited angles (usually 90 degree step), though there is no such internal limit for arcs angles. Now the patched tool is expected to work like this: 1.point at the center, 2.the first point of arc (up to 270 degrees; sets the arc direction), 3.the second point of arc (the direction is the same as at the end of 2.; when the new arc overlaps the arc drawn in step 2., a full circle is created); 4.return to 3. until ESC (`F3' moves the starting point to end of the latest arc and returns to 3.) During 3. and 4. Shift-click moves the center of the arc to the pointer. Switching from/to Line tool draws chained segments. HOW TO TEST Select Arc tool; draw different arcs with lines pressing different buttons, and `F2', `F3' and `U' keys. Signed-off-by: Bert Timmerman --- doc/pcb.texi | 47 +++++++--------- src/action.c | 161 +++++++++++++++++++++++++++++++------------------------ src/crosshair.c | 139 +++++++++++++++++++++++++++++++++++------------- src/crosshair.h | 1 + src/set.c | 78 +++++++++++++++++++++++---- 5 files changed, 281 insertions(+), 145 deletions(-) diff --git a/doc/pcb.texi b/doc/pcb.texi index 7608c28..b7faa56 100644 --- a/doc/pcb.texi +++ b/doc/pcb.texi @@ -35,6 +35,8 @@ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009 Dan McMahill Copyright (C) 2004 DJ Delorie +Copyright (C) 2008 Ineiev (modified arctool documentation) + This program is free software; you may redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -570,24 +572,21 @@ that the connection may break if the line width shrinks slightly. @section Arcs @cindex arc -@pcb{} can handle arcs of any angular extent, but when you -create an arc with the Arc tool, it will -be a quarter circle (this means they always bend a right angle). Arcs are very similar -to lines otherwise. They are created on the active layer and have the same thickness +@pcb{} can handle arcs of any angular extent with one-degree precision. +Arcs are very similar to lines. +They are created on the active layer and have the same thickness that new lines will have. The various clicks for creating lines work pretty much the same way for creating arcs. -In order to make the arc curve in the desired direction, drag the mouse along -the tangent line from the starting position towards the end position. If the grid is -too coarse, it may not be possible to distinguish whether you've moved over then up, -or up then over, so if you can't seem to make the arc go in the direction you want, try pressing -the @emph{Shift} key while drawing the arc. Decreasing the grid spacing may also help. -Alternatively you can draw the wrong arc, then -rotate and move it where you want. Like the Line tool, after an arc is drawn a new -starting point is established at the end point. + +In order to make the arc curve, click on center of the arc and +then on it's starting point. After that point at the arc end. To draw an arc longer +than 270 degrees, click on an intermediate point first to determine the line +direction; then click on the end of the arc. @emph{Shift}-click moves the center +of the arc. @emph{F3} begins a new arc starting at the end of the previous one. Whenever a starting point is established by either the Line or Arc tools it will be retained if you switch directly between the -tools (e.g. @emph{F2} key for Lines, @emph{F8} key for Arcs. Arcs can either touch or +tools (e.g. @emph{F2} key for Lines, @emph{F3} key for Arcs. Arcs can either touch or clear polygons just like lines do. Of course connection searches, undo and all the other features you'd expect work with arcs too. @@ -1430,21 +1429,17 @@ displayed in the status line. @cindex arc, an example An Arc is drawn with the @emph{arc-tool}. Get there either by selecting it -from the @emph{Tool palette} or by pressing @emph{F8}. Press @emph{Btn1} to -define the starting point for the arc. Drag the mouse towards the desired +from the @emph{Tool palette} or by pressing @emph{F3}. Press @emph{Btn1} to +define the center for the arc. Press @emph{Btn1} again to define the starting +point of the arc. Drag the mouse towards the desired end point along the path you want the arc to follow. The outline of the arc that -will be created is shown on the screen as you move the mouse. Arcs are always -forced to be 90 degrees and have symmetrical length and width ( i.e. they are -a quarter circle). The next @emph{Btn1} click creates the arc. It will have +will be created is shown on the screen as you move the mouse. +The next @emph{Btn1} click creates the arc. It will have the same width as new lines (displayed in the status line) and appear on the -active layer. The arc leaves the starting point towards the cross hair along -the axis whose distance from the cross hair is largest. Normally this means that -if you drag along the path you want the arc to follow, you'll get what you -want. If the grid is set to the arc radius, then the two distances will be -equal and you won't be able to get all of the possible directions. If this -is thwarting your desires, reduce the grid spacing (@emph{!ShiftG}) and -try again. - +active layer. The arc leaves the starting point at the same place in order +to enable you draw arcs longer than 270 degrees. You can click @emph{Btn1} +again to increase the arc length and press @emph{U} to draw a shorter arc. +@emph{F3} starts a new segment at the end of the previous one. @node Polygons @subsection Polygons and Rectangles diff --git a/src/action.c b/src/action.c index b29d40b..5858f69 100644 --- a/src/action.c +++ b/src/action.c @@ -1042,70 +1042,82 @@ NotifyMode (void) switch (Crosshair.AttachedBox.State) { case STATE_FIRST: - Crosshair.AttachedBox.Point1.X = - Crosshair.AttachedBox.Point2.X = Note.X; - Crosshair.AttachedBox.Point1.Y = - Crosshair.AttachedBox.Point2.Y = Note.Y; + Crosshair.AttachedLine.Point1.X = + Crosshair.AttachedLine.Point2.X = Note.X; + Crosshair.AttachedLine.Point1.Y = + Crosshair.AttachedLine.Point2.Y = Note.Y; Crosshair.AttachedBox.State = STATE_SECOND; + Crosshair.AttachedLine.State = STATE_FIRST; break; case STATE_SECOND: + Crosshair.AttachedLine.Point2.X = Note.X; + Crosshair.AttachedLine.Point2.Y = Note.Y; + Crosshair.AttachedBox.State = STATE_THIRD; + break; + case STATE_THIRD: { ArcType *arc; - Coord wx, wy; - Angle sa, dir; - - wx = Note.X - Crosshair.AttachedBox.Point1.X; - wy = Note.Y - Crosshair.AttachedBox.Point1.Y; - if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx))) - { - Crosshair.AttachedBox.Point2.X = - Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx); - sa = (wx >= 0) ? 0 : 180; -#ifdef ARC45 - if (abs (wy) / 2 >= abs (wx)) - dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45; - else -#endif - dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90; - } - else - { - Crosshair.AttachedBox.Point2.Y = - Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy); - sa = (wy >= 0) ? -90 : 90; -#ifdef ARC45 - if (abs (wx) / 2 >= abs (wy)) - dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45; - else -#endif - dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90; - wy = wx; + Coord arcx, arcy, sa, dir; + Coord preserved_dir; + Coord r; + + arc = 0; + if (gui->shift_is_pressed ()) + { + void rebase_attached_arc (Coord *preserved_dir); + Coord cx,cy,x,y; + + cx = Crosshair.AttachedLine.Point2.X; + cy = Crosshair.AttachedLine.Point2.Y; + x = Crosshair.X; + y = Crosshair.Y; + + if (x != cx || y != cy) + { + Crosshair.AttachedLine.Point1.X = Crosshair.X; + Crosshair.AttachedLine.Point1.Y = Crosshair.Y; + if (Crosshair.AttachedLine.State == STATE_SECOND) + rebase_attached_arc (&preserved_dir); + } + else + break; } - if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT, - Crosshair. - AttachedBox. - Point2.X, - Crosshair. - AttachedBox. - Point2.Y, - abs (wy), - abs (wy), - sa, - dir, - Settings. - LineThickness, - 2 * Settings. - Keepaway, - MakeFlags - (TEST_FLAG - (CLEARNEWFLAG, - PCB) ? - CLEARLINEFLAG : - 0)))) + + r = Crosshair.AttachedObject.BoundingBox.X2; + + if (r == 0) + break; + + arcx = Crosshair.AttachedLine.Point1.X; + arcy = Crosshair.AttachedLine.Point1.Y; + + sa = Crosshair.AttachedObject.BoundingBox.X1; + dir = Crosshair.AttachedObject.BoundingBox.Y1; + + if (Crosshair.AttachedLine.State == STATE_SECOND) + Undo (true); /* remove previous arc */ + + if (!gui->shift_is_pressed () + || Crosshair.AttachedLine.State == STATE_SECOND) + arc = CreateNewArcOnLayer + (CURRENT, arcx, arcy, r, r, sa, dir, + Settings.LineThickness, 2 * Settings.Keepaway, + MakeFlags (TEST_FLAG (CLEARNEWFLAG, PCB) + ? CLEARLINEFLAG : 0) + ); + if (arc) { BoxType *bx; + if (Crosshair.AttachedObject.BoundingBox.Y1 >= 360) + Crosshair.AttachedBox.State = STATE_FIRST; + sa = (Crosshair.AttachedObject.BoundingBox.X1 + + Crosshair.AttachedObject.BoundingBox.Y1); + preserved_dir = dir; + while (sa > 180) sa -= 360; + while (sa < -180) sa += 360; + Crosshair.AttachedObject.Y = sa; bx = GetArcEnds (arc); Crosshair.AttachedBox.Point1.X = @@ -1114,10 +1126,9 @@ NotifyMode (void) Crosshair.AttachedBox.Point2.Y = bx->Y2; AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc); IncrementUndoSerialNumber (); - addedLines++; DrawArc (CURRENT, arc); Draw (); - Crosshair.AttachedBox.State = STATE_THIRD; + Crosshair.AttachedLine.State = STATE_SECOND; } break; } @@ -6293,21 +6304,29 @@ ActionUndo (int argc, char **argv, Coord x, Coord y) } if (Crosshair.AttachedBox.State == STATE_THIRD) { - void *ptr1, *ptr2, *ptr3; - BoxType *bx; - /* guaranteed to succeed */ - SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3, - Crosshair.AttachedBox.Point1.X, - Crosshair.AttachedBox.Point1.Y, 0); - bx = GetArcEnds ((ArcType *) ptr2); - Crosshair.AttachedBox.Point1.X = - Crosshair.AttachedBox.Point2.X = bx->X1; - Crosshair.AttachedBox.Point1.Y = - Crosshair.AttachedBox.Point2.Y = bx->Y1; - AdjustAttachedObjects (); - if (--addedLines == 0) - Crosshair.AttachedBox.State = STATE_SECOND; - } + if (Crosshair.AttachedLine.State == STATE_SECOND) + { + void *ptr1, *ptr2, *ptr3; + BoxType *bx; + /* guaranteed to succeed */ + SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3, + Crosshair.AttachedBox.Point1.X, + Crosshair.AttachedBox.Point1.Y, 0); + bx = GetArcEnds ((ArcType *) ptr2); + Crosshair.AttachedBox.Point1.X = + Crosshair.AttachedBox.Point2.X = bx->X1; + Crosshair.AttachedBox.Point1.Y = + Crosshair.AttachedBox.Point2.Y = bx->Y1; + AdjustAttachedObjects (); + Crosshair.AttachedLine.State = STATE_FIRST; + } + else /* Crosshair.AttachedLine.State != STATE_SECOND */ + { + Crosshair.AttachedBox.State = STATE_SECOND; + RestoreCrosshair (); + return 0; + } + } /* Crosshair.AttachedBox.State == STATE_THIRD */ } /* undo the last destructive operation */ if (Undo (true)) diff --git a/src/crosshair.c b/src/crosshair.c index f0526b8..eff0ac6 100644 --- a/src/crosshair.c +++ b/src/crosshair.c @@ -96,6 +96,82 @@ XORPolygon (PolygonType *polygon, Coord dx, Coord dy) } } +/* Recomputes start angle and span of the attached arc */ +void +rebase_attached_arc (Coord *preserved_dir) +{ + Coord arcx, arcy, dx, dy, cx, cy; + double sa, dir; + Coord r; + + Crosshair.AttachedObject.BoundingBox.X2 = 0; + + arcx = Crosshair.AttachedLine.Point1.X; + arcy = Crosshair.AttachedLine.Point1.Y; + dx = Crosshair.AttachedLine.Point2.X - arcx; + dy = Crosshair.AttachedLine.Point2.Y - arcy; + + if (dx == 0 && dy == 0) + return; + + r = sqrt (dx * (double) dx + dy * (double) dy) + .5; + cx = Crosshair.X - arcx; + cy = Crosshair.Y - arcy; + sa = atan2 (dy, -dx); + + Crosshair.AttachedObject.BoundingBox.X1 = + floor (sa * RAD_TO_DEG + .5); + Crosshair.AttachedObject.BoundingBox.X2 = r; + + if (preserved_dir) + { + Crosshair.AttachedObject.BoundingBox.Y1 = *preserved_dir; + return; + } + + dir = atan2 (cy, -cx) - sa; + + if (Crosshair.AttachedLine.State != STATE_SECOND + && dx * (double) cx + dy * (double) cy > 0) + { + /*arc direction is determined*/ + while (dir < -M_PI) dir += 2 * M_PI; + while (dir > M_PI) dir -= 2 * M_PI; + Crosshair.AttachedObject.X = dir > 0 ? 1: -1; + } + else + { + /*follow the chosen direction*/ + double offset = Crosshair.AttachedObject.X < 0 ? M_PI: -M_PI; + while (dir + offset < -M_PI) dir += 2 * M_PI; + while (dir + offset > M_PI) dir -= 2 * M_PI; + } + + if (Crosshair.AttachedLine.State == STATE_SECOND) + { + /*check whether we should draw a full circle*/ + double sa_ = sa, dir_ = dir; + double sa1 = Crosshair.AttachedObject.Y / RAD_TO_DEG; + + while (sa1 < 0) sa1 += 2 * M_PI; + if (dir_ < 0) + { + sa_ = sa_ + dir; + dir_ = -dir_; + } + while (sa_ < 0) sa_ += 2 * M_PI; + if (sa1 < sa_) + { + dir_ -= 2 * M_PI - sa_; + sa_ = 0; + } + if (dir_ < sa1 - sa_) + dir = 2 * M_PI; + } /* Crosshair.AttachedLine.State == STATE_SECOND */ + dir = floor (dir * RAD_TO_DEG + .5); + Crosshair.AttachedObject.BoundingBox.Y1 = dir; +} + /*----------------------------------------------------------- * Draws the outline of an arc */ @@ -107,46 +183,27 @@ XORDrawAttachedArc (Coord thick) Coord wx, wy; Angle sa, dir; Coord wid = thick / 2; + Coord r; + + rebase_attached_arc (NULL); - wx = Crosshair.X - Crosshair.AttachedBox.Point1.X; - wy = Crosshair.Y - Crosshair.AttachedBox.Point1.Y; - if (wx == 0 && wy == 0) + arc.X = Crosshair.AttachedLine.Point1.X; + arc.Y = Crosshair.AttachedLine.Point1.Y; + r = Crosshair.AttachedObject.BoundingBox.X2; + + if (r == 0) return; - arc.X = Crosshair.AttachedBox.Point1.X; - arc.Y = Crosshair.AttachedBox.Point1.Y; - if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx))) - { - arc.X = Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx); - sa = (wx >= 0) ? 0 : 180; -#ifdef ARC45 - if (abs (wy) >= 2 * abs (wx)) - dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45; - else -#endif - dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90; - } - else - { - arc.Y = Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy); - sa = (wy >= 0) ? -90 : 90; -#ifdef ARC45 - if (abs (wx) >= 2 * abs (wy)) - dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45; - else -#endif - dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90; - wy = wx; - } - wy = abs (wy); + + sa = Crosshair.AttachedObject.BoundingBox.X1; + dir = Crosshair.AttachedObject.BoundingBox.Y1; arc.StartAngle = sa; arc.Delta = dir; - arc.Width = arc.Height = wy; + arc.Width = arc.Height = r; bx = GetArcEnds (&arc); - /* sa = sa - 180; */ - gui->draw_arc (Crosshair.GC, arc.X, arc.Y, wy + wid, wy + wid, sa, dir); + gui->draw_arc (Crosshair.GC, arc.X, arc.Y, r + wid, r + wid, sa, dir); if (wid > pixel_slop) { - gui->draw_arc (Crosshair.GC, arc.X, arc.Y, wy - wid, wy - wid, sa, dir); + gui->draw_arc (Crosshair.GC, arc.X, arc.Y, r - wid, r - wid, sa, dir); gui->draw_arc (Crosshair.GC, bx->X1, bx->Y1, wid, wid, sa, -180 * SGN (dir)); gui->draw_arc (Crosshair.GC, bx->X2, bx->Y2, @@ -582,17 +639,25 @@ DrawAttached (void) break; case ARC_MODE: - if (Crosshair.AttachedBox.State != STATE_FIRST) + if (Crosshair.AttachedBox.State == STATE_SECOND) + XORDrawAttachedLine + (Crosshair.AttachedLine.Point1.X, + Crosshair.AttachedLine.Point1.Y, + Crosshair.X, Crosshair.Y, Settings.LineThickness); + if (Crosshair.AttachedBox.State == STATE_THIRD) { XORDrawAttachedArc (Settings.LineThickness); + XORDrawAttachedLine + (Crosshair.AttachedLine.Point1.X, + Crosshair.AttachedLine.Point1.Y, + Crosshair.X, Crosshair.Y, 1); if (TEST_FLAG (SHOWDRCFLAG, PCB)) { gui->set_color (Crosshair.GC, Settings.CrossColor); - XORDrawAttachedArc (Settings.LineThickness + - 2 * (PCB->Bloat + 1)); + XORDrawAttachedArc (Settings.LineThickness + + 2 * (PCB->Bloat + 1)); gui->set_color (Crosshair.GC, Settings.CrosshairColor); } - } break; diff --git a/src/crosshair.h b/src/crosshair.h index 2dd5e7e..0ae8be7 100644 --- a/src/crosshair.h +++ b/src/crosshair.h @@ -52,5 +52,6 @@ void SetCrosshairRange (Coord, Coord, Coord, Coord); void InitCrosshair (void); void DestroyCrosshair (void); void FitCrosshairIntoGrid (Coord, Coord); +void rebase_attached_arc (Coord *preserved_dir); #endif diff --git a/src/set.c b/src/set.c index 21351da..133a56a 100644 --- a/src/set.c +++ b/src/set.c @@ -238,6 +238,7 @@ void SetMode (int Mode) { static bool recursing = false; + static int skip_line_to_line; /* protect the cursor while changing the mode * perform some additional stuff depending on the new mode * reset 'state' of attached objects @@ -246,6 +247,13 @@ SetMode (int Mode) return; recursing = true; notify_crosshair_change (false); + if (Settings.Mode == LINE_MODE && Mode == LINE_MODE + && skip_line_to_line) + { + skip_line_to_line = 0; + Crosshair.AttachedBox.State = STATE_FIRST; + goto finish; + } addedLines = 0; Crosshair.AttachedObject.Type = NO_TYPE; Crosshair.AttachedObject.State = STATE_FIRST; @@ -265,26 +273,73 @@ SetMode (int Mode) if (Settings.Mode == LINE_MODE && Mode == ARC_MODE && Crosshair.AttachedLine.State != STATE_FIRST) { + Coord x, y; + + Crosshair.AttachedBox.State = STATE_THIRD; Crosshair.AttachedLine.State = STATE_FIRST; - Crosshair.AttachedBox.State = STATE_SECOND; - Crosshair.AttachedBox.Point1.X = Crosshair.AttachedBox.Point2.X = - Crosshair.AttachedLine.Point1.X; - Crosshair.AttachedBox.Point1.Y = Crosshair.AttachedBox.Point2.Y = - Crosshair.AttachedLine.Point1.Y; + + x = Crosshair.AttachedLine.Point1.X; + y = Crosshair.AttachedLine.Point1.Y; + Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X; + Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y; + Crosshair.AttachedLine.Point2.X = x; + Crosshair.AttachedLine.Point2.Y = y; + AdjustAttachedObjects (); } else if (Settings.Mode == ARC_MODE && Mode == LINE_MODE && - Crosshair.AttachedBox.State != STATE_FIRST) + Crosshair.AttachedLine.State == STATE_FIRST) { + Coord x, y; + Crosshair.AttachedBox.State = STATE_FIRST; Crosshair.AttachedLine.State = STATE_SECOND; - Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X = - Crosshair.AttachedBox.Point1.X; - Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y = - Crosshair.AttachedBox.Point1.Y; - Settings.Mode = Mode; + + x = Crosshair.AttachedLine.Point1.X; + y = Crosshair.AttachedLine.Point1.Y; + Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X; + Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y; + Crosshair.AttachedLine.Point2.X = x; + Crosshair.AttachedLine.Point2.Y = y; + skip_line_to_line = !0; + AdjustAttachedObjects (); } + else if (Settings.Mode == ARC_MODE && (Mode == ARC_MODE || Mode == LINE_MODE)) + { + if(Crosshair.AttachedLine.State == STATE_SECOND) + {/* begin the next segment at the end of the previous */ + double angle; + Coord sa, r, center_x, center_y, end_x, end_y; + + sa = Crosshair.AttachedObject.Y; + r = Crosshair.AttachedObject.BoundingBox.X2; + angle = sa / RAD_TO_DEG; + + center_x = Crosshair.AttachedLine.Point1.X; + center_y = Crosshair.AttachedLine.Point1.Y; + + end_x = center_x - r * cos (angle); + end_y = center_y + r * sin (angle); + + if (Mode == ARC_MODE) + { + Crosshair.AttachedLine.Point2.X = end_x; + Crosshair.AttachedLine.Point2.Y = end_y; + Crosshair.AttachedLine.State = STATE_FIRST; + Crosshair.AttachedBox.State = STATE_THIRD; + } + + if (Mode == LINE_MODE) + { + Crosshair.AttachedLine.Point1.X = end_x; + Crosshair.AttachedLine.Point1.Y = end_y; + Crosshair.AttachedBox.State = STATE_FIRST; + Crosshair.AttachedLine.State = STATE_THIRD; + skip_line_to_line = !0; + } + } + } /* Cancel rubberband move */ else if (Settings.Mode == MOVE_MODE) MoveObjectAndRubberband (Crosshair.AttachedObject.Type, @@ -308,6 +363,7 @@ SetMode (int Mode) } } +finish: Settings.Mode = Mode; if (Mode == PASTEBUFFER_MODE) -- 1.7.3.4