#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:
{
void DrawingView::ToolDraw(Painter * painter)
{
- if (Global::tool == TTLine)
+ switch (Global::tool)
{
+ case TTLine:
if (Global::toolState == TSNone)
{
painter->DrawHandle(toolPoint[0]);
QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
informativeText = text.arg(absLength).arg(absAngle);
}
- }
- else if (Global::tool == TTCircle)
- {
+
+ break;
+
+ case TTCircle:
if (Global::toolState == TSNone)
{
painter->DrawHandle(toolPoint[0]);
QString text = tr("Radius: %1 in.");
informativeText = text.arg(length);
}
- }
- else if (Global::tool == TTArc)
- {
+
+ break;
+
+ case TTArc:
if (Global::toolState == TSNone)
{
painter->DrawHandle(toolPoint[0]);
QString text = tr("Arc span: %1") + QChar(0x00B0);
informativeText = text.arg(RADIANS_TO_DEGREES * span);
}
- }
- else if (Global::tool == TTRotate)
- {
+
+ break;
+
+ case TTRotate:
if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
painter->DrawHandle(toolPoint[0]);
else if ((Global::toolState == TSPoint2) && shiftDown)
if (ctrlDown)
informativeText += " (Copy)";
}
- }
- else if (Global::tool == TTMirror)
- {
+
+ break;
+
+ case TTMirror:
if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1))
painter->DrawHandle(toolPoint[0]);
else if ((Global::toolState == TSPoint2) && shiftDown)
if (ctrlDown)
informativeText += " (Copy)";
}
- }
- else if (Global::tool == TTDimension)
- {
+
+ break;
+
+ case TTDimension:
if (Global::toolState == TSNone)
{
painter->DrawHandle(toolPoint[0]);
QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2");
informativeText = text.arg(absLength).arg(absAngle);
}
- }
- else if (Global::tool == TTTrim)
- {
+
+ break;
+
+ case TTTrim:
if (toolObj[0] != NULL)
{
// We're assuming ATM it's just a line...
// QString text = tr("Arc span: %1") + QChar(0x00B0);
// informativeText = text.arg(RADIANS_TO_DEGREES * span);
}
- }
- else if (Global::tool == TTParallel)
- {
+
+ break;
+
+ case TTParallel:
if (Global::toolState == TSPoint1)
{
painter->SetPen(0xFF00FF, 2.0, LSSolid);
}
}
}
+
+ break;
+
+ default:
+ break;
}
}
if (Global::fixedLength)
{
if (dragged->type == OTLine)
- {
- dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
- }
+ dragged->length = ((Line *)dragged)->Length();
+ }
+
+ // Needed for fixed angle handling
+ if (Global::fixedAngle)
+ {
+ if (dragged->type == OTLine)
+ dragged->p[2] = ((Line *)dragged)->Unit();
}
if (dragged->type == OTCircle)
// 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 (selection.Contains(bounds))
+ a->selected = true;
- if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
- bounds.setLeft(-1.0);
+ break;
+ }
- if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
- bounds.setBottom(-1.0);
+ case OTPolyline:
+ {
+ Polyline * pl = (Polyline *)obj;
+ Rect bounds = pl->Bounds();
- 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);
-
- 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;
{
bool needUpdate = false;
+ // Make sure we don't hit test stuff on an invisible layer...
+ if (Global::layerHidden[obj->layer] == true)
+ return false;
+
switch (obj->type)
{
case OTLine:
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;
case OTLine:
if (obj->hitPoint[0])
{
+/*
+N.B.: Mixing fixed length with fixed angle (and in this order) is probably *not* going to work out in any meaningful way, and we should probably make the GUI force these to be mutually exclusive. Besides, this combined effect already works by dragging the line segment by clicking on it. :-P
+*/
if (Global::fixedLength)
{
- Vector line = point - obj->p[1];
- Vector unit = line.Unit();
+ Vector unit = Vector::Unit(obj->p[1], point);
point = obj->p[1] + (unit * obj->length);
}
+ if (Global::fixedAngle)
+ {
+ // Calculate the component of the current vector along the
+ // fixed angle: A_compB = (A • Bu) * Bu (p[2] has the unit
+ // vector "Bu".)
+ double magnitudeAlongB = Vector::Dot(Vector(point - obj->p[1]), obj->p[2]);
+ point = obj->p[1] + (obj->p[2] * magnitudeAlongB);
+ }
+
obj->p[0] = point;
}
else if (obj->hitPoint[1])
{
if (Global::fixedLength)
{
- Vector line = point - obj->p[0];
- Vector unit = line.Unit();
+ Vector unit = Vector::Unit(obj->p[0], point);
point = obj->p[0] + (unit * obj->length);
}
+ if (Global::fixedAngle)
+ {
+ double magnitudeAlongB = Vector::Dot(Vector(point - obj->p[0]), obj->p[2]);
+ point = obj->p[0] + (obj->p[2] * magnitudeAlongB);
+ }
+
obj->p[1] = point;
}
else if (obj->hitObject)
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;