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;
+ Point lastp;
+ double lastbump;
+
+ for(VPVectorIter i=pl->points.begin(); i!=pl->points.end(); 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]);
+ }
+
+ lastp = p;
+ lastbump = bump;
+ }
+ else
+ {
+ lastp = ((Object *)(*i))->p[0];
+ lastbump = ((Object *)(*i))->length;
+ }
+ }
+
+ break;
+ }
+
case OTDimension:
{
Dimension * d = (Dimension *)obj;
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;
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 < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
rect.b = -1.0;
+#endif
rect *= a->radius[0];
rect.Translate(a->p[0]);
{
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;
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())
+ // Swap 'em if the span is negative...
+ if (a->angle[1] < 0)
{
- double temp = bounds.bottom();
- bounds.setBottom(bounds.top());
- bounds.setTop(temp);
+ end = a->angle[0];
+ start = end + a->angle[1];
}
-#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;
+#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.setTop(1.0);
+ bounds.t = 1.0;
if ((start < HALF_TAU) && (end > HALF_TAU))
- bounds.setLeft(-1.0);
+ bounds.l = -1.0;
if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
- bounds.setBottom(-1.0);
+ bounds.b = -1.0;
if ((start < TAU) && (end > TAU))
- bounds.setRight(1.0);
+ bounds.r = 1.0;
if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
- bounds.setTop(1.0);
+ bounds.t = 1.0;
if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
- bounds.setLeft(-1.0);
+ bounds.l = -1.0;
if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
- bounds.setBottom(-1.0);
+ bounds.b = -1.0;
+#endif
- 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);
+ bounds *= a->radius[0];
+ bounds.Translate(a->p[0]);
- if (Global::selection.contains(bounds))
+ if (selection.Contains(bounds))
a->selected = true;
break;
}
+ case OTPolyline:
+ {
+ Polyline * pl = (Polyline *)obj;
+ Rect r(((Object *)(pl->points[0]))->p[0]);
+
+ 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))
+ pl->selected = true;
+
+ break;
+ }
+
case OTText:
{
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;
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)
{
obj->radius[0] = Vector::Magnitude(obj->p[0], point);
QString text = QObject::tr("Radius: %1");
- informativeText = text.arg(obj->radius[0], 0, 'd', 4);
+ informativeText = text.arg(obj->radius[0], 0, 'f', 4);
}
break;
fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &p.x, &p.y, &r, &a1, &a2);
obj = (Object *)new Arc(p, r, a1, a2);
}
+ else if (strcmp(buffer, "POLYLINE") == 0)
+ {
+ long int size;
+ obj = (Object *)new Polyline();
+ fscanf(file, "(%li)", &size);
+ fscanf(file, " (%i, %f, %i)\n", &obj->color, &obj->thickness, &obj->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);
+ }
+ }
else if (strcmp(buffer, "TEXT") == 0)
{
Point p;
{
obj->layer = foundLayer;
- if (extended && (obj->type != OTContainer))
+ if (extended && (obj->type != OTContainer) && (obj->type != OTPolyline))
{
fscanf(file, " (%i, %f, %i)\n", &obj->color, &obj->thickness, &obj->style);
}
switch (obj->type)
{
case OTLine:
- fprintf(file, "LINE %i (%lf,%lf) (%lf,%lf)", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y);
+ fprintf(file, "LINE %i (%.16lf,%.16lf) (%.16lf,%.16lf)", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y);
break;
case OTCircle:
- fprintf(file, "CIRCLE %i (%lf,%lf) %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0]);
+ fprintf(file, "CIRCLE %i (%.16lf,%.16lf) %.16lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0]);
break;
case OTEllipse:
break;
case OTArc:
- fprintf(file, "ARC %i (%lf,%lf) %lf, %lf, %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0], obj->angle[0], obj->angle[1]);
+ fprintf(file, "ARC %i (%.16lf,%.16lf) %.16lf, %.16lf, %.16lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0], obj->angle[0], obj->angle[1]);
break;
- case OTPolygon:
+ case OTPolyline:
+ {
+ Polyline * p = (Polyline *)obj;
+ 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++)
+ {
+ Object * po = (Object *)(*i);
+ fprintf(file, "(%.16lf,%.16lf,%.16lf)\n", po->p[0].x, po->p[0].y, po->length);
+ }
+ }
break;
case OTDimension:
- fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y, ((Dimension *)obj)->subtype, ((Dimension *)obj)->offset);
+ fprintf(file, "DIMENSION %i (%.16lf,%.16lf) (%.16lf,%.16lf) %i %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y, ((Dimension *)obj)->subtype, ((Dimension *)obj)->offset);
break;
case OTSpline:
break;
break;
}
- if (obj->type != OTContainer)
+ if ((obj->type != OTContainer) && (obj->type != OTPolyline))
fprintf(file, " (%i, %f, %i)\n", obj->color, obj->thickness, obj->style);
return true;
return normal;
}
+
+Circle Geometry::FindCircleForThreePoints(Point p1, Point p2, Point p3)
+{
+ // We use matrices and determinants to find the center and radius of the
+ // circle, given the three points passed in:
+ //
+ // [(x^2 + y^2) x y 1 ]
+ // [(x1^2 + y1^2) x1 y1 1 ]
+ // [(x2^2 + y2^2) x2 y2 1 ]
+ // [(x3^2 + y3^2) x3 y3 1 ]
+ //
+ // Then, center <x0, y0> is found by:
+ //
+ // x0 = 1/2 • M12/M11, y0 = -1/2 • M13/M11
+ //
+ // The radius can be found with the center and any of the points via
+ // Pythagoras, or with:
+ //
+ // r^2 = x0^2 + y0^2 + M14/M11
+ //
+ // If M11 = 0, then the three points are colinear and there is no circle.
+ // (M## is the minor determinant of the 4x4 matrix, where a 3x3 matrix is
+ // created by deleting the row and column of the 4x4 with the indices
+ // given.)
+
+ Circle c;
+ double m[3][4];
+
+ m[0][0] = (p1.x * p1.x) + (p1.y * p1.y);
+ m[0][1] = p1.x;
+ m[0][2] = p1.y;
+ m[0][3] = 1.0;
+ m[1][0] = (p2.x * p2.x) + (p2.y * p2.y);
+ m[1][1] = p2.x;
+ m[1][2] = p2.y;
+ m[1][3] = 1.0;
+ m[2][0] = (p3.x * p3.x) + (p3.y * p3.y);
+ m[2][1] = p3.x;
+ m[2][2] = p3.y;
+ m[2][3] = 1.0;
+
+ double minor11 = Determinant3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3]);
+
+ // Sanity check: if M11 is zero, the points are colinear.
+ if (minor11 == 0)
+ return c;
+
+ double minor12 = Determinant3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3]);
+ double minor13 = Determinant3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3]);
+ double minor14 = Determinant3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]);
+
+ c.p[0].x = 0.5 * (minor12 / minor11);
+ c.p[0].y = -0.5 * (minor13 / minor11);
+ c.radius[0] = sqrt((c.p[0].x * c.p[0].x) + (c.p[0].y * c.p[0].y) + (minor14 / minor11));
+
+ return c;
+}
+
+double Geometry::Determinant3x3(double a11, double a12, double a13, double a21, double a22, double a23, double a31, double a32, double a33)
+{
+ return (a11 * ((a22 * a33) - (a32 * a23)))
+ - (a12 * ((a21 * a33) - (a31 * a23)))
+ + (a13 * ((a21 * a32) - (a31 * a22)));
+}
+
+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);
+ Vector mpNormal = Vector::Normal(tail, head); // Normal points to the left
+
+ // Flip the normal if the bump is pointing left
+ if (bump < 0)
+ mpNormal = -mpNormal;
+
+ // N.B.: The radius can also be found with r = (a² + h²) / 2h where a is
+ // the 1/2 length of the line segment and h is the bump length.
+// double radius = (length + (1.0 / length)) / 2.0;
+ double radius = 0.5 * (((length * length) + (bumpLen * bumpLen)) / bumpLen);
+ Vector ctrVec = mpNormal * (radius - bumpLen);
+ Point center = midpoint + ctrVec;
+
+ // Find arc endpoints...
+ double angle1 = Vector::Angle(center, tail);
+ double angle2 = Vector::Angle(center, head);
+ double span = angle2 - angle1;
+
+ // Fix span depending on which way the arc is being drawn...
+ if (bump > 0 && span < 0)
+ span += TAU;
+
+ if (bump < 0 && span > 0)
+ span -= TAU;
+
+ return Arc(center, radius, angle1, span);
+}
static void FindTangents(Object *, Object *);
static Point NearestTo(Point, Point, Point);
static Vector GetNormalOfPointAndLine(Point, Line *);
+ static Circle FindCircleForThreePoints(Point, Point, Point);
+ static double Determinant3x3(double, double, double, double, double, double, double, double, double);
+ static Arc Unpack(Point, Point, double);
};
#endif // __GEOMETRY_H__
bool Global::ignoreClicks = false;
bool Global::dontMove = false;
bool Global::selectionInProgress = false;
-QRectF Global::selection;
+Rect Global::selection;
int Global::tool = TTNone;
int Global::toolState = TSNone;
#include <stdint.h>
#include <string>
#include <vector>
-#include <QRectF>
+#include "rect.h"
#include "vector.h"
class QFont;
public:
static double gridSpacing;
static bool selectionInProgress;
- static QRectF selection;
+ static Rect selection;
static QFont * font;
static Point snapPoint;
static bool snapPointIsValid;
painter->drawPoint(v.x, v.y);
}
+// The rect passed in is in Qt coordinates... (?)
+void Painter::DrawRoundedRect(Rect rect, double radiusX, double radiusY)
+{
+ if (!painter)
+ return;
+
+ QRectF r(QPointF(rect.l, rect.t), QPointF(rect.r, rect.b));
+ painter->drawRoundedRect(r, radiusX, radiusY);
+}
+
// The rect passed in is in Qt coordinates...
void Painter::DrawRoundedRect(QRectF rect, double radiusX, double radiusY)
{
// The rect passed in is in Cartesian but we want to pad it by a set number of
// pixels (currently set at 8), so the pad looks the same regardless of zoom.
-void Painter::DrawPaddedRect(QRectF rect)
+void Painter::DrawPaddedRect(Rect rect)
{
if (!painter)
return;
- Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
- Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
+ Vector v1 = CartesianToQtCoords(rect.TopLeft());
+ Vector v2 = CartesianToQtCoords(rect.BottomRight());
QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
painter->drawRect(screenRect);
}
-void Painter::DrawRect(QRectF rect)
+void Painter::DrawRect(Rect rect)
{
if (!painter)
return;
- Vector v1 = CartesianToQtCoords(Vector(rect.x(), rect.y()));
- Vector v2 = CartesianToQtCoords(Vector(rect.right(), rect.bottom()));
+ Vector v1 = CartesianToQtCoords(rect.TopLeft());
+ Vector v2 = CartesianToQtCoords(rect.BottomRight());
QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
painter->drawRect(screenRect);
}
-void Painter::DrawText(QRectF rect, int type, QString text)
+void Painter::DrawText(Rect rect, int type, QString text)
{
if (!painter)
return;
- painter->drawText(rect, (Qt::AlignmentFlag)type, text);
+ QRectF r(QPointF(rect.l, rect.t), QPointF(rect.r, rect.b));
+ painter->drawText(r, (Qt::AlignmentFlag)type, text);
}
void Painter::DrawArrowhead(Vector head, Vector tail, double size)
void DrawHLine(double);
void DrawVLine(double);
void DrawPoint(int, int);
+ void DrawRoundedRect(Rect, double, double);
void DrawRoundedRect(QRectF, double, double);
- void DrawPaddedRect(QRectF);
- void DrawRect(QRectF);
- void DrawText(QRectF, int, QString);
+ void DrawPaddedRect(Rect);
+ void DrawRect(Rect);
+ void DrawText(Rect, int, QString);
void DrawArrowhead(Vector, Vector, double);
void DrawCrosshair(Vector);
void DrawInformativeText(QString);
// JLH 11/10/2016 Created this file
//
+
#include "rect.h"
-//#include <stdio.h>
+#include <math.h>
Rect::Rect(): l(0), r(0), t(0), b(0)
{
}
-Rect::Rect(double ll, double rr, double tt, double bb):
+Rect::Rect(double tt, double ll, double bb, double rr):
l(ll), r(rr), t(tt), b(bb)
{
Normalize();
Normalize();
}
+Rect::Rect(Point p): l(p.x), r(p.x), t(p.y), b(p.y)
+{
+}
+
Rect & Rect::operator*=(double scale)
{
l *= scale;
return *this;
}
+Rect & Rect::operator+=(Point p)
+{
+ if (p.x < l)
+ l = p.x;
+
+ if (p.x > r)
+ r = p.x;
+
+ if (p.y < b)
+ b = p.y;
+
+ if (p.y > t)
+ t = p.y;
+
+ return *this;
+}
+
+//
+// We use this to give a rect an array-like access, which allows access of the
+// rect in TLBR order (from 0 to 3). Also, values greater than 3 are treated
+// as mod 4.
+//
+double & Rect::operator[](int idx)
+{
+ idx = idx % 4;
+
+ switch (idx)
+ {
+ case 0:
+ return t;
+ case 1:
+ return l;
+ case 2:
+ return b;
+ }
+
+ return r;
+}
+
void Rect::Normalize(void)
{
if (l > r)
double Rect::Width(void)
{
- return r - l;
+ return fabs(r - l);
}
double Rect::Height(void)
{
- return t - b;
+ return fabs(t - b);
}
bool Rect::Contains(Point p)
return ((p.x >= l) && (p.x <= r) && (p.y >= b) && (p.y <= t) ? true : false);
}
+bool Rect::Contains(Rect rect)
+{
+ return ((rect.l >= l) && (rect.r <= r) && (rect.b >= b) && (rect.t <= t) ? true : false);
+}
+
Point Rect::TopLeft(void)
{
return Point(l, t);
double l, r, t, b;
Rect();
- Rect(double ll, double rr, double tt, double bb);
+ Rect(double tt, double ll, double bb, double rr);
Rect(Point tl, Point br);
+ Rect(Point);
Rect & operator*=(double scale);
Rect & operator|=(Rect x);
+ Rect & operator+=(Point p);
+ double & operator[](int);
void Normalize(void);
void Translate(Point p);
void Expand(double amt);
double Width(void);
double Height(void);
bool Contains(Point p);
+ bool Contains(Rect r);
Point TopLeft(void);
Point TopRight(void);
Point BottomLeft(void);
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;
struct Polyline {
OBJECT_COMMON;
- VPVector objects;
- bool closed;
+ 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) { objects.push_back(obj); }
- void Add(VPVector objs) { objects.insert(objects.end(), objs.begin(), objs.end()); }
+ void Add(void * obj) { points.push_back(obj); }
+ void Add(VPVector objs) { points.insert(points.end(), objs.begin(), objs.end()); }
};
struct Spline {
return acos(a.Dot(b) / (a.Magnitude() * b.Magnitude()));
}
+
+/*static*/ Point Vector::Midpoint(Point p1, Point p2)
+{
+ return Point((p1.x + p2.x) / 2.0, (p1.y + p2.y) / 2.0);
+}
static double Parameter(Vector v1, Vector v2, Vector p);
static Vector Normal(Vector v1, Vector v2);
static double AngleBetween(Vector a, Vector b);
+ static Point Midpoint(Point p1, Point p2);
public:
double x, y, z;