src/settingsdialog.h \
src/spline.h \
src/text.h \
+ src/triangulateaction.h \
src/trimaction.h \
src/vector.h
src/settingsdialog.cpp \
src/spline.cpp \
src/text.cpp \
+ src/triangulateaction.cpp \
src/trimaction.cpp \
src/vector.cpp
<file>snap-to-grid-tool.png</file>
<file>splash.png</file>
<file>trim-tool.png</file>
+ <file>triangulate-tool.png</file>
<file>quit.png</file>
<file>zoom-in.png</file>
<file>zoom-out.png</file>
#include "painter.h"
#include "rotateaction.h"
#include "settingsdialog.h"
+#include "triangulateaction.h"
#include "trimaction.h"
}
+void ApplicationWindow::TriangulateTool(void)
+{
+ ClearUIToolStatesExcept(triangulateAct);
+ SetInternalToolStates();
+}
+
+
void ApplicationWindow::AddLineTool(void)
{
ClearUIToolStatesExcept(addLineAct);
if (exception != trimAct)
trimAct->setChecked(false);
+
+ if (exception != triangulateAct)
+ triangulateAct->setChecked(false);
}
drawing->SetToolActive(mirrorAct->isChecked() ? new MirrorAction() : NULL);
drawing->SetToolActive(rotateAct->isChecked() ? new RotateAction() : NULL);
drawing->SetToolActive(trimAct->isChecked() ? new TrimAction() : NULL);
+ drawing->SetToolActive(triangulateAct->isChecked() ? new TriangulateAction() : NULL);
if (drawing->toolAction)
Object::ignoreClicks = true;
trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true);
connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool()));
+ triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true);
+ connect(triangulateAct, SIGNAL(triggered()), this, SLOT(TriangulateTool()));
+
//Hm. I think we'll have to have separate logic to do the "Radio Group Toolbar" thing...
// Yup, in order to turn them off, we'd have to have an "OFF" toolbar button. Ick.
menu->addAction(rotateAct);
menu->addAction(mirrorAct);
menu->addAction(trimAct);
+ menu->addAction(triangulateAct);
menu->addAction(connectAct);
menu->addAction(disconnectAct);
menu->addSeparator();
toolbar->addAction(rotateAct);
toolbar->addAction(mirrorAct);
toolbar->addAction(trimAct);
+ toolbar->addAction(triangulateAct);
toolbar->addAction(deleteAct);
toolbar->addAction(connectAct);
toolbar->addAction(disconnectAct);
void RotateTool(void);
void MirrorTool(void);
void TrimTool(void);
+ void TriangulateTool(void);
void AddLineTool(void);
void AddCircleTool(void);
void AddArcTool(void);
QAction * disconnectAct;
QAction * mirrorAct;
QAction * trimAct;
+ QAction * triangulateAct;
// Class variables
public:
if (snapToGrid)
point = SnapPointToGrid(point);
+ if (snapPointIsValid)
+ point = snapPoint;
+
/*
State Management:
We want the arc to go into OSSelected mode if we click on it but don't drag.
SaveHitState();
bool hovered = HitTest(point);
needUpdate = HitStateChanged();
+
+ if (snapToGrid)
+ point = SnapPointToGrid(point);
+
+ if (snapPointIsValid)
+ point = snapPoint;
+
objectWasDragged = (draggingCenter | draggingEdge | draggingRotate | draggingSpan);
if (objectWasDragged)
hitArc = true;
#else
if ((length * Painter::zoom) < 8.0)
+ {
hitCenter = true;
+ snapPoint = position;
+ snapPointIsValid = true;
+ }
else if (((fabs(length - radius) * Painter::zoom) < 2.0)
&& AngleInArcSpan(pointerAngle))
hitArc = true;
else if ((Vector::Magnitude(handle2, point) * Painter::zoom) < 8.0)
+ {
hitRotate = true;
+ snapPoint = handle2;
+ snapPointIsValid = true;
+ }
else if ((Vector::Magnitude(handle3, point) * Painter::zoom) < 8.0)
+ {
hitSpan = true;
+ snapPoint = handle3;
+ snapPointIsValid = true;
+ }
#endif
return (hitCenter || hitArc || hitRotate || hitSpan ? true : false);
if (snapToGrid)
point = SnapPointToGrid(point);
+ if (snapPointIsValid)
+ point = snapPoint;
+
draggingCenter = hitCenter;
draggingEdge = hitCircle;
SaveHitState();
bool hovered = HitTest(point);
needUpdate = HitStateChanged();
+
+ if (snapToGrid)
+ point = SnapPointToGrid(point);
+
+ if (snapPointIsValid)
+ point = snapPoint;
+
objectWasDragged = (draggingEdge | draggingCenter);
if (objectWasDragged)
pointed at length with our on screen length.
*/
if ((length * Painter::zoom) < 8.0)
+ {
hitCenter = true;
+ snapPoint = position;
+ snapPointIsValid = true;
+ }
//wrong: else if ((length < (radius + 2.0)) && (length > (radius - 2.0)))
/*NB: The following should be identical to what we have down below, but it doesn't work out that way... :-P */
//close, but no else if (((length * Painter::zoom) < ((radius * Painter::zoom) + 2.0)) && ((length * Painter::zoom) > ((radius * Painter::zoom) - 2.0)))
/*virtual*/ bool Container::PointerMoved(Vector point)
{
std::vector<Object *>::iterator i;
- lastObjectHovered = NULL;
+ lastObjectHovered = penultimateObjectHovered = NULL;
if (!isTopLevelContainer)
{
{
// if (objects[i]->GetState() == OSSelected)
if ((*i)->PointerMoved(point))
+ {
+ penultimateObjectHovered = lastObjectHovered;
lastObjectHovered = *i;
+ }
}
// Generic container doesn't need this???
bool isTopLevelContainer;
Object * lastObjectClicked;
Object * lastObjectHovered;
+ Object * penultimateObjectHovered;
private:
bool dragging;
bool draggingHandle1;
if (snapToGrid)
point = SnapPointToGrid(point);
+ if (snapPointIsValid)
+ point = snapPoint;
+
if (hitPoint1)
{
oldState = state;
bool hovered = HitTest(point);
needUpdate = HitStateChanged();
+ if (snapToGrid)
+ point = SnapPointToGrid(point);
+
+ if (snapPointIsValid)
+ point = snapPoint;
+
objectWasDragged = (/*draggingLine |*/ draggingHandle1 | draggingHandle2);
if (objectWasDragged)
/*virtual*/ void Dimension::Enumerate(FILE * file)
{
- fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, type);
+ fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, dimensionType);
}
#include "arc.h"
#include "circle.h"
#include "dimension.h"
+#include "geometry.h"
#include "line.h"
#include "painter.h"
if (Object::snapToGrid)
point = Object::SnapPointToGrid(point);
+ // We always snap to object points, and they take precendence over
+ // grid points...
+ if (Object::snapPointIsValid)
+ point = Object::snapPoint;
+
toolAction->MouseDown(point);
}
{
Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
Object::selection.setBottomRight(QPointF(point.x, point.y));
+ // Only needs to be done here, as mouse down is always preceded by movement
+ Object::snapPointIsValid = false;
+ // Scrolling...
if (event->buttons() & Qt::MiddleButton)
{
point = Vector(event->x(), event->y());
// Since we're using Qt coords for scrolling, we have to adjust them here to
// conform to Cartesian coords, since the origin is using Cartesian. :-)
-// Vector delta(point, oldPoint);
Vector delta(oldPoint, point);
delta /= Painter::zoom;
delta.y = -delta.y;
return;
}
- // Grid processing...
+#if 0
+ // Grid processing... (only snap here is left button is down)
if ((event->buttons() & Qt::LeftButton) && Object::snapToGrid)
{
point = Object::SnapPointToGrid(point);
}
- oldPoint = point;
+ // Snap points on objects always take precedence over the grid, whether
+ // dragging an object or not...
+//thisnowok
+ if (Object::snapPointIsValid)
+ {
+// Uncommenting this causes the cursor to become unresponsive after the first
+// object is added.
+// point = Object::snapPoint;
+ }
+#endif
+
+// oldPoint = point;
//we should keep track of the last point here and only pass this down *if* the point
//changed...
- document.PointerMoved(point);
- if (document.NeedsUpdate() || Object::selectionInProgress)
- update();
+ // This returns true if we've moved over an object...
+ if (document.PointerMoved(point))
+ {
+ // Do object snapping here. Grid snapping on mouse down is done in the
+ // objects themselves, only because we have to hit test the raw point,
+ // not the snapped point. There has to be a better way...!
+ if (document.penultimateObjectHovered)
+ {
+ // Two objects are hovered, see if we have an intersection point
+ if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
+ {
+ double t;
+ int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
+
+ if (n == 1)
+ {
+ Object::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
+ Object::snapPointIsValid = true;
+ }
+ }
+ else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
+ {
+ Point p1, p2;
+ int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
+
+ if (n == 1)
+ {
+ Object::snapPoint = p1;
+ Object::snapPointIsValid = true;
+ }
+ else if (n == 2)
+ {
+ double d1 = Vector(point, p1).Magnitude();
+ double d2 = Vector(point, p2).Magnitude();
+
+ if (d1 < d2)
+ Object::snapPoint = p1;
+ else
+ Object::snapPoint = p2;
+
+ Object::snapPointIsValid = true;
+ }
+ }
+ }
+// else
+// {
+ // Otherwise, it was a single object hovered...
+// }
+ }
if (toolAction)
{
if (Object::snapToGrid)
- {
point = Object::SnapPointToGrid(point);
- oldPoint = point;
- }
+
+ // We always snap to object points, and they take precendence over
+ // grid points...
+ if (Object::snapPointIsValid)
+ point = Object::snapPoint;
toolAction->MouseMoved(point);
- update();
}
+
+ // This is used to draw the tool crosshair...
+ oldPoint = point;
+
+ if (document.NeedsUpdate() || Object::selectionInProgress || toolAction)
+ update();
}
#include "circle.h"
#include "dimension.h"
#include "line.h"
+#include "mathconstants.h"
Point Geometry::IntersectionOfLineAndLine(Point p1, Point p2, Point p3, Point p4)
}
-// Finds the intesection(s) between a line and a circle (if any)
+// Finds the intersection(s) between a line and a circle (if any)
int Geometry::Intersects(Line * l, Circle * c, double * tp/*= 0*/, double * up/*= 0*/, double * vp/*= 0*/, double * wp/*= 0*/)
{
#if 0
// Now we have to check for intersection points.
// Tangent case: (needs to return something)
if ((distance == c->radius) && (t >= 0.0) && (t <= 1.0))
+ {
+ // Need to set tp & up to something... !!! FIX !!!
+ if (tp)
+ *tp = t;
+
+ if (up)
+ *up = Vector(c->position, p).Angle();
+
return 1;
+ }
// The line intersects the circle in two points (possibly). Use Pythagorus
// to find them for testing.
}
+// Finds the intersection(s) between a circle and a circle (if any)
+// There can be 0, 1, or 2 intersections.
+// Returns the angles of the points of intersection in tp thru wp, with the
+// angles returned as c1, c2, c1, c2 (if applicable--in the 1 intersection case,
+// only the first two angles are returned: c1, c2).
+int Geometry::Intersects(Circle * c1, Circle * c2, double * tp/*= 0*/, double * up/*= 0*/, double * vp/*= 0*/, double * wp/*= 0*/, Point * p1/*= 0*/, Point * p2/*= 0*/)
+{
+ // Get the distance between centers. If the distance plus the radius of the
+ // smaller circle is less than the radius of the larger circle, there is no
+ // intersection. If the distance is greater than the sum of the radii,
+ // there is no intersection. If the distance is equal to the sum of the
+ // radii, they are tangent and intersect at one point. Otherwise, they
+ // intersect at two points.
+ Vector centerLine(c1->position, c2->position);
+ double d = centerLine.Magnitude();
+//printf("Circle #1: pos=<%lf, %lf>, r=%lf\n", c1->position.x, c1->position.y, c1->radius);
+//printf("Circle #2: pos=<%lf, %lf>, r=%lf\n", c2->position.x, c2->position.y, c2->radius);
+//printf("Distance between #1 & #2: %lf\n", d);
+
+ // Check to see if we actually have an intersection, and return failure if not
+ if ((fabs(c1->radius - c2->radius) > d) || ((c1->radius + c2->radius) < d))
+ return 0;
+
+ // There are *two* tangent cases!
+ if (((c1->radius + c2->radius) == d) || (fabs(c1->radius - c2->radius) == d))
+ {
+ // Need to return something in tp & up!! !!! FIX !!! [DONE]
+ if (tp)
+ *tp = centerLine.Angle();
+
+ if (up)
+ *up = centerLine.Angle() + PI;
+
+ return 1;
+ }
+
+ // Find the distance from the center of c1 to the perpendicular chord
+ // (which contains the points of intersection)
+ double x = ((d * d) - (c2->radius * c2->radius) + (c1->radius * c1->radius))
+ / (2.0 * d);
+ // Find the the length of the perpendicular chord
+// Not needed...!
+ double a = sqrt((-d + c2->radius - c1->radius) * (-d - c2->radius + c1->radius) * (-d + c2->radius + c1->radius) * (d + c2->radius + c1->radius)) / d;
+
+ // Now, you can use pythagorus to find the length of the hypotenuse, but we
+ // already know that length, it's the radius! :-P
+ // What's needed is the angle of the center line and the radial line. Since
+ // there's two intersection points, there's also four angles (two for each
+ // circle)!
+ // We can use the arccos to find the angle using just the radius and the
+ // distance to the perpendicular chord...!
+ double angleC1 = acos(x / c1->radius);
+ double angleC2 = acos((d - x) / c2->radius);
+
+ if (tp)
+ *tp = centerLine.Angle() - angleC1;
+
+ if (up)
+ *up = (centerLine.Angle() + PI) - angleC2;
+
+ if (vp)
+ *vp = centerLine.Angle() + angleC1;
+
+ if (wp)
+ *wp = (centerLine.Angle() + PI) + angleC2;
+
+ if (p1)
+ *p1 = c1->position + (centerLine.Unit() * x) + (Vector::Normal(Vector(), centerLine) * (a / 2.0));
+
+ if (p2)
+ *p2 = c1->position + (centerLine.Unit() * x) - (Vector::Normal(Vector(), centerLine) * (a / 2.0));
+
+ return 2;
+}
+
+
+// should we just do common trig solves, like AAS, ASA, SAS, SSA?
+// Law of Cosines:
+// c^2 = a^2 + b^2 -2ab*cos(C)
+// Solving for C:
+// cos(C) = (c^2 - a^2 - b^2) / -2ab = (a^2 + b^2 - c^2) / 2ab
+// Law of Sines:
+// a / sin A = b / sin B = c / sin C
+
+// Solve the angles of the triangle given the sides. Angles returned are
+// opposite of the given sides (so a1 consists of sides s2 & s3, and so on).
+void Geometry::FindAnglesForSides(double s1, double s2, double s3, double * a1, double * a2, double * a3)
+{
+ // Use law of cosines to find 1st angle
+ double cosine1 = ((s2 * s2) + (s3 * s3) - (s1 * s1)) / (2.0 * s2 * s3);
+
+ // Check for a valid triangle
+ if ((cosine1 < -1.0) || (cosine1 > 1.0))
+ return;
+
+ double angle1 = acos(cosine1);
+
+ // Use law of sines to find 2nd & 3rd angles
+// sin A / a = sin B / b
+// sin B = (sin A / a) * b
+ double angle2 = asin(s2 * (sin(angle1) / s1));
+ double angle3 = asin(s3 * (sin(angle1) / s1));
+
+ if (a1)
+ *a1 = angle1;
+
+ if (a2)
+ *a2 = angle2;
+
+ if (a3)
+ *a3 = angle3;
+}
+
static int Intersects(Line *, Line *, double * tp = 0, double * up = 0);
static int Intersects(Line *, Dimension *, double * tp = 0, double * up = 0);
static int Intersects(Line * l, Circle * c, double * tp = 0, double * up = 0, double * vp = 0, double * wp = 0);
+ static int Intersects(Circle * c1, Circle * c2, double * tp = 0, double * up = 0, double * vp = 0, double * wp = 0, Point * p1 = 0, Point * p2 = 0);
+ static void FindAnglesForSides(double s1, double s2, double s3, double * a1, double * a2, double * a3);
};
#endif // __GEOMETRY_H__
// Someone told us to fuck off, so we'll fuck off. :-)
if (ignoreClicks)
-// return false;
return hit;
// Now that we've done our hit testing on the non-snapped point, snap it if
if (snapToGrid)
point = SnapPointToGrid(point);
+ if (snapPointIsValid)
+ point = snapPoint;
+
// this is shite. this should be checked for in the Container, not here!
#warning "!!! This should be checked for in Container, not here !!!"
// If we're part of a non-top-level container, send this signal to it
return true;
}
-/*
-There's a small problem here with the implementation: You can have a dimension tied
-to only one point while at the same time you can have a dimension sitting on this line.
-Since there's only *one* dimPoint for each point, this can be problematic...
-
-We solve this by allowing only *one* Dimension object to be attached to the Line,
-Arc, etc. and by giving the Dimension object a pointer to our endpoints.
-
-Problem still arises when we delete this object; The attached Dimension object will
-then have bad pointers! What it *should* do is delete the object if and only if this
-line is not attached to any other object. If it is, then one of those attachment
-points should be sent to the dimension object (done for position & endpoint).
-
-NOTE: The STL vector<T> *does not* take ownership of pointers, therefore is suitable
- for our purposes
-
-Also: It would be nice to have a preview of the dimension being drawn, with a modifier
-key to make it draw/show on the other side...
-
-TODO: Make Dimension preview with modifier keys for showing on other side
-*/
-/*
-
-N.B.: This no longer works, as the DrawDimension object takes precedence over this code.
- THIS DOES NOTHING ANYMORE!!!
-
-*/
-#if 0
- // Is the dimension tool active? Let's use it:
- if (dimensionActive)
- {
- // User clicked on the line itself (endpoint checks should preceed this one):
- // (Priorities are taken care of in HitTest()...)
- if (hitLine)
- {
-#if 0
- if (attachedDimension == NULL)
- {
- // How to get this object into the top level container???
-/*
-The real question is do we care. I think so, because if this isn't in the top
-level container, it won't get drawn...
-But we can fix that by making this object call any attached object's (like
-a dimension only) Draw() function... :-/
-*/
- attachedDimension = new Dimension(&position, &endpoint, DTLinear, this);
-
- if (parent != NULL)
- parent->Add(attachedDimension);
- }
- else
- {
- // If there's one already there, tell it to flip sides...
- attachedDimension->FlipSides();
- }
-#else
- // New approach here: We look for connected objects.
- Object * attachedDimension = FindAttachedDimension();
-
- if (attachedDimension)
- {
- // If there's an attached Dimension, tell it to switch sides...
- ((Dimension *)attachedDimension)->FlipSides();
- }
- else
- {
- // Otherwise, we make a new one and attach it here.
- attachedDimension = new Dimension(Connection(this, 0), Connection(this, 1.0), DTLinear, this);
- connected.push_back(Connection(attachedDimension, 0));
- connected.push_back(Connection(attachedDimension, 1.0));
-
- if (parent != NULL)
- parent->Add(attachedDimension);
- }
-#endif
-
- return true;
- }
- }
-#endif
-
if (state == OSInactive)
{
//How to translate this into pixels from Document space???
return false;
}
- // Hit test tells us what we hit (if anything) through boolean variables. (It
- // also tells us whether or not the state changed. --not any more)
+ // Hit test tells us what we hit (if anything) through boolean variables.
SaveHitState();
bool hovered = HitTest(point);
needUpdate = HitStateChanged();
+ if (snapToGrid)
+ point = SnapPointToGrid(point);
+
+ if (snapPointIsValid)
+ point = snapPoint;
+
objectWasDragged = (draggingLine | draggingHandle1 | draggingHandle2);
if (objectWasDragged)
Vector lineSegment = endpoint - position;
Vector v1 = point - position;
Vector v2 = point - endpoint;
+ Vector v3 = point - Center(); // Midpoint, for snapping...
double t = Geometry::ParameterOfLineAndPoint(position, endpoint, point);
double distance;
/ lineSegment.Magnitude());
if ((v1.Magnitude() * Painter::zoom) < 8.0)
+ {
hitPoint1 = true;
+ snapPoint = position;
+ snapPointIsValid = true;
+ }
else if ((v2.Magnitude() * Painter::zoom) < 8.0)
+ {
hitPoint2 = true;
+ snapPoint = endpoint;
+ snapPointIsValid = true;
+ }
else if ((distance * Painter::zoom) < 5.0)
hitLine = true;
+ // Not a manipulation point, but a snapping point:
+ if ((v3.Magnitude() * Painter::zoom) < 8.0)
+ {
+ snapPoint = Center();
+ snapPointIsValid = true;
+ }
+
return (hitPoint1 || hitPoint2 || hitLine ? true : false);
}
QRectF Object::selection;
double Object::gridSpacing;
int Object::currentLayer = 0;
+Point Object::snapPoint;
+bool Object::snapPointIsValid = false;
Object::Object(): position(Vector(0, 0)), parent(0), type(OTObject),
static QRectF selection;
static double gridSpacing; // Grid spacing in base units
static int currentLayer;
+ static Point snapPoint;
+ static bool snapPointIsValid;
};
#endif // __OBJECT_H__
--- /dev/null
+// triangulateaction.cpp: Action class for creating triangles from lines
+//
+// Part of the Architektonas Project
+// (C) 2014 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO WHEN WHAT
+// --- ---------- ------------------------------------------------------------
+// JLH 05/07/2014 Created this file
+//
+
+#include "triangulateaction.h"
+#include "applicationwindow.h"
+#include "circle.h"
+#include "container.h"
+#include "drawingview.h"
+#include "geometry.h"
+#include "line.h"
+#include "mathconstants.h"
+#include "painter.h"
+
+
+enum { FIRST_LINE, SECOND_LINE, THIRD_LINE };
+
+
+TriangulateAction::TriangulateAction(): state(FIRST_LINE),// t(0), u(1.0),
+ line1(NULL), line2(NULL), line3(NULL),
+ doc(&(ApplicationWindow::drawing->document))
+//, line(NULL),
+// shiftWasPressedOnNextPoint(false), ctrlWasPressed(false),
+// mirror(new Container(Vector()))
+{
+// ApplicationWindow::drawing->document.CopySelectedContentsTo(mirror);
+// mirror->Save();
+}
+
+
+TriangulateAction::~TriangulateAction()
+{
+}
+
+
+/*virtual*/ void TriangulateAction::Draw(Painter * painter)
+{
+ Object * obj = doc->lastObjectHovered;
+
+ if (obj == NULL)
+ return;
+
+ // This assumes a Line, but it might not be!
+ painter->SetPen(QPen(Qt::blue, 2.0, Qt::DotLine));
+// Vector v(((Line *)obj)->position, ((Line *)obj)->endpoint);
+ Point p1 = ((Line *)obj)->position;
+ Point p2 = ((Line *)obj)->endpoint;
+ painter->DrawLine(p1, p2);
+#if 0
+ if (state == FIRST_POINT)
+ {
+ painter->DrawHandle(p1);
+ }
+ else
+ {
+ Vector reflectedP2 = -(p2 - p1);
+ Point newP2 = p1 + reflectedP2;
+ painter->DrawLine(newP2, p2);
+ painter->DrawHandle(p1);
+
+ double absAngle = (Vector(p2 - p1).Angle()) * RADIANS_TO_DEGREES;
+
+ // Keep the angle between 0 and 180 degrees
+ if (absAngle > 180.0)
+ absAngle -= 180.0;
+
+ QString text = QChar(0x2221) + QObject::tr(": %1");
+ text = text.arg(absAngle);
+
+ if (ctrlWasPressed)
+ text += " (Copy)";
+
+ painter->DrawInformativeText(text);
+
+ // Draw the mirror only if there's been a line to mirror around
+ if (p1 != p2)
+ mirror->Draw(painter);
+ }
+#endif
+}
+
+
+/*virtual*/ void TriangulateAction::MouseDown(Vector /*point*/)
+{
+#if 0
+// this is not accurate enough. need to use the actual intersection point, not
+// just the parameter(s).
+ Object * toTrim = doc->lastObjectHovered;
+
+ if (toTrim == NULL)
+ return;
+
+//it would be nice to do it like this, but if we bisect the object, we have to
+//create an extra one...
+// toTrim->Trim(t, u);
+
+ Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint);
+
+ // Check to see which case we have...
+ // We're trimming point #1...
+ if (t == 0)
+ {
+ ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u);
+// u = 1.0;
+ }
+ else if (u == 1.0)
+ {
+ ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
+// t = 0;
+ }
+ else
+ {
+ Point p1 = ((Line *)toTrim)->position + (v * t);
+ Point p2 = ((Line *)toTrim)->position + (v * u);
+ Point p3 = ((Line *)toTrim)->endpoint;
+ ((Line *)toTrim)->endpoint = p1;
+ Line * line = new Line(p2, p3);
+ emit ObjectReady(line);
+// t = 0, u = 1.0;
+ }
+
+ doc->lastObjectHovered = NULL;
+#endif
+}
+
+
+/*virtual*/ void TriangulateAction::MouseMoved(Vector point)
+{
+#if 0
+ if (state == FIRST_POINT)
+ p1 = point;
+// else
+// {
+// p2 = point;
+// mirror->Restore();
+// mirror->Mirror(p1, p2);
+// }
+#endif
+
+#if 0
+// Container & doc = ApplicationWindow::drawing->document;
+// int items = doc.ItemsSelected();
+ Object * toTrim = doc->lastObjectHovered;
+// double closestPt1 = 0, closestPt2 = 1.0;
+ t = 0, u = 1.0;
+
+ if (toTrim == NULL)
+ return;
+
+ if (toTrim->type != OTLine)
+ return;
+
+ double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point);
+
+ std::vector<Object *>::iterator i;
+
+ for(i=doc->objects.begin(); i!=doc->objects.end(); i++)
+ {
+ // Can't trim against yourself... :-P
+ if (*i == toTrim)
+ continue;
+
+ Object * trimAgainst = *i;
+ double t1;//, u1;
+
+ if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine))
+ continue;
+
+ int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1);
+
+ if (intersects)
+ {
+ // Now what? We don't know which side to trim!
+ // ... now we do, we know which side of the Line we're on!
+ if ((t1 > t) && (t1 < pointHoveredT))
+ t = t1;
+
+ if ((t1 < u) && (t1 > pointHoveredT))
+ u = t1;
+ }
+ }
+#endif
+}
+
+
+/*virtual*/ void TriangulateAction::MouseReleased(void)
+{
+#if 0
+ if (state == FIRST_POINT)
+ {
+ p2 = p1;
+ state = NEXT_POINT;
+ }
+ else if (state == NEXT_POINT)
+ {
+ if (!ctrlWasPressed)
+ {
+ state = FIRST_POINT;
+ ApplicationWindow::drawing->document.MirrorSelected(p1, p2);
+
+ mirror->Clear();
+ ApplicationWindow::drawing->document.CopySelectedContentsTo(mirror);
+ mirror->Save();
+ }
+ else
+ {
+ mirror->CopyContentsTo(&(ApplicationWindow::drawing->document));
+ }
+ }
+#endif
+ Object * obj = doc->lastObjectHovered;
+
+ if (obj == NULL)
+ return;
+
+ if (obj->type != OTLine)
+ return;
+
+ if (state == FIRST_LINE)
+ {
+ line1 = (Line *)obj;
+ line1->state = OSSelected;
+ state = SECOND_LINE;
+ }
+ else if (state == SECOND_LINE)
+ {
+ line2 = (Line *)obj;
+ line2->state = OSSelected;
+ state = THIRD_LINE;
+ }
+ else if (state == THIRD_LINE)
+ {
+ line3 = (Line *)obj;
+ state = FIRST_LINE;
+ Triangulate();
+ }
+}
+
+
+/*virtual*/ void TriangulateAction::KeyDown(int /*key*/)
+{
+#if 0
+ if ((key == Qt::Key_Shift) && (state == NEXT_POINT))
+ {
+ shiftWasPressedOnNextPoint = true;
+ p1Save = p1;
+ p1 = p2;
+ state = FIRST_POINT;
+ emit NeedRefresh();
+ }
+ else if (key == Qt::Key_Control)
+ {
+ ctrlWasPressed = true;
+ emit NeedRefresh();
+ }
+#endif
+#if 0
+ if ((t == 0) && (u == 1.0))
+ return;
+
+ t = 0, u = 1.0;
+#endif
+}
+
+
+/*virtual*/ void TriangulateAction::KeyReleased(int /*key*/)
+{
+#if 0
+ if ((key == Qt::Key_Shift) && shiftWasPressedOnNextPoint)
+ {
+ shiftWasPressedOnNextPoint = false;
+ p2 = p1;
+ p1 = p1Save;
+ state = NEXT_POINT;
+ emit NeedRefresh();
+ }
+ else if (key == Qt::Key_Control)
+ {
+ ctrlWasPressed = false;
+ emit NeedRefresh();
+ }
+#endif
+}
+
+
+void TriangulateAction::Triangulate(void)
+{
+// N.B.: Should connect the line segments together, once made into a triangle...
+ double length2 = Vector(line2->position, line2->endpoint).Magnitude();
+ double length3 = Vector(line3->position, line3->endpoint).Magnitude();
+#if 0
+ double angle1, angle2, angle3;
+ double length1 = Vector(line1->position, line1->endpoint).Magnitude();
+ Geometry::FindAnglesForSides(length1, length2, length3, &angle1, &angle2, &angle3);
+printf("Triangulate: l1=%lf, l2=%lf, l3=%lf, a1=%lf, a2=%lf, a3=%lf\n", length1, length2, length3, angle1, angle2, angle3);
+
+ // We use line1 as the base. Move the other lines to it.
+ double line1Angle = Vector(line1->position, line1->endpoint).Angle();
+ double newLine2Angle = line1Angle + angle3;
+ Vector v;
+ v.SetAngleAndLength(newLine2Angle, length2);
+printf(" line1Angle=%lf, newLine2Angle=%lf\n", line1Angle, newLine2Angle);
+
+ line2->position = line1->endpoint;
+ line2->endpoint = line1->endpoint - v;
+
+// double line2Angle = Vector(line2->position, line2->endpoint).Angle();
+// double newLine3Angle = line2Angle + angle1;
+// Vector v2;
+// v2.SetAngleAndLength(newLine3Angle, length3);
+#else
+ Circle c1(line1->position, length2), c2(line1->endpoint, length3);
+ Point p1, p2;
+ int n = Geometry::Intersects(&c1, &c2, 0, 0, 0, 0, &p1, &p2);
+//printf("Circle-circle intersections: n=%i, <%lf, %lf, %lf>, <%lf, %lf, %lf>\n", n, p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
+#endif
+ line2->position = line1->endpoint;
+ line2->endpoint = p1;
+
+ line3->position = line2->endpoint;
+ line3->endpoint = line1->position;
+
+ line1->state = OSInactive;
+ line2->state = OSInactive;
+}
+
--- /dev/null
+#ifndef __TRIANGULATEACTION_H__
+#define __TRIANGULATEACTION_H__
+
+#include "action.h"
+
+class Container;
+class Line;
+
+class TriangulateAction: public Action
+{
+ public:
+ TriangulateAction();
+ ~TriangulateAction();
+
+ virtual void Draw(Painter *);
+ virtual void MouseDown(Vector);
+ virtual void MouseMoved(Vector);
+ virtual void MouseReleased(void);
+ virtual void KeyDown(int);
+ virtual void KeyReleased(int);
+
+ private:
+ void Triangulate(void);
+
+ private:
+ int state;
+ Line * line1, * line2, * line3;
+// Vector p1, p2, p1Save;
+// double t, u;
+ Container * doc;
+};
+
+#endif // __TRIANGULATEACTION_H__
+
-// TrimAction.cpp: Action class for mirroring selected objects
+// trimaction.cpp: Action class for mirroring selected objects
//
// Part of the Architektonas Project
// (C) 2011 Underground Software
}
-/*virtual*/ void TrimAction::MouseDown(Vector point)
+/*virtual*/ void TrimAction::MouseDown(Vector /*point*/)
{
// this is not accurate enough. need to use the actual intersection point, not
// just the parameter(s).
}
-/*virtual*/ void TrimAction::KeyDown(int key)
+/*virtual*/ void TrimAction::KeyDown(int /*key*/)
{
#if 0
if ((key == Qt::Key_Shift) && (state == NEXT_POINT))
}
-/*virtual*/ void TrimAction::KeyReleased(int key)
+/*virtual*/ void TrimAction::KeyReleased(int /*key*/)
{
#if 0
if ((key == Qt::Key_Shift) && shiftWasPressedOnNextPoint)
}
+// Create vector from angle + length (2D; z is set to zero)
+void Vector::SetAngleAndLength(double angle, double length)
+{
+ x = cos(angle) * length;
+ y = sin(angle) * length;
+ z = 0;
+}
+
+
Vector Vector::operator=(Vector const v)
{
x = v.x, y = v.y, z = v.z;
// (Not sure which is head or tail, or which hand the normal lies)
// [v1 should be the tail, v2 should be the head, in which case the normal should
// rotate anti-clockwise.]
-///*static*/ Vector Vector::Normal(Vector v1, Vector v2)
/*static*/ Vector Vector::Normal(Vector tail, Vector head)
{
-// Vector v = (v1 - v2).Unit();
Vector v = (head - tail).Unit();
return Vector(-v.y, v.x);
}
//
// Various structures used for 3 dimensional imaging
//
-// by James L. Hammons
-// (C) 2001 Underground Software
+// by James Hammons
+// (C) 2001, 2014 Underground Software
//
#ifndef __VECTOR_H__
#define __VECTOR_H__
-// What we'll do here is create the vector type and use typedef to alias Point to it. Yeah, that's it.
+// What we'll do here is create the vector type and use typedef to alias Point
+// to it. Yeah, that's it.
class Vector
{
public:
Vector(double xx = 0, double yy = 0, double zz = 0);
Vector(Vector tail, Vector head); // Create vector from two points
+ void SetAngleAndLength(double angle, double length);
Vector operator=(Vector const v);
Vector operator+(Vector const v);
Vector operator-(Vector const v);