From dacd9dc2c1ac093e4945f7befaeb5ff1801a0f52 Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Sat, 15 Jan 2022 12:05:22 -0600 Subject: [PATCH] More polyline upgrading: points and arcs are now editable with the GUI. --- architektonas.pro | 1 + src/applicationwindow.cpp | 3 ++- src/drawingview.cpp | 40 +++++++++++++++++++++-------- src/geometry.cpp | 53 ++++++++++++++++++++++++++++++++++++++- src/geometry.h | 3 +++ src/structs.cpp | 25 ++++++++++++++++-- src/structs.h | 3 ++- 7 files changed, 112 insertions(+), 16 deletions(-) diff --git a/architektonas.pro b/architektonas.pro index 5f68a70..9e16793 100644 --- a/architektonas.pro +++ b/architektonas.pro @@ -98,6 +98,7 @@ SOURCES = \ src/promptlineedit.cpp \ src/rect.cpp \ src/settingsdialog.cpp \ + src/structs.cpp \ src/units.cpp \ src/utils.cpp \ src/vector.cpp diff --git a/src/applicationwindow.cpp b/src/applicationwindow.cpp index 8bbdbc1..b91177d 100644 --- a/src/applicationwindow.cpp +++ b/src/applicationwindow.cpp @@ -1140,7 +1140,8 @@ void ApplicationWindow::ReadSettings(void) QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); QSize size = settings.value("size", QSize(400, 400)).toSize(); drawing->useAntialiasing = settings.value("useAntialiasing", true).toBool(); - snapToGridAct->setChecked(settings.value("snapToGrid", true).toBool()); + Global::snapToGrid = settings.value("snapToGrid", true).toBool(); + snapToGridAct->setChecked(Global::snapToGrid); resize(size); move(pos); restoreState(settings.value("windowState").toByteArray()); diff --git a/src/drawingview.cpp b/src/drawingview.cpp index e65bbe9..78fe961 100644 --- a/src/drawingview.cpp +++ b/src/drawingview.cpp @@ -2752,7 +2752,10 @@ bool DrawingView::HitTest(Object * obj, Point point) } if (objDist < 5.0) + { pl->hitObject = true; + pl->ptNum = i; + } } else { @@ -2771,7 +2774,10 @@ bool DrawingView::HitTest(Object * obj, Point point) span -= TAU; if (((fabs(length - a.radius[0]) * Global::zoom) < 2.5) && (fabs(span) < fabs(a.angle[1]))) + { pl->hitObject = true; + pl->ptNum = i; + } } } @@ -3143,18 +3149,30 @@ N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not* case OTPolyline: { -#if 1 - // Do this for now... - ((Polyline *)obj)->Translate(delta); -// Polyline * pl = (Polyline *)obj; - -// for(long unsigned int i=0; ipoints.size(); i++) -// pl->points[i] += delta; -#else Polyline * pl = (Polyline *)obj; - for(long unsigned int i=0; i<(pl->points.size()-1); i++) -#endif + if (!shiftDown) + pl->Translate(delta); + else + { + // Move selected point by itself (if hit) + if (pl->hitPoint[0]) + { + point.b = pl->points[pl->ptNum].b; + pl->points[pl->ptNum] = point; + } + else if (pl->hitObject) + { + // move arc radius, or maybe move line segment? :-/ + int ptNum = pl->ptNum; + Arc a = Geometry::FindArcForThreePoints(pl->points[ptNum], pl->points[ptNum + 1], point); + pl->points[ptNum].b = Geometry::Pack(&a); + + // This doesn't quite get there... Maybe < 0.001? + if (fabs(pl->points[ptNum].b) < EPSILON) + pl->points[ptNum].b = 0; + } + } break; } @@ -3166,7 +3184,7 @@ N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not* obj->p[1] = point; else if (obj->hitObject) { - // Move measurement lines in/out + // Move extension lines in/out if (shiftDown) { Dimension * d = (Dimension *)obj; diff --git a/src/geometry.cpp b/src/geometry.cpp index f491856..82d24e8 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -525,6 +525,21 @@ Vector Geometry::GetNormalOfPointAndLine(Point p, Line * l) return normal; } +// +// Returns the orientation of the points when traversed in order (from p1 to +// p2 to p3). +1 = CCW, -1 = CW, 0 = colinear +// +int Geometry::Orientation(Point p1, Point p2, Point p3) +{ + double xx = p2.x - p1.x, yy = p2.y - p1.y; + double aa = p3.x - p1.x, bb = p3.y - p1.y; + + // Mult by -1 if screen coords already transformed + double zz = (xx * bb) - (yy * aa); + + return (zz < 0 ? -1 : (zz == 0 ? 0 : 1)); +} + Circle Geometry::FindCircleForThreePoints(Point p1, Point p2, Point p3) { // We use matrices and determinants to find the center and radius of the @@ -582,6 +597,27 @@ Circle Geometry::FindCircleForThreePoints(Point p1, Point p2, Point p3) return c; } +Arc Geometry::FindArcForThreePoints(Point p1, Point p2, Point p3) +{ + int orientation = Orientation(p1, p2, p3); + + // Sanity check: if points are colinear, there is no arc + if (orientation == 0) + return Arc(); + + Circle c = FindCircleForThreePoints(p1, p2, p3); + double a1 = Vector::Angle(c.p[0], p1); + double a2 = Vector::Angle(c.p[0], p2); + double span = a2 - a1; + + if (orientation < 0 && span > 0) + span -= TAU; + else if (orientation > 0 && span < 0) + span += TAU; + + return Arc(c.p[0], c.radius[0], a1, span); +} + double Geometry::Determinant3x3(double a11, double a12, double a13, double a21, double a22, double a23, double a31, double a32, double a33) { return (a11 * ((a22 * a33) - (a32 * a23))) @@ -602,7 +638,6 @@ Arc Geometry::Unpack(Point tail, Point head, double bump) // N.B.: The radius can also be found with r = (a² + h²) / 2h where a is // the 1/2 length of the line segment and h is the bump length. -// double radius = (length + (1.0 / length)) / 2.0; double radius = 0.5 * (((length * length) + (bumpLen * bumpLen)) / bumpLen); Vector ctrVec = mpNormal * (radius - bumpLen); Point center = midpoint + ctrVec; @@ -621,3 +656,19 @@ Arc Geometry::Unpack(Point tail, Point head, double bump) return Arc(center, radius, angle1, span); } + +double Geometry::Pack(Arc * a) +{ + Point p1 = a->p[0] + (Vector(cos(a->angle[0]), sin(a->angle[0])) * a->radius[0]); + Point p2 = a->p[0] + (Vector(cos(a->angle[0] + a->angle[1]), sin(a->angle[0] + a->angle[1])) * a->radius[0]); + double endpointLen = Vector::Magnitude(p1, p2) / 2.0; + + // Bump height can be found with h = r ± sqr(r² - a²) where r is the + // radius, and a is the 1/2 length of the endpoint's line segment. The + // plus/minus term is positive if the arc span is less than 1/2 TAU, + // negative if the arc span is greater than 1/2 TAU. + double discriminant = sqrt((a->radius[0] * a->radius[0]) - (endpointLen * endpointLen)); + double bumpLen = a->radius[0] + (discriminant * (fabs(a->angle[1]) < HALF_TAU ? 1.0 : -1.0)); + + return (bumpLen / endpointLen) * (a->angle[1] > 0 ? -1.0 : 1.0); +} diff --git a/src/geometry.h b/src/geometry.h index 0a05b37..d657db6 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -24,9 +24,12 @@ class Geometry static void FindTangents(Object *, Object *); static Point NearestTo(Point, Point, Point); static Vector GetNormalOfPointAndLine(Point, Line *); + static int Orientation(Point, Point, Point); static Circle FindCircleForThreePoints(Point, Point, Point); + static Arc FindArcForThreePoints(Point, Point, Point); static double Determinant3x3(double, double, double, double, double, double, double, double, double); static Arc Unpack(Point, Point, double); + static double Pack(Arc *); }; #endif // __GEOMETRY_H__ diff --git a/src/structs.cpp b/src/structs.cpp index b731fae..883e934 100644 --- a/src/structs.cpp +++ b/src/structs.cpp @@ -97,9 +97,30 @@ Arc::Arc(): type(OTArc), id(Global::objectID++) { } -Arc::Arc(Vector pt1, double r, double a1, double a2, float th/*= 1.0*/, uint32_t c/*= 0*/, int l/*= LSSolid*/): type(OTArc), id(Global::objectID++), layer(0), color(c), thickness(th), style(l), selected(false), hovered(false), hitObject(false) +Arc::Arc(Vector ctr, double r, double a1, double a2, float th/*= 1.0*/, uint32_t c/*= 0*/, int l/*= LSSolid*/): type(OTArc), id(Global::objectID++), layer(0), color(c), thickness(th), style(l), selected(false), hovered(false), hitObject(false) { - p[0] = pt1; radius[0] = r; angle[0] = a1, angle[1] = a2; hitPoint[0] = hitPoint[1] = hitPoint[2] = false; + p[0] = ctr; + radius[0] = r; + angle[0] = a1; + angle[1] = a2; + hitPoint[0] = hitPoint[1] = hitPoint[2] = false; +} + +Arc::Arc(Vector ctr, double r, Point p1, Point p2, float th/*= 1.0*/, uint32_t c/*= 0*/, int l/*= LSSolid*/): type(OTArc), id(Global::objectID++), layer(0), color(c), thickness(th), style(l), selected(false), hovered(false), hitObject(false) +{ + p[0] = ctr; + radius[0] = r; + angle[0] = Vector::Angle(ctr, p1); +/* +s = 10, e = 20, span = 10 (why not -350?) +s = 20, e = 10, span = -10 (why not 350?) +s = 10, e = 350, span = 340 (why not -20?) +s = 350, e = 10, span = -340 (why not 20?) + +below is still not right, we need one more point to disambiguate this... +*/ + angle[1] = Vector::Angle(ctr, p2) - angle[0]; + hitPoint[0] = hitPoint[1] = hitPoint[2] = false; } Rect Arc::Bounds(void) diff --git a/src/structs.h b/src/structs.h index e7e7b30..ea2245f 100644 --- a/src/structs.h +++ b/src/structs.h @@ -71,7 +71,8 @@ struct Arc { OBJECT_COMMON; Arc(); - Arc(Vector pt1, double r, double a1, double a2, float th = 1.0, uint32_t c = 0, int l = LSSolid); + Arc(Vector ctr, double r, double a1, double a2, float th = 1.0, uint32_t c = 0, int l = LSSolid); + Arc(Vector ctr, double r, Point p1, Point p2, float th = 1.0, uint32_t c = 0, int l = LSSolid); Rect Bounds(void); }; -- 2.37.2