From 676007c81e292079daaa7188f4fbf2757ae77ef8 Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Thu, 14 Mar 2013 16:41:26 -0500 Subject: [PATCH] Fixes to accomodate object connections. Object connections turns out to be one of the most difficult things to implement in this project. I think the way that I have it set up now is the least bad out of all of the implementation ideas I've looked at. It remains to be seen if I've guessed right. --- architektonas.pro | 2 + src/arc.h | 3 +- src/circle.h | 2 +- src/connection.cpp | 28 +++++++++++ src/connection.h | 20 ++++++++ src/container.cpp | 8 ++-- src/dimension.cpp | 113 ++++++++++++++++++++++++++++++++++++++++++--- src/dimension.h | 20 ++++---- src/fileio.cpp | 79 +++++++++++++++++++++++++++++++ src/line.cpp | 100 ++++++++++++++++++++++++++++++++++++++- src/line.h | 2 + src/object.cpp | 57 +++++++++++++++++++++-- src/object.h | 13 ++++-- 13 files changed, 416 insertions(+), 31 deletions(-) create mode 100644 src/connection.cpp create mode 100644 src/connection.h diff --git a/architektonas.pro b/architektonas.pro index 1d1000d..5287f4d 100644 --- a/architektonas.pro +++ b/architektonas.pro @@ -46,6 +46,7 @@ HEADERS = \ src/applicationwindow.h \ src/arc.h \ src/circle.h \ + src/connection.h \ src/container.h \ src/dimension.h \ src/drawingview.h \ @@ -68,6 +69,7 @@ SOURCES = \ src/applicationwindow.cpp \ src/arc.cpp \ src/circle.cpp \ + src/connection.cpp \ src/container.cpp \ src/dimension.cpp \ src/drawingview.cpp \ diff --git a/src/arc.h b/src/arc.h index 262fc85..1f554ba 100644 --- a/src/arc.h +++ b/src/arc.h @@ -14,9 +14,8 @@ class Arc: public Object 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); diff --git a/src/circle.h b/src/circle.h index 72bdea2..57cde7b 100644 --- a/src/circle.h +++ b/src/circle.h @@ -15,7 +15,7 @@ class Circle: public Object virtual void PointerMoved(Vector); virtual void PointerReleased(void); virtual void Enumerate(FILE *); - virtual Object * Copy(void); +// virtual Object * Copy(void); protected: bool HitTest(Point); diff --git a/src/connection.cpp b/src/connection.cpp new file mode 100644 index 0000000..2dd8e24 --- /dev/null +++ b/src/connection.cpp @@ -0,0 +1,28 @@ +// +// 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 +// +// 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() +{ +} + diff --git a/src/connection.h b/src/connection.h new file mode 100644 index 0000000..485c4c0 --- /dev/null +++ b/src/connection.h @@ -0,0 +1,20 @@ +#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__ diff --git a/src/container.cpp b/src/container.cpp index 48e412a..a8cf0ad 100644 --- a/src/container.cpp +++ b/src/container.cpp @@ -67,7 +67,7 @@ Container & Container::operator=(const Container & from) { 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); } } @@ -152,8 +152,9 @@ Like so: { 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; @@ -161,6 +162,7 @@ Dimension * dimension = objects[i]->GetAttachedDimension(); // 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(); @@ -177,7 +179,7 @@ if (dimension) 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 diff --git a/src/dimension.cpp b/src/dimension.cpp index ad2ffcb..d2aeaf6 100644 --- a/src/dimension.cpp +++ b/src/dimension.cpp @@ -26,6 +26,7 @@ Dimension::Dimension(Vector p1, Vector p2, DimensionType dt/*= DTLinear*/ ,Objec } +#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), @@ -33,6 +34,17 @@ Dimension::Dimension(Vector * p1, Vector * p2, DimensionType dt/*= DTLinear*/ , 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() @@ -44,11 +56,19 @@ 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)); @@ -327,6 +347,7 @@ about keeping track of old states... } +#if 0 void Dimension::SetPoint1(Vector * v) { point1 = v; @@ -339,8 +360,85 @@ void Dimension::SetPoint2(Vector * 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; @@ -351,6 +449,7 @@ Vector Dimension::GetPoint2(void) { return endpoint; } +#endif void Dimension::FlipSides(void) @@ -360,16 +459,16 @@ 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); -} - diff --git a/src/dimension.h b/src/dimension.h index 52f838f..be8e5c5 100644 --- a/src/dimension.h +++ b/src/dimension.h @@ -1,6 +1,7 @@ #ifndef __DIMENSION_H__ #define __DIMENSION_H__ +#include "connection.h" #include "object.h" enum DimensionType { DTLinear, DTRadial, DTDiametric, DTCircumferential, DTLeader }; @@ -9,7 +10,8 @@ class Dimension: public Object { 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 *); @@ -18,11 +20,11 @@ class Dimension: public Object 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: @@ -37,8 +39,10 @@ class Dimension: public Object 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__ diff --git a/src/fileio.cpp b/src/fileio.cpp index fedddea..4a1c25f 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -24,6 +24,85 @@ #include "dimension.h" #include "line.h" +/* +How to handle connected objects +------------------------------- + +Every Object has a vector 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 }; diff --git a/src/line.cpp b/src/line.cpp index 739842b..244af94 100644 --- a/src/line.cpp +++ b/src/line.cpp @@ -32,6 +32,9 @@ Line::Line(Vector p1, Vector p2, Object * p/*= NULL*/): Object(p1, p), endpoint( 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) @@ -42,6 +45,7 @@ Line::~Line() // 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) @@ -123,6 +127,7 @@ TODO: Make Dimension preview with modifier keys for showing on other side // (Priorities are taken care of in HitTest()...) if (hitLine) { +#if 0 if (attachedDimension == NULL) { // How to get this object into the top level container??? @@ -142,6 +147,26 @@ a dimension only) Draw() function... :-/ // 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; } @@ -372,6 +397,7 @@ about keeping track of old states... return 0; } + #if 0 void Line::SetDimensionOnPoint1(Dimension * dimension) { @@ -394,23 +420,71 @@ void Line::SetDimensionOnLine(Dimension * dimension/*=NULL*/) // 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= 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; +} + diff --git a/src/line.h b/src/line.h index de1bbfe..e9b73c7 100644 --- a/src/line.h +++ b/src/line.h @@ -19,9 +19,11 @@ class Line: public Object 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); diff --git a/src/object.cpp b/src/object.cpp index 8f9377b..925d9b4 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -16,6 +16,7 @@ // #include "object.h" +#include // Initialize static variables bool Object::fixedAngle = false; @@ -28,19 +29,22 @@ bool Object::snapToGrid = true; 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; iDisconnectAll(this); } @@ -113,6 +117,51 @@ Object::~Object() } +// 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 #include // This is a container #include "vector.h" // This is the mathematical construct -#include +#include "connection.h" class Painter; class QFont; @@ -31,9 +32,13 @@ class Object 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 @@ -53,8 +58,8 @@ class Object ObjectState state; ObjectState oldState; bool needUpdate; - Dimension * attachedDimension; - std::vector connected; +// Dimension * attachedDimension; + std::vector connected; // Class variables static QFont * font; -- 2.37.2