src/applicationwindow.h \
src/arc.h \
src/circle.h \
+ src/connection.h \
src/container.h \
src/dimension.h \
src/drawingview.h \
src/applicationwindow.cpp \
src/arc.cpp \
src/circle.cpp \
+ src/connection.cpp \
src/container.cpp \
src/dimension.cpp \
src/drawingview.cpp \
virtual bool Collided(Vector);
virtual void PointerMoved(Vector);
virtual void PointerReleased(void);
-// virtual bool NeedsUpdate(void);
virtual void Enumerate(FILE *);
- virtual Object * Copy(void);
+// virtual Object * Copy(void);
private:
bool AngleInArcSpan(double angle);
virtual void PointerMoved(Vector);
virtual void PointerReleased(void);
virtual void Enumerate(FILE *);
- virtual Object * Copy(void);
+// virtual Object * Copy(void);
protected:
bool HitTest(Point);
--- /dev/null
+//
+// connection.cpp: Object connection support
+//
+// Part of the Architektonas Project
+// (C) 2013 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// Who When What
+// --- ---------- -------------------------------------------------------------
+// JLH 03/14/2013 Created this file
+//
+
+#include "connection.h"
+
+
+Connection::Connection(Object * o/*= NULL*/, double param/*= 0*/)
+{
+ object = o;
+ t = param;
+}
+
+
+Connection::~Connection()
+{
+}
+
--- /dev/null
+#ifndef __CONNECTION_H__
+#define __CONNECTION_H__
+
+//#include "object.h"
+
+//enum DimensionType { DTLinear, DTRadial, DTDiametric, DTCircumferential, DTLeader };
+class Object;
+
+class Connection
+{
+ public:
+ Connection(Object * o = 0, double param = 0);
+ ~Connection();
+
+ public:
+ Object * object;
+ double t;
+};
+
+#endif // __CONNECTION_H__
{
for(int i=0; i<(int)objects.size(); i++)
{
-printf("Container: About to draw (object = $%X)\n", objects[i]);
+//printf("Container: About to draw (object = $%X)\n", objects[i]);
objects[i]->Draw(painter);
}
}
{
if (objects[i]->Collided(point))
{
+#if 0
Dimension * dimension = objects[i]->GetAttachedDimension();
-
+#endif
Object * objectToDelete = objects[i];
objects.erase(objects.begin() + i); // Calls the destructor, (deletes the object, I presume... O_o) [NOPE! SURE DOESN'T!]
delete objectToDelete;
// If this object had an attached dimension, reattach it to another object, if any...
// The only problem with that approach is if the object it gets attached to is deleted,
// it will call the dimension to use a NULL pointer and BLAMMO
+#if 0
if (dimension)
{
Vector p1 = dimension->GetPoint1();
dimension->SetPoint2(objectP2);
}
}
-
+#endif
// This only allows deleting objects one at a time...
break;
// however, this way of doing things could be problematic if we don't
}
+#if 0
// This is bad, p1 & p2 could be NULL, causing much consternation...
Dimension::Dimension(Vector * p1, Vector * p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
Object(*p1, p), endpoint(*p2),
length(p2->Magnitude()), type(dt), point1(p1), point2(p2)
{
}
+#endif
+
+
+// This is bad, p1 & p2 could be NULL, causing much consternation...
+Dimension::Dimension(Connection p1, Connection p2, DimensionType dt/*= DTLinear*/ , Object * p/*= NULL*/):
+/* Object(p1.object->GetPointForParameter(p1.t), p),
+ endpoint(p2.object->GetPointForParameter(p2.t)),*/
+ dragging(false), draggingHandle1(false), draggingHandle2(false),
+ /*length(p2->Magnitude()),*/length(0), type(dt), point1(p1), point2(p2)
+{
+}
Dimension::~Dimension()
{
// If there are valid Vector pointers in here, use them to update the internal
// positions. Otherwise, we just use the internal positions by default.
+#if 0
if (point1)
position = *point1;
if (point2)
endpoint = *point2;
+#else
+ if (point1.object)
+ position = point1.object->GetPointAtParameter(point1.t);
+
+ if (point2.object)
+ endpoint = point2.object->GetPointAtParameter(point2.t);
+#endif
if (state == OSSelected)
painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
}
+#if 0
void Dimension::SetPoint1(Vector * v)
{
point1 = v;
point2 = v;
needUpdate = true;
}
+#endif
+
+
+/*virtual*/ void Dimension::Enumerate(FILE * file)
+{
+ fprintf(file, "DIMENSION (%lf,%lf) (%lf,%lf) %i\n", position.x, position.y, endpoint.x, endpoint.y, type);
+}
+
+
+// Dimensions are special: they contain exactly *two* points. Here, we check
+// only for zero/non-zero in returning the correct points.
+/*virtual*/ Vector Dimension::GetPointAtParameter(double parameter)
+{
+ if (parameter == 0)
+ return position;
+
+ return endpoint;
+}
+
+
+/*virtual*/ void Dimension::Connect(Object * obj, double param)
+{
+ // There are four possibilities here...
+ // The param is only looking for 0 or 1 here.
+ if (point1.object == NULL && point2.object == NULL)
+ {
+ point1.object = obj;
+ point1.t = param;
+ }
+ else if (point1.object == NULL && point2.object != NULL)
+ {
+ if (point2.t == param)
+ point2.object = obj;
+ else
+ {
+ point1.object = obj;
+ point1.t = param;
+ }
+ }
+ else if (point1.object != NULL && point2.object == NULL)
+ {
+ if (point1.t == param)
+ point1.object = obj;
+ else
+ {
+ point2.object = obj;
+ point2.t = param;
+ }
+ }
+ else if (point1.object != NULL && point2.object != NULL)
+ {
+ if (point1.t == param)
+ point1.object = obj;
+ else
+ point2.object = obj;
+ }
+}
+/*virtual*/ void Dimension::Disconnect(Object * obj, double param)
+{
+ if (point1.object == obj && point1.t == param)
+ point1 = NULL;
+ else if (point2.object == obj && point2.t == param)
+ point2 = NULL;
+}
+
+
+/*virtual*/ void Dimension::DisconnectAll(Object * obj)
+{
+ if (point1.object == obj)
+ point1 = NULL;
+
+ if (point2.object == obj)
+ point2 = NULL;
+}
+
+
+#if 0
Vector Dimension::GetPoint1(void)
{
return position;
{
return endpoint;
}
+#endif
void Dimension::FlipSides(void)
position = endpoint;
endpoint = tmp;
#else
- Vector * tmp = point1;
+ Connection tmp = point1;
point1 = point2;
point2 = tmp;
+// double tmp = point1.t;
+// point1.t = point2.t;
+// point2.t = tmp;
+// Object * tmp = point1.object;
+// point1.object = point2.object;
+// point2.object = tmp;
#endif
needUpdate = true;
}
-
-/*virtual*/ void Dimension::Enumerate(FILE * file)
-{
- fprintf(file, "DIMENSION (%lf,%lf) (%lf,%lf) %i\n", position.x, position.y, endpoint.x, endpoint.y, type);
-}
-
#ifndef __DIMENSION_H__
#define __DIMENSION_H__
+#include "connection.h"
#include "object.h"
enum DimensionType { DTLinear, DTRadial, DTDiametric, DTCircumferential, DTLeader };
{
public:
Dimension(Vector, Vector, DimensionType dt = DTLinear, Object * p = 0);
- Dimension(Vector *, Vector *, DimensionType dt = DTLinear, Object * p = 0);
+// Dimension(Vector *, Vector *, DimensionType dt = DTLinear, Object * p = 0);
+ Dimension(Connection, Connection, DimensionType dt = DTLinear, Object * p = 0);
~Dimension();
virtual void Draw(Painter *);
virtual void PointerMoved(Vector);
virtual void PointerReleased(void);
virtual void Enumerate(FILE *);
- virtual Object * Copy(void);
- void SetPoint1(Vector *);
- void SetPoint2(Vector *);
- Vector GetPoint1(void);
- Vector GetPoint2(void);
+// virtual Object * Copy(void);
+ virtual Vector GetPointAtParameter(double parameter);
+ virtual void Connect(Object *, double);
+ virtual void Disconnect(Object *, double);
+ virtual void DisconnectAll(Object *);
void FlipSides(void);
protected:
double length;
DimensionType type;
- Vector * point1; // These couple to external points; if there
- Vector * point2; // are none then fall back to endpoint/position
+ // We use these in lieu of the built-in connected[] array; no reason to
+ // do this way especially
+ Connection point1;
+ Connection point2;
};
#endif // __DIMENSION_H__
#include "dimension.h"
#include "line.h"
+/*
+How to handle connected objects
+-------------------------------
+
+Every Object has a vector<Object *> which enumerates all Objects connected to
+the one we're looking at. So it looks like we'll have to take a two pass
+approach to loading and saving.
+
+Basically, in the saving case, first we write out all objects, keeping a
+pointer-to-index-number record. Second, we loop through all the objects we
+wrote out, writing connection lists. Format (indices are 1-based):
+
+CONNECTIONS
+1: 12 3
+3: 1
+12: 1
+ENDCONNECTIONS
+
+In the reading case, we do pretty much the same: we construct pointer-to-index-
+number list, then read the connection list. The PTIN connects the index to the
+Object pointers we've created. Then we simply call the Object's Connect()
+function to connect the objects.
+
+Small problem though: How does a Dimension know which points on a Line it's
+connected to? This approach tells the Dimension it's connected to the Line and
+the Line that it's connected to the Dimension, but not which points.
+
+How to handle them then? Do we list the point with the Object pointed at? A
+Line can contain an infinite number of points to connect with besides its
+endpoints.
+
+So with each connection Object in the vector, there would also have to be a
+corresponding point to go with it, that would be gotten from the other Object's
+Connect() function. Or, instead of a point, a parameter value?
+
+Doing that, with the Line and a parameter "t", if t == 0 we have endpoint 1.
+if t == 1, then we have endpoint 2. With a Circle, the parameter is a number
+between 0 and 1 (scaled to 0 to 2π). With an Arc, the parameter goes from 0 to
+1, 0 being enpoint 1 and 1 being endpoint 2.
+
+How does this work for moving objects that are connected? Again, with the Line
+and Dimension. The Line's connections looks like this:
+
+Object *: dim1, t = 0
+Object *: dim1, t = 1
+
+Dimension looks like this:
+
+Object *: line1, t = 0
+Object *: line1, t = 1
+
+For Dimensions, it can query the connected object (if any) using something like
+GetPointForParameter(). That way it can figure out where its endpoints are. If
+there is no connected point, then it uses its internal point.
+
+
+Dimensions are special cases of lines: They have exactly *two* points and none
+in between. Therefore, the Dimension object only needs to have two points. But
+those points can be connected to multiple objects. The can also be connected to
+no points/Objects too.
+
+How to describe them and their connections (or lack thereof)?
+
+Would have to be a 2nd pass, after all objects have been written out in order.
+Then you could do something like:
+
+DIMCONNECTIONS
+8 (the Dimension #): 1 (the Object # for point 1) 1 (the Object # for point 2)
+ENDDIMCONNECTIONS
+
+
+
+Connection attributes: E.g., between a Line a Circle, it can be tangent,
+perpendicular, or an arbitrary angle. How to encode that information? It's not
+intrinsic to either the Line or the Circle, but is a function of the
+relationship between them by virtue of their connection.
+
+*/
+
enum ObjectType { OTContainer, OTContainerEnd, OTLine, OTCircle, OTArc, OTDimension,
OTPolygon, OTText, OTImage, OTBlock, OTEndOfFile };
Line::~Line()
{
+// Taking care of connections should be done by the Container, as we don't know
+// anything about any other object connected to this one.
+#if 0
// If there are any attached Dimensions, we must set the attachment points
// to NULL since they will no longer be valid.
if (attachedDimension)
// IT WOULD BE NICE to have any object points attached to this line automagically
// connect to this dimension object at this point, instead of just becoming
// detached.
+#endif
}
/*virtual*/ void Line::Draw(Painter * painter)
// (Priorities are taken care of in HitTest()...)
if (hitLine)
{
+#if 0
if (attachedDimension == NULL)
{
// How to get this object into the top level container???
// 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;
}
return 0;
}
+
#if 0
void Line::SetDimensionOnPoint1(Dimension * dimension)
{
// If they don't pass one in, create it for the caller.
if (dimension == NULL)
{
- dimension = new Dimension(&position, &endpoint, DTLinear, this);
+printf("Line::SetDimensionOnLine(): Creating new dimension...\n");
+// dimension = new Dimension(position, endpoint, DTLinear, this);
+ dimension = new Dimension(Connection(this, 0), Connection(this, 1.0), DTLinear, this);
if (parent)
+{
+printf("Line::SetDimensionOnLine(): Adding to parent...\n");
parent->Add(dimension);
+}
+ }
+ else
+ {
+ dimension->Connect(this, 0);
+ dimension->Connect(this, 1.0);
}
- attachedDimension = dimension;
+ // Make sure the Dimension is connected to us...
+#if 0
+ connected.push_back(Connection(dimension, 0));
+ connected.push_back(Connection(dimension, 1.0));
+#else
+ Connect(dimension, 0);
+ Connect(dimension, 1.0);
+#endif
+// attachedDimension = dimension;
+
+#if 0
// After we set the points here, we don't have to care about them anymore.
if (dimension)
{
dimension->SetPoint1(&position);
dimension->SetPoint2(&endpoint);
}
+#endif
}
#endif
+
+Object * Line::FindAttachedDimension(void)
+{
+ // Is there anything connected to this line? If not, return NULL
+ if (connected.size() < 2)
+ return NULL;
+
+ // Otherwise, we have to search our objects to see if there's a likely
+ // candidate. In this case, we're looking for a pointer to the same object
+ // with a parameter of 0 and 1 respectively. This is O((n^2)/2).
+ for(uint i=0; i<connected.size(); i++)
+ {
+ for(uint j=i+1; j<connected.size(); j++)
+ {
+printf("Line: connected[i]=%X, connected[j]=%X, connected[i].t=%lf, connected[j].t=%lf\n", connected[i].object, connected[j].object, connected[i].t, connected[j].t);
+ if ((connected[i].object == connected[j].object)
+ && ((connected[i].t == 0 && connected[j].t == 1.0)
+ || (connected[i].t == 1.0 && connected[j].t == 0)))
+ return connected[i].object;
+ }
+ }
+
+ // Didn't find anything, so return NULL
+ return NULL;
+}
+
+
bool Line::HitTest(Point point)
{
SaveState();
return new Line(position, endpoint, parent);
}
+
+/*virtual*/ Vector Line::GetPointAtParameter(double parameter)
+{
+ if (parameter <= 0)
+ return position;
+ else if (parameter >= 1.0)
+ return endpoint;
+
+ // Our parameter lies between zero and one, so calculate it!
+ Vector v(endpoint, position);
+ double length = v.Magnitude();
+ // We scale the magnitude of v so that it lies between 0 and 1...
+ // By multiplying the parameter by the magnitude, we obtain the point we
+ // want. No scaling necessary as it's inherent in the approach!
+ double spotOnLength = length * parameter;
+
+ // To get our point, we use the initial point of the line and add in our
+ // scaled point.
+ Vector result = position + (v * spotOnLength);
+ return result;
+}
+
virtual Vector * GetPointAt(Vector);
virtual void Enumerate(FILE *);
virtual Object * Copy(void);
+ virtual Vector GetPointAtParameter(double parameter);
// void SetDimensionOnPoint1(Dimension *);
// void SetDimensionOnPoint2(Dimension *);
void SetDimensionOnLine(Dimension * d = 0);
+ Object * FindAttachedDimension(void);
protected:
bool HitTest(Point);
//
#include "object.h"
+#include <stdlib.h>
// Initialize static variables
bool Object::fixedAngle = false;
Object::Object(): position(Vector(0, 0)), parent(0), state(OSInactive), oldState(OSInactive),
- needUpdate(false), attachedDimension(0)
+ needUpdate(false)//, attachedDimension(0)
{
}
Object::Object(Vector v, Object * passedInParent/*= 0*/): position(v), parent(passedInParent),
- state(OSInactive), oldState(OSInactive), needUpdate(false), attachedDimension(0)
+ state(OSInactive), oldState(OSInactive), needUpdate(false)//, attachedDimension(0)
{
}
Object::~Object()
{
+printf("Object: Destroyed!\n");
+ for(uint i=0; i<connected.size(); i++)
+ connected[i].object->DisconnectAll(this);
}
}
+// This returns a point on the object at 'parameter', which is between 0 and 1.
+// Default is to return the object's position.
+/*virtual*/ Vector Object::GetPointAtParameter(double)
+{
+ return position;
+}
+
+
+// Since these functions are pretty much non-object specific, we can implement
+// them here. :-)
+/*virtual*/ void Object::Connect(Object * obj, double parameter)
+{
+ connected.push_back(Connection(obj, parameter));
+}
+
+
+/*virtual*/ void Object::Disconnect(Object * obj, double parameter)
+{
+ for(uint i=0; i<connected.size(); i++)
+ {
+ if (connected[i].object == obj && connected[i].t == parameter)
+ {
+ connected.erase(connected.begin() + i);
+ return;
+ }
+ }
+}
+
+
+/*virtual*/ void Object::DisconnectAll(Object * obj)
+{
+ // According the std::vector docs, only items at position i and beyond are
+ // invalidated, everything before i is still valid. So we use that here.
+ for(uint i=0; i<connected.size();)
+ {
+ // If we found our object, erase it from the vector but don't advance
+ // the iterator. Otherwise, advance the iterator. :-)
+ if (connected[i].object == obj)
+ connected.erase(connected.begin() + i);
+ else
+ i++;
+ }
+}
+
+
ObjectState Object::GetState(void)
{
return state;
}
-Dimension * Object::GetAttachedDimension(void)
+/*Dimension * Object::GetAttachedDimension(void)
{
return attachedDimension;
-}
+}*/
// Class methods...
#ifndef __OBJECT_H__
#define __OBJECT_H__
+#include <stdio.h>
#include <vector> // This is a container
#include "vector.h" // This is the mathematical construct
-#include <stdio.h>
+#include "connection.h"
class Painter;
class QFont;
virtual Vector * GetPointAt(Vector);
virtual void Enumerate(FILE *);
virtual Object * Copy(void);
+ virtual Vector GetPointAtParameter(double parameter);
+ virtual void Connect(Object *, double);
+ virtual void Disconnect(Object *, double);
+ virtual void DisconnectAll(Object *);
ObjectState GetState(void);
void Reparent(Object *);
- Dimension * GetAttachedDimension(void);
+// Dimension * GetAttachedDimension(void);
//Hm. Object * Connect(Object *);
// Class methods
ObjectState state;
ObjectState oldState;
bool needUpdate;
- Dimension * attachedDimension;
- std::vector<Object *> connected;
+// Dimension * attachedDimension;
+ std::vector<Connection> connected;
// Class variables
static QFont * font;