#include "painter.h"
#include "penwidget.h"
#include "structs.h"
+#include "units.h"
#include "utils.h"
#define BACKGROUND_MAX_SIZE 512
gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
scale(1.0), offsetX(-10), offsetY(-10), supressSelected(false),
document(true),
- gridPixels(0), collided(false), scrollDrag(false), hoverPointValid(false),
- hoveringIntersection(false), dragged(NULL), draggingObject(false),
+ gridPixels(0), collided(false), scrollDrag(false),
+ hoverPointValid(false), hoveringIntersection(false),
+ dragged(NULL), draggingObject(false),
angleSnap(false), dirty(false)
{
//wtf? doesn't work except in c++11??? document = { 0 };
painter->SetBrush(0xF0F0F0);
painter->SetPen(0xF0F0F0, 1, 1);
- painter->DrawRect(QRectF(QPointF(ul.x, ul.y), QPointF(br.x, br.y)));
+ painter->DrawRect(Rect(ul, br));
double spacing = Global::gridSpacing;
break;
+ case OTPolyline:
+ {
+ painter->SetBrush(QBrush(Qt::NoBrush));
+ Polyline * pl = (Polyline *)obj;
+
+ for(long unsigned int i=0; i<(pl->points.size()-1); i++)
+ {
+ Point p1 = pl->points[i];
+ Point p2 = pl->points[i + 1];
+
+ if (p1.b == 0)
+ painter->DrawLine(p1, p2);
+ else
+ {
+ Arc a = Geometry::Unpack(p1, p2, p1.b);
+ painter->DrawArc(a.p[0], a.radius[0], a.angle[0], a.angle[1]);
+ }
+ }
+
+ break;
+ }
+
case OTDimension:
{
Dimension * d = (Dimension *)obj;
painter->SetFont(QFont("Arial", 8.0 * Global::zoom * scaledThickness));
Point ctr = p2 + (Vector(p2, p1) / 2.0);
- QString dimText;
-
- if (length < 12.0)
- dimText = QString("%1\"").arg(length);
- else
- {
- double feet = (double)((int)length / 12);
- double inches = length - (feet * 12.0);
-
- if (inches == 0)
- dimText = QString("%1'").arg(feet);
- else
- dimText = QString("%1' %2\"").arg(feet).arg(inches);
- }
+ QString dimText = GetDimensionText(&document, length);
/*
Where is the text offset? It looks like it's drawing in the center, but obviously it isn't. It isn't here, it's in Painter::DrawAngledText().
break;
}
+#if 0
case OTPolygon:
{
break;
}
-
- case OTPolyline:
- {
- break;
- }
+#endif
case OTContainer:
{
// Didn't hit any object and not using a tool, so do a selection
// rectangle
Global::selectionInProgress = true;
- Global::selection.setTopLeft(QPointF(point.x, point.y));
- Global::selection.setBottomRight(QPointF(point.x, point.y));
+ Global::selection.l = Global::selection.r = point.x;
+ Global::selection.t = Global::selection.b = point.y;
select = GetSelection();
}
else if (event->button() == Qt::MiddleButton)
}
Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
- Global::selection.setBottomRight(QPointF(point.x, point.y));
+ Global::selection.r = point.x;
+ Global::selection.b = point.y;
// Only needs to be done here, as mouse down is always preceded by movement
Global::snapPointIsValid = false;
hoveringIntersection = false;
point.y = floor(point.y);
point.z = 0; // Make *sure* Z doesn't go anywhere!!!
point *= Global::gridSpacing;
+
return point;
}
case OTArc:
{
Arc * a = (Arc *)obj;
+ rect = a->Bounds();
+ break;
+ }
- double start = a->angle[0];
- double end = start + 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;
-
- // 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;
-
- rect *= a->radius[0];
- rect.Translate(a->p[0]);
+ case OTPolyline:
+ {
+ Polyline * p = (Polyline *)obj;
+ rect = p->Bounds();
break;
}
{
Object * obj = (Object *)(*i);
obj->selected = false;
+ Rect selection = Global::selection;
+ selection.Normalize();
switch (obj->type)
{
{
Line * l = (Line *)obj;
- if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y))
+ if (selection.Contains(l->p[0]) && selection.Contains(l->p[1]))
l->selected = true;
break;
case OTCircle:
{
Circle * c = (Circle *)obj;
+ Vector radVec(c->radius[0], c->radius[0]);
- if (Global::selection.contains(c->p[0].x - c->radius[0], c->p[0].y - c->radius[0]) && Global::selection.contains(c->p[0].x + c->radius[0], c->p[0].y + c->radius[0]))
+ if (selection.Contains(c->p[0] - radVec) && selection.Contains(c->p[0] + radVec))
c->selected = true;
break;
case OTArc:
{
Arc * a = (Arc *)obj;
+ Rect bounds = a->Bounds();
- double start = a->angle[0];
- double end = start + a->angle[1];
- QPointF p1(cos(start), sin(start));
- QPointF p2(cos(end), sin(end));
- QRectF bounds(p1, p2);
-
-#if 1
- // Swap X/Y coordinates if they're backwards...
- if (bounds.left() > bounds.right())
- {
- double temp = bounds.left();
- bounds.setLeft(bounds.right());
- bounds.setRight(temp);
- }
-
- if (bounds.bottom() > bounds.top())
- {
- double temp = bounds.bottom();
- bounds.setBottom(bounds.top());
- bounds.setTop(temp);
- }
-#else
- // Doesn't work as advertised! For shame!
- bounds = bounds.normalized();
-#endif
-
- // If the end of the arc is before the beginning, add 360 degrees
- // to it
- if (end < start)
- end += TAU;
-
- // Adjust the bounds depending on which axes are crossed
- if ((start < QTR_TAU) && (end > QTR_TAU))
- bounds.setTop(1.0);
-
- if ((start < HALF_TAU) && (end > HALF_TAU))
- bounds.setLeft(-1.0);
-
- if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
- bounds.setBottom(-1.0);
-
- if ((start < TAU) && (end > TAU))
- bounds.setRight(1.0);
-
- if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
- bounds.setTop(1.0);
-
- if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
- bounds.setLeft(-1.0);
+ if (selection.Contains(bounds))
+ a->selected = true;
- if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
- bounds.setBottom(-1.0);
+ break;
+ }
- bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0]));
- bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0]));
- bounds.translate(a->p[0].x, a->p[0].y);
+ case OTPolyline:
+ {
+ Polyline * pl = (Polyline *)obj;
+ Rect bounds = pl->Bounds();
- if (Global::selection.contains(bounds))
- a->selected = true;
+ if (selection.Contains(bounds))
+ pl->selected = true;
break;
}
Text * t = (Text *)obj;
Rect r(obj->p[0], Point(t->p[0].x + t->extents.Width(), t->p[0].y - t->extents.Height()));
- if (Global::selection.contains(r.l, r.t) && Global::selection.contains(r.r, r.b))
+ if (selection.Contains(r))
t->selected = true;
break;
{
Container * c = (Container *)obj;
- if (Global::selection.contains(c->p[0].x, c->p[0].y) && Global::selection.contains(c->p[1].x, c->p[1].y))
+ if (selection.Contains(c->p[0]) && selection.Contains(c->p[1]))
c->selected = true;
break;
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;
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;
// 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]);
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;
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;
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;
if (r.Contains(point))
obj->hitObject = true;
- obj->hovered = (obj->hitObject ? true : false);
+ obj->hovered = obj->hitObject;
if (oldHO != obj->hitObject)
needUpdate = true;
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, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 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;
obj->angle[1] += TAU;
QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
- informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+ informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'f', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'f', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'f', 2);
return;
}
double angle = Vector::Angle(obj->p[0], point);
obj->angle[0] = angle;
QString text = QObject::tr("Start angle: %1") + QChar(0x00B0);
- informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4);
+ informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'f', 4);
}
else if (obj->hitPoint[2])
{
obj->angle[1] += TAU;
QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0);
- informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2);
+ informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'f', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'f', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'f', 2);
return;
}
if (obj->angle[0] < 0)
obj->angle[0] += TAU;
+ double endAngle = obj->angle[0] + obj->angle[1];
+
+ if (endAngle > TAU)
+ endAngle -= TAU;
+
QString text = QObject::tr("End angle: %1") + QChar(0x00B0);
- informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4);
+ informativeText = text.arg(endAngle * RADIANS_TO_DEGREES, 0, 'f', 4);
}
else if (obj->hitObject)
{
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, 'd', 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;