connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int)));
connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle()));
connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int)));
+ connect(this, SIGNAL(ReloadLayers()), lw, SLOT(Reload()));
connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
}
void ApplicationWindow::FileNew(void)
{
- // Should warn the user if drawing hasn't been saved... !!! FIX !!!
+ // Warn the user if drawing has changed and hasn't been saved...
+ if (drawing->dirty)
+ {
+ QMessageBox msg;
+
+ msg.setText("The document has been modified.");
+ msg.setInformativeText("Do you want to save your changes?");
+ msg.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
+ msg.setDefaultButton(QMessageBox::Save);
+ int response = msg.exec();
+
+ switch (response)
+ {
+ case QMessageBox::Save:
+ // Save was clicked
+ FileSave();
+ break;
+ case QMessageBox::Discard:
+ // Don't Save was clicked
+ break;
+ case QMessageBox::Cancel:
+ // Cancel was clicked
+ return;
+// break;
+ }
+ }
+
+ FileIO::ResetLayerVectors();
+ emit ReloadLayers();
DeleteContents(drawing->document.objects);
drawing->document.objects.clear();
+ drawing->dirty = false;
drawing->update();
documentName.clear();
setWindowTitle("Architektonas - Untitled");
//printf("FileOpen: container size = %li\n", container.objects.size());
// Keep memory leaks from happening by getting rid of the old document
DeleteContents(drawing->document.objects);
+ emit ReloadLayers();
// We can do this because the vector is just a bunch of pointers to our
- // Objects, and the Containers (non-empty) can be moved way with no problem.
+ // Objects, and the Containers (non-empty) can be moved this way with no
+ // problem.
drawing->document = container;
+ drawing->dirty = false;
drawing->update();
documentName = filename;
setWindowTitle(QString("Architektonas - %1").arg(documentName));
return;
}
+ drawing->dirty = false;
setWindowTitle(QString("Architektonas - %1").arg(documentName));
statusBar()->showMessage(tr("Drawing saved."));
}
}
+void ApplicationWindow::contextMenuEvent(QContextMenuEvent * event)
+{
+ QMenu menu(this);
+ menu.addAction(mirrorAct);
+ menu.addAction(rotateAct);
+ menu.addAction(trimAct);
+ menu.exec(event->globalPos());
+}
+
+
void ApplicationWindow::SnapToGridTool(void)
{
Global::snapToGrid = snapToGridAct->isChecked();
ClearSelected(c->objects);
c->selected = true;
c->layer = Global::currentLayer;
+
+ Rect r = drawing->GetObjectExtents((Object *)c);
+ c->p[0] = r.TopLeft();
+ c->p[1] = r.BottomRight();
+
drawing->select.clear();
drawing->select.push_back(c);
statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
void ApplicationWindow::HandleDimensionSize(QString text)
{
+/*
+This is the third input on the view toolbar; not sure what it was supposed to do...
+*/
// Parse the text...
bool ok;
double value = text.toDouble(&ok);
drawing->update();
}
+void ApplicationWindow::EditCopy(void)
+{
+ if (drawing->select.size() > 0)
+ {
+ DeleteContents(clipboard);
+ CopySelectedObjectsTo(clipboard, drawing->document.objects);
+ }
+}
+
+
+void ApplicationWindow::EditCut(void)
+{
+ if (drawing->select.size() > 0)
+ {
+ DeleteContents(clipboard);
+ MoveSelectedObjectsTo(clipboard, drawing->document.objects);
+ drawing->update();
+ }
+}
+
+
+void ApplicationWindow::EditPaste(void)
+{
+ if (clipboard.size() > 0)
+ {
+ // We want to maybe make it so that the pasted objects are being moved in a "mouse drag" state...
+ // This only moves the cut/copied from the clipboard to the drawing.
+// AddObjectsTo(drawing->document.objects, clipboard);
+ CopyObjects(clipboard, drawing->document.objects);
+ drawing->update();
+ }
+}
+
void ApplicationWindow::CreateActions(void)
{
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()));
+ editCutAct = CreateAction(tr("&Cut Objects"), tr("Cut Objects"), tr("Cut objects from the drawing to the clipboard."), QIcon(":/res/editcut2.png"), QKeySequence(tr("Ctrl+x")));
+ connect(editCutAct, SIGNAL(triggered()), this, SLOT(EditCut()));
+
+ editCopyAct = CreateAction(tr("&Copy Objects"), tr("Copy Objects"), tr("Copy objects from the drawing to the clipboard."), QIcon(":/res/editcopy2.png"), QKeySequence(tr("Ctrl+c")));
+ connect(editCopyAct, SIGNAL(triggered()), this, SLOT(EditCopy()));
+
+ editPasteAct = CreateAction(tr("&Paste Objects"), tr("Paste Objects"), tr("Paste objects from the clipboard to the drawing."), QIcon(":/res/editpaste2.png"), QKeySequence(tr("Ctrl+v")));
+ connect(editPasteAct, SIGNAL(triggered()), this, SLOT(EditPaste()));
//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.
//
// Consolidates action creation from a multi-step process to a single-step one.
//
-QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
- QIcon icon, QKeySequence key, bool checkable/*= false*/)
+QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
+ QString statustip, QIcon icon, QKeySequence key, bool checkable/*= false*/)
{
QAction * action = new QAction(icon, name, this);
action->setToolTip(tooltip);
// This is essentially the same as the previous function, but this allows more
// than one key sequence to be added as key shortcuts.
//
-QAction * ApplicationWindow::CreateAction(QString name, QString tooltip, QString statustip,
- QIcon icon, QKeySequence key1, QKeySequence key2, bool checkable/*= false*/)
+QAction * ApplicationWindow::CreateAction(QString name, QString tooltip,
+ QString statustip, QIcon icon, QKeySequence key1, QKeySequence key2,
+ bool checkable/*= false*/)
{
QAction * action = new QAction(icon, name, this);
action->setToolTip(tooltip);
menu->addAction(connectAct);
menu->addAction(disconnectAct);
menu->addSeparator();
+ menu->addAction(editCutAct);
+ menu->addAction(editCopyAct);
+ menu->addAction(editPasteAct);
menu->addAction(deleteAct);
menu->addSeparator();
menu->addAction(addLineAct);
toolbar->addAction(mirrorAct);
toolbar->addAction(trimAct);
toolbar->addAction(triangulateAct);
+ toolbar->addAction(editCutAct);
+ toolbar->addAction(editCopyAct);
+ toolbar->addAction(editPasteAct);
toolbar->addAction(deleteAct);
toolbar->addAction(connectAct);
toolbar->addAction(disconnectAct);
protected:
void closeEvent(QCloseEvent * event);
+ void contextMenuEvent(QContextMenuEvent * event);
private slots:
void FileNew(void);
void HandleGridSizeInPixels(int);
void HandleGridSizeInBaseUnits(QString);
void HandleDimensionSize(QString);
+ void EditCut(void);
+ void EditCopy(void);
+ void EditPaste(void);
+
+ signals:
+ void ReloadLayers(void);
private:
void ClearUIToolStatesExcept(QAction *);
QAction * mirrorAct;
QAction * trimAct;
QAction * triangulateAct;
+ QAction * editCutAct;
+ QAction * editCopyAct;
+ QAction * editPasteAct;
+
+ std::vector<void *> clipboard;
// Class variables
public:
+//
// drawingview.cpp
//
// Part of the Architektonas Project
//
// - Redo rendering code to *not* use Qt's transform functions, as they are tied
// to a left-handed system and we need a right-handed one. [DONE]
+// - Fixed length tool doesn't work on lines [DONE]
//
// STILL TO BE DONE:
//
// - Lots of stuff
// - Layer locking (hiding works)
+// - Fixed angle tool doesn't work on lines
+// - Make it so "dirty" flag reflects drawing state
//
// Uncomment this for debugging...
ctrlDown(false), altDown(false),
gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
scale(1.0), offsetX(-10), offsetY(-10), document(true),
- gridPixels(0), collided(false), hoveringIntersection(false),
- dragged(NULL), draggingObject(false), angleSnap(false)
+ gridPixels(0), collided(false), scrollDrag(false), hoverPointValid(false),
+ hoveringIntersection(false), dragged(NULL), draggingObject(false),
+ angleSnap(false), dirty(false)
{
//wtf? doesn't work except in c++11??? document = { 0 };
setBackgroundRole(QPalette::Base);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
Global::gridSpacing = 12.0; // In base units (inch is default)
-#if 0
- Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
+
+ Line * line = new Line(Vector(5, 5), Vector(50, 40), 2.0, 0xFF7F00, LSDash);
document.Add(line);
- document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
- document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
- document.Add(new Circle(Vector(100, 100), 36, &document));
- document.Add(new Circle(Vector(50, 150), 49, &document));
- document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
- document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
-#if 1
- Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
- line->SetDimensionOnLine(dimension);
- document.Add(dimension);
-#else
- // Alternate way to do the above...
- line->SetDimensionOnLine();
-#endif
-#else
- Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document);
- line->p[0] = Vector(5, 5);
- line->p[1] = Vector(50, 40);
- line->type = OTLine;
- line->thickness = 2.0;
- line->style = LSDash;
- line->color = 0xFF7F00;
- line->layer = 0;
- document.objects.push_back(line);
- document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83)));
- document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2)));
- document.objects.push_back(new Circle(Vector(100, 100), 36));
- document.objects.push_back(new Circle(Vector(50, 150), 49));
- document.objects.push_back(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
- document.objects.push_back(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
- document.objects.push_back(new Dimension(Vector(50, 40), Vector(5, 5)));
- document.objects.push_back(new Text(Vector(10, 83), "Here is some awesome text!"));
-#endif
+ document.Add(new Line(Vector(50, 40), Vector(10, 83)));
+ document.Add(new Line(Vector(10, 83), Vector(17, 2)));
+ document.Add(new Circle(Vector(100, 100), 36));
+ document.Add(new Circle(Vector(50, 150), 49));
+ document.Add(new Arc(Vector(300, 300), 32, TAU / 8.0, TAU * 0.65)),
+ document.Add(new Arc(Vector(200, 200), 60, TAU / 4.0, TAU * 0.75));
+ document.Add(new Text(Vector(10, 83), "Here is some awesome text!"));
+
+ AddDimensionTo(line);
/*
Here we set the grid size in pixels--12 in this case. Initially, we have our
}
+void DrawingView::focusOutEvent(QFocusEvent * /*event*/)
+{
+// printf("DrawingView::focusOutEvent()...\n");
+ // Make sure all modkeys being held are marked as released when the app
+ // loses focus (N.B.: This only works because the app sets the focus policy
+ // of this object to something other than Qt::NoFocus)
+ shiftDown = ctrlDown = altDown = false;
+ scrollDrag = false;
+ setCursor(Qt::ArrowCursor);
+}
+
+
void DrawingView::paintEvent(QPaintEvent * /*event*/)
{
QPainter qtPainter(this);
if (hoveringIntersection)
painter.DrawHandle(intersectionPoint);
+ if (hoverPointValid)
+ painter.DrawHandle(hoverPoint);
+
if (!informativeText.isEmpty())
painter.DrawInformativeText(informativeText);
}
//
// Renders objects in the passed in vector
//
+/*
+N.B.: Since we have "hoverPointValid" drawing regular object handles above,
+ we can probably do away with a lot of them that are being done down below.
+ !!! FIX !!!
+ [Well, it seems to work OK *except* when you move one of the points, then you get to see nothing. Is it worth fixing there to get rid of problems here? Have to investigate...]
+*/
void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer, bool ignoreLayer/*= false*/)
{
std::vector<void *>::iterator i;
if (obj->hitPoint[1])
painter->DrawHandle(obj->p[1]);
+ if (obj->hitObject)
+ painter->DrawSmallHandle(Geometry::Midpoint((Line *)obj));
+
break;
+
case OTCircle:
painter->SetBrush(QBrush(Qt::NoBrush));
painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]);
painter->DrawHandle(obj->p[0]);
break;
+
case OTArc:
painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]);
painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0]));
break;
+
case OTDimension:
{
Dimension * d = (Dimension *)obj;
break;
}
+
case OTText:
{
Text * t = (Text *)obj;
painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness, t->angle[0]);
break;
}
+
case OTSpline:
{
break;
}
+
case OTPolygon:
{
break;
}
+
case OTContainer:
{
// Containers require recursive rendering...
Container * c = (Container *)obj;
- RenderObjects(painter, (*c).objects, layer);
+printf("About to render container: # objs=%i, layer=%i\n", (*c).objects.size(), layer);
+ RenderObjects(painter, (*c).objects, layer, ignoreLayer);
//printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
// Containers also have special indicators showing they are selected
if (c->selected || c->hitObject)
{
- Rect r = GetObjectExtents(obj);
- painter->DrawRectCorners(r);
+// Rect r = GetObjectExtents(obj);
+// painter->DrawRectCorners(r);
+ painter->DrawRectCorners(Rect(c->p[0], c->p[1]));
}
break;
}
+
default:
break;
}
}
+#if 0
void DrawingView::GetHovered(std::vector<void *> & v)
{
v.clear();
// }
}
}
+#endif
+
+
+std::vector<void *> DrawingView::GetHovered(void)
+{
+ std::vector<void *> v;
+ std::vector<void *>::iterator i;
+
+ for(i=document.objects.begin(); i!=document.objects.end(); i++)
+ {
+ if (((Object *)(*i))->hovered)
+// {
+//printf("GetHovered: adding object (%X) to hover... hp1=%s, hp2=%s, hl=%s\n", (*i), (((Line *)(*i))->hitPoint[0] ? "true" : "false"), (((Line *)(*i))->hitPoint[1] ? "true" : "false"), (((Line *)(*i))->hitObject ? "true" : "false"));
+ v.push_back(*i);
+// }
+ }
+
+ return v;
+}
void DrawingView::resizeEvent(QResizeEvent * /*event*/)
MirrorHandler(mode, p);
else if (Global::tool == TTDimension)
DimensionHandler(mode, p);
+ else if (Global::tool == TTTriangulate)
+ TriangulateHandler(mode, p);
+ else if (Global::tool == TTTrim)
+ TrimHandler(mode, p);
+ else if (Global::tool == TTParallel)
+ ParallelHandler(mode, p);
}
toolPoint[1] = p;
break;
+
case ToolMouseMove:
if (Global::toolState == TSNone)
toolPoint[0] = p;
toolPoint[1] = p;
break;
+
case ToolMouseUp:
if (Global::toolState == TSNone)
{
toolPoint[1] = p;
break;
+
case ToolMouseMove:
if (Global::toolState == TSNone)
toolPoint[0] = p;
toolPoint[1] = p;
break;
+
case ToolMouseUp:
if (Global::toolState == TSNone)
{
toolPoint[3] = p;
break;
+
case ToolMouseMove:
if (Global::toolState == TSNone)
toolPoint[0] = p;
}
break;
+
case ToolMouseUp:
if (Global::toolState == TSNone)
{
if (Global::toolState == TSNone)
{
toolPoint[0] = p;
- SavePointsFrom(select, toolScratch);
+// SavePointsFrom(select, toolScratch);
+ CopyObjects(select, toolScratch2);
Global::toolState = TSPoint1;
}
else if (Global::toolState == TSPoint1)
toolPoint[1] = p;
break;
+
case ToolMouseMove:
if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
toolPoint[0] = p;
angleSnap = true;
double angle = Vector(toolPoint[0], toolPoint[1]).Angle();
std::vector<void *>::iterator j = select.begin();
- std::vector<Object>::iterator i = toolScratch.begin();
+// std::vector<Object>::iterator i = toolScratch.begin();
+ std::vector<void *>::iterator i = toolScratch2.begin();
- for(; i!=toolScratch.end(); i++, j++)
+// for(; i!=toolScratch.end(); i++, j++)
+ for(; i!=toolScratch2.end(); i++, j++)
{
- Object obj = *i;
- Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle);
- Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle);
- Object * obj2 = (Object *)(*j);
- obj2->p[0] = p1;
- obj2->p[1] = p2;
+// Object objT = *i;
+// Point p1 = Geometry::RotatePointAroundPoint(objT.p[0], toolPoint[0], angle);
+// Point p2 = Geometry::RotatePointAroundPoint(objT.p[1], toolPoint[0], angle);
+ Object * objT = (Object *)(*i);
+ Object * objS = (Object *)(*j);
- if (obj.type == OTArc)
+ Point p1 = Geometry::RotatePointAroundPoint(objT->p[0], toolPoint[0], angle);
+ Point p2 = Geometry::RotatePointAroundPoint(objT->p[1], toolPoint[0], angle);
+
+ objS->p[0] = p1;
+ objS->p[1] = p2;
+
+// if (objT.type == OTArc || objT.type == OTText)
+ if (objT->type == OTArc || objT->type == OTText)
{
- obj2->angle[0] = obj.angle[0] + angle;
+// objS->angle[0] = objT.angle[0] + angle;
+ objS->angle[0] = objT->angle[0] + angle;
- if (obj2->angle[0] > TAU)
- obj2->angle[0] -= TAU;
+ if (objS->angle[0] > TAU)
+ objS->angle[0] -= TAU;
+ }
+// else if (objT.type == OTContainer)
+ else if (objT->type == OTContainer)
+ {
+ // OK, this doesn't work because toolScratch only has points and nothing in the containers... [ACTUALLY... toolScratch is is a vector of type Object... which DOESN'T have an objects vector in it...]
+// Container * c = (Container *)&objT;
+ Container * c = (Container *)objT;
+ Container * c2 = (Container *)objS;
+ std::vector<void *>::iterator l = c->objects.begin();
+ // TODO: Rotate items in the container
+ // TODO: Make this recursive
+ for(std::vector<void *>::iterator k=c2->objects.begin(); k!=c2->objects.end(); k++, l++)
+ {
+ Object * obj3 = (Object *)(*k);
+ Object * obj4 = (Object *)(*l);
+
+ p1 = Geometry::RotatePointAroundPoint(obj4->p[0], toolPoint[0], angle);
+ p2 = Geometry::RotatePointAroundPoint(obj4->p[1], toolPoint[0], angle);
+
+ obj3->p[0] = p1;
+ obj3->p[1] = p2;
+// obj3->angle[0] = objT.angle[0] + angle;
+ obj3->angle[0] = obj4->angle[0] + angle;
+
+ if (obj3->angle[0] > TAU)
+ obj3->angle[0] -= TAU;
+ }
+
+ Rect r = GetObjectExtents(objS);
+ c2->p[0] = r.TopLeft();
+ c2->p[1] = r.BottomRight();
}
}
}
break;
+
case ToolMouseUp:
if (Global::toolState == TSPoint1)
{
CopyObjects(select, temp);
ClearSelected(temp);
AddObjectsTo(document.objects, temp);
- RestorePointsTo(select, toolScratch);
+// RestorePointsTo(select, toolScratch);
+ RestorePointsTo(select, toolScratch2);
return;
}
toolPoint[0] = p;
Global::toolState = TSPoint1;
- SavePointsFrom(select, toolScratch);
+// SavePointsFrom(select, toolScratch);
+ DeleteContents(toolScratch2);
+ CopyObjects(select, toolScratch2);
}
break;
+
case ToolKeyDown:
// Reset the selection if shift held down...
if (shiftDown)
- RestorePointsTo(select, toolScratch);
+// RestorePointsTo(select, toolScratch);
+ RestorePointsTo(select, toolScratch2);
break;
+
case ToolKeyUp:
// Reset selection when key is let up
if (!shiftDown)
RotateHandler(ToolMouseMove, toolPoint[1]);
break;
+
case ToolCleanup:
- RestorePointsTo(select, toolScratch);
+// RestorePointsTo(select, toolScratch);
+ RestorePointsTo(select, toolScratch2);
+ DeleteContents(toolScratch2);
}
}
toolPoint[1] = p;
break;
+
case ToolMouseMove:
if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone))
toolPoint[0] = p;
obj2->p[0] = p1;
obj2->p[1] = p2;
+/*
+N.B.: When mirroring an arc thru a horizontal axis, this causes the arc to have
+ a negative start angle which makes it impossible to interact with.
+ !!! FIX !!!
+*/
if (obj.type == OTArc)
{
// This is 2*mirror angle - obj angle - obj span
}
break;
+
case ToolMouseUp:
if (Global::toolState == TSPoint1)
{
}
break;
+
case ToolKeyDown:
// Reset the selection if shift held down...
if (shiftDown)
RestorePointsTo(select, toolScratch);
break;
+
case ToolKeyUp:
// Reset selection when key is let up
if (!shiftDown)
MirrorHandler(ToolMouseMove, toolPoint[1]);
break;
+
case ToolCleanup:
RestorePointsTo(select, toolScratch);
}
toolPoint[1] = p;
break;
+
case ToolMouseMove:
if (Global::toolState == TSNone)
toolPoint[0] = p;
toolPoint[1] = p;
break;
+
case ToolMouseUp:
if (Global::toolState == TSNone)
{
}
+void DrawingView::TriangulateHandler(int mode, Point/*p*/)
+{
+ switch (mode)
+ {
+ case ToolMouseDown:
+ {
+ // Skip if nothing hovered...
+ if (numHovered != 1)
+ break;
+
+ std::vector<void *> hover = GetHovered();
+ Object * obj = (Object *)hover[0];
+
+ // Skip if it's not a line...
+ if (obj->type != OTLine)
+ break;
+
+ if (Global::toolState == TSNone)
+ toolObj[0] = obj;
+ else if (Global::toolState == TSPoint2)
+ toolObj[1] = obj;
+ else
+ toolObj[2] = obj;
+
+ break;
+ }
+#if 0
+ case ToolMouseMove:
+ if (Global::toolState == TSNone)
+ toolPoint[0] = p;
+ else if (Global::toolState == TSPoint2)
+ toolPoint[1] = p;
+ else if (Global::toolState == TSPoint3)
+ {
+ toolPoint[2] = p;
+ angleSnap = true;
+ }
+ else
+ {
+ toolPoint[3] = p;
+ angleSnap = true;
+ }
+
+ break;
+#endif
+ case ToolMouseUp:
+ if (Global::toolState == TSNone)
+ {
+ Global::toolState = TSPoint2;
+ }
+ else if (Global::toolState == TSPoint2)
+ {
+/* if (shiftDown)
+ {
+ // Key override is telling us to start arc at new center, not
+ // continue the current one.
+ toolPoint[0] = toolPoint[1];
+ return;
+ }*/
+
+ Global::toolState = TSPoint3;
+ }
+ else
+ {
+ double len2 = Vector::Magnitude(toolObj[1]->p[0], toolObj[1]->p[1]);
+ double len3 = Vector::Magnitude(toolObj[2]->p[0], toolObj[2]->p[1]);
+
+ Circle c1(toolObj[0]->p[0], len2);
+ Circle c2(toolObj[0]->p[1], len3);
+
+ Geometry::CheckCircleToCircleIntersection((Object *)&c1, (Object *)&c2);
+
+ // Only move lines if the triangle formed by them is not degenerate
+ if (Global::numIntersectPoints > 0)
+ {
+ toolObj[1]->p[0] = toolObj[0]->p[0];
+ toolObj[1]->p[1] = Global::intersectPoint[0];
+
+ toolObj[2]->p[0] = Global::intersectPoint[0];
+ toolObj[2]->p[1] = toolObj[0]->p[1];
+ }
+
+ Global::toolState = TSNone;
+ }
+ }
+}
+
+
+void DrawingView::TrimHandler(int mode, Point p)
+{
+/*
+n.b.: this code is lifted straight out of the old oo code. needs to be updated.
+*/
+ switch (mode)
+ {
+ case ToolMouseDown:
+ {
+#if 0
+ Object * toTrim = doc->lastObjectHovered;
+
+ if (toTrim == NULL)
+ return;
+
+ 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);
+ }
+ else if (u == 1.0)
+ {
+ ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
+ }
+ 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);
+ }
+
+ doc->lastObjectHovered = NULL;
+#endif
+ }
+ break;
+
+ case ToolMouseMove:
+ {
+#if 0
+ Object * toTrim = doc->lastObjectHovered;
+ 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
+ // Bail out if nothing hovered...
+ if (numHovered != 1)
+ {
+ toolObj[0] = NULL;
+ return;
+ }
+
+ std::vector<void *> hover = GetHovered();
+ Object * obj = (Object *)hover[0];
+
+ // Skip if it's not a line...
+ if (obj->type != OTLine)
+ {
+ toolObj[0] = NULL;
+ return;
+ }
+
+ toolObj[0] = obj;
+ double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p);
+
+ // Currently only deal with line against line trimming, can expand to
+ // others as well (line/circle, circle/circle, line/arc, etc)
+ std::vector<void *>::iterator i;
+ for(i=document.objects.begin(); i!=document.objects.end(); i++)
+ {
+ obj = (Object *)(*i);
+
+ if (obj == toolObj[0])
+ continue;
+ else if (obj->type != OTLine)
+ continue;
+
+ Geometry::CheckLineToLineIntersection(toolObj[0], obj);
+
+ if (Global::numIntersectParams > 0)
+ {
+ // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown.
+ }
+ }
+ }
+ break;
+
+ case ToolMouseUp:
+ break;
+
+ case ToolKeyDown:
+ break;
+
+ case ToolKeyUp:
+ break;
+
+ case ToolCleanup:
+ break;
+ }
+}
+
+
+void DrawingView::ParallelHandler(int mode, Point /*p*/)
+{
+ switch (mode)
+ {
+ case ToolMouseDown:
+ break;
+
+ case ToolMouseMove:
+ break;
+
+ case ToolMouseUp:
+ break;
+
+ case ToolKeyDown:
+ break;
+
+ case ToolKeyUp:
+ break;
+
+ case ToolCleanup:
+ break;
+ }
+}
+
+
void DrawingView::mousePressEvent(QMouseEvent * event)
{
if (event->button() == Qt::LeftButton)
{
+printf("mousePressEvent::Qt::LeftButton numHovered=%li\n", numHovered);
Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
// Handle tool processing, if any
{
if (hoveringIntersection)
point = intersectionPoint;
+ else if (hoverPointValid)
+ point = hoverPoint;
else if (Global::snapToGrid)
point = SnapPointToGrid(point);
{
AddHoveredToSelection();
update(); // needed??
- GetHovered(hover); // prolly needed
- dragged = (Object *)hover[0];
+// GetHovered(hover); // prolly needed
+ std::vector<void *> hover2 = GetHovered();
+ dragged = (Object *)hover2[0];
draggingObject = true;
+printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]);
// Alert the pen widget
- emit(ObjectSelected(dragged));
+ emit ObjectSelected(dragged);
// See if anything is using just a straight click on a handle
if (HandleObjectClicked())
// We do it *after*... why? (doesn't seem to confer any advantage...)
if (hoveringIntersection)
oldPoint = intersectionPoint;
+ else if (hoverPointValid)
+ oldPoint = hoverPoint;
else if (Global::snapToGrid)
oldPoint = SnapPointToGrid(point);
+ // Needed for fixed length handling
+ if (Global::fixedLength)
+ {
+ if (dragged->type == OTLine)
+ {
+ dragged->length = Vector::Magnitude(dragged->p[0], dragged->p[1]);
+ }
+ }
+
+ if (dragged->type == OTCircle)
+ {
+ // Save for informative text, uh, er, informing
+ dragged->length = dragged->radius[0];
+ }
+
return;
}
// Do object hit testing...
bool needUpdate = HitTestObjects(point);
- GetHovered(hover);
+// GetHovered(hover);
+ std::vector<void *> hover2 = GetHovered();
+{
+if (needUpdate)
+{
+ printf("mouseMoveEvent:: numHovered=%li, hover2.size()=%li\n", numHovered, hover2.size());
+
+ if (hover2.size() > 0)
+ printf(" (hover2[0]=$%llX, type=%s)\n", hover2[0], objName[((Object *)hover2[0])->type]);
+}
+}
// Check for multi-hover...
if (numHovered > 1)
{
//need to check for case where hover is over 2 circles and a 3rd's center...
- Object * obj1 = (Object *)hover[0], * obj2 = (Object *)hover[1];
+ Object * obj1 = (Object *)hover2[0], * obj2 = (Object *)hover2[1];
Geometry::Intersects(obj1, obj2);
int numIntersecting = Global::numIntersectParams;
intersectionPoint = v1;
}
}
+ else if (numHovered == 1)
+ {
+ Object * obj = (Object *)hover2[0];
+
+ if (obj->type == OTLine)
+ {
+/*
+Not sure that this is the best way to handle this, but it works(TM)...
+Except when lines are overlapping, then it doesn't work... !!! FIX !!!
+*/
+ Point midpoint = Geometry::Midpoint((Line *)obj);
+ Vector v1 = Vector::Magnitude(midpoint, point);
+
+ if ((v1.Magnitude() * Global::zoom) < 8.0)
+ {
+ QString text = tr("Midpoint <%1, %2>");
+ informativeText = text.arg(midpoint.x).arg(midpoint.y);
+ hoverPointValid = true;
+ hoverPoint = midpoint;
+ needUpdate = true;
+ }
+ }
+ }
// Handle object movement (left button down & over an object)
if ((event->buttons() & Qt::LeftButton) && draggingObject && !Global::tool)
// Should we be doing this automagically? Hmm...
// Clear our vectors
select.clear();
- hover.clear();
+// hover.clear();
// Scoop 'em up
std::vector<void *>::iterator i;
{
scrollDrag = true;
setCursor(Qt::SizeAllCursor);
-// oldPoint = Vector();
oldPoint = oldScrollPoint;
}
bounds = bounds.normalized();
#endif
- // If the end of the arc is before the beginning, add 360 degrees to it
+ // If the end of the arc is before the beginning, add 360 degrees
+ // to it
if (end < start)
end += TAU;
break;
}
- default:
+ case OTContainer:
+ {
+ 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))
+ c->selected = true;
+
break;
}
+
+// default:
+// break;
+ }
}
}
if (draggingObject && (obj == dragged))
continue;
- if (HitTest(obj, point))
+ if (HitTest(obj, point) == true)
needUpdate = true;
if (obj->hovered)
{
numHovered++;
//printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
- emit(ObjectHovered(obj));
+ emit ObjectHovered(obj);
}
}
else if ((hCS2Point.Magnitude() * Global::zoom) < 8.0)
obj->hitPoint[4] = true;
-// return (hitPoint1 || hitPoint2 || hitLine || hitFlipSwitch || hitChangeSwitch1 || hitChangeSwitch2 ? true : false);
obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitPoint[3] || obj->hitPoint[4] || obj->hitObject ? true : false);
if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHP3 != obj->hitPoint[3]) || (oldHP4 != obj->hitPoint[4]) || (oldHO != obj->hitObject))
case OTContainer:
{
- // Containers must be recursively tested...
+ // Containers must be recursively tested... Or do they???
+/*
+So the idea here is to flatten the structure, *then* test the objects within. Flattening includes the Containers as well; we can do this because it's pointers all the way down.
+*/
+// bool oldHitObj = c->hitObject, oldHovered = c->hovered;
+// Object * oldClicked = c->clicked;
+/*
+still need to compare old state to new state, and set things up based upon that...
+likely we can just rely on the object itself and steal its state like we have in the commented out portion below; can prolly rewrite the HitTest() portion to be one line: needUpdate = HitTest(cObj, point);
+Well, you could if there was only one object in the Container. But since there isn't, we have to keep the if HitTest() == true then needUpdate = true bit. Because otherwise, a false result anywhere will kill the needed update elsewhere.
+*/
+
Container * c = (Container *)obj;
c->hitObject = false;
c->hovered = false;
- std::vector<void *>::iterator i;
+ c->clicked = NULL;
- for(i=c->objects.begin(); i!=c->objects.end(); i++)
+ std::vector<void *> flat = Flatten(c);
+
+//printf("HitTest::OTContainer (size=%li)\n", flat.size());
+ for(std::vector<void *>::iterator i=flat.begin(); i!=flat.end(); i++)
{
- Object * cObj = (Object *)*i;
+ Object * cObj = (Object *)(*i);
+
+ // Skip the flattened containers (if any)...
+ if (cObj->type == OTContainer)
+ continue;
- if (HitTest(cObj, point))
+ // We do it this way instead of needUpdate = HitTest() because we
+ // are checking more than one object, and that way of doing will
+ // not return consistent results.
+ if (HitTest(cObj, point) == true)
+// {
+//printf("HitTest::OTContainer, subobj ($%llX) hit!\n", cObj);
needUpdate = true;
+// c->hitObject = true;
+// c->clicked = cObj;
+// c->hovered = true;
+// }
+ // Same reasons for doing it this way here apply.
if (cObj->hitObject == true)
+ {
+//printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
c->hitObject = true;
+ c->clicked = cObj;
+ }//*/
+
+ if (cObj->hitPoint[0] == true)
+ {
+//printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
+ c->hitPoint[0] = true;
+ c->clicked = cObj;
+ }//*/
+
+ if (cObj->hitPoint[1] == true)
+ {
+//printf("HitTest::cObj->hitObject == true! ($%llX)\n", cObj);
+ c->hitPoint[1] = true;
+ c->clicked = cObj;
+ }//*/
if (cObj->hovered == true)
- c->hovered = true;
+ c->hovered = true;//*/
}
break;
{
case OTLine:
if (obj->hitPoint[0])
+ {
+ if (Global::fixedLength)
+ {
+ Vector line = point - obj->p[1];
+ Vector unit = line.Unit();
+ point = obj->p[1] + (unit * obj->length);
+ }
+
obj->p[0] = point;
+ }
else if (obj->hitPoint[1])
+ {
+ if (Global::fixedLength)
+ {
+ Vector line = point - obj->p[0];
+ Vector unit = line.Unit();
+ point = obj->p[0] + (unit * obj->length);
+ }
+
obj->p[1] = point;
+ }
else if (obj->hitObject)
{
obj->p[0] += delta;
obj->p[0] = point;
else if (obj->hitObject)
{
-//this doesn't work. we need to save this on mouse down for this to work correctly!
-// double oldRadius = obj->radius[0];
+ double oldRadius = obj->length;
obj->radius[0] = Vector::Magnitude(obj->p[0], point);
- QString text = QObject::tr("Radius: %1");//\nScale: %2%");
- informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
+ QString text = QObject::tr("Radius: %1\nScale: %2%");
+ informativeText = text.arg(obj->radius[0], 0, 'd', 4).arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0);
}
break;
case OTContainer:
// This is shitty, but works for now until I can code up something
// nicer :-)
- TranslateObject(obj, delta);
+/*
+The idea is to make it so whichever point on the object in question is being dragged becomes the snap point for the container; shouldn't be too difficult to figure out how to do this.
+*/
+// TranslateObject(obj, delta);
+ TranslateContainer((Container *)obj, point, delta);
break;
default:
}
}
+
+void DrawingView::AddDimensionTo(void * o)
+{
+ Object * obj = (Object *)o;
+
+ switch (obj->type)
+ {
+ case OTLine:
+ document.Add(new Dimension(obj->p[0], obj->p[1]));
+ break;
+ case OTCircle:
+ break;
+ case OTEllipse:
+ break;
+ case OTArc:
+ break;
+ case OTSpline:
+ break;
+ }
+}
+
void RenderObjects(Painter *, std::vector<void *> &, int, bool ignoreLayer = false);
void AddHoveredToSelection(void);
void GetSelection(std::vector<void *> &);
- void GetHovered(std::vector<void *> &);
+// void GetHovered(std::vector<void *> &);
+ std::vector<void *> GetHovered(void);
void ToolHandler(int, Point);
void ToolDraw(Painter *);
void LineHandler(int, Point);
void RotateHandler(int, Point);
void MirrorHandler(int, Point);
void DimensionHandler(int, Point);
+ void TriangulateHandler(int, Point);
+ void TrimHandler(int, Point);
+ void ParallelHandler(int, Point);
Rect GetObjectExtents(Object *);
void CheckObjectBounds(void);
bool HitTestObjects(Point);
bool HitTest(Object *, Point);
bool HandleObjectClicked(void);
void HandleObjectMovement(Point);
+ void AddDimensionTo(void * obj);
public slots:
void DeleteCurrentLayer(int);
void ObjectSelected(Object *);
protected:
+ void focusOutEvent(QFocusEvent * event);
void paintEvent(QPaintEvent * event);
void resizeEvent(QResizeEvent * event);
void mousePressEvent(QMouseEvent * event);
public:
bool useAntialiasing;
-// uint32_t numSelected;
uint32_t numHovered;
bool shiftDown;
bool ctrlDown;
public:
std::vector<void *> select;
- std::vector<void *> hover;
+// std::vector<void *> hover;
std::vector<void *> toolObjects;
std::vector<Object> toolScratch;
+ std::vector<void *> toolScratch2;
Point toolPoint[32];
+ Object * toolObj[32];
Point intersectionPoint;
Point hoverPoint;
bool hoverPointValid;
Object * dragged;
bool draggingObject;
bool angleSnap;
+ bool dirty;
};
#endif // __DRAWINGVIEW_H__
#include <stdlib.h>
#include <string.h>
#include <vector>
+#include "applicationwindow.h"
+#include "drawingview.h"
+#include "global.h"
#include "structs.h"
/*
virtual Object function that reports the object by itself if it's a non-
Container, otherwise it enumerates all objects within itself. */
- fprintf(file, "ARCHITEKTONAS DRAWING V1.1\n");
+ fprintf(file, "ARCHITEKTONAS DRAWING V1.2\n");
+ fprintf(file, "LAYERS %i\n", Global::numLayers);
+
+ for(int i=0; i<Global::numLayers; i++)
+ fprintf(file, "%i %i \"%s\"\n", (Global::layerHidden[i] ? 1 : 0), (Global::layerLocked[i] ? 1 : 0), Global::layerName[i].c_str());
+
+ fprintf(file, "ACTIVE %i\n", Global::activeLayer);
WriteObjectToFile(file, (Object *)c);
fprintf(file, "END\n");
return true;
{
float version;
- fscanf(file, "ARCHITEKTONAS DRAWING V%f", &version);
+ fscanf(file, "ARCHITEKTONAS DRAWING V%f\n", &version);
+
+ // Clear out layer vectors
+ Global::layerHidden.clear();
+ Global::layerLocked.clear();
+ Global::layerName.clear();
if (version == 1.0f)
return LoadVersion1_0(file, drawing);
else if (version == 1.1f)
return LoadVersion1_1(file, drawing);
+ else if (version == 1.2f)
+ return LoadVersion1_2(file, drawing);
//printf("LoadAtnsFile: Could not locate version! (version=%f)\n", version);
return false;
}
+/*static*/ void FileIO::ResetLayerVectors(void)
+{
+ // Set up layer vectors
+ Global::layerHidden.insert(Global::layerHidden.begin(), false);
+ Global::layerLocked.insert(Global::layerLocked.begin(), false);
+ Global::layerName.insert(Global::layerName.begin(), "Background");
+ Global::numLayers = 1;
+ Global::activeLayer = 0;
+}
+
+
/*static*/ bool FileIO::LoadVersion1_0(FILE * file, Container * drawing)
{
// Approach: read each object in the file, one by one. If the object is a
// This will require a stack to maintain the current Container.
std::vector<Container *> containerStack;
Container * currentTopContainer = drawing;
+ ResetLayerVectors();
while (!feof(file))
{
/*static*/ bool FileIO::LoadVersion1_1(FILE * file, Container * drawing)
{
+ // Approach: read each object in the file, one by one. If the object is a
+ // Container, add objects to it until an "endContainer" marker is found.
+ // This will require a stack to maintain the current Container.
+ std::vector<Container *> containerStack;
+ Container * currentTopContainer = drawing;
+ ResetLayerVectors();
+
+ while (!feof(file))
+ {
+ // Reconstruct the object (extended format!)
+ Object * obj = GetObjectFromFile(file, true);
+
+ // objectFileType is set in GetObjectFromFile()...
+ if (objectFileType == OTFObject)
+ {
+ if (obj == NULL)
+ return false;
+
+ currentTopContainer->objects.push_back(obj);
+//printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
+
+ // If the object is a container, push current TLC on the stack and
+ // set it as the new TLC
+ if (obj->type == OTContainer)
+ {
+ containerStack.push_back(currentTopContainer);
+ currentTopContainer = (Container *)obj;
+ }
+ }
+ else if (objectFileType == OTFContainerEnd)
+ {
+ // Add the extents of the current container
+ Rect r = ApplicationWindow::drawing->GetObjectExtents((Object *)currentTopContainer);
+ currentTopContainer->p[0] = r.TopLeft();
+ currentTopContainer->p[1] = r.BottomRight();
+//printf("Container extents: <%lf, %lf>, <%lf, %lf>\n", r.l, r.t, r.r, r.b);
+
+ // Container is done, so pop the stack to get back the previous TLC
+ currentTopContainer = containerStack.back();
+ containerStack.pop_back();
+ }
+ else if (objectFileType == OTFEndOfFile)
+ {
+//printf("Load: container size = %li\n", drawing->objects.size());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/*static*/ bool FileIO::LoadVersion1_2(FILE * file, Container * drawing)
+{
+ int hidden, locked;
+ char textBuffer[65536];
+
+ // Load layer information first
+ fscanf(file, "LAYERS %i\n", &Global::numLayers);
+
+ for(int i=0; i<Global::numLayers; i++)
+ {
+ fscanf(file, "%i %i \"%[^\"]\"\n", &hidden, &locked, textBuffer);
+ Global::layerHidden.push_back(hidden ? true : false);
+ Global::layerLocked.push_back(locked ? true : false);
+ Global::layerName.push_back(textBuffer);
+ }
+
+ fscanf(file, "ACTIVE %i\n", &Global::activeLayer);
+
// Approach: read each object in the file, one by one. If the object is a
// Container, add objects to it until an "endContainer" marker is found.
// This will require a stack to maintain the current Container.
}
else if (objectFileType == OTFContainerEnd)
{
+ // Add the extents of the current container
+ Rect r = ApplicationWindow::drawing->GetObjectExtents((Object *)currentTopContainer);
+ currentTopContainer->p[0] = r.TopLeft();
+ currentTopContainer->p[1] = r.BottomRight();
+//printf("Container extents: <%lf, %lf>, <%lf, %lf>\n", r.l, r.t, r.r, r.b);
+
// Container is done, so pop the stack to get back the previous TLC
currentTopContainer = containerStack.back();
containerStack.pop_back();
}
// Need to add pen attributes as well... do that for v1.1 :-P
+ // And fill attributes... do that for v1.3 (1.2 added layers)
if (strcmp(buffer, "LINE") == 0)
{
Point p1, p2;
public:
static bool SaveAtnsFile(FILE *, Container *);
static bool LoadAtnsFile(FILE *, Container *);
+ static void ResetLayerVectors(void);
private:
static bool LoadVersion1_0(FILE *, Container *);
static bool LoadVersion1_1(FILE *, Container *);
+ static bool LoadVersion1_2(FILE *, Container *);
// static bool GetObjectFromFile(FILE *, Object *, Object **, int *);
static Object * GetObjectFromFile(FILE *, bool extended = false);
static bool WriteObjectToFile(FILE *, Object *);
int Global::numLayers = 1;
std::vector<bool> Global::layerHidden;
std::vector<bool> Global::layerLocked;
-
+std::vector<std::string> Global::layerName;
// to do any instantiation shite.
#include <stdint.h>
+#include <string>
#include <vector>
#include <QRectF>
#include "vector.h"
static int numLayers;
static std::vector<bool> layerHidden;
static std::vector<bool> layerLocked;
+ static std::vector<std::string> layerName;
};
#endif // __GLOBALS_H__
// This is required, otherwise the layout engine puts too much space around
// this widget. :-/
mainLayout->setContentsMargins(0, 0, 0, 0);
-
+
invisible->setFlat(true);
invisible->setIcon(visibleIcon);
invisible->setCheckable(true);
void LayerItemWidget::HandleHideToggle(bool state)
{
// printf("Eye is: %s\n", !state ? "OPEN" : "closed");
- emit(HideToggled(parent, state));
+ emit HideToggled(parent, state);
}
void LayerItemWidget::HandleLockToggle(bool state)
{
// printf("Lock is: %s\n", !state ? "OPEN" : "closed");
- emit(LockToggled(parent, state));
+ emit LockToggled(parent, state);
}
Global::numLayers = 1;
Global::layerHidden.clear();
Global::layerLocked.clear();
+ Global::layerName.clear();
Global::layerHidden.push_back(false);
Global::layerLocked.push_back(false);
+ Global::layerName.push_back("Background");
}
}
+void LayerWidget::Reload(void)
+{
+ list->clear();
+
+ for(int i=0; i<Global::numLayers; i++)
+ {
+ QListWidgetItem * qlwi = new QListWidgetItem();
+ LayerItemWidget * liw = new LayerItemWidget(Global::layerName[i].c_str(), Global::layerHidden[i], Global::layerLocked[i], qlwi);
+ list->insertItem(0, qlwi);
+ list->setItemWidget(qlwi, liw);
+
+ // Set up SIGNAL/SLOTs for this LayerItemWidget
+ connect(liw, SIGNAL(HideToggled(QListWidgetItem *, bool)), this, SLOT(HandleHideToggle(QListWidgetItem *, bool)));
+ connect(liw, SIGNAL(LockToggled(QListWidgetItem *, bool)), this, SLOT(HandleLockToggle(QListWidgetItem *, bool)));
+ }
+
+ int layer = (Global::numLayers - Global::activeLayer) - 1;
+ list->setCurrentRow(layer, QItemSelectionModel::SelectCurrent);
+ SetButtonStates();
+}
+
+
void LayerWidget::HandleLayerSelected(int currentRow)
{
-//printf("LayerWidget::HandleLayerSelected(): currentRow = %i\n", currentRow);
-// emit(LayerSelected(currentRow));
+ // If LayerWidget is empty, bail out
+ if (currentRow == -1)
+ return;
// This is numbered opposite of how it's presented. In other words, the
// bottom of the list is 0, and items above it count upwards. So like this:
//
// which is the opposite of the internal numbering.
Global::activeLayer = (Global::numLayers - currentRow) - 1;
-//printf("LayerWidget::HandleLayerSelected(): currentRow = %i, numLayers = %i, active = %i\n", currentRow, Global::numLayers, Global::activeLayer);
+
// Set button states to sane values
SetButtonStates();
}
//printf("Item #%i, new hide state is %s\n", currentRow, (state ? "ON" : "off"));
//printf("LayerWidget: New hide state of layer %i is %s.\n", layer, (state ? "ON" : "off"));
// We do this last, because otherwise the Document would get the wrong state
- emit(LayerToggled());
+ emit LayerToggled();
}
void LayerWidget::AddLayer(void)
{
- // We always stick the newest layer at the top of the list...
+ // We always stick the newest layer at the top of the list (visually, the
+ // top of the list is the end, the bottom is the beginning)...
int count = list->count();
QString text = QString("Layer #%1").arg(count);
QListWidgetItem * qlwi = new QListWidgetItem();
SetButtonStates();
// Fix up the global state
- Global::layerHidden.insert(Global::layerHidden.begin(), false);
- Global::layerLocked.insert(Global::layerLocked.begin(), false);
+ Global::layerHidden.push_back(false);
+ Global::layerLocked.push_back(false);
+ Global::layerName.push_back(text.toUtf8().data());
Global::numLayers++;
}
// HandleLayerSelected() to be fired off which causes the numbers to
// be off. You have been warned!
// Tell the DrawingView to delete this layer in its Container:
- emit(LayerDeleted(Global::activeLayer));
+ emit LayerDeleted(Global::activeLayer);
int currentRow = list->currentRow();
QListWidgetItem * qlwi = list->currentItem();
int layer = (Global::numLayers - currentRow) - 1;
Global::layerHidden.erase(Global::layerHidden.begin() + layer);
Global::layerLocked.erase(Global::layerLocked.begin() + layer);
+ Global::layerName.erase(Global::layerName.begin() + layer);
Global::numLayers--;
// If we're deleting from the top of the list, we have to decrement the
QString result = QInputDialog::getText(this, tr("Edit Layer Name"), tr("Layer Name:"), QLineEdit::Normal, s, &ok);
if (ok && !result.isEmpty())
+ {
li->name->setText(result);
+
+ int layer = (Global::numLayers - Global::activeLayer) - 1;
+ std::vector<std::string>::iterator i = Global::layerName.begin() + layer;
+ (*i) = result.toUtf8().data();
+ }
}
old = Global::layerLocked[layer];
Global::layerLocked[layer] = Global::layerLocked[layer + 1];
Global::layerLocked[layer + 1] = old;
+ std::string oldStr = Global::layerName[layer];
+ Global::layerName[layer] = Global::layerName[layer + 1];
+ Global::layerName[layer + 1] = oldStr;
// We also have to tell the document to shuffle its layers too
- emit(LayersSwapped(layer, layer + 1));
+ emit LayersSwapped(layer, layer + 1);
}
old = Global::layerLocked[layer];
Global::layerLocked[layer] = Global::layerLocked[layer - 1];
Global::layerLocked[layer - 1] = old;
+ std::string oldStr = Global::layerName[layer];
+ Global::layerName[layer] = Global::layerName[layer - 1];
+ Global::layerName[layer - 1] = oldStr;
// We also have to tell the document to shuffle its layers too
- emit(LayersSwapped(layer, layer - 1));
+ emit LayersSwapped(layer, layer - 1);
}
LayerWidget(void);
~LayerWidget();
+ public slots:
+ void Reload(void);
+
private slots:
void HandleLayerSelected(int);
void HandleHideToggle(QListWidgetItem *, bool);
enum DimensionType { DTLinear = 0, DTLinearVert, DTLinearHorz, DTRadial, DTDiametric, DTCircumferential, DTAngular, DTLeader, DTCount };
-enum ToolType { TTNone, TTLine, TTCircle, TTEllipse, TTArc, TTDimension, TTText, TTPolygon, TTSpline, TTRotate, TTMirror, TTTrim, TTTriangulate, TTDelete };
+enum ToolType { TTNone, TTLine, TTCircle, TTEllipse, TTArc, TTDimension, TTText, TTPolygon, TTSpline, TTRotate, TTMirror, TTTrim, TTTriangulate, TTDelete, TTParallel };
enum ToolState { TSNone, TSPoint1, TSPoint2, TSPoint3, TSPoint4, TSDone };
+const char objName[OTCount][16] = {
+ "None", "Line", "Circle", "Ellipse", "Arc", "Polygon", "Dimension",
+ "Spline", "Text", "Container"
+};
+const char dimName[DTCount][32] = {
+ "Linear", "Vertical", "Horizontal", "Radial", "Diametric",
+ "Circumferential", "Angular", "Leader"
+};
+
#define OBJECT_COMMON \
int type; \
uint32_t id; \
bool hitObject; \
Point p[5]; \
double angle[2]; \
- double radius[2];
+ double radius[2]; \
+ double length;
struct Object {
OBJECT_COMMON;
struct Line {
OBJECT_COMMON;
- Line(): type(OTLine), id(Global::objectID++) {}
+ Line(): type(OTLine), id(Global::objectID++), selected(false), hovered(false), hitObject(false) { hitPoint[0] = hitPoint[1] = false; }
Line(Vector pt1, Vector pt2, float th = 1.0, uint32_t c = 0, int l = LSSolid):
type(OTLine), id(Global::objectID++), layer(0), color(c), thickness(th),
- style(l), selected(false), hovered(false), hitObject(false) { p[0] = pt1; p[1] = pt2; }
+ style(l), selected(false), hovered(false), hitObject(false) { p[0] = pt1; p[1] = pt2; hitPoint[0] = hitPoint[1] = false; }
};
struct Circle {
Circle(Vector pt1, double r, float th = 1.0, uint32_t c = 0, int l = LSSolid):
type(OTCircle), id(Global::objectID++), layer(0), color(c), thickness(th),
style(l), selected(false), hovered(false), hitObject(false)
- { p[0] = pt1; radius[0] = r; }
+ { p[0] = pt1; radius[0] = r; hitPoint[0] = hitPoint[1] = false; }
};
struct Ellipse {
Ellipse(Vector pt1, Vector pt2, double r1, double r2, float th = 1.0, uint32_t c = 0, int l = LSSolid):
type(OTEllipse), id(Global::objectID++), layer(0), color(c), thickness(th),
style(l), selected(false), hovered(false), hitObject(false)
- { p[0] = pt1; p[1] = pt2; radius[0] = r1; radius[1] = r2; }
+ { p[0] = pt1; p[1] = pt2; radius[0] = r1; radius[1] = r2; hitPoint[0] = hitPoint[1] = false; }
};
struct Arc {
Arc(Vector pt1, double r, double a1, double a2, float th = 1.0, uint32_t c = 0, int l = LSSolid):
type(OTArc), id(Global::objectID++), layer(0), color(c), thickness(th),
style(l), selected(false), hovered(false), hitObject(false)
- { p[0] = pt1; radius[0] = r; angle[0] = a1, angle[1] = a2; }
+ { p[0] = pt1; radius[0] = r; angle[0] = a1, angle[1] = a2; hitPoint[0] = hitPoint[1] = hitPoint[2] = false; }
};
struct Dimension {
Point lp[2]; // Line point, the actual dimension line
Object * obj[2]; // Pointer to attached objects (circle, lines for angle)
- Dimension(): type(OTDimension), id(Global::objectID++) {}
+ Dimension(): type(OTDimension), id(Global::objectID++), selected(false),
+ hovered(false), hitObject(false)
+ { hitPoint[0] = hitPoint[1] = hitPoint[2] = hitPoint[3] = hitPoint[4] = false; }
Dimension(Vector pt1, Vector pt2, DimensionType dt = DTLinear, float th = 1.0, uint32_t c = 0x0000FF, int l = LSSolid):
type(OTDimension), id(Global::objectID++), layer(0), color(c),
thickness(th), style(l), selected(false), hovered(false),
- hitObject(false), subtype(dt), offset(0) { p[0] = pt1; p[1] = pt2; }
+ hitObject(false), subtype(dt), offset(0)
+ { p[0] = pt1; p[1] = pt2; hitPoint[0] = hitPoint[1] = hitPoint[2] = hitPoint[3] = hitPoint[4] = false; }
};
struct Text {
std::vector<void *> objects;
double scale;
bool topLevel;
+ Object * clicked;
- Container(bool tl = false): type(OTContainer), id(Global::objectID++), selected(false), hovered(false), hitObject(false), topLevel(tl) {}
+ Container(bool tl = false): type(OTContainer), id(Global::objectID++), selected(false), hovered(false), hitObject(false), topLevel(tl), clicked(NULL) {}
+ void Add(void * obj) { objects.push_back(obj); }
// void DeleteContents(void) {}
/* void DeleteContents(Container * c)
{
// utils.cpp: Stuff that's useful to have kicking around, in one spot
//
// Part of the Architektonas Project
-// (C) 2015 Underground Software
+// (C) 2020 Underground Software
// See the README and GPLv3 files for licensing and warranty information
//
// JLH = James Hammons <jlhamm@acm.org>
#include "utils.h"
#include <string.h> // For memcpy()
+#include "geometry.h"
//
//
void CopyObjects(std::vector<void *> & from, std::vector<void *> & to)
{
- std::vector<void *>::iterator i;
-
- for(i=from.begin(); i!=from.end(); i++)
+ for(std::vector<void *>::iterator i=from.begin(); i!=from.end(); i++)
{
Object * obj = (Object *)(*i);
Object * newObject = CopyObject(obj);
//
Object * CopyObject(Object * obj)
{
- Object * newObject = NULL;
+ void * newObject = NULL;
switch (obj->type)
{
case OTLine:
- newObject = (Object *)new Line();
+ newObject = new Line();
memcpy(newObject, obj, sizeof(Line));
break;
case OTCircle:
- newObject = (Object *)new Circle();
+ newObject = new Circle();
memcpy(newObject, obj, sizeof(Circle));
break;
case OTEllipse:
- newObject = (Object *)new Ellipse();
+ newObject = new Ellipse();
memcpy(newObject, obj, sizeof(Ellipse));
break;
case OTArc:
- newObject = (Object *)new Arc();
+ newObject = new Arc();
memcpy(newObject, obj, sizeof(Arc));
break;
case OTDimension:
- newObject = (Object *)new Dimension();
+ newObject = new Dimension();
memcpy(newObject, obj, sizeof(Dimension));
break;
#if 0
case OTSpline:
- newObject = (Object *)new Spline();
+ newObject = new Spline();
memcpy(newObject, obj, sizeof(Spline));
break;
#endif
case OTText:
- newObject = (Object *)new Text();
+ newObject = new Text();
memcpy(newObject, obj, sizeof(Text));
((Text *)newObject)->s = ((Text *)obj)->s;
break;
case OTContainer:
- newObject = (Object *)new Container();
+ newObject = new Container();
//this won't work...
// memcpy(newObject, obj, sizeof(Line));
+ ((Container *)newObject)->p[0] = obj->p[0];
+ ((Container *)newObject)->p[1] = obj->p[1];
CopyObjects(((Container *)obj)->objects, ((Container *)newObject)->objects);
break;
default:
}
// Fix objectID
- if (newObject && (newObject->type != OTContainer))
- newObject->id = Global::objectID;
+ if (newObject && (((Object *)newObject)->type != OTContainer))
+ ((Object *)newObject)->id = Global::objectID++;
- return newObject;
+ return (Object *)newObject;
}
}
+//hmm, this won't work, as these are just pointers...
+//[should work now]
+void CopySelectedObjectsTo(std::vector<void *> & dest, std::vector<void *> & from)
+{
+ for(std::vector<void *>::iterator i=from.begin(); i!=from.end(); i++)
+ {
+ Object * obj = (Object *)(*i);
+
+ if (obj->selected)
+// {
+// Object * newObject = CopyObject(obj);
+ dest.push_back(CopyObject(obj));
+// }
+ }
+}
+
+
void AddObjectsTo(std::vector<void *> & dest, std::vector<void *> & from)
{
for(std::vector<void *>::iterator i=from.begin(); i!=from.end(); i++)
//
// Recursively go down thru the Container's vectors, deleting all the objects
-// contained therein. Once that is done, the main Container can be deleted. We
-// don't have to worry about the underlying std::vectors, as they have their
+// contained therein. Once that is done, the main Container can be deleted.
+// We don't have to worry about the underlying std::vectors, as they have their
// own destructors--plus they don't take ownership of objects, which is why we
-// have to keep track of that stuff ourselves. :-P Believe it or not, this is a
-// Good Thing(TM). ;-)
+// have to keep track of that stuff ourselves. :-P Believe it or not, this is
+// a Good Thing(TM). ;-)
//
void DeleteContents(std::vector<void *> & v)
{
delete obj;
}
+
+ v.clear();
}
+
void DeleteSelectedObjects(std::vector<void *> & v)
{
std::vector<void *>::iterator i = v.begin();
for(std::vector<void *>::iterator i=v.begin(); i!=v.end(); i++)
{
- memcpy(&o, (*i), sizeof(Object));
+ memcpy(&o, (Object *)(*i), sizeof(Object));
save.push_back(o);
}
}
}
+void RestorePointsTo(std::vector<void *> & v, std::vector<void *> & s)
+{
+ std::vector<void *>::iterator i = s.begin();
+ std::vector<void *>::iterator j = v.begin();
+
+ for(; i!=s.end(); i++, j++)
+ {
+ Object * objS = (Object *)(*i);
+ Object * objV = (Object *)(*j);
+
+ if (objV->type == OTContainer)
+ {
+ RestorePointsTo(((Container *)objV)->objects, ((Container *)objS)->objects);
+ return;
+ }
+
+ objV->p[0] = objS->p[0];
+ objV->p[1] = objS->p[1];
+ objV->angle[0] = objS->angle[0];
+ objV->angle[1] = objS->angle[1];
+//we don't do this because we want to keep selected & friends from changing
+// memcpy(obj2, *j, sizeof(Object));
+ }
+}
+
+
void TranslateObject(Object * obj, Point delta)
{
if (obj->type == OTContainer)
for(i=c->objects.begin(); i!=c->objects.end(); i++)
TranslateObject((Object *)*i, delta);
}
- else
- {
+// else
+// {
obj->p[0] += delta;
obj->p[1] += delta;
+// }
+}
+
+
+/*
+So we need to make it so that we pick the container's point clicked on, and translate all the other parts *not* clicked on.
+*/
+void TranslateContainer(Container * c, Point point, Point delta)
+{
+ if (c->clicked == NULL)
+ {
+// TranslateObject((Object *)c, delta);
+ return;
}
+
+static int i=0;
+printf("TranslateContainer: boop (%i)\n", i++);
+ Point clickedPoint;
+
+ switch (c->clicked->type)
+ {
+ case OTLine:
+ if (c->clicked->hitPoint[0])
+ clickedPoint = c->clicked->p[0];
+ else if (c->clicked->hitPoint[1])
+ clickedPoint = c->clicked->p[1];
+ else if (c->clicked->hitObject)
+ clickedPoint = Geometry::Midpoint((Line *)(c->clicked));
+
+ break;
+
+ case OTCircle:
+ if (c->clicked->hitPoint[0])
+ clickedPoint = c->clicked->p[0];
+ else if (c->clicked->hitObject)
+ clickedPoint = point;
+
+ break;
+
+ case OTArc:
+ break;
+
+ case OTDimension:
+ break;
+
+ case OTText:
+ break;
+ }
+
+ Point clickedDelta = point - clickedPoint;
+ TranslateObject((Object *)c, clickedDelta);
}
}
+std::vector<void *> Flatten(Container * src)
+{
+ std::vector<void *> flat;
+ std::vector<void *>::iterator i;
+
+ for(i=src->objects.begin(); i!=src->objects.end(); i++)
+ {
+ flat.push_back(*i);
+ Object * obj = (Object *)(*i);
+
+ // Recursively add objects to the flat vector, if necessary
+ if (obj->type == OTContainer)
+ {
+ std::vector<void *> sub = Flatten((Container *)obj);
+ flat.insert(flat.end(), sub.begin(), sub.end());
+ }
+ }
+
+ return flat;
+}
+
void CopyObjects(std::vector<void *> & from, std::vector<void *> & to);
Object * CopyObject(Object * obj);
void MoveSelectedObjectsTo(std::vector<void *> & dest, std::vector<void *> & from);
+void CopySelectedObjectsTo(std::vector<void *> & dest, std::vector<void *> & from);
void AddObjectsTo(std::vector<void *> & dest, std::vector<void *> & from);
void ClearSelected(std::vector<void *> & v);
void SelectAll(std::vector<void *> & v);
void RemoveSelectedObjects(std::vector<void *> & v);
void SavePointsFrom(std::vector<void *> & v, std::vector<Object> & s);
void RestorePointsTo(std::vector<void *> & v, std::vector<Object> & s);
+void RestorePointsTo(std::vector<void *> & v, std::vector<void *> & s);
void TranslateObject(Object * obj, Point delta);
+void TranslateContainer(Container * c, Point point, Point delta);
void TranslateObjects(std::vector<void *> & v, Point delta);
+std::vector<void *> Flatten(Container * src);
#endif // __UTILS_H__
-