]> Shamusworld >> Repos - architektonas/commitdiff
Added angle snap to whole degrees, ability to manipulate Dimensions.
authorShamus Hammons <jlhamm@acm.org>
Fri, 10 Feb 2017 22:09:24 +0000 (16:09 -0600)
committerShamus Hammons <jlhamm@acm.org>
Fri, 10 Feb 2017 22:09:24 +0000 (16:09 -0600)
src/drawingview.cpp
src/drawingview.h
src/geometry.cpp
src/structs.h

index 9fb0332a2ff9ccc5b327f460fafbc21435bebc39..943a9b9f4cd9edb088df0a5d8a75a9824512d4cd 100644 (file)
@@ -49,7 +49,7 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
        gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
        scale(1.0), offsetX(-10), offsetY(-10), document(true),
        gridPixels(0), collided(false), hoveringIntersection(false),
-       dragged(NULL), draggingObject(false)
+       dragged(NULL), draggingObject(false), angleSnap(false)
 {
 //wtf? doesn't work except in c++11??? document = { 0 };
        setBackgroundRole(QPalette::Base);
@@ -444,7 +444,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                        Vector v(d->p[0], d->p[1]);
                        double angle = v.Angle();
                        Vector unit = v.Unit();
-                       Vector linePt1 = d->p[0], linePt2 = d->p[1];
+                       d->lp[0] = d->p[0], d->lp[1] = d->p[1];
                        Vector ortho;
                        double x1, y1, length;
 
@@ -465,7 +465,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                                        angle = QTR_TAU;
                                }
 
-                               linePt1.x = linePt2.x = x1;
+                               d->lp[0].x = d->lp[1].x = x1;
                                length = fabs(d->p[0].y - d->p[1].y);
                        }
                        else if (d->subtype == DTLinearHorz)
@@ -485,22 +485,22 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
                                        angle = HALF_TAU;
                                }
 
-                               linePt1.y = linePt2.y = y1;
+                               d->lp[0].y = d->lp[1].y = y1;
                                length = fabs(d->p[0].x - d->p[1].x);
                        }
                        else if (d->subtype == DTLinear)
                        {
-                               angle = Vector(linePt1, linePt2).Angle();
-                               ortho = Vector::Normal(linePt1, linePt2);
+                               angle = Vector(d->lp[0], d->lp[1]).Angle();
+                               ortho = Vector::Normal(d->lp[0], d->lp[1]);
                                length = v.Magnitude();
                        }
 
-                       unit = Vector(linePt1, linePt2).Unit();
+                       unit = Vector(d->lp[0], d->lp[1]).Unit();
 
-                       Point p1 = linePt1 + (ortho * 10.0 * scaledThickness);
-                       Point p2 = linePt2 + (ortho * 10.0 * scaledThickness);
-                       Point p3 = linePt1 + (ortho * 16.0 * scaledThickness);
-                       Point p4 = linePt2 + (ortho * 16.0 * scaledThickness);
+                       Point p1 = d->lp[0] + (ortho * 10.0 * scaledThickness);
+                       Point p2 = d->lp[1] + (ortho * 10.0 * scaledThickness);
+                       Point p3 = d->lp[0] + (ortho * 16.0 * scaledThickness);
+                       Point p4 = d->lp[1] + (ortho * 16.0 * scaledThickness);
                        Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness);
                        Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness);
 
@@ -514,7 +514,7 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
 
                        // Calculate whether or not the arrowheads are too crowded to put
                        // inside the extension lines. 9.0 is the length of the arrowhead.
-                       double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness));
+                       double t = Geometry::ParameterOfLineAndPoint(d->lp[0], d->lp[1], d->lp[1] - (unit * 9.0 * scaledThickness));
 
                        // On the screen, it's acting like this is actually 58%...
                        // This is correct, we want it to happen at > 50%
@@ -557,6 +557,51 @@ void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int
 
                        painter->DrawAngledText(ctr, angle, dimText, scaledThickness);
 
+                       if (d->hitObject)
+                       {
+                               Point hp1 = (p1 + p2) / 2.0;
+                               Point hp2 = (p1 + hp1) / 2.0;
+                               Point hp3 = (hp1 + p2) / 2.0;
+
+                               if (d->hitPoint[2])
+                               {
+                                       painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
+                                       painter->SetBrush(QBrush(QColor(Qt::magenta)));
+                                       painter->DrawArrowHandle(hp1, ortho.Angle() + HALF_TAU);
+                                       painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
+                               }
+
+                               painter->DrawHandle(hp1);
+                               painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
+
+                               if (d->hitPoint[3])
+                               {
+                                       painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
+                                       painter->SetBrush(QBrush(QColor(Qt::magenta)));
+                                       painter->DrawArrowToLineHandle(hp2, (d->subtype == DTLinearVert ? v.Angle() - QTR_TAU : (v.Angle() < HALF_TAU ? HALF_TAU : 0)));
+                                       painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
+                               }
+
+                               painter->DrawHandle(hp2);
+                               painter->SetPen(QPen(Qt::blue, 1.0 * Global::zoom * scaledThickness, Qt::SolidLine));
+
+                               if (d->hitPoint[4])
+                               {
+                                       painter->SetPen(QPen(Qt::magenta, 1.0, Qt::SolidLine));
+                                       painter->SetBrush(QBrush(QColor(Qt::magenta)));
+                                       painter->DrawArrowToLineHandle(hp3, (d->subtype == DTLinearHorz ? v.Angle() - QTR_TAU : (v.Angle() > HALF_TAU && v.Angle() < THREE_QTR_TAU ? THREE_QTR_TAU : QTR_TAU)));
+                                       painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DotLine));
+                               }
+
+                               painter->DrawHandle(hp3);
+                       }
+
+                       if (obj->hitPoint[0])
+                               painter->DrawHandle(obj->p[0]);
+
+                       if (obj->hitPoint[1])
+                               painter->DrawHandle(obj->p[1]);
+
                        break;
                }
                case OTText:
@@ -646,6 +691,9 @@ void DrawingView::resizeEvent(QResizeEvent * /*event*/)
 
 void DrawingView::ToolHandler(int mode, Point p)
 {
+       // Drop angle snap until it's needed
+       angleSnap = false;
+
        if (Global::tool == TTLine)
                LineHandler(mode, p);
        else if (Global::tool == TTCircle)
@@ -904,9 +952,15 @@ void DrawingView::ArcHandler(int mode, Point p)
                else if (Global::toolState == TSPoint2)
                        toolPoint[1] = p;
                else if (Global::toolState == TSPoint3)
+               {
                        toolPoint[2] = p;
+                       angleSnap = true;
+               }
                else
+               {
                        toolPoint[3] = p;
+                       angleSnap = true;
+               }
 
                break;
        case ToolMouseUp:
@@ -920,7 +974,7 @@ void DrawingView::ArcHandler(int mode, Point p)
                {
                        if (shiftDown)
                        {
-                               // Key override is telling us to start circle at new center, not
+                               // Key override is telling us to start arc at new center, not
                                // continue the current one.
                                toolPoint[0] = toolPoint[1];
                                return;
@@ -980,6 +1034,7 @@ void DrawingView::RotateHandler(int mode, Point p)
                        if (shiftDown)
                                return;
 
+                       angleSnap = true;
                        double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
                        std::vector<void *>::iterator j = select.begin();
                        std::vector<Object>::iterator i = toolScratch.begin();
@@ -1082,6 +1137,7 @@ void DrawingView::MirrorHandler(int mode, Point p)
                        if (shiftDown)
                                return;
 
+                       angleSnap = true;
                        double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
                        std::vector<void *>::iterator j = select.begin();
                        std::vector<Object>::iterator i = toolScratch.begin();
@@ -1197,6 +1253,14 @@ void DrawingView::mousePressEvent(QMouseEvent * event)
                        dragged = (Object *)hover[0];
                        draggingObject = true;
 
+                       // See if anything is using just a straight click on a handle
+                       if (HandleObjectClicked())
+                       {
+                               draggingObject = false;
+                               update();
+                               return;
+                       }
+
                        // Needed for grab & moving objects
                        // We do it *after*... why? (doesn't seem to confer any advantage...)
                        if (hoveringIntersection)
@@ -1328,7 +1392,12 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event)
                else if (hoverPointValid)
                        point = hoverPoint;
                else if (Global::snapToGrid)
-                       point = SnapPointToGrid(point);
+               {
+                       if (angleSnap)
+                               point = SnapPointToAngle(point);
+                       else
+                               point = SnapPointToGrid(point);
+               }
 
                ToolHandler(ToolMouseMove, point);
        }
@@ -1501,6 +1570,24 @@ Point DrawingView::SnapPointToGrid(Point point)
 }
 
 
+Point DrawingView::SnapPointToAngle(Point point)
+{
+       // Snap to a single digit angle (using toolpoint #1 as the center)
+       double angle = Vector::Angle(toolPoint[0], point);
+       double length = Vector::Magnitude(toolPoint[0], point);
+
+       // Convert from radians to degrees
+       double degAngle = angle * RADIANS_TO_DEGREES;
+       double snapAngle = (double)((int)(degAngle + 0.5));
+
+       Vector v;
+       v.SetAngleAndLength(snapAngle * DEGREES_TO_RADIANS, length);
+       point = toolPoint[0] + v;
+
+       return point;
+}
+
+
 Rect DrawingView::GetObjectExtents(Object * obj)
 {
        // Default to empty rect, if object checks below fail for some reason
@@ -1796,9 +1883,17 @@ bool DrawingView::HitTest(Object * obj, Point point)
                if ((length * Global::zoom) < 8.0)
                        obj->hitPoint[0] = true;
                else if ((length2 * Global::zoom) < 8.0)
+               {
                        obj->hitPoint[1] = true;
+                       hoverPoint = handle1;
+                       hoverPointValid = true;
+               }
                else if ((length3 * Global::zoom) < 8.0)
+               {
                        obj->hitPoint[2] = true;
+                       hoverPoint = handle2;
+                       hoverPointValid = true;
+               }
                else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
                        obj->hitObject = true;
 
@@ -1809,6 +1904,62 @@ bool DrawingView::HitTest(Object * obj, Point point)
 
                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;
+               obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitPoint[3] = obj->hitPoint[4] = obj->hitObject = false;
+
+               Dimension * d = (Dimension *)obj;
+
+               Vector orthogonal = Vector::Normal(d->lp[0], d->lp[1]);
+               // Get our line parallel to our points
+               float scaledThickness = Global::scale * obj->thickness;
+               Point p1 = d->lp[0] + (orthogonal * 10.0 * scaledThickness);
+               Point p2 = d->lp[1] + (orthogonal * 10.0 * scaledThickness);
+               Point p3(p1, point);
+
+               Vector v1(d->p[0], point);
+               Vector v2(d->p[1], point);
+               Vector lineSegment(p1, p2);
+               double t = Geometry::ParameterOfLineAndPoint(p1, p2, point);
+               double distance;
+               Point midpoint = (p1 + p2) / 2.0;
+               Point hFSPoint = Point(midpoint, point);
+               Point hCS1Point = Point((p1 + midpoint) / 2.0, point);
+               Point hCS2Point = Point((midpoint + p2) / 2.0, point);
+
+               if (t < 0.0)
+                       distance = v1.Magnitude();
+               else if (t > 1.0)
+                       distance = v2.Magnitude();
+               else
+                       // distance = ?Det?(ls, v1) / |ls|
+                       distance = fabs((lineSegment.x * p3.y - p3.x * lineSegment.y)
+                               / lineSegment.Magnitude());
+
+               if ((v1.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[0] = true;
+               else if ((v2.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[1] = true;
+               else if ((distance * Global::zoom) < 5.0)
+                       obj->hitObject = true;
+
+               if ((hFSPoint.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[2] = true;
+               else if ((hCS1Point.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[3] = true;
+               else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
+                       obj->hitPoint[4] = true;
+
+//             return (hitPoint1 || hitPoint2 || hitLine || hitFlipSwitch || hitChangeSwitch1 || hitChangeSwitch2 ? true : false);
+               obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
+
+               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;
+
+
+               break;
+       }
        case OTContainer:
        {
                // Containers must be recursively tested...
@@ -1841,6 +1992,50 @@ bool DrawingView::HitTest(Object * obj, Point point)
 }
 
 
+bool DrawingView::HandleObjectClicked(void)
+{
+       if (dragged->type == OTDimension)
+       {
+               Dimension * d = (Dimension *)dragged;
+
+               if (d->hitPoint[2])
+               {
+                       // Hit the "flip sides" switch, so flip 'em
+                       Point temp = d->p[0];
+                       d->p[0] = d->p[1];
+                       d->p[1] = temp;
+                       return true;
+               }
+               else if (d->hitPoint[3])
+               {
+                       // There are three cases here: aligned, horizontal, & vertical.
+                       // Aligned and horizontal do the same thing, vertical goes back to
+                       // linear.
+                       if (d->subtype == DTLinearVert)
+                               d->subtype = DTLinear;
+                       else
+                               d->subtype = DTLinearVert;
+
+                       return true;
+               }
+               else if (d->hitPoint[4])
+               {
+                       // There are three cases here: aligned, horizontal, & vertical.
+                       // Aligned and vertical do the same thing, horizontal goes back to
+                       // linear.
+                       if (d->subtype == DTLinearHorz)
+                               d->subtype = DTLinear;
+                       else
+                               d->subtype = DTLinearHorz;
+
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+
 void DrawingView::HandleObjectMovement(Point point)
 {
        Point delta = point - oldPoint;
@@ -1864,6 +2059,7 @@ void DrawingView::HandleObjectMovement(Point point)
                }
 
                break;
+
        case OTCircle:
                if (obj->hitPoint[0])
                        obj->p[0] = point;
@@ -1878,6 +2074,7 @@ void DrawingView::HandleObjectMovement(Point point)
                }
 
                break;
+
        case OTArc:
                if (obj->hitPoint[0])
                        obj->p[0] = point;
@@ -1946,6 +2143,20 @@ void DrawingView::HandleObjectMovement(Point point)
                }
 
                break;
+
+       case OTDimension:
+               if (obj->hitPoint[0])
+                       obj->p[0] = point;
+               else if (obj->hitPoint[1])
+                       obj->p[1] = point;
+               else if (obj->hitObject)
+               {
+                       obj->p[0] += delta;
+                       obj->p[1] += delta;
+               }
+
+               break;
+
        case OTContainer:
                // This is shitty, but works for now until I can code up something
                // nicer :-)
index 8e7357ef841ca6e0ebeed8ed2419bf2befaae26f..92ac72a692abc14945124569c45d8d6a699f66c4 100644 (file)
@@ -21,6 +21,7 @@ class DrawingView: public QWidget
                void SetGridSize(uint32_t);
                void UpdateGridBackground(void);
                Point SnapPointToGrid(Point);
+               Point SnapPointToAngle(Point);
                void RenderObjects(Painter *, std::vector<void *> &, int, bool ignoreLayer = false);
                void AddHoveredToSelection(void);
                void GetSelection(std::vector<void *> &);
@@ -36,6 +37,7 @@ class DrawingView: public QWidget
                void CheckObjectBounds(void);
                bool HitTestObjects(Point);
                bool HitTest(Object *, Point);
+               bool HandleObjectClicked(void);
                void HandleObjectMovement(Point);
 
        public slots:
@@ -92,6 +94,7 @@ class DrawingView: public QWidget
                bool hoveringIntersection;
                Object * dragged;
                bool draggingObject;
+               bool angleSnap;
 };
 
 #endif // __DRAWINGVIEW_H__
index e5a6af2cd991b5c5f74ee5e198073e18f43053fe..b52ff7e97d058aaa7626b2568a0b667bc647c0d1 100644 (file)
@@ -340,3 +340,15 @@ Point Geometry::GetPointForParameter(Object * obj, double t)
        return Point(0, 0);
 }
 
+
+/*
+How to find the tangent of a point off a circle:
+
+ •  Calculate the midpoint on the point and the center of the circle
+ •  Get the length of the line segment from the and the center divided by two
+ •  Use that length to construct a circle with the point at the center and the
+    radius equal to that length
+ •  The intersection of the two circles are the tangent points
+
+*/
+
index bc68adf76801316d1b790aa3d72695b879b0a3c8..54e2f51bffb088afd2476e1e67a46363b8216c78 100644 (file)
@@ -24,9 +24,9 @@ enum ToolState { TSNone, TSPoint1, TSPoint2, TSPoint3, TSPoint4, TSDone };
        int style;        \
        bool selected;    \
        bool hovered;     \
-       bool hitPoint[4]; \
+       bool hitPoint[5]; \
        bool hitObject;   \
-       Point p[4];       \
+       Point p[5];       \
        double angle[2];  \
        double radius[2];
 
@@ -73,11 +73,13 @@ struct Dimension {
        OBJECT_COMMON;
        int subtype;
        double offset;
+       Point lp[2];    // Line point, the actual dimension line
 
        Dimension(): type(OTDimension), id(Global::objectID++) {}
        Dimension(Vector pt1, Vector pt2, DimensionType dt = DTLinear, 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) { p[0] = pt1; p[1] = pt2; }
+               type(OTDimension), id(Global::objectID++), layer(0), color(c),
+               thickness(th), style(l), selected(false), hovered(false),
+               hitObject(false), subtype(dt), offset(0) { p[0] = pt1; p[1] = pt2; }
 };
 
 struct Text {