]> Shamusworld >> Repos - architektonas/commitdiff
Further progress on Polylines: Polylines can be selected and moved.
authorShamus Hammons <jlhamm@acm.org>
Fri, 14 Jan 2022 01:20:08 +0000 (19:20 -0600)
committerShamus Hammons <jlhamm@acm.org>
Fri, 14 Jan 2022 01:20:08 +0000 (19:20 -0600)
This required a bit of shuffling under the hood to support this, but the
short version is that now Polylines will properly respond to mouse
movement near their contours and control points.  They still can't be
modified or created as of yet, but that's coming.  :-)  Also, slight
change to Circle and Arc handling: now clicking on their contours will
translate them instead of changing their radii.  Holding SHIFT will
allow changing their radii.

src/drawingview.cpp
src/fileio.cpp
src/geometry.cpp
src/objectwidget.cpp
src/structs.cpp [new file with mode: 0644]
src/structs.h
src/utils.cpp
src/vector.cpp
src/vector.h

index 39196a753646a9eb7929e8d699b9e21f89c8fce3..e65bbe92de99f12d47f6f2e12596c25d33485c1d 100644 (file)
@@ -459,31 +459,18 @@ void DrawingView::RenderObjects(Painter * painter, VPVector & v, int layer, bool
                {
                        painter->SetBrush(QBrush(Qt::NoBrush));
                        Polyline * pl = (Polyline *)obj;
-                       Point lastp;
-                       double lastbump;
 
-                       for(VPVectorIter i=pl->points.begin(); i!=pl->points.end(); i++)
+                       for(long unsigned int i=0; i<(pl->points.size()-1); i++)
                        {
-                               if (i != pl->points.begin())
-                               {
-                                       Point p = ((Object *)(*i))->p[0];
-                                       double bump = ((Object *)(*i))->length;
-
-                                       if (lastbump == 0)
-                                               painter->DrawLine(lastp, p);
-                                       else
-                                       {
-                                               Arc a = Geometry::Unpack(lastp, p, lastbump);
-                                               painter->DrawArc(a.p[0], a.radius[0], a.angle[0], a.angle[1]);
-                                       }
+                               Point p1 = pl->points[i];
+                               Point p2 = pl->points[i + 1];
 
-                                       lastp = p;
-                                       lastbump = bump;
-                               }
+                               if (p1.b == 0)
+                                       painter->DrawLine(p1, p2);
                                else
                                {
-                                       lastp = ((Object *)(*i))->p[0];
-                                       lastbump = ((Object *)(*i))->length;
+                                       Arc a = Geometry::Unpack(p1, p2, p1.b);
+                                       painter->DrawArc(a.p[0], a.radius[0], a.angle[0], a.angle[1]);
                                }
                        }
 
@@ -2397,6 +2384,7 @@ Point DrawingView::SnapPointToGrid(Point point)
        point.y = floor(point.y);
        point.z = 0;                                    // Make *sure* Z doesn't go anywhere!!!
        point *= Global::gridSpacing;
+
        return point;
 }
 
@@ -2441,68 +2429,14 @@ Rect DrawingView::GetObjectExtents(Object * obj)
        case OTArc:
        {
                Arc * a = (Arc *)obj;
+               rect = a->Bounds();
+               break;
+       }
 
-               double start = a->angle[0];
-               double end = start + a->angle[1];
-
-               // Swap 'em if the span is negative...
-               if (a->angle[1] < 0)
-               {
-                       end = a->angle[0];
-                       start = end + a->angle[1];
-               }
-
-               rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
-
-               // If the end of the arc is before the beginning, add 360 degrees to it
-               if (end < start)
-                       end += TAU;
-
-/*
-Find which quadrant the start angle is in (consider the beginning of the 90° angle to be in the quadrant, the end to be in the next quadrant).  Then, divide the span into 90° segments.  The integer portion is the definite axis crossings; the remainder needs more scrutiny.  There will be an additional axis crossing if the the sum of the start angle and the remainder is > 90°.
-*/
-#if 1
-               int quadStart = (int)(a->angle[0] / QTR_TAU);
-               double qsRemain = a->angle[0] - ((double)quadStart * QTR_TAU);
-               int numAxes = (int)((a->angle[1] + qsRemain) / QTR_TAU);
-
-               double axis[4] = { 0, 0, 0, 0 };
-               axis[0] = rect.t, axis[1] = rect.l, axis[2] = rect.b, axis[3] = rect.r;
-               double box[4] = { 1.0, -1.0, -1.0, 1.0 };
-
-               for(int i=0; i<numAxes; i++)
-                       axis[(quadStart + i) % 4] = box[(quadStart + i) % 4];
-
-               // The rect is constructed the same way we traverse the axes: TLBR
-               Rect r2(axis[0], axis[1], axis[2], axis[3]);
-
-               rect |= r2;
-#else
-               // Adjust the bounds depending on which axes are crossed
-               if ((start < QTR_TAU) && (end > QTR_TAU))
-                       rect.t = 1.0;
-
-               if ((start < HALF_TAU) && (end > HALF_TAU))
-                       rect.l = -1.0;
-
-               if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
-                       rect.b = -1.0;
-
-               if ((start < TAU) && (end > TAU))
-                       rect.r = 1.0;
-
-               if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
-                       rect.t = 1.0;
-
-               if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
-                       rect.l = -1.0;
-
-               if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
-                       rect.b = -1.0;
-#endif
-
-               rect *= a->radius[0];
-               rect.Translate(a->p[0]);
+       case OTPolyline:
+       {
+               Polyline * p = (Polyline *)obj;
+               rect = p->Bounds();
                break;
        }
 
@@ -2569,58 +2503,7 @@ void DrawingView::CheckObjectBounds(void)
                case OTArc:
                {
                        Arc * a = (Arc *)obj;
-
-                       double start = a->angle[0];
-                       double end = start + a->angle[1];
-
-                       // Swap 'em if the span is negative...
-                       if (a->angle[1] < 0)
-                       {
-                               end = a->angle[0];
-                               start = end + a->angle[1];
-                       }
-
-                       // If the end of the arc is before the beginning, add 360 degrees
-                       // to it
-                       if (end < start)
-                               end += TAU;
-
-#if 1
-                       int quadStart = (int)(a->angle[0] / QTR_TAU);
-                       double qsRemain = a->angle[0] - ((double)quadStart * QTR_TAU);
-                       int numAxes = (int)((a->angle[1] + qsRemain) / QTR_TAU);
-
-                       Rect bounds(sin(start), cos(start), sin(end), cos(end));
-                       const double box[4] = { 1.0, -1.0, -1.0, 1.0 };
-
-                       for(int i=0; i<numAxes; i++)
-                               bounds[(quadStart + i) % 4] = box[(quadStart + i) % 4];
-#else
-                       // Adjust the bounds depending on which axes are crossed
-                       if ((start < QTR_TAU) && (end > QTR_TAU))
-                               bounds.t = 1.0;
-
-                       if ((start < HALF_TAU) && (end > HALF_TAU))
-                               bounds.l = -1.0;
-
-                       if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
-                               bounds.b = -1.0;
-
-                       if ((start < TAU) && (end > TAU))
-                               bounds.r = 1.0;
-
-                       if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
-                               bounds.t = 1.0;
-
-                       if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
-                               bounds.l = -1.0;
-
-                       if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
-                               bounds.b = -1.0;
-#endif
-
-                       bounds *= a->radius[0];
-                       bounds.Translate(a->p[0]);
+                       Rect bounds = a->Bounds();
 
                        if (selection.Contains(bounds))
                                a->selected = true;
@@ -2631,15 +2514,9 @@ void DrawingView::CheckObjectBounds(void)
                case OTPolyline:
                {
                        Polyline * pl = (Polyline *)obj;
-                       Rect r(((Object *)(pl->points[0]))->p[0]);
+                       Rect bounds = pl->Bounds();
 
-                       for(int i=0; i<(pl->points.size()-1); i++)
-                       {
-                               r += ((Object *)(pl->points[i]))->p[0];
-                               r += ((Object *)(pl->points[i + 1]))->p[0];
-                       }
-
-                       if (selection.Contains(r))
+                       if (selection.Contains(bounds))
                                pl->selected = true;
 
                        break;
@@ -2745,7 +2622,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                else if ((distance * Global::zoom) < 5.0)
                        obj->hitObject = true;
 
-               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject);
 
                if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
                        needUpdate = true;
@@ -2768,7 +2645,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
                        obj->hitObject = true;
 
-               obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+               obj->hovered = (obj->hitPoint[0] || obj->hitObject);
 
                if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
                        needUpdate = true;
@@ -2790,7 +2667,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                // Get the span that we're pointing at...
                double span = angle - obj->angle[0];
 
-               // N.B.: Still need to hit test the arc start & arc span handles...
+               // N.B.: Still need to hit test the arc start & arc span handles... [looks like it's DONE?]
                double spanAngle = obj->angle[0] + obj->angle[1];
                Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
                Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
@@ -2818,7 +2695,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
                        obj->hitObject = true;
 
-               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject);
 
                if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
                        needUpdate = true;
@@ -2826,6 +2703,86 @@ bool DrawingView::HitTest(Object * obj, Point point)
                break;
        }
 
+       case OTPolyline:
+       {
+               Polyline * pl = (Polyline *)obj;
+               bool oldHP0 = pl->hitPoint[0], oldHO = pl->hitObject;
+               pl->hitPoint[0] = pl->hitObject = false;
+
+               for(long unsigned int i=0; i<(pl->points.size()-1); i++)
+               {
+                       Point p1 = pl->points[i];
+                       Point p2 = pl->points[i + 1];
+
+                       double dist1 = Vector::Magnitude(p1, point) * Global::zoom;
+                       double dist2 = Vector::Magnitude(p2, point) * Global::zoom;
+
+                       // Check for endpoints of lines and/or arcs first
+                       if (dist1 < 8.0)
+                       {
+                               pl->hitPoint[0] = true;
+                               hoverPoint = p1;
+                               hoverPointValid = true;
+                               pl->ptNum = i;
+                       }
+                       else if (dist2 < 8.0)
+                       {
+                               pl->hitPoint[0] = true;
+                               hoverPoint = p2;
+                               hoverPointValid = true;
+                               pl->ptNum = i + 1;
+                       }
+                       // Check for object (line/arc) last
+                       else if (p1.b == 0)
+                       {
+                               double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
+                               double objDist;
+
+                               // No bump == check for line proximity
+                               if (t < 0.0)
+                                       objDist = dist1;
+                               else if (t > 1.0)
+                                       objDist = dist2;
+                               else
+                               {
+                                       Line l(p1, p2);
+                                       Vector v1 = l.Vect();
+                                       Vector v2(p1, point);
+                                       objDist = fabs((v1.x * v2.y - v2.x * v1.y) / l.Length()) * Global::zoom;
+                               }
+
+                               if (objDist < 5.0)
+                                       pl->hitObject = true;
+                       }
+                       else
+                       {
+                               // We have a bump == check for arc proximity
+                               Arc a = Geometry::Unpack(p1, p2, p1.b);
+                               double length = Vector::Magnitude(a.p[0], point);
+                               double angle = Vector::Angle(a.p[0], point);
+                               double span = angle - a.angle[0];
+
+                               // Ensure point span is positive if we have a positive arc span
+                               if (span < 0 && a.angle[1] > 0)
+                                       span += TAU;
+
+                               // Ensure point span is negative if we have a negative arc span
+                               if (span > 0 && a.angle[1] < 0)
+                                       span -= TAU;
+
+                               if (((fabs(length - a.radius[0]) * Global::zoom) < 2.5) && (fabs(span) < fabs(a.angle[1])))
+                                       pl->hitObject = true;
+                       }
+               }
+
+               pl->hovered = (pl->hitPoint[0] || pl->hitObject);
+
+               if ((oldHP0 != pl->hitPoint[0]) || (oldHO != pl->hitObject))
+                       needUpdate = true;
+
+               break;
+       }
+
        case OTDimension:
        {
                bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHP3 = obj->hitPoint[3], oldHP4 = obj->hitPoint[4], oldHO = obj->hitObject;
@@ -2878,7 +2835,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
                        obj->hitPoint[4] = true;
 
-               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject);
 
                if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
                        needUpdate = true;
@@ -2898,7 +2855,7 @@ bool DrawingView::HitTest(Object * obj, Point point)
                if (r.Contains(point))
                        obj->hitObject = true;
 
-               obj->hovered = (obj->hitObject ? true : false);
+               obj->hovered = obj->hitObject;
 
                if (oldHO != obj->hitObject)
                        needUpdate = true;
@@ -3096,11 +3053,16 @@ N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not*
                        obj->p[0] = point;
                else if (obj->hitObject)
                {
-                       double oldRadius = obj->length;
-                       obj->radius[0] = Vector::Magnitude(obj->p[0], point);
+                       if (shiftDown)
+                       {
+                               double oldRadius = obj->length;
+                               obj->radius[0] = Vector::Magnitude(obj->p[0], point);
 
-                       QString text = QObject::tr("Radius: %1\nScale: %2%");
-                       informativeText = text.arg(obj->radius[0], 0, 'f', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'f', 0);
+                               QString text = QObject::tr("Radius: %1\nScale: %2%");
+                               informativeText = text.arg(obj->radius[0], 0, 'f', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'f', 0);
+                       }
+                       else
+                               obj->p[0] += delta;
                }
 
                break;
@@ -3169,16 +3131,34 @@ N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not*
                {
                        if (shiftDown)
                        {
-                               return;
+                               obj->radius[0] = Vector::Magnitude(obj->p[0], point);
+                               QString text = QObject::tr("Radius: %1");
+                               informativeText = text.arg(obj->radius[0], 0, 'f', 4);
                        }
-
-                       obj->radius[0] = Vector::Magnitude(obj->p[0], point);
-                       QString text = QObject::tr("Radius: %1");
-                       informativeText = text.arg(obj->radius[0], 0, 'f', 4);
+                       else
+                               obj->p[0] += delta;
                }
 
                break;
 
+       case OTPolyline:
+       {
+#if 1
+               // Do this for now...
+               ((Polyline *)obj)->Translate(delta);
+//             Polyline * pl = (Polyline *)obj;
+
+//             for(long unsigned int i=0; i<pl->points.size(); i++)
+//                     pl->points[i] += delta;
+#else
+               Polyline * pl = (Polyline *)obj;
+
+               for(long unsigned int i=0; i<(pl->points.size()-1); i++)
+#endif
+
+               break;
+       }
+
        case OTDimension:
                if (obj->hitPoint[0])
                        obj->p[0] = point;
index ad303f469cdcb0713a9afcb4e67533c1b428c3f3..8058a41d490aba1d0e7b79847e8067326fb61a3c 100644 (file)
@@ -413,16 +413,22 @@ if (errno)
        else if (strcmp(buffer, "POLYLINE") == 0)
        {
                long int size;
-               obj = (Object *)new Polyline();
+               std::vector<Point> pts;
+               uint32_t color;
+               float thickness;
+               int style;
+
                fscanf(file, "(%li)", &size);
-               fscanf(file, " (%i, %f, %i)\n", &obj->color, &obj->thickness, &obj->style);
+               fscanf(file, " (%i, %f, %i)\n", &color, &thickness, &style);
 
                for(int i=0; i<size; i++)
                {
-                       Object * po = new Object();
-                       fscanf(file, "(%lf,%lf,%lf)\n", &po->p[0].x, &po->p[0].y, &po->length);
-                       ((Polyline *)obj)->Add(po);
+                       Point p;
+                       fscanf(file, "(%lf,%lf,%lf)\n", &p.x, &p.y, &p.b);
+                       pts.push_back(p);
                }
+
+               obj = (Object *)new Polyline(pts, thickness, color, style);
        }
        else if (strcmp(buffer, "TEXT") == 0)
        {
@@ -496,10 +502,9 @@ if (errno)
                fprintf(file, "POLYLINE %i (%li)", p->layer, p->points.size());
                fprintf(file, " (%i, %f, %i)\n", obj->color, obj->thickness, obj->style);
 
-               for(VPVectorIter i=p->points.begin(); i!=p->points.end(); i++)
+               for(long unsigned int i=0; i<p->points.size(); i++)
                {
-                       Object * po = (Object *)(*i);
-                       fprintf(file, "(%.16lf,%.16lf,%.16lf)\n", po->p[0].x, po->p[0].y, po->length);
+                       fprintf(file, "(%.16lf,%.16lf,%.16lf)\n", p->points[i].x, p->points[i].y, p->points[i].b);
                }
        }
                break;
index 6b3935db57895998a63026dcb592c7b604229260..f4918560990d8ddcd61709daf54aae49c6b4fadc 100644 (file)
@@ -593,7 +593,7 @@ Arc Geometry::Unpack(Point tail, Point head, double bump)
 {
        double length = Vector::Magnitude(tail, head) / 2.0;
        double bumpLen = length * fabs(bump);
-       Point midpoint = Vector::Midpoint(tail, head);
+       Point midpoint = (tail + head) / 2.0;
        Vector mpNormal = Vector::Normal(tail, head); // Normal points to the left
 
        // Flip the normal if the bump is pointing left
index f31e4f549406e6bf989a7c8bda6be2e95e5f68e7..739d1c174ccd85c0c8fbc5415e6578310bea837f 100644 (file)
@@ -81,9 +81,6 @@ void ObjectWidget::ShowInfo(Object * obj)
                s += QString("Center: &lt;%1, %2&gt;<br>Radius: %3<br>Start: %4&#x00B0;<br>Span: %5&#x00B0;<br>").arg(obj->p[0].x).arg(obj->p[0].y).arg(obj->radius[0]).arg(obj->angle[0] * RADIANS_TO_DEGREES).arg(obj->angle[1] * RADIANS_TO_DEGREES);
                break;
 
-       case OTPolygon:
-               break;
-
        case OTDimension:
        {
                Dimension * d = (Dimension *)obj;
@@ -91,6 +88,9 @@ void ObjectWidget::ShowInfo(Object * obj)
                break;
        }
 
+       case OTPolyline:
+               break;
+
        case OTSpline:
                break;
 
diff --git a/src/structs.cpp b/src/structs.cpp
new file mode 100644 (file)
index 0000000..b731fae
--- /dev/null
@@ -0,0 +1,254 @@
+//
+// structs.cpp: Useful structs to describe objects
+//
+// Part of the Architektonas Project
+// (C) 2022 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO  WHEN        WHAT
+// ---  ----------  ------------------------------------------------------------
+// JLH  01/13/2022  Created this file
+
+#include "structs.h"
+#include <math.h>
+#include "mathconstants.h"
+
+const char objName[OTCount][16] = {
+       "None", "Line", "Circle", "Ellipse", "Arc", "Polygon", "Dimension",
+       "Spline", "Text", "Container"
+};
+const char dimName[DTCount][32] = {
+       "Linear", "Vertical", "Horizontal", "Radial", "Diametric",
+       "Circumferential", "Angular", "Leader"
+};
+
+const char buShortName[BUCount][8] = {
+       "in", "ft", "yd", "mi", "mm", "cm", "m", "km"
+};
+
+const double buInInches[BUCount] = { 1.0, 12.0, 36.0, 1.0/25.4, 1.0/2.54, 1.0/0.0254, 1.0/0.0000254 };
+
+//
+// struct Line
+//
+Line::Line(): type(OTLine), id(Global::objectID++), selected(false), hovered(false), hitObject(false)
+{
+       hitPoint[0] = hitPoint[1] = false;
+}
+
+Line::Line(Vector pt1, Vector pt2, float th/*= 1.0*/, uint32_t c/* = 0*/, int l/*= LSSolid*/): type(OTLine), id(Global::objectID++), layer(0), color(c), thickness(th), style(l), selected(false), hovered(false), hitObject(false)
+{
+       p[0] = pt1;
+       p[1] = pt2;
+       hitPoint[0] = hitPoint[1] = false;
+}
+
+Vector Line::Vect(void)
+{
+       return Vector(p[0], p[1]);
+}
+
+Vector Line::Unit(void)
+{
+       return Vector(p[0], p[1]).Unit();
+}
+
+double Line::Length(void)
+{
+       return Vector(p[0], p[1]).Magnitude();
+}
+
+//
+// struct Circle
+//
+Circle::Circle(): type(OTCircle), id(Global::objectID++)
+{
+}
+
+Circle::Circle(Vector pt1, double r, float th/*= 1.0*/, uint32_t c/*= 0*/, int l/*= LSSolid*/): type(OTCircle), id(Global::objectID++), layer(0), color(c), thickness(th), style(l), selected(false), hovered(false), hitObject(false)
+{
+       p[0] = pt1;
+       radius[0] = r;
+       hitPoint[0] = hitPoint[1] = false;
+}
+
+//
+// struct Ellipse
+//
+Ellipse::Ellipse(): type(OTEllipse), id(Global::objectID++)
+{
+}
+
+Ellipse::Ellipse(Vector pt1, Vector pt2, double r1, double r2, float th/*= 1.0*/, uint32_t c/*= 0*/, int l/*= LSSolid*/): type(OTEllipse), id(Global::objectID++), layer(0), color(c), thickness(th), style(l), selected(false), hovered(false), hitObject(false)
+{
+       p[0] = pt1;
+       p[1] = pt2;
+       radius[0] = r1;
+       radius[1] = r2;
+       hitPoint[0] = hitPoint[1] = false;
+}
+
+//
+// struct Arc
+//
+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)
+{
+       p[0] = pt1; radius[0] = r; angle[0] = a1, angle[1] = a2; hitPoint[0] = hitPoint[1] = hitPoint[2] = false;
+}
+
+Rect Arc::Bounds(void)
+{
+       // Swap start & end angles if the span is negative...
+       double start = (angle[1] > 0 ? angle[0] : angle[0] + angle[1]);
+       double end = (angle[1] > 0 ? angle[0] + angle[1] : angle[0]);
+
+       // If the end of the arc is before the beginning, add 360 degrees
+       // to it
+       if (end < start)
+               end += TAU;
+
+       // Find which quadrant the start angle is in (consider the beginning of
+       // the 90° angle to be in the quadrant, the end to be in the next
+       // quadrant).  Then, divide the span into 90° segments.  The integer
+       // portion is the definite axis crossings; the remainder needs more
+       // scrutiny.  There will be an additional axis crossing if the the sum of
+       // the start angle and the remainder is > 90°.
+       int quadStart = (int)(start / QTR_TAU);
+       double qsRemain = start - ((double)quadStart * QTR_TAU);
+       int numAxes = (int)((fabs(angle[1]) + qsRemain) / QTR_TAU);
+
+       Rect bounds(sin(start), cos(start), sin(end), cos(end));
+       const double box[4] = { 1.0, -1.0, -1.0, 1.0 };
+
+       for(int i=0; i<numAxes; i++)
+               bounds[(quadStart + i) % 4] = box[(quadStart + i) % 4];
+
+       bounds *= radius[0];
+       bounds.Translate(p[0]);
+
+       return bounds;
+}
+
+//
+// struct Dimension
+//
+Dimension::Dimension(): type(OTDimension), id(Global::objectID++), selected(false), hovered(false), hitObject(false)
+{
+       hitPoint[0] = hitPoint[1] = hitPoint[2] = hitPoint[3] = hitPoint[4] = false;
+}
+
+Dimension::Dimension(Vector pt1, Vector pt2, DimensionType dt/*= DTLinear*/, double offs/*= 0*/, float th/*= 1.0*/, uint32_t c/*= 0x0000FF*/, int l/*= LSSolid*/): type(OTDimension), id(Global::objectID++), layer(0), color(c), thickness(th), style(l), selected(false), hovered(false), hitObject(false), subtype(dt), offset(offs)
+{
+       p[0] = pt1;
+       p[1] = pt2;
+       hitPoint[0] = hitPoint[1] = hitPoint[2] = hitPoint[3] = hitPoint[4] = false;
+}
+
+//
+// struct Text
+//
+Text::Text(): type(OTText), id(Global::objectID++)
+{
+}
+
+Text::Text(Vector pt1, const char * str, float th/*= 10.0*/, uint32_t c/*= 0*/): type(OTText), id(Global::objectID++), layer(0), color(c), thickness(th), style(LSSolid), selected(false), hovered(false), hitObject(false), measured(false), s(str)
+{
+       p[0] = pt1;
+       angle[0] = 0;
+}
+
+//prolly don't need this, as this just a special case of a polyline...
+#if 0
+//struct Polygon {
+       Polygon(): type(OTPolygon), id(Global::objectID++) {}
+#endif
+
+//
+// struct Polyline
+//
+Polyline::Polyline(float th/*= 1.0*/, uint32_t c/*= 0*/, int l/*= LSSolid*/): type(OTPolyline), id(Global::objectID++), layer(0), color(c), thickness(th), style(l), selected(false), hovered(false), hitObject(false)
+{
+}
+
+Polyline::Polyline(std::vector<Point> pts, float th/*= 1.0*/, uint32_t c/*= 0*/, int l/*= LSSolid*/): type(OTPolyline), id(Global::objectID++), layer(0), color(c), thickness(th), style(l), selected(false), hovered(false), hitObject(false)
+{
+       points = pts;
+}
+
+void Polyline::Add(Point p)
+{
+       points.push_back(p);
+}
+
+void Polyline::Add(std::vector<Point> pts)
+{
+       points.insert(points.end(), pts.begin(), pts.end());
+}
+
+Rect Polyline::Bounds(void)
+{
+       Rect bounds(points[0]);
+
+       for(long unsigned int i=0; i<(points.size()-1); i++)
+       {
+/*
+Need to check for arc bounds as well here...
+*/
+//             bounds += points[i];
+               bounds += points[i + 1];
+       }
+
+       return bounds;
+}
+
+void Polyline::Translate(Point delta)
+{
+       for(long unsigned int i=0; i<points.size(); i++)
+               points[i] += delta;
+}
+
+//
+// struct Spline
+//
+Spline::Spline(): type(OTSpline), id(Global::objectID++)
+{
+}
+
+//
+// struct Container
+//
+Container::Container(bool tl/*= false*/): type(OTContainer), id(Global::objectID++), selected(false), hovered(false), hitObject(false), topLevel(tl), clicked(NULL), baseUnit(0), unitStyle(0), decimalPrecision(3), fractionalPrecision(4)
+{
+}
+
+void Container::Add(void * obj)
+{
+       objects.push_back(obj);
+}
+
+void Container::Add(VPVector objs)
+{
+       objects.insert(objects.end(), objs.begin(), objs.end());
+}
+
+//     void DeleteContents(void) {}
+/*     void DeleteContents(Container * c)
+       {
+               std::vector<void *>::iterator i;
+
+               for(i=c->objects.begin(); i!=c->objects.end(); i++)
+               {
+                       Object * obj = (Object *)(*i);
+
+                       if (obj->type == OTContainer)
+                               DeleteContainer((Container *)obj);
+
+                       delete *i;
+               }
+       }*/
index 8250a002c8dab39c8ff820b210e70ea60613bac0..e7e7b30643ffa82465922fe2fdc3fead7d6a61cb 100644 (file)
@@ -2,13 +2,13 @@
 #define __STRUCTS_H__
 
 #include <stdint.h>
-#include <vector>
 #include <string>
+#include <vector>
 #include "global.h"
 #include "rect.h"
 #include "vector.h"
 
-enum ObjectType { OTNone = 0, OTLine, OTCircle, OTEllipse, OTArc, OTPolygon, OTDimension, OTSpline, OTText, OTContainer, OTPolyline, OTCount };
+enum ObjectType { OTNone = 0, OTLine, OTCircle, OTEllipse, OTArc, OTDimension, OTPolyline, OTSpline, OTText, OTContainer, OTCount };
 
 enum DimensionType { DTLinear = 0, DTLinearVert, DTLinearHorz, DTRadial, DTDiametric, DTCircumferential, DTAngular, DTLeader, DTCount };
 
@@ -18,20 +18,10 @@ enum ToolState { TSNone, TSPoint1, TSPoint2, TSPoint3, TSPoint4, TSDone };
 
 enum BasicUnit { BUInch = 0, BUFoot, BUYard, BUMile, BUMM, BUCM, BUM, BUKM, BUCount };
 
-const char objName[OTCount][16] = {
-       "None", "Line", "Circle", "Ellipse", "Arc", "Polygon", "Dimension",
-       "Spline", "Text", "Container"
-};
-const char dimName[DTCount][32] = {
-       "Linear", "Vertical", "Horizontal", "Radial", "Diametric",
-       "Circumferential", "Angular", "Leader"
-};
-
-const char buShortName[BUCount][8] = {
-       "in", "ft", "yd", "mi", "mm", "cm", "m", "km"
-};
-
-const double buInInches[BUCount] = { 1.0, 12.0, 36.0, 1.0/25.4, 1.0/2.54, 1.0/0.0254, 1.0/0.0000254 };
+extern const char objName[OTCount][16];
+extern const char dimName[DTCount][32];
+extern const char buShortName[BUCount][8];
+extern const double buInInches[BUCount];
 
 #define OBJECT_COMMON \
        int type;         \
@@ -56,43 +46,33 @@ struct Object {
 struct Line {
        OBJECT_COMMON;
 
-       Line(): type(OTLine), id(Global::objectID++), selected(false), hovered(false), hitObject(false) { hitPoint[0] = hitPoint[1] = false; }
-       Line(Vector pt1, Vector pt2, float th = 1.0, uint32_t c = 0, int l = LSSolid):
-               type(OTLine), id(Global::objectID++), layer(0), color(c), thickness(th),
-               style(l), selected(false), hovered(false), hitObject(false) { p[0] = pt1; p[1] = pt2; hitPoint[0] = hitPoint[1] = false; }
-       Vector Vect(void) { return Vector(p[0], p[1]); }
-       Vector Unit(void) { return Vector(p[0], p[1]).Unit(); }
-       double Length(void) { return Vector(p[0], p[1]).Magnitude(); }
+       Line();
+       Line(Vector pt1, Vector pt2, float th = 1.0, uint32_t c = 0, int l = LSSolid);
+       Vector Vect(void);
+       Vector Unit(void);
+       double Length(void);
 };
 
 struct Circle {
        OBJECT_COMMON;
 
-       Circle(): type(OTCircle), id(Global::objectID++) {}
-       Circle(Vector pt1, double r, float th = 1.0, uint32_t c = 0, int l = LSSolid):
-               type(OTCircle), id(Global::objectID++), layer(0), color(c), thickness(th),
-               style(l), selected(false), hovered(false), hitObject(false)
-               { p[0] = pt1; radius[0] = r; hitPoint[0] = hitPoint[1] = false; }
+       Circle();
+       Circle(Vector pt1, double r, float th = 1.0, uint32_t c = 0, int l = LSSolid);
 };
 
 struct Ellipse {
        OBJECT_COMMON;
 
-       Ellipse(): type(OTEllipse), id(Global::objectID++) {}
-       Ellipse(Vector pt1, Vector pt2, double r1, double r2, float th = 1.0, uint32_t c = 0, int l = LSSolid):
-               type(OTEllipse), id(Global::objectID++), layer(0), color(c), thickness(th),
-               style(l), selected(false), hovered(false), hitObject(false)
-               { p[0] = pt1; p[1] = pt2; radius[0] = r1; radius[1] = r2; hitPoint[0] = hitPoint[1] = false; }
+       Ellipse();
+       Ellipse(Vector pt1, Vector pt2, double r1, double r2, float th = 1.0, uint32_t c = 0, int l = LSSolid);
 };
 
 struct Arc {
        OBJECT_COMMON;
 
-       Arc(): type(OTArc), id(Global::objectID++) {}
-       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)
-               { p[0] = pt1; radius[0] = r; angle[0] = a1, angle[1] = a2; hitPoint[0] = hitPoint[1] = hitPoint[2] = false; }
+       Arc();
+       Arc(Vector pt1, double r, double a1, double a2, float th = 1.0, uint32_t c = 0, int l = LSSolid);
+       Rect Bounds(void);
 };
 
 struct Dimension {
@@ -102,14 +82,8 @@ struct Dimension {
        Point lp[2];            // Line point, the actual dimension line
        Object * obj[2];        // Pointer to attached objects (circle, lines for angle)
 
-       Dimension(): type(OTDimension), id(Global::objectID++), selected(false),
-               hovered(false), hitObject(false)
-               { hitPoint[0] = hitPoint[1] = hitPoint[2] = hitPoint[3] = hitPoint[4] = false; }
-       Dimension(Vector pt1, Vector pt2, DimensionType dt = DTLinear, double offs = 0, float th = 1.0, uint32_t c = 0x0000FF, int l = LSSolid):
-               type(OTDimension), id(Global::objectID++), layer(0), color(c),
-               thickness(th), style(l), selected(false), hovered(false),
-               hitObject(false), subtype(dt), offset(offs)
-               { p[0] = pt1; p[1] = pt2; hitPoint[0] = hitPoint[1] = hitPoint[2] = hitPoint[3] = hitPoint[4] = false; }
+       Dimension();
+       Dimension(Vector pt1, Vector pt2, DimensionType dt = DTLinear, double offs = 0, float th = 1.0, uint32_t c = 0x0000FF, int l = LSSolid);
 };
 
 struct Text {
@@ -118,37 +92,29 @@ struct Text {
        bool measured;
        std::string s;
 
-       Text(): type(OTText), id(Global::objectID++) {}
-       Text(Vector pt1, const char * str, float th = 10.0, uint32_t c = 0):
-               type(OTText), id(Global::objectID++), layer(0), color(c), thickness(th),
-               style(LSSolid), selected(false), hovered(false), hitObject(false),
-               measured(false), s(str) { p[0] = pt1; angle[0] = 0; }
-};
-
-//prolly don't need this, as this just a special case of a polyline...
-struct Polygon {
-       OBJECT_COMMON;
-       int sides;
-
-       Polygon(): type(OTPolygon), id(Global::objectID++) {}
+       Text();
+       Text(Vector pt1, const char * str, float th = 10.0, uint32_t c = 0);
 };
 
 struct Polyline {
        OBJECT_COMMON;
-       VPVector points;
-//need this?  could just repeat the endpoint as well...
-//     bool closed;
-       Object * clicked;
-
-       Polyline(): type(OTPolyline), id(Global::objectID++), selected(false), hovered(false), hitObject(false), clicked(NULL) {}
-       void Add(void * obj) { points.push_back(obj); }
-       void Add(VPVector objs) { points.insert(points.end(), objs.begin(), objs.end()); }
+       std::vector<Point> points;
+//     bool closed; //need this?  could just repeat the endpoint as well...
+//     Object * clicked;
+       int ptNum;
+
+       Polyline(float th = 1.0, uint32_t c = 0, int l = LSSolid);
+       Polyline(std::vector<Point>, float th = 1.0, uint32_t c = 0, int l = LSSolid);
+       void Add(Point);
+       void Add(std::vector<Point>);
+       Rect Bounds(void);
+       void Translate(Point);
 };
 
 struct Spline {
        OBJECT_COMMON;
 
-       Spline(): type(OTSpline), id(Global::objectID++) {}
+       Spline();
 };
 
 struct Container {
@@ -163,24 +129,9 @@ struct Container {
        int decimalPrecision;    // 0-5, which, +1, is # of decimal places
        int fractionalPrecision; // 0-5, which, +1, is 1/(2^n)
 
-       Container(bool tl = false): type(OTContainer), id(Global::objectID++), selected(false), hovered(false), hitObject(false), topLevel(tl), clicked(NULL), baseUnit(0), unitStyle(0), decimalPrecision(3), fractionalPrecision(4) {}
-       void Add(void * obj) { objects.push_back(obj); }
-       void Add(VPVector objs) { objects.insert(objects.end(), objs.begin(), objs.end()); }
-//     void DeleteContents(void) {}
-/*     void DeleteContents(Container * c)
-       {
-               std::vector<void *>::iterator i;
-
-               for(i=c->objects.begin(); i!=c->objects.end(); i++)
-               {
-                       Object * obj = (Object *)(*i);
-
-                       if (obj->type == OTContainer)
-                               DeleteContainer((Container *)obj);
-
-                       delete *i;
-               }
-       }*/
+       Container(bool tl = false);
+       void Add(void * obj);
+       void Add(VPVector objs);
 };
 
 #endif // __STRUCTS_H__
index c038f3670ee25598662841a19669e80956e5c9ac..cd05ba735e95f30ac96aacf56f205e6548840a79 100644 (file)
@@ -73,6 +73,11 @@ Object * CopyObject(Object * obj)
                newObject = new Dimension();
                memcpy(newObject, obj, sizeof(Dimension));
                break;
+       case OTPolyline:
+               newObject = new Polyline();
+               memcpy(newObject, obj, sizeof(Polyline));
+               ((Polyline *)newObject)->points = ((Polyline *)obj)->points;
+               break;
        case OTSpline:
                newObject = new Spline();
                memcpy(newObject, obj, sizeof(Spline));
@@ -137,6 +142,12 @@ Object * CopyObject2(Object * obj)
                memcpy(newObject, obj, sizeof(Dimension));
                break;
 
+       case OTPolyline:
+               newObject = new Polyline();
+               memcpy(newObject, obj, sizeof(Polyline));
+               ((Polyline *)newObject)->points = ((Polyline *)obj)->points;
+               break;
+
        case OTSpline:
                newObject = new Spline();
                memcpy(newObject, obj, sizeof(Spline));
@@ -399,6 +410,8 @@ void TranslateObject(Object * obj, Point delta)
                for(VPVectorIter i=c->objects.begin(); i!=c->objects.end(); i++)
                        TranslateObject((Object *)*i, delta);
        }
+       else if (obj->type == OTPolyline)
+               ((Polyline *)obj)->Translate(delta);
 
        obj->p[0] += delta;
        obj->p[1] += delta;
@@ -482,6 +495,10 @@ void TranslateObjects(VPVector & v, Point delta)
                        Container * c = (Container *)obj;
                        TranslateObjects(c->objects, delta);
                }
+               else if (obj->type == OTPolyline)
+               {
+                       ((Polyline *)obj)->Translate(delta);
+               }
        }
 }
 
index 8b2dda2a22301372e956b8bad7fcb2da5fd56d37..fbe45ff70f82b9713ff454b4e8eb432407f66069 100644 (file)
 
 #include "vector.h"
 
-#include <math.h>                                                              // For sqrt()
+#include <math.h>                              // For sqrt()
 #include "mathconstants.h"
 
 // Vector implementation
 
-Vector::Vector(double xx/*= 0*/, double yy/*= 0*/, double zz/*= 0*/): x(xx), y(yy), z(zz)
+Vector::Vector(double xx/*= 0*/, double yy/*= 0*/, double zz/*= 0*/): x(xx), y(yy), z(zz), b(0)
 {
 }
 
-Vector::Vector(Vector tail, Vector head): x(head.x - tail.x), y(head.y - tail.y), z(head.z - tail.z)
+Vector::Vector(Vector tail, Vector head): x(head.x - tail.x), y(head.y - tail.y), z(head.z - tail.z), b(0)
 {
 }
 
-Vector::Vector(const Vector &v): x(v.x), y(v.y), z(v.z)
+Vector::Vector(const Vector &v): x(v.x), y(v.y), z(v.z), b(v.b)
 {
 }
 
@@ -43,7 +43,7 @@ void Vector::SetAngleAndLength(double angle, double length)
 
 Vector Vector::operator=(Vector const v)
 {
-       x = v.x, y = v.y, z = v.z;
+       x = v.x, y = v.y, z = v.z, b = v.b;
 
        return *this;
 }
@@ -267,7 +267,7 @@ bool Vector::isZero(double epsilon/*= 1e-6*/)
        return Vector(-v.y, v.x);
 }
 
-/*static*/ double Vector::AngleBetween(Vector a, Vector b)
+/*static*/ double Vector::AngleBetween(Vector a1, Vector a2)
 {
        // This is done using the following formula:
        // (a . b) = ||a|| ||b|| cos(theta)
@@ -276,10 +276,10 @@ bool Vector::isZero(double epsilon/*= 1e-6*/)
        // Also, the vectors a & b have to be non-zero.
        // Also, have to check using an epsilon because acos will not return an
        // exact value if the vectors are orthogonal.
-       if (a.isZero() || b.isZero())
+       if (a1.isZero() || a2.isZero())
                return 0;
 
-       return acos(a.Dot(b) / (a.Magnitude() * b.Magnitude()));
+       return acos(a1.Dot(a2) / (a1.Magnitude() * a2.Magnitude()));
 }
 
 /*static*/ Point Vector::Midpoint(Point p1, Point p2)
index 97985c50aeb5286f15cf80efcff444c51c3e00dc..b6292ab997c77f06463ddec29731790803e9d0e6 100644 (file)
@@ -57,11 +57,11 @@ class Vector
                static double Angle(Point p1, Point p2);
                static double Parameter(Vector v1, Vector v2, Vector p);
                static Vector Normal(Vector v1, Vector v2);
-               static double AngleBetween(Vector a, Vector b);
+               static double AngleBetween(Vector a1, Vector a2);
                static Point Midpoint(Point p1, Point p2);
 
        public:
-               double x, y, z;
+               double x, y, z, b; // "b" is an extra used mainly for arc bumps
 };
 
 #endif // __VECTOR_H__