--- /dev/null
+Stuff To Be Implemented/Fixed
+-----------------------------
+
+ - Add Arc
+ - Add Polygon
+ - Manipulate Dimension
+ - Object connections
+ - Group selection (kind done, needs more work though)
+ - Take movement code out of Objects and put it into top level Container
+ - Fix snap to grid to honor both states (right now, it's a weird mix of states)
+ - Add OSD routines so they don't have to be implemented in Objects
+ - Add OSD to Object creation
+ - Add layers
+ - Add blocks
+ - Add page layout
+ - Add pen color/style/width to Objects
+ - Add fill/hatch to Objects
+ - Fix zooming to be more intuitive
+ - Add other Dimension types, like radial, diametric, leader
+ - Add Ellipse
+ - Add Spline
+ - Add Text
+
<file>eye-closed.png</file>
<file>lock-open.png</file>
<file>lock-closed.png</file>
+ <file>connect-tool.png</file>
+ <file>disconnect-tool.png</file>
</qresource>
</RCC>
groupAct = CreateAction(tr("&Group"), tr("Group"), tr("Group/ungroup selected objects."), QIcon(":/res/group-tool.png"), QKeySequence("g"));
connect(groupAct, SIGNAL(triggered()), this, SLOT(HandleGrouping()));
+ connectAct = CreateAction(tr("&Connect"), tr("Connect"), tr("Connect objects at point."), QIcon(":/res/connect-tool.png"), QKeySequence("c,c"));
+
+ disconnectAct = CreateAction(tr("&Disconnect"), tr("Disconnect"), tr("Disconnect objects joined at point."), QIcon(":/res/disconnect-tool.png"), QKeySequence("d,d"));
+
//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.
/* QActionGroup * group = new QActionGroup(this);
menu->addAction(fixAngleAct);
menu->addAction(fixLengthAct);
menu->addAction(rotateAct);
+ menu->addAction(connectAct);
+ menu->addAction(disconnectAct);
menu->addSeparator();
menu->addAction(deleteAct);
menu->addSeparator();
toolbar->addAction(fixLengthAct);
toolbar->addAction(rotateAct);
toolbar->addAction(deleteAct);
+ toolbar->addAction(connectAct);
+ toolbar->addAction(disconnectAct);
toolbar->addSeparator();
toolbar->addAction(addLineAct);
toolbar->addAction(addCircleAct);
QAction * zoomOutAct;
QAction * snapToGridAct;
QAction * groupAct;
+ QAction * connectAct;
+ QAction * disconnectAct;
};
#endif // __APPLICATIONWINDOW_H__
QRectF boundary;
for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
-// for(int i=0; i<(int)objects.size(); i++)
{
-#if 0
-//printf("Container: About to draw (object = $%X)\n", objects[i]);
- objects[i]->Draw(painter);
- bounds = bounds.united(objects[i].RectangularExtents());
-#else
(*i)->Draw(painter);
boundary = boundary.united((*i)->Extents());
-#endif
}
- if (state == OSSelected)
+ if ((state == OSSelected) || hit)
{
- painter->SetPen(QPen(Qt::magenta, 2.0, Qt::DashLine));
+ if (hit)
+ painter->SetPen(QPen(Qt::magenta, 1.0, Qt::DashLine));
+ else
+ painter->SetPen(QPen(Qt::blue, 2.0, Qt::DashLine));
+
painter->SetBrush(QBrush(Qt::NoBrush));
painter->DrawRect(boundary);
}
return position;
}
+
/*
We need at least *three* handles for this object:
- one for moving
state = (collision ? OSSelected : OSInactive);
if (state == OSSelected)
+ {
DeselectAll();
+ dragging = true;
+ oldPoint = point;
+ }
}
return collision;
// every object for collision.
/*virtual*/ void Container::PointerMoved(Vector point)
{
+ std::vector<Object *>::iterator i;
+
if (!isTopLevelContainer)
{
// check for selection rectangle too
+ if (selectionInProgress)
+ {
+ if (selection.contains(Extents()))
+ state = OSSelected;
+ else
+ state = OSInactive;
+ return;
+ }
- needUpdate = true;
+ // No need to do any checking if we're already selected...
+// if (state == OSSelected)
+// return;
- for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+// oldState = state;
+// needUpdate = true;
+// if (dragging &&
+ bool oldHit = hit;
+ hit = false;
+
+ for(i=objects.begin(); i!=objects.end(); i++)
{
if ((*i)->HitTest(point))
{
- state = OSSelected;
- return;
+// state = OSSelected;
+// return;
+ hit = true;
+ break;
}
}
- state = OSInactive;
+ needUpdate = (oldHit != hit ? true : false);
+// state = oldState;
+
+ if (dragging)
+ {
+ Vector delta = point - oldPoint;
+
+ for(i=objects.begin(); i!=objects.end(); i++)
+ (*i)->Translate(delta);
+
+ oldPoint = point;
+ needUpdate = true;
+ }
+
return;
}
-// objectWasDragged = true;
-//printf("CONTAINER: PointerMoved()\n");
-
for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
-// for(int i=0; i<(int)objects.size(); i++)
{
-//// if (objects[i]->GetState() == OSSelected)
-// objects[i]->PointerMoved(point);
+// if (objects[i]->GetState() == OSSelected)
(*i)->PointerMoved(point);
}
/*virtual*/ void Container::PointerReleased(void)
{
+ if (!isTopLevelContainer)
+ {
+ dragging = false;
+ return;
+ }
+#if 0
dragging = false;
draggingHandle1 = false;
draggingHandle2 = false;
// Here we check for just a click: If object was clicked and dragged, then
// revert to the old state (OSInactive). Otherwise, keep the new state that
// we set.
-/*Maybe it would be better to just check for "object was dragged" state and not have to worry
-about keeping track of old states...
+/*
+Maybe it would be better to just check for "object was dragged" state and not
+have to worry about keeping track of old states...
*/
if (objectWasDragged)
state = oldState;
//Note that the preceeding is unnecessary for a generic container!
+#endif
- for(int i=0; i<(int)objects.size(); i++)
- objects[i]->PointerReleased();
+// for(int i=0; i<(int)objects.size(); i++)
+// objects[i]->PointerReleased();
+ for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
+ (*i)->PointerReleased();
}
{
for(std::vector<Object *>::iterator i=objects.begin(); i!=objects.end(); i++)
{
-// Object * object = *i;
-
if ((*i)->type == OTDimension)
-// if (object->type == OTDimension)
- {
((Dimension *)(*i))->size = newSize;
- }
if ((*i)->type == OTContainer)
- {
((Container *)(*i))->ResizeAllDimensions(newSize);
- }
}
}
/*virtual*/ void Container::Enumerate(FILE * file)
{
// Only put "CONTAINER" markers if *not* the top level container
- if (parent != NULL)
+// if (parent != NULL)
+ if (!isTopLevelContainer)
fprintf(file, "CONTAINER\n");
for(uint i=0; i<objects.size(); i++)
objects[i]->Enumerate(file);
- if (parent != NULL)
+// if (parent != NULL)
+ if (!isTopLevelContainer)
fprintf(file, "ENDCONTAINER\n");
}
bool draggingHandle1;
bool draggingHandle2;
bool objectWasDragged;
+ bool hit;
};
#endif // __CONTAINER_H__
// Calculate whether or not the arrowheads are too crowded to put inside
// the extension lines. 9.0 is the length of the arrowhead.
- double t = Vector::Parameter(position, endpoint, endpoint - (unit * 9.0 * size));
+// double t = Vector::Parameter(position, endpoint, endpoint - (unit * 9.0 * size));
+// double t = Vector::Parameter(position, endpoint, position + (unit * 9.0 * size));
+ double t = Vector::Parameter(endpoint, position, position + (unit * 9.0 * size));
//printf("Dimension::Draw(): t = %lf\n", t);
- if (t > 0.42)
+// On the screen, it's acting like this is actually 58%...
+// This is correct, we want it to happen at > 50%
+ if (t > 0.58)
{
// Draw main dimension line + arrowheads
painter->DrawLine(p1, p2);
{
// We can assume this, since this is a mouse down event here.
objectWasDragged = false;
+ SaveState();
HitTest(point);
+// return StateChanged();
+// this is shite. this should be checked for in the Container, not here!
// If we're part of a non-top-level container, send this signal to it
if (parent->type == OTContainer && !((Container *)parent)->isTopLevelContainer
&& (hitLine || hitPoint1 || hitPoint2))
#if 0
if (selection.normalized().contains(Extents()))
#else
- if (selection.normalized().contains(position.x, position.y)
- && selection.normalized().contains(endpoint.x, endpoint.y))
+// if (selection.normalized().contains(position.x, position.y)
+// && selection.normalized().contains(endpoint.x, endpoint.y))
+ if (selection.contains(position.x, position.y)
+ && selection.contains(endpoint.x, endpoint.y))
#endif
state = OSSelected;
else
return;
}
- // Hit test tells us what we hit (if anything) through boolean variables. It
- // also tells us whether or not the state changed.
- needUpdate = HitTest(point);
+ // 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)
+ SaveState();
+ HitTest(point);
+ needUpdate = StateChanged();
objectWasDragged = (draggingLine | draggingHandle1 | draggingHandle2);
Vector point1 = (draggingHandle1 ? endpoint : position);
Vector point2 = (draggingHandle1 ? position : endpoint);
-#if 0
- Vector current(point2, point1);
- Vector v = current.Unit() * length;
- Vector v2 = point1 + v;
-
- //bleh
- if (!Object::fixedLength)
- v2 = point2;
-#endif
-
if (Object::fixedAngle)
{
// Here we calculate the component of the current vector along the fixed angle.
}
else // endpoint
{
-// Vector v1 = endpoint - position;
Vector v = Vector(endpoint - position).Unit() * length;
endpoint = position + v;
}
}
else
{
- // Otherwise, we calculate the new length, just in case on the next move
- // it turns out to have a fixed length. :-)
+ // Otherwise, we calculate the new length, just in case on the next
+ // move it turns out to have a fixed length. :-)
length = Vector(endpoint - position).Magnitude();
}
if (!Object::fixedAngle)
{
- // Calculate the new angle, just in case on the next move it turns out to
- // be fixed. :-)
+ // Calculate the new angle, just in case on the next move it turns
+ // out to be fixed. :-)
angle = Vector(endpoint - position).Unit();
}
}
draggingHandle1 = false;
draggingHandle2 = false;
-// hitPoint1 = hitPoint2 = hitLine = false;
-
- // Here we check for just a click: If object was clicked and dragged, then
- // revert to the old state (OSInactive). Otherwise, keep the new state that
- // we set.
-/*Maybe it would be better to just check for "object was dragged" state and not have to worry
-about keeping track of old states...
-*/
if (objectWasDragged)
state = oldState;
}
/*virtual*/ bool Line::HitTest(Point point)
{
- SaveState();
+// SaveState();
hitPoint1 = hitPoint2 = hitLine = false;
Vector lineSegment = endpoint - position;
Vector v1 = point - position;
Vector v2 = point - endpoint;
- double parameterizedPoint = lineSegment.Dot(v1) / lineSegment.Magnitude(), distance;
+ double t = Vector::Parameter(position, endpoint, point);
+ double distance;
// Geometric interpretation:
- // The parameterized point on the vector lineSegment is where the perpendicular
- // intersects lineSegment. If pp < 0, then the perpendicular lies beyond the 1st
- // endpoint. If pp > length of ls, then the perpendicular lies beyond the 2nd endpoint.
-
- if (parameterizedPoint < 0.0)
- distance = v1.Magnitude();
- else if (parameterizedPoint > lineSegment.Magnitude())
- distance = v2.Magnitude();
- else
- // distance = ?Det?(ls, v1) / |ls|
- distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y) / lineSegment.Magnitude());
+ // The parameter "t" on the vector lineSegment is where the normal of
+ // lineSegment coincides with point. If t < 0, the normal lies beyond the
+ // 1st endpoint. If t > 1, then the normal lies beyond the 2nd endpoint. We
+ // only calculate the length of the normal between the point and the
+ // lineSegment when the parameter is between 0 and 1.
- // Geometric interpretation of the above:
+ // Geometric interpretation of "distance = ?Det?(ls, v1) / |ls|":
// If the segment endpoints are s and e, and the point is p, then the test
// for the perpendicular intercepting the segment is equivalent to insisting
// that the two dot products {s-e}.{s-p} and {e-s}.{e-p} are both non-negative.
// all other rows, we end up with the matrix on the right which greatly
// simplifies the calculation of the determinant.
-//How do we determine distance here? Especially if zoomed in or out???
-//#warning "!!! Distances tested for may not be valid if zoomed in or out !!!"
-// [FIXED]
+ if (t < 0.0)
+ distance = v1.Magnitude();
+ else if (t > 1.0)
+ distance = v2.Magnitude();
+ else
+ // distance = ?Det?(ls, v1) / |ls|
+ distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
+ / lineSegment.Magnitude());
+
if ((v1.Magnitude() * Painter::zoom) < 8.0)
hitPoint1 = true;
else if ((v2.Magnitude() * Painter::zoom) < 8.0)
else if ((distance * Painter::zoom) < 5.0)
hitLine = true;
- return StateChanged();
+ return (hitPoint1 | hitPoint2 | hitLine ? true : false);
+// return StateChanged();
}
}
+/*virtual*/ void Line::Translate(Vector amount)
+{
+ position += amount;
+ endpoint += amount;
+}
+
+
+/*virtual*/ void Line::Rotate(Vector point, double angle)
+{
+}
+
+
+/*virtual*/ void Line::Scale(Vector point, double amount)
+{
+}
+
+
void Line::SetDimensionOnLine(Dimension * dimension/*=NULL*/)
{
// If they don't pass one in, create it for the caller.
virtual Vector GetPointAtParameter(double parameter);
virtual QRectF Extents(void);
// virtual ObjectType Type(void);
+ virtual void Translate(Vector);
+ virtual void Rotate(Vector, double);
+ virtual void Scale(Vector, double);
void SetDimensionOnLine(Dimension * d = 0);
Object * FindAttachedDimension(void);
#endif
+/*virtual*/ void Object::Translate(Vector)
+{
+}
+
+
+/*virtual*/ void Object::Rotate(Vector, double)
+{
+}
+
+
+/*virtual*/ void Object::Scale(Vector, double)
+{
+}
+
+
ObjectState Object::GetState(void)
{
return state;
virtual void DisconnectAll(Object *);
virtual QRectF Extents(void);
// virtual ObjectType Type(void);// = 0; // Pure virtual, must be implemented
+ virtual void Translate(Vector);
+ virtual void Rotate(Vector, double);
+ virtual void Scale(Vector, double);
ObjectState GetState(void);
void Reparent(Object *);
// Dimension * GetAttachedDimension(void);
double magnitude = lineSegment.Magnitude();\r
Vector pointSegment = p - v1;\r
double t = lineSegment.Dot(pointSegment) / (magnitude * magnitude);\r
-\r
return t;\r
}\r
\r
+\r
+// Return the normal to the linesegment formed by the passed in points.\r
+// (Not sure which is head or tail, or which hand the normal lies)\r
+/*static*/ Vector Vector::Normal(Vector v1, Vector v2)\r
+{\r
+ Vector v = (v2 - v1).Unit();\r
+ return Vector(-v.y, v.x);\r
+}\r
+\r
static double Dot(Vector v1, Vector v2);\r
static double Magnitude(Vector v1, Vector v2);\r
static double Parameter(Vector v1, Vector v2, Vector p);\r
+ static Vector Normal(Vector v1, Vector v2);\r
\r
public:\r
double x, y, z;\r