Sun, 29 Jan 2017 15:05:14 +0200
Major overhaul of object→document relationship: added the Model class which models the object buffer. Each object is to be included in a model (an invariant that currently does not hold). A document is a subclass of a model. The LDObject is also now agnostic about selection, and the selection is now a set. A lot of things are probably broken now but it's a major step forward.
The LDObject::destroy method is also now gone. The model decides when objects are destroyed and calls the destructor directly. The end result removes a lot of cruft and adds structure to LDObject relations.
Notes:
- Inlining does not currently work (nothing simply gets inlined in)
- More work is required to ensure that each object actually goes into a model
--- a/CMakeLists.txt Sat Jan 28 17:47:06 2017 +0200 +++ b/CMakeLists.txt Sun Jan 29 15:05:14 2017 +0200 @@ -50,6 +50,7 @@ src/matrixinput.cpp src/messageLog.cpp src/miscallenous.cpp + src/model.cpp src/partdownloader.cpp src/partdownloadrequest.cpp src/primitives.cpp @@ -108,6 +109,7 @@ src/matrixinput.h src/messageLog.h src/miscallenous.h + src/model.h src/partdownloader.h src/partdownloadrequest.h src/primitives.h
--- a/src/basics.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/basics.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -151,11 +151,13 @@ break; case OBJ_SubfileReference: - for (LDObject* it : static_cast<LDSubfileReference*> (obj)->inlineContents (true, false)) - { - calcObject (it); - it->destroy(); - } + { + Model model; + static_cast<LDSubfileReference*>(obj)->inlineContents(model, true, false); + + for (LDObject* it : model.objects()) + calcObject(it); + } break; default:
--- a/src/basics.h Sat Jan 28 17:47:06 2017 +0200 +++ b/src/basics.h Sun Jan 29 15:05:14 2017 +0200 @@ -21,6 +21,7 @@ #include <QObject> #include <QStringList> #include <QMetaType> +#include <QSet> #include <QVector3D> #include <QVector> #include <functional> @@ -245,6 +246,12 @@ } template<typename T> +int countof(const QSet<T>& set) +{ + return set.size(); +} + +template<typename T> int countof(const std::initializer_list<T>& vector) { return vector.size();
--- a/src/documentloader.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/documentloader.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -23,8 +23,9 @@ #include "mainwindow.h" #include "dialogs/openprogressdialog.h" -DocumentLoader::DocumentLoader (bool onForeground, QObject *parent) : +DocumentLoader::DocumentLoader (Model* model, bool onForeground, QObject *parent) : QObject (parent), + _model(model), m_warningCount (0), m_isDone (false), m_hasAborted (false), @@ -55,9 +56,9 @@ return m_isOnForeground; } -const LDObjectList& DocumentLoader::objects() const +const QVector<LDObject*>& DocumentLoader::objects() const { - return m_objects; + return _model->objects(); } void DocumentLoader::read (QIODevice* fp) @@ -98,10 +99,6 @@ // User wishes to abort, so stop here now. if (hasAborted()) { - for (LDObject* obj : m_objects) - obj->destroy(); - - m_objects.clear(); m_isDone = true; return; } @@ -126,7 +123,7 @@ ++m_warningCount; } - m_objects << obj; + _model->addObject(obj); } m_progress = i;
--- a/src/documentloader.h Sat Jan 28 17:47:06 2017 +0200 +++ b/src/documentloader.h Sun Jan 29 15:05:14 2017 +0200 @@ -18,6 +18,7 @@ #pragma once #include "main.h" +#include "model.h" // // DocumentLoader @@ -30,13 +31,13 @@ Q_OBJECT public: - DocumentLoader (bool onForeground = false, QObject* parent = 0); + DocumentLoader (Model* model, bool onForeground = false, QObject* parent = 0); Q_SLOT void abort(); bool hasAborted(); bool isDone() const; bool isOnForeground() const; - const LDObjectList& objects() const; + const QVector<LDObject*>& objects() const; int progress() const; void read (QIODevice* fp); Q_SLOT void start(); @@ -44,7 +45,7 @@ private: class OpenProgressDialog* m_progressDialog; - LDObjectList m_objects; + Model* _model; QStringList m_lines; int m_progress; int m_warningCount;
--- a/src/documentmanager.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/documentmanager.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -271,15 +271,13 @@ return nullptr; } -LDObjectList DocumentManager::loadFileContents (QFile* fp, int* numWarnings, bool* ok) +void DocumentManager::loadFileContents(QIODevice* input, Model& model, int* numWarnings, bool* ok) { - LDObjectList objs; - if (numWarnings) *numWarnings = 0; - DocumentLoader* loader = new DocumentLoader (m_loadingMainFile); - loader->read (fp); + DocumentLoader* loader = new DocumentLoader {&model, m_loadingMainFile}; + loader->read(input); loader->start(); // After start() returns, if the loader isn't done yet, it's delaying @@ -292,10 +290,6 @@ // If we wanted the success value, supply that now if (ok) *ok = not loader->hasAborted(); - - objs = loader->objects(); - delete loader; - return objs; } LDDocument* DocumentManager::openDocument (QString path, bool search, bool implicit, LDDocument* fileToOverride, @@ -335,7 +329,9 @@ int numWarnings; bool ok; - LDObjectList objs = loadFileContents (fp, &numWarnings, &ok); + Model model; + loadFileContents(fp, model, &numWarnings, &ok); + load->merge(model); fp->close(); fp->deleteLater(); @@ -348,8 +344,6 @@ return nullptr; } - load->addObjects (objs); - if (m_loadingMainFile) { m_window->changeDocument (load); @@ -411,7 +405,7 @@ print (tr ("Logoed studs loaded.\n")); } -bool DocumentManager::preInline (LDDocument* doc, LDObjectList& objs, bool deep, bool renderinline) +bool DocumentManager::preInline (LDDocument* doc, Model& model, bool deep, bool renderinline) { // Possibly substitute with logoed studs: // stud.dat -> stud-logo.dat @@ -423,12 +417,12 @@ if (doc->name() == "stud.dat" and m_logoedStud) { - objs = m_logoedStud->inlineContents (deep, renderinline); + m_logoedStud->inlineContents(model, deep, renderinline); return true; } else if (doc->name() == "stud2.dat" and m_logoedStud2) { - objs = m_logoedStud2->inlineContents (deep, renderinline); + m_logoedStud2->inlineContents(model, deep, renderinline); return true; } }
--- a/src/documentmanager.h Sat Jan 28 17:47:06 2017 +0200 +++ b/src/documentmanager.h Sun Jan 29 15:05:14 2017 +0200 @@ -21,6 +21,8 @@ #include "main.h" #include "hierarchyelement.h" +class Model; + class DocumentManager : public QObject, public HierarchyElement { Q_OBJECT @@ -39,13 +41,13 @@ QString findDocumentPath (QString relpath, bool subdirs); LDDocument* getDocumentByName (QString filename); bool isSafeToCloseAll(); - LDObjectList loadFileContents (QFile* fp, int* numWarnings, bool* ok); + void loadFileContents(QIODevice* fp, Model& model, int* numWarnings, bool* ok); void loadLogoedStuds(); LDDocument* openDocument (QString path, bool search, bool implicit, LDDocument* fileToOverride = nullptr, bool* aborted = nullptr); QFile* openLDrawFile (QString relpath, bool subdirs, QString* pathpointer); void openMainModel (QString path); - bool preInline (LDDocument* doc, LDObjectList&, bool deep, bool renderinline); + bool preInline (LDDocument* doc, Model& model, bool deep, bool renderinline); private: Documents m_documents;
--- a/src/editHistory.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/editHistory.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -165,7 +165,8 @@ void AddHistoryEntry::undo() const { - parent()->document()->getObject (m_index)->destroy(); + LDObject* object = parent()->document()->getObject(m_index); + parent()->document()->remove(object); } void AddHistoryEntry::redo() const @@ -201,16 +202,16 @@ void EditHistoryEntry::undo() const { - LDObject* obj = parent()->document()->getObject (m_index); - LDObject* newobj = ParseLine (m_oldCode); - obj->replace (newobj); + LDObject* object = parent()->document()->getObject (m_index); + LDObject* newObject = ParseLine (m_oldCode); + parent()->document()->replace(object, newObject); } void EditHistoryEntry::redo() const { - LDObject* obj = parent()->document()->getObject (m_index); - LDObject* newobj = ParseLine (m_newCode); - obj->replace (newobj); + LDObject* object = parent()->document()->getObject (m_index); + LDObject* newObject = ParseLine (m_newCode); + parent()->document()->replace(object, newObject); } //
--- a/src/editmodes/circleMode.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/editmodes/circleMode.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -191,7 +191,7 @@ int l = (relZ == X ? 1 : 0); int m = (relZ == Y ? 1 : 0); int n = (relZ == Z ? 1 : 0); - math()->rotateObjects (l, m, n, -m_angleOffset, objs); + math()->rotateObjects (l, m, n, -m_angleOffset, objs.toVector()); } finishDraw (objs);
--- a/src/editmodes/magicWandMode.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/editmodes/magicWandMode.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -180,12 +180,12 @@ currentDocument()->clearSelection(); case Additive: for (LDObject* obj : m_selection) - obj->select(); + currentDocument()->addToSelection(obj); break; case Subtractive: for (LDObject* obj : m_selection) - obj->deselect(); + currentDocument()->removeFromSelection(obj); break; case InternalRecursion:
--- a/src/glCompiler.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/glCompiler.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -258,7 +258,7 @@ continue; } - if (it.key()->document() == currentDocument() and not it.key()->isHidden()) + if (it.key()->model() == currentDocument() and not it.key()->isHidden()) vbodata += it->data[vbonum]; ++it; @@ -285,7 +285,7 @@ void GLCompiler::compileObject (LDObject* obj) { - if (obj == nullptr or obj->document() == nullptr or obj->document()->isCache()) + if (obj == nullptr) return; ObjectVBOInfo info;
--- a/src/glRenderer.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/glRenderer.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -909,7 +909,7 @@ void GLRenderer::pick(const QRect& range, bool additive) { makeCurrent(); - QSet<LDObject*> priorSelection = selectedObjects().toSet(); + QSet<LDObject*> priorSelection = selectedObjects(); QSet<LDObject*> newSelection; // If we're doing an additive selection, we start off with the existing selection. @@ -975,14 +975,14 @@ // Select all objects that we now have selected that were not selected before. for (LDObject* object : newSelection - priorSelection) { - object->select(); + m_document->addToSelection(object); compileObject(object); } // Likewise, deselect whatever was selected that isn't anymore. for (LDObject* object : priorSelection - newSelection) { - object->deselect(); + m_document->removeFromSelection(object); compileObject(object); } @@ -1447,11 +1447,11 @@ LDObject* nextobj = ovlobj->next(); if (nextobj and nextobj->type() == OBJ_Empty) - nextobj->destroy(); + document()->remove(nextobj); // If the overlay object was there and the overlay itself is // not, remove the object. - ovlobj->destroy(); + document()->remove(ovlobj); } else if (meta.image and ovlobj == nullptr) { @@ -1489,7 +1489,7 @@ document()->insertObject (i, ovlobj); if (found) - document()->insertObject (i + 1, LDSpawn<LDEmpty>()); + document()->emplaceAt<LDEmpty>(i + 1); } } @@ -1563,13 +1563,9 @@ { PrimitiveTreeItem* item = static_cast<PrimitiveTreeItem*> (m_window->getPrimitivesTree()->currentItem()); QString primitiveName = item->primitive()->name; - LDSubfileReference* ref = LDSpawn<LDSubfileReference>(); - ref->setColor (MainColor); + LDSubfileReference* ref = currentDocument()->emplaceAt<LDSubfileReference>(m_window->suggestInsertPoint()); ref->setFileInfo (m_documents->getDocumentByName (primitiveName)); - ref->setPosition (Origin); - ref->setTransformationMatrix (Matrix::identity); - currentDocument()->insertObject (m_window->suggestInsertPoint(), ref); - ref->select(); + currentDocument()->addToSelection(ref); m_window->buildObjectList(); m_window->renderer()->refresh(); ev->acceptProposedAction();
--- a/src/hierarchyelement.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/hierarchyelement.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -59,7 +59,7 @@ } -const LDObjectList& HierarchyElement::selectedObjects() +const QSet<LDObject*>& HierarchyElement::selectedObjects() { return m_window->selectedObjects(); }
--- a/src/hierarchyelement.h Sat Jan 28 17:47:06 2017 +0200 +++ b/src/hierarchyelement.h Sun Jan 29 15:05:14 2017 +0200 @@ -38,7 +38,7 @@ public: HierarchyElement (QObject* parent); - const LDObjectList& selectedObjects(); + const QSet<LDObject*>& selectedObjects(); LDDocument* currentDocument(); GuiUtilities* guiUtilities() const; PrimitiveManager* primitives();
--- a/src/ldDocument.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/ldDocument.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -40,18 +40,13 @@ m_history (new EditHistory (this)), m_flags(IsCache | VerticesOutdated | NeedsVertexMerge | NeedsRecache), m_savePosition(-1), - m_tabIndex(-1), - m_triangleCount(0), + m_tabIndex(-1), m_gldata (new LDGLData), m_manager (parent) {} LDDocument::~LDDocument() { m_flags |= IsBeingDestroyed; - - for (int i = 0; i < countof(m_objects); ++i) - m_objects[i]->destroy(); - delete m_history; delete m_gldata; } @@ -66,11 +61,6 @@ m_name = value; } -const LDObjectList& LDDocument::objects() const -{ - return m_objects; -} - EditHistory* LDDocument::history() const { return m_history; @@ -121,21 +111,6 @@ m_defaultName = value; } -int LDDocument::triangleCount() -{ - if (checkFlag(NeedsTriangleRecount)) - { - m_triangleCount = 0; - - for (LDObject* obj : m_objects) - m_triangleCount += obj->triangleCount(); - - unsetFlag(NeedsTriangleRecount); - } - - return m_triangleCount; -} - void LDDocument::openForEditing() { if (isCache()) @@ -316,14 +291,6 @@ // ============================================================================= // -void LDDocument::clear() -{ - for (LDObject* obj : objects()) - forgetObject (obj); -} - -// ============================================================================= -// static void CheckTokenCount (const QStringList& tokens, int num) { if (countof(tokens) != num) @@ -575,18 +542,13 @@ { if (obj->type() == OBJ_SubfileReference) { - LDSubfileReference* ref = static_cast<LDSubfileReference*> (obj); - LDDocument* fileInfo = m_documents->getDocumentByName (ref->fileInfo()->name()); + LDSubfileReference* reference = static_cast<LDSubfileReference*> (obj); + LDDocument* fileInfo = m_documents->getDocumentByName (reference->fileInfo()->name()); if (fileInfo) - { - ref->setFileInfo (fileInfo); - } + reference->setFileInfo (fileInfo); else - { - ref->replace (LDSpawn<LDError> (ref->asText(), - format ("Could not open %1", ref->fileInfo()->name()))); - } + emplaceReplacement<LDError>(reference, reference->asText(), format("Could not open %1", reference->fileInfo()->name())); } // Reparse gibberish files. It could be that they are invalid because @@ -603,25 +565,12 @@ // ============================================================================= // -int LDDocument::addObject (LDObject* obj) +void LDDocument::addObjects (const LDObjectList& objects) { - history()->add (new AddHistoryEntry (countof(objects()), obj)); - m_objects << obj; - addKnownVertices (obj); - obj->setDocument (this); - setFlag(NeedsTriangleRecount); - m_window->renderer()->compileObject (obj); - return getObjectCount() - 1; -} - -// ============================================================================= -// -void LDDocument::addObjects (const LDObjectList& objs) -{ - for (LDObject* obj : objs) + for (LDObject* object : objects) { - if (obj) - addObject (obj); + if (object) + addObject (object); } } @@ -630,11 +579,9 @@ void LDDocument::insertObject (int pos, LDObject* obj) { history()->add (new AddHistoryEntry (pos, obj)); - m_objects.insert (pos, obj); - obj->setDocument (this); - setFlag(NeedsTriangleRecount); + Model::insertObject(pos, obj); m_window->renderer()->compileObject (obj); - + connect(obj, SIGNAL(codeChanged(int,QString,QString)), this, SLOT(objectChanged(int,QString,QString))); #ifdef DEBUG if (not isCache()) @@ -642,6 +589,14 @@ #endif } +void LDDocument::objectChanged(int position, QString before, QString after) +{ + LDObject* object = static_cast<LDObject*>(sender()); + addToHistory(new EditHistoryEntry {position, before, after}); + m_window->renderer()->compileObject(object); + m_window->currentDocument()->redoVertices(); +} + // ============================================================================= // void LDDocument::addKnownVertices (LDObject* obj) @@ -657,61 +612,18 @@ needVertexMerge(); } -// ============================================================================= -// -void LDDocument::forgetObject (LDObject* obj) +LDObject* LDDocument::withdrawAt(int position) { - int idx = obj->lineNumber(); - - if (m_objects[idx] == obj) - { - obj->deselect(); - - if (not isCache() and not checkFlag(IsBeingDestroyed)) - { - history()->add (new DelHistoryEntry (idx, obj)); - m_objectVertices.remove (obj); - } + LDObject* object = getObject(position); - m_objects.removeAt (idx); - setFlag(NeedsTriangleRecount); - obj->setDocument (nullptr); - } -} - -// ============================================================================= -// -void LDDocument::setObjectAt (int idx, LDObject* obj) -{ - if (idx < 0 or idx >= countof(m_objects)) - return; - - // Mark this change to history - if (not m_history->isIgnoring()) + if (not isCache() and not checkFlag(IsBeingDestroyed)) { - QString oldcode = getObject (idx)->asText(); - QString newcode = obj->asText(); - m_history->add (new EditHistoryEntry (idx, oldcode, newcode)); + history()->add(new DelHistoryEntry {position, object}); + m_objectVertices.remove(object); } - m_objectVertices.remove (m_objects[idx]); - m_objects[idx]->deselect(); - m_objects[idx]->setDocument (nullptr); - obj->setDocument (this); - addKnownVertices (obj); - m_window->renderer()->compileObject (obj); - m_objects[idx] = obj; - needVertexMerge(); -} - -// ============================================================================= -// -LDObject* LDDocument::getObject (int pos) const -{ - if (pos < countof(m_objects)) - return m_objects[pos]; - else - return nullptr; + m_selection.remove(object); + return Model::withdrawAt(position); } // ============================================================================= @@ -748,8 +660,10 @@ if (checkFlag(NeedsRecache)) { m_vertices.clear(); + Model model; + inlineContents(model, true, true); - for (LDObject* obj : inlineContents (true, true)) + for (LDObject* obj : model.objects()) { if (obj->type() == OBJ_SubfileReference) { @@ -774,8 +688,10 @@ if (checkFlag(VerticesOutdated)) { m_objectVertices.clear(); + Model model; + inlineContents(model, true, false); - for (LDObject* obj : inlineContents (true, false)) + for (LDObject* obj : model) addKnownVertices (obj); mergeVertices(); @@ -808,12 +724,10 @@ // ============================================================================= // ----------------------------------------------------------------------------- -LDObjectList LDDocument::inlineContents (bool deep, bool renderinline) +void LDDocument::inlineContents(Model& model, bool deep, bool renderinline) { - LDObjectList objs; - - if (m_manager->preInline (this, objs, deep, renderinline)) - return objs; // Manager dealt with this inline + if (m_manager->preInline(this, model, deep, renderinline)) + return; // Manager dealt with this inline for (LDObject* obj : objects()) { @@ -823,26 +737,27 @@ // Got another sub-file reference, inline it if we're deep-inlining. If not, // just add it into the objects normally. Yay, recursion! - if (deep == true and obj->type() == OBJ_SubfileReference) - { - for (LDObject* otherobj : static_cast<LDSubfileReference*> (obj)->inlineContents (deep, renderinline)) - objs << otherobj; - } + if (deep and obj->type() == OBJ_SubfileReference) + static_cast<LDSubfileReference*>(obj)->inlineContents(model, deep, renderinline); else - objs << obj->createCopy(); + model.addObject(obj->createCopy()); } - - return objs; } // ============================================================================= // void LDDocument::addToSelection (LDObject* obj) // [protected] { - if (obj->isSelected() and obj->document() == this) + if (not m_selection.contains(obj) and obj->model() == this) { - m_sel << obj; + m_selection.insert(obj); m_window->renderer()->compileObject (obj); + + // If this object is inverted with INVERTNEXT, select the INVERTNEXT as well. + LDBfc* invertnext; + + if (obj->previousIsInvertnext(invertnext)) + addToSelection(invertnext); } } @@ -850,10 +765,16 @@ // void LDDocument::removeFromSelection (LDObject* obj) // [protected] { - if (not obj->isSelected() and obj->document() == this) + if (m_selection.contains(obj)) { - m_sel.removeOne (obj); + m_selection.remove(obj); m_window->renderer()->compileObject (obj); + + // If this object is inverted with INVERTNEXT, deselect the INVERTNEXT as well. + LDBfc* invertnext; + + if (obj->previousIsInvertnext(invertnext)) + removeFromSelection(invertnext); } } @@ -861,34 +782,31 @@ // void LDDocument::clearSelection() { - for (LDObject* obj : m_sel) - { - obj->deselect(); - m_window->renderer()->compileObject (obj); - } + for (LDObject* object : m_selection) + m_window->renderer()->compileObject(object); - m_sel.clear(); + m_selection.clear(); } // ============================================================================= // -const LDObjectList& LDDocument::getSelection() const +const QSet<LDObject*>& LDDocument::getSelection() const { - return m_sel; + return m_selection; } // ============================================================================= // -void LDDocument::swapObjects (LDObject* one, LDObject* other) +bool LDDocument::swapObjects (LDObject* one, LDObject* other) { - int a = m_objects.indexOf (one); - int b = m_objects.indexOf (other); - - if (a != b and a != -1 and b != -1) + if (Model::swapObjects(one, other)) { - m_objects[b] = one; - m_objects[a] = other; - addToHistory (new SwapHistoryEntry (one->id(), other->id())); + addToHistory(new SwapHistoryEntry {one->id(), other->id()}); + return true; + } + else + { + return false; } } @@ -923,7 +841,3 @@ setFlag(NeedsVertexMerge); } -void LDDocument::recountTriangles() -{ - setFlag(NeedsTriangleRecount); -}
--- a/src/ldDocument.h Sat Jan 28 17:47:06 2017 +0200 +++ b/src/ldDocument.h Sun Jan 29 15:05:14 2017 +0200 @@ -22,6 +22,7 @@ #include "ldObject.h" #include "editHistory.h" #include "glShared.h" +#include "model.h" class EditHistory; class OpenProgressDialog; @@ -39,7 +40,7 @@ // The default name is a placeholder, initially suggested name for a file. The // primitive generator uses this to give initial names to primitives. // -class LDDocument : public QObject, public HierarchyElement +class LDDocument : public QObject, public Model, public HierarchyElement { Q_OBJECT @@ -51,7 +52,6 @@ NeedsVertexMerge = (1 << 2), IsBeingDestroyed = (1 << 3), NeedsRecache = (1 << 4), // The next polygon inline of this document rebuilds stored polygon data. - NeedsTriangleRecount = (1 << 5), }; Q_DECLARE_FLAGS(Flags, Flag) @@ -60,11 +60,9 @@ ~LDDocument(); void addHistoryStep(); - int addObject (LDObject* obj); void addObjects (const LDObjectList& objs); void addToHistory (AbstractHistoryEntry* entry); void addToSelection (LDObject* obj); - void clear(); void clearHistory(); void clearSelection(); void close(); @@ -72,14 +70,13 @@ void forgetObject (LDObject* obj); QString fullPath(); QString getDisplayName(); - LDObject* getObject (int pos) const; int getObjectCount() const; - const LDObjectList& getSelection() const; + const QSet<LDObject*>& getSelection() const; LDGLData* glData(); bool hasUnsavedChanges() const; EditHistory* history() const; void initializeCachedData(); - LDObjectList inlineContents (bool deep, bool renderinline); + void inlineContents(Model& model, bool deep, bool renderinline); QList<LDPolygon> inlinePolygons(); const QSet<Vertex>& inlineVertices(); void insertObject (int pos, LDObject* obj); @@ -87,7 +84,7 @@ bool isSafeToClose(); QString name() const; void needVertexMerge(); - const LDObjectList& objects() const; + void objectRemoved(LDObject* object, int index); void openForEditing(); const QList<LDPolygon>& polygonData() const; void recountTriangles(); @@ -100,22 +97,25 @@ void setDefaultName (QString value); void setFullPath (QString value); void setName (QString value); - void setObjectAt (int idx, LDObject* obj); void setSavePosition (long value); void setTabIndex (int value); - void swapObjects (LDObject* one, LDObject* other); + bool swapObjects (LDObject* one, LDObject* other); int tabIndex() const; - int triangleCount(); void undo(); void vertexChanged (const Vertex& a, const Vertex& b); static QString shortenName (QString a); // Turns a full path into a relative path +public slots: + void objectChanged(int position, QString before, QString after); + +protected: + LDObject* withdrawAt(int position); + private: QString m_name; QString m_fullPath; QString m_defaultName; - LDObjectList m_objects; EditHistory* m_history; Flags m_flags; long m_savePosition; @@ -124,7 +124,7 @@ QList<LDPolygon> m_polygonData; QMap<LDObject*, QSet<Vertex>> m_objectVertices; QSet<Vertex> m_vertices; - LDObjectList m_sel; + QSet<LDObject*> m_selection; LDGLData* m_gldata; DocumentManager* m_manager;
--- a/src/ldObject.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/ldObject.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -37,20 +37,19 @@ enum { MAX_LDOBJECT_IDS = (1 << 24) }; #define LDOBJ_DEFAULT_CTOR(T,BASE) \ - T :: T (LDDocument* document) : \ - BASE (document) {} + T :: T (Model* model) : \ + BASE {model} {} // ============================================================================= // LDObject constructors // -LDObject::LDObject (LDDocument* document) : +LDObject::LDObject (Model* model) : m_isHidden (false), - m_isSelected (false), - m_isDestroyed (false), - m_document (nullptr) + m_isSelected (false), + _model (nullptr) { - if (document) - document->addObject (this); + if (model) + model->addObject (this); memset (m_coords, 0, sizeof m_coords); @@ -67,8 +66,8 @@ m_randomColor = QColor::fromHsv (rand() % 360, rand() % 256, rand() % 96 + 128); } -LDSubfileReference::LDSubfileReference (LDDocument* document) : - LDMatrixObject (document) {} +LDSubfileReference::LDSubfileReference (Model* model) : + LDMatrixObject (model) {} LDOBJ_DEFAULT_CTOR (LDEmpty, LDObject) LDOBJ_DEFAULT_CTOR (LDError, LDObject) @@ -83,8 +82,12 @@ LDObject::~LDObject() { - if (not m_isDestroyed) - print ("Warning: Object #%1 (%2) was not destroyed before being deleted\n", id(), this); + // Delete the GL lists + if (g_win) + g_win->renderer()->forgetObject(this); + + // Remove this object from the list of LDObjects + g_allObjects.erase(g_allObjects.find(id())); } // ============================================================================= @@ -217,10 +220,7 @@ if (idx != -1) { // Replace the instance of the old object with the new object - document()->setObjectAt (idx, other); - - // Remove the old object - destroy(); + model()->setObjectAt(idx, other); } } @@ -231,10 +231,9 @@ if (idx != -1 and not others.isEmpty()) { for (int i = 1; i < countof(others); ++i) - document()->insertObject (idx + i, others[i]); + model()->insertObject (idx + i, others[i]); - document()->setObjectAt (idx, others[0]); - destroy(); + model()->setObjectAt(idx, others[0]); } } @@ -244,8 +243,8 @@ // void LDObject::swap (LDObject* other) { - if (document() == other->document()) - document()->swapObjects (this, other); + if (model() == other->model()) + model()->swapObjects (this, other); } int LDObject::triangleCount() const @@ -323,34 +322,9 @@ // ============================================================================= // -// Deletes this object -// -void LDObject::destroy() +void LDObject::setDocument (Model* model) { - deselect(); - - // If this object was associated to a file, remove it off it now - if (document()) - document()->forgetObject (this); - - // Delete the GL lists - if (g_win) - g_win->renderer()->forgetObject (this); - - // Remove this object from the list of LDObjects - g_allObjects.erase (g_allObjects.find (id())); - m_isDestroyed = true; - delete this; -} - -// ============================================================================= -// -void LDObject::setDocument (LDDocument* document) -{ - m_document = document; - - if (document == nullptr) - m_isSelected = false; + _model = model; } // ============================================================================= @@ -392,15 +366,16 @@ // ============================================================================= // ----------------------------------------------------------------------------- -LDObjectList LDSubfileReference::inlineContents (bool deep, bool render) +void LDSubfileReference::inlineContents(Model& model, bool deep, bool render) { - LDObjectList objs = fileInfo()->inlineContents (deep, render); + Model inlined; + fileInfo()->inlineContents(inlined, deep, render); // Transform the objects - for (LDObject* obj : objs) - TransformObject (obj, transformationMatrix(), position(), color()); + for (LDObject* object : inlined) + TransformObject(object, transformationMatrix(), position(), color()); - return objs; + model.merge(inlined); } // ============================================================================= @@ -449,11 +424,11 @@ // int LDObject::lineNumber() const { - if (document()) + if (model()) { - for (int i = 0; i < document()->getObjectCount(); ++i) + for (int i = 0; i < model()->size(); ++i) { - if (document()->getObject (i) == this) + if (model()->getObject(i) == this) return i; } } @@ -510,12 +485,7 @@ // LDObject* LDObject::next() const { - int idx = lineNumber(); - - if (idx == -1 or idx == document()->getObjectCount() - 1) - return nullptr; - - return document()->getObject (idx + 1); + return model()->getObject(lineNumber() + 1); } // ============================================================================= @@ -524,12 +494,7 @@ // LDObject* LDObject::previous() const { - int idx = lineNumber(); - - if (idx <= 0) - return nullptr; - - return document()->getObject (idx - 1); + return model()->getObject(lineNumber() - 1); } // ============================================================================= @@ -597,14 +562,9 @@ return m_randomColor; } -LDDocument* LDObject::document() const +Model* LDObject::model() const { - return m_document; -} - -bool LDObject::isDestroyed() const -{ - return m_isDestroyed; + return _model; } // ============================================================================= @@ -668,14 +628,15 @@ // void LDSubfileReference::invert() { - if (document() == nullptr) + if (model() == nullptr) return; // Check whether subfile is flat int axisSet = (1 << X) | (1 << Y) | (1 << Z); - LDObjectList objs = fileInfo()->inlineContents (true, false); + Model model; + fileInfo()->inlineContents(model, true, false); - for (LDObject* obj : objs) + for (LDObject* obj : model.objects()) { for (int i = 0; i < obj->numVertices(); ++i) { @@ -724,13 +685,13 @@ if (bfc and bfc->statement() == BfcStatement::InvertNext) { // This is prefixed with an invertnext, thus remove it. - bfc->destroy(); + this->model()->remove(bfc); return; } } // Not inverted, thus prefix it with a new invertnext. - document()->insertObject (idx, new LDBfc (BfcStatement::InvertNext)); + this->model()->insertObject (idx, new LDBfc (BfcStatement::InvertNext)); } // ============================================================================= @@ -768,7 +729,7 @@ // ============================================================================= // -LDLine* LDCondLine::toEdgeLine() +LDLine* LDCondLine::becomeEdgeLine() { LDLine* replacement = new LDLine; @@ -776,20 +737,15 @@ replacement->setVertex (i, vertex (i)); replacement->setColor (color()); - replace (replacement); + model()->replace(this, replacement); return replacement; } // ============================================================================= // -LDObject* LDObject::fromID (int id) +LDObject* LDObject::fromID(int32 id) { - auto it = g_allObjects.find (id); - - if (it != g_allObjects.end()) - return *it; - - return nullptr; + return g_allObjects.value(id); } // ============================================================================= @@ -809,29 +765,22 @@ // makes history stuff work out of the box. // template<typename T> -static void changeProperty (LDObject* obj, T* ptr, const T& val) +static void changeProperty(LDObject* object, T* property, const T& value) { - int idx; - - if (*ptr == val) + if (*property == value) return; - if (obj->document() and (idx = obj->lineNumber()) != -1) - { - QString before = obj->asText(); - *ptr = val; - QString after = obj->asText(); + int position = object->lineNumber(); - if (before != after) - { - obj->document()->addToHistory (new EditHistoryEntry (idx, before, after)); - g_win->renderer()->compileObject (obj); - g_win->currentDocument()->redoVertices(); - } + if (position != -1) + { + QString before = object->asText(); + *property = value; + emit object->codeChanged(position, before, object->asText()); } else { - *ptr = val; + *property = value; } } @@ -860,12 +809,12 @@ changeProperty (this, &m_coords[i], vert); } -LDMatrixObject::LDMatrixObject (LDDocument* document) : - LDObject (document), +LDMatrixObject::LDMatrixObject (Model* model) : + LDObject (model), m_position (Origin) {} -LDMatrixObject::LDMatrixObject (const Matrix& transform, const Vertex& pos, LDDocument* document) : - LDObject (document), +LDMatrixObject::LDMatrixObject (const Matrix& transform, const Vertex& pos, Model* model) : + LDObject (model), m_position (pos), m_transformationMatrix (transform) {} @@ -907,8 +856,8 @@ changeProperty (this, &m_transformationMatrix, val); } -LDError::LDError (QString contents, QString reason, LDDocument* document) : - LDObject (document), +LDError::LDError (QString contents, QString reason, Model* model) : + LDObject (model), m_contents (contents), m_reason (reason) {} @@ -1062,19 +1011,16 @@ return Vertex(); } -LDObjectList LDBezierCurve::rasterize (int segments) +void LDBezierCurve::rasterize(Model& model, int segments) { QVector<LDPolygon> polygons = rasterizePolygons(segments); - LDObjectList result; for (LDPolygon& poly : polygons) { LDLine* line = LDSpawn<LDLine> (poly.vertices[0], poly.vertices[1]); line->setColor (poly.color); - result << line; + model.addObject(line); } - - return result; } QVector<LDPolygon> LDBezierCurve::rasterizePolygons(int segments) @@ -1104,44 +1050,17 @@ // ============================================================================= // -// Selects this object. -// -void LDObject::select() -{ - if (not isSelected() and document()) - { - m_isSelected = true; - document()->addToSelection (this); - } -} - -// ============================================================================= -// -// Removes this object from selection -// -void LDObject::deselect() -{ - if (isSelected() and document()) - { - m_isSelected = false; - document()->removeFromSelection (this); - - // If this object is inverted with INVERTNEXT, deselect the INVERTNEXT as well. - LDBfc* invertnext; - - if (previousIsInvertnext (invertnext)) - invertnext->deselect(); - } -} - -// ============================================================================= -// LDObject* LDObject::createCopy() const { LDObject* copy = ParseLine (asText()); return copy; } +LDSubfileReference::LDSubfileReference(LDDocument* reference, const Matrix& transformationMatrix, + const Vertex& position, Model* model) : + LDMatrixObject {transformationMatrix, position, model}, + m_fileInfo {reference} {} + // ============================================================================= // LDDocument* LDSubfileReference::fileInfo() const @@ -1153,8 +1072,8 @@ { changeProperty (this, &m_fileInfo, newReferee); - if (document()) - document()->recountTriangles(); + if (model()) + model()->recountTriangles(); // If it's an immediate subfile reference (i.e. this subfile is in an opened document), we need to pre-compile the // GL polygons for the document if they don't exist already.
--- a/src/ldObject.h Sat Jan 28 17:47:06 2017 +0200 +++ b/src/ldObject.h Sun Jan 29 15:05:14 2017 +0200 @@ -23,10 +23,12 @@ #include "glShared.h" #include "colors.h" +class Model; + #define LDOBJ(T) \ public: \ static constexpr LDObjectType SubclassType = OBJ_##T; \ - LD##T (LDDocument* document = nullptr); \ + LD##T (Model* model = nullptr); \ \ virtual LDObjectType type() const override \ { \ @@ -88,25 +90,24 @@ // which is a token of the object's type. The object can be casted into // sub-classes based on this enumerator. // -class LDObject +class LDObject : public QObject { + Q_OBJECT + public: - LDObject (LDDocument* document = nullptr); + LDObject (Model* model = nullptr); virtual QString asText() const = 0; // This object as LDraw code LDColor color() const; LDObject* createCopy() const; virtual LDColor defaultColor() const = 0; // What color does the object default to? - void deselect(); - void destroy(); - LDDocument* document() const; + Model* model() const; LDPolygon* getPolygon(); virtual void getVertices (QSet<Vertex>& verts) const; virtual bool hasMatrix() const = 0; // Does this object have a matrix and position? (see LDMatrixObject) qint32 id() const; virtual void invert() = 0; // Inverts this object (winding is reversed) virtual bool isColored() const = 0; - bool isDestroyed() const; bool isHidden() const; virtual bool isScemantic() const = 0; // Does this object have meaning in the part model? bool isSelected() const; @@ -119,9 +120,8 @@ QColor randomColor() const; void replace (LDObject* other); void replace (const LDObjectList& others); - void select(); void setColor (LDColor color); - void setDocument (LDDocument* document); + void setDocument (Model* model); void setHidden (bool value); void setVertex (int i, const Vertex& vert); void swap (LDObject* other); @@ -131,18 +131,21 @@ const Vertex& vertex (int i) const; static QString describeObjects (const LDObjectList& objs); - static LDObject* fromID (int id); + static LDObject* fromID(int32 id); static LDObject* getDefault (const LDObjectType type); static QString typeName (LDObjectType type); +signals: + void codeChanged(int position, QString before, QString after); + protected: + friend class Model; virtual ~LDObject(); private: bool m_isHidden; bool m_isSelected; - bool m_isDestroyed; - LDDocument* m_document; + Model* _model; qint32 m_id; LDColor m_color; QColor m_randomColor; @@ -179,8 +182,8 @@ Vertex m_position; public: - LDMatrixObject (LDDocument* document = nullptr); - LDMatrixObject (const Matrix& transformationMatrix, const Vertex& pos, LDDocument* document = nullptr); + LDMatrixObject (Model* model = nullptr); + LDMatrixObject (const Matrix& transformationMatrix, const Vertex& pos, Model* model = nullptr); const Vertex& position() const; void setCoordinate (const Axis ax, double value); @@ -208,7 +211,7 @@ LDOBJ_NO_MATRIX public: - LDError (QString contents, QString reason, LDDocument* document = nullptr); + LDError (QString contents, QString reason, Model* model = nullptr); QString reason() const; QString contents() const; QString fileReferenced() const; @@ -248,7 +251,7 @@ LDOBJ_NO_MATRIX public: - LDComment (QString text, LDDocument* document = nullptr); + LDComment (QString text, LDDocument* model = nullptr); QString text() const; void setText (QString value); @@ -287,7 +290,7 @@ LDOBJ_NO_MATRIX public: - LDBfc (const BfcStatement type, LDDocument* document = nullptr); + LDBfc (const BfcStatement type, LDDocument* model = nullptr); BfcStatement statement() const; void setStatement (BfcStatement value); @@ -315,10 +318,12 @@ LDOBJ_HAS_MATRIX public: + LDSubfileReference(LDDocument* reference, const Matrix& transformationMatrix, const Vertex& position, Model* model = nullptr); + // Inlines this subfile. LDDocument* fileInfo() const; virtual void getVertices (QSet<Vertex>& verts) const override; - LDObjectList inlineContents (bool deep, bool render); + void inlineContents(Model& model, bool deep, bool render); QList<LDPolygon> inlinePolygons(); void setFileInfo (LDDocument* fileInfo); int triangleCount() const override; @@ -343,7 +348,7 @@ LDOBJ_NO_MATRIX public: - LDLine (Vertex v1, Vertex v2, LDDocument* document = nullptr); + LDLine (Vertex v1, Vertex v2, LDDocument* model = nullptr); }; // @@ -362,8 +367,8 @@ LDOBJ_NO_MATRIX public: - LDCondLine (const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3, LDDocument* document = nullptr); - LDLine* toEdgeLine(); + LDCondLine (const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3, LDDocument* model = nullptr); + LDLine* becomeEdgeLine(); }; // @@ -384,7 +389,7 @@ LDOBJ_NO_MATRIX public: - LDTriangle (Vertex const& v1, Vertex const& v2, Vertex const& v3, LDDocument* document = nullptr); + LDTriangle (Vertex const& v1, Vertex const& v2, Vertex const& v3, LDDocument* model = nullptr); int triangleCount() const override; }; @@ -405,7 +410,7 @@ LDOBJ_NO_MATRIX public: - LDQuad (const Vertex& v1, const Vertex& v2, const Vertex& v3, const Vertex& v4, LDDocument* document = nullptr); + LDQuad (const Vertex& v1, const Vertex& v2, const Vertex& v3, const Vertex& v4, LDDocument* model = nullptr); // Split this quad into two triangles QList<LDTriangle*> splitToTriangles(); @@ -461,9 +466,9 @@ public: LDBezierCurve (const Vertex& v0, const Vertex& v1, - const Vertex& v2, const Vertex& v3, LDDocument* document = nullptr); + const Vertex& v2, const Vertex& v3, LDDocument* model = nullptr); Vertex pointAt (qreal t) const; - LDObjectList rasterize (int segments); + void rasterize(Model& model, int segments); QVector<LDPolygon> rasterizePolygons (int segments); };
--- a/src/ldobjectiterator.h Sat Jan 28 17:47:06 2017 +0200 +++ b/src/ldobjectiterator.h Sun Jan 29 15:05:14 2017 +0200 @@ -24,14 +24,14 @@ class LDObjectIterator { public: - LDObjectIterator (LDDocument* doc) : - m_list (doc->objects()), + LDObjectIterator (Model* model) : + m_list (model->objects()), m_i (-1) { seekTillValid(); } - LDObjectIterator (const LDObjectList& objs) : + LDObjectIterator (const QVector<LDObject*>& objs) : m_list (objs), m_i (-1) { @@ -111,7 +111,7 @@ } private: - const LDObjectList& m_list; + const QVector<LDObject*>& m_list; int m_i; }; @@ -120,8 +120,12 @@ { QVector<T*> result; - for (LDObjectIterator<T> it (stuff); it.isValid(); ++it) - result << it; + for (LDObject* object : stuff) + { + T* casted = dynamic_cast<T*>(object); + if (casted != nullptr) + result << casted; + } return result; }
--- a/src/mainwindow.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/mainwindow.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -358,14 +358,14 @@ if (selectedObjects().isEmpty()) return 0; - LDObjectList selCopy = selectedObjects(); + QSet<LDObject*> selectionCopy = selectedObjects(); // Delete the objects that were being selected - for (LDObject* obj : selCopy) - obj->destroy(); + for (LDObject* object : selectionCopy) + m_currentDocument->remove(object); refresh(); - return countof(selCopy); + return countof(selectionCopy); } // --------------------------------------------------------------------------------------------------------------------- @@ -508,7 +508,8 @@ if (selectedObjects().isEmpty()) return; - LDObject* obj = selectedObjects().first(); + // TODO: Need a way to properly figure out the first selected object! + LDObject* obj = *selectedObjects().begin(); ui.objectList->scrollToItem (m_objectsInList[obj]); } @@ -519,7 +520,7 @@ if (m_isSelectionLocked == true or m_currentDocument == nullptr) return; - QSet<LDObject*> priorSelection = selectedObjects().toSet(); + QSet<LDObject*> priorSelection = selectedObjects(); // Get the objects from the object list selection m_currentDocument->clearSelection(); @@ -531,7 +532,7 @@ { if (item == m_objectsInList[obj]) { - obj->select(); + m_currentDocument->addToSelection(obj); break; } } @@ -542,7 +543,7 @@ updateSelection(); // Update the GL renderer - for (LDObject* obj : (priorSelection + selectedObjects().toSet())) + for (LDObject* obj : priorSelection + selectedObjects()) renderer()->compileObject (obj); renderer()->update(); @@ -595,8 +596,9 @@ int MainWindow::suggestInsertPoint() { // If we have a selection, put the item after it. + // TODO: fix this properly! if (not selectedObjects().isEmpty()) - return selectedObjects().last()->lineNumber() + 1; + return (*(selectedObjects().end() - 1))->lineNumber() + 1; // Otherwise place the object at the end. return m_currentDocument->getObjectCount(); @@ -720,7 +722,7 @@ void MainWindow::spawnContextMenu (const QPoint& position) { const bool single = (countof(selectedObjects()) == 1); - LDObject* singleObj = single ? selectedObjects().first() : nullptr; + LDObject* singleObj = single ? *selectedObjects().begin() : nullptr; bool hasSubfiles = false; @@ -785,18 +787,18 @@ // void MainWindow::deleteByColor (LDColor color) { - LDObjectList objs; + LDObjectList unwanted; - for (LDObject* obj : m_currentDocument->objects()) + for (LDObject* object : m_currentDocument->objects()) { - if (not obj->isColored() or obj->color() != color) + if (not object->isColored() or object->color() != color) continue; - objs << obj; + unwanted.append(object); } - for (LDObject* obj : objs) - obj->destroy(); + for (LDObject* obj : unwanted) + m_currentDocument->remove(obj); } // --------------------------------------------------------------------------------------------------------------------- @@ -1289,7 +1291,7 @@ // --------------------------------------------------------------------------------------------------------------------- // -const LDObjectList& MainWindow::selectedObjects() +const QSet<LDObject*>& MainWindow::selectedObjects() { return m_currentDocument->getSelection(); }
--- a/src/mainwindow.h Sat Jan 28 17:47:06 2017 +0200 +++ b/src/mainwindow.h Sun Jan 29 15:05:14 2017 +0200 @@ -101,7 +101,7 @@ bool save (LDDocument* doc, bool saveAs); void saveShortcuts(); void scrollToSelection(); - const LDObjectList& selectedObjects(); + const QSet<LDObject*>& selectedObjects(); void setQuickColors (const QVector<ColorToolbarItem> &colors); void spawnContextMenu (const QPoint& position); int suggestInsertPoint();
--- a/src/mathfunctions.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/mathfunctions.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -33,7 +33,7 @@ } -void MathFunctions::rotateObjects(int l, int m, int n, double angle, const LDObjectList& objects) const +void MathFunctions::rotateObjects(int l, int m, int n, double angle, const QVector<LDObject*>& objects) const { Vertex rotationPoint = getRotationPoint (objects); double cosAngle = cos(angle); @@ -83,7 +83,7 @@ } -Vertex MathFunctions::getRotationPoint(const LDObjectList& objs) const +Vertex MathFunctions::getRotationPoint(const QVector<LDObject*>& objs) const { switch (RotationPoint (m_config->rotationPointType())) {
--- a/src/mathfunctions.h Sat Jan 28 17:47:06 2017 +0200 +++ b/src/mathfunctions.h Sun Jan 29 15:05:14 2017 +0200 @@ -33,8 +33,8 @@ public: MathFunctions(QObject* parent); - void rotateObjects(int l, int m, int n, double angle, const LDObjectList& objects) const; - Vertex getRotationPoint(const LDObjectList& objs) const; + void rotateObjects(int l, int m, int n, double angle, const QVector<LDObject*>& objects) const; + Vertex getRotationPoint(const QVector<LDObject*>& objs) const; private: void rotateVertex(Vertex& vertex, const Vertex& rotationPoint, const Matrix& transformationMatrix) const;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/model.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -0,0 +1,194 @@ +#include "model.h" +#include "ldObject.h" + +Model::Model() {} + +Model::~Model() +{ + for (int i = 0; i < countof(_objects); ++i) + delete _objects[i]; +} + +void Model::addObject(LDObject *object) +{ + insertObject(size(), object); +} + +int Model::size() const +{ + return _objects.size(); +} + +const QVector<LDObject*>& Model::objects() const +{ + return _objects; +} + +void Model::insertObject(int position, LDObject* object) +{ + if (object->model() and object->model() != this) + object->model()->withdraw(object); + + if (not object->model()) + { + _objects.insert(position, object); + _needsTriangleRecount = true; + object->setDocument(this); + print("Object %1 added to position %2", object->id(), position); + } +} + +bool Model::swapObjects(LDObject* one, LDObject* other) +{ + int a = _objects.indexOf(one); + int b = _objects.indexOf(other); + + if (a != b and a != -1 and b != -1) + { + _objects[b] = one; + _objects[a] = other; + return true; + } + else + { + return false; + } +} + +bool Model::setObjectAt(int idx, LDObject* obj) +{ + if (idx < 0 or idx >= countof(_objects)) + { + return false; + } + else + { + removeAt(idx); + insertObject(idx, obj); + return true; + } +} + +LDObject* Model::getObject(int position) const +{ + if (position >= 0 and position < countof(_objects)) + return _objects[position]; + else + return nullptr; +} + +void Model::remove(LDObject* object) +{ + int position = object->lineNumber(); + printf("Going to remove %d from %p at %d (there are %d objects)\n", object->id(), this, position, countof(objects())); + if (_objects[position] == object) + removeAt(position); +} + +void Model::removeAt(int position) +{ + LDObject* object = withdrawAt(position); + delete object; +} + +void Model::replace(LDObject* object, LDObject* newObject) +{ + if (object->model() == this) + setObjectAt(object->lineNumber(), newObject); +} + +void Model::replace(LDObject *object, Model &model) +{ + if (object->model() == this) + { + int position = object->lineNumber(); + + for (int i = countof(model.objects()) - 1; i >= 1; i -= 1) + insertObject(position + i, model.objects()[i]); + + setObjectAt(position, model.objects()[0]); + } +} + +void Model::recountTriangles() +{ + _needsTriangleRecount = true; +} + +int Model::triangleCount() const +{ + if (_needsTriangleRecount) + { + _triangleCount = 0; + + for (LDObject* object : objects()) + _triangleCount += object->triangleCount(); + + _needsTriangleRecount = false; + } + + return _triangleCount; +} + +void Model::merge(Model& other, int position) +{ + if (position < 0) + position = countof(_objects); + + // Copy the vector of objects to copy, so that we don't change the vector while iterating over it. + QVector<LDObject*> objectsCopy = other._objects; + + // Inform the contents of their new owner + for (LDObject* object : objectsCopy) + { + insertObject(position, object); + position += 1; + } + + other.clear(); +} + +QVector<LDObject*>::iterator Model::begin() +{ + return _objects.begin(); +} + +QVector<LDObject*>::iterator Model::end() +{ + return _objects.end(); +} + +void Model::clear() +{ + for (int i = _objects.size() - 1; i >= 0; i -= 1) + removeAt(i); + + _needsTriangleRecount = true; +} + +/* + * Drops the object from the model. The object becomes a free object as a result (thus violating the invariant that every object + * has a model!). The caller must immediately add the withdrawn object to another model. + */ +void Model::withdraw(LDObject* object) +{ + if (object->model() == this) + { + int position = object->lineNumber(); + print("Withdrawing %1 from %2 at %3\n", object->id(), this, position); + + if (_objects[position] == object) + withdrawAt(position); + } +} + +/* + * Drops an object from the model at the provided position. The caller must immediately put the result value object into a new model. + */ +LDObject* Model::withdrawAt(int position) +{ + LDObject* object = _objects[position]; + _objects.removeAt(position); + _needsTriangleRecount = true; + return object; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/model.h Sun Jan 29 15:05:14 2017 +0200 @@ -0,0 +1,81 @@ +#pragma once +#include "main.h" +#include "ldObject.h" + +class Model +{ +public: + Model(); + Model(const Model& other) = delete; + ~Model(); + + virtual void addObject(LDObject* object); + virtual void insertObject(int position, LDObject* object); + virtual bool swapObjects(LDObject* one, LDObject* other); + virtual bool setObjectAt(int idx, LDObject* obj); + void removeAt(int position); + void remove(LDObject* object); + void replace(LDObject *object, Model& model); + void clear(); + void merge(Model& other, int position = -1); + void replace(LDObject* object, LDObject* newObject); + int size() const; + const QVector<LDObject*>& objects() const; + LDObject* getObject(int position) const; + void recountTriangles(); + int triangleCount() const; + QVector<LDObject*>::iterator begin(); + QVector<LDObject*>::iterator end(); + + template<typename T, typename... Args> + T* emplace(Args&& ...args) + { + T* object = constructObject<T>(args...); + addObject(object); + return object; + } + + template<typename T, typename... Args> + T* emplaceAt(int position, Args&& ...args) + { + T* object = constructObject<T>(args...); + insertObject(position, object); + return object; + } + + template<typename T, typename... Args> + T* emplaceReplacement(LDObject* object, Args&& ...args) + { + if (object->model() == this) + { + int position = object->lineNumber(); + T* replacement = constructObject<T>(args...); + setObjectAt(position, replacement); + return replacement; + } + else + return nullptr; + } + +protected: + template<typename T, typename... Args> + T* constructObject(Args&& ...args) + { + static_assert (std::is_base_of<LDObject, T>::value, "Can only use this function with LDObject-derivatives"); + T* object = new T {args..., this}; + + // Set default color. Relying on virtual functions, this cannot be done in the c-tor. + // TODO: store -1 as the default color + if (object->isColored()) + object->setColor(object->defaultColor()); + + return object; + } + + void withdraw(LDObject* object); + virtual LDObject* withdrawAt(int position); + + QVector<LDObject*> _objects; + mutable int _triangleCount = 0; + mutable bool _needsTriangleRecount; +};
--- a/src/toolsets/algorithmtoolset.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/toolsets/algorithmtoolset.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -51,8 +51,9 @@ void AlgorithmToolset::splitQuads() { int num = 0; + QVector<LDObject*> selected = selectedObjects().toList().toVector(); - for (LDObjectIterator<LDQuad> it (selectedObjects()); it.isValid(); ++it) + for (LDObjectIterator<LDQuad> it (selected); it.isValid(); ++it) { // Find the index of this quad int index = it->lineNumber(); @@ -77,7 +78,7 @@ if (countof(selectedObjects()) != 1) return; - LDObject* obj = selectedObjects()[0]; + LDObject* obj = *(selectedObjects().begin()); QDialog* dlg = new QDialog; Ui::EditRawUI ui; @@ -102,10 +103,9 @@ void AlgorithmToolset::makeBorders() { - LDObjectList objs = selectedObjects(); int num = 0; - for (LDObject* obj : objs) + for (LDObject* obj : selectedObjects()) { const LDObjectType type = obj->type(); @@ -277,9 +277,9 @@ { int num = 0; - for (LDObjectIterator<LDCondLine> it (selectedObjects()); it.isValid(); ++it) + for (LDCondLine* line : filterByType<LDCondLine>(selectedObjects())) { - it->toEdgeLine(); + line->becomeEdgeLine(); ++num; } @@ -422,7 +422,7 @@ for (LDObject* seg : newsegs) currentDocument()->insertObject (ln++, seg); - obj->destroy(); + currentDocument()->remove(obj); } m_window->buildObjectList(); @@ -458,7 +458,8 @@ QString fullsubname; // Where to insert the subfile reference? - int refidx (selectedObjects()[0]->lineNumber()); + // TODO: the selection really should be sorted by position... + int refidx = (*selectedObjects().begin())->lineNumber(); // Determine title of subfile if (titleobj) @@ -568,8 +569,8 @@ { // Save was successful. Delete the original selection now from the // main document. - for (LDObject* obj : selectedObjects()) - obj->destroy(); + for (LDObject* object : selectedObjects()) + currentDocument()->remove(object); // Add a reference to the new subfile to where the selection was LDSubfileReference* ref = LDSpawn<LDSubfileReference>();
--- a/src/toolsets/basictoolset.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/toolsets/basictoolset.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -37,12 +37,12 @@ int BasicToolset::copyToClipboard() { - LDObjectList objs = selectedObjects(); + const QSet<LDObject*>& objects = selectedObjects(); int count = 0; qApp->clipboard()->clear(); QString data; - for (LDObject* obj : objs) + for (LDObject* obj : objects) { if (not data.isEmpty()) data += "\n"; @@ -79,7 +79,7 @@ { LDObject* pasted = ParseLine (line); currentDocument()->insertObject (idx++, pasted); - pasted->select(); + currentDocument()->addToSelection(pasted); ++num; } @@ -96,33 +96,36 @@ void BasicToolset::doInline (bool deep) { - for (LDSubfileReference* ref : filterByType<LDSubfileReference> (selectedObjects())) + for (LDSubfileReference* reference : filterByType<LDSubfileReference> (selectedObjects())) { // Get the index of the subfile so we know where to insert the // inlined contents. - int idx = ref->lineNumber(); + int idx = reference->lineNumber(); if (idx != -1) { - LDObjectList objs = ref->inlineContents (deep, false); - + Model inlined; + reference->inlineContents(inlined, deep, false); + // Merge in the inlined objects - for (LDObject* inlineobj : objs) + print("Inlined %1 objects.\n", countof(inlined.objects())); + for (LDObject* inlinedObject : inlined.objects()) { - QString line = inlineobj->asText(); - inlineobj->destroy(); - LDObject* newobj = ParseLine (line); - currentDocument()->insertObject (idx++, newobj); - newobj->select(); + currentDocument()->insertObject (idx++, inlinedObject); + currentDocument()->addToSelection(inlinedObject); } - + // Delete the subfile now as it's been inlined. - ref->destroy(); + currentDocument()->remove(reference); } } for (LDBezierCurve* curve : filterByType<LDBezierCurve> (selectedObjects())) - curve->replace (curve->rasterize(grid()->bezierCurveSegments())); + { + Model curveModel; + curve->rasterize(curveModel, grid()->bezierCurveSegments()); + currentDocument()->replace(curve, curveModel); + } } void BasicToolset::inlineShallow() @@ -187,7 +190,7 @@ LDObject* obj = ParseLine (line); currentDocument()->insertObject (idx, obj); - obj->select(); + currentDocument()->addToSelection(obj); idx++; } @@ -200,7 +203,7 @@ if (selectedObjects().isEmpty()) return; - LDObjectList objs = selectedObjects(); + QSet<LDObject*> objs = selectedObjects(); // If all selected objects have the same color, said color is our default // value to the color selection dialog. @@ -263,7 +266,7 @@ { if (countof(selectedObjects()) == 1) { - LDObject* obj = selectedObjects().first(); + LDObject* obj = *selectedObjects().begin(); AddObjectDialog::staticDialog (obj->type(), obj); } }
--- a/src/toolsets/extprogramtoolset.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/toolsets/extprogramtoolset.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -170,20 +170,16 @@ if (obj->type() == OBJ_SubfileReference) { LDSubfileReference* ref = static_cast<LDSubfileReference*> (obj); - LDObjectList objs = ref->inlineContents (true, false); - writeObjects (objs, f); - - for (LDObject* obj : objs) - obj->destroy(); + Model model; + ref->inlineContents(model, true, false); + writeObjects(model.objects().toList(), f); } else if (obj->type() == OBJ_BezierCurve) { LDBezierCurve* curve = static_cast<LDBezierCurve*> (obj); - LDObjectList objs = curve->rasterize(grid()->bezierCurveSegments()); - writeObjects (objs, f); - - for (LDObject* obj : objs) - obj->destroy(); + Model model; + curve->rasterize(model, grid()->bezierCurveSegments()); + writeObjects(model.objects().toList(), f); } else f.write ((obj->asText() + "\r\n").toUtf8()); @@ -215,7 +211,7 @@ // void ExtProgramToolset::writeSelection (QString fname) { - writeObjects (selectedObjects(), fname); + writeObjects (selectedObjects().toList(), fname); } // ============================================================================= @@ -327,7 +323,8 @@ // TODO: I don't like how I need to go to the document manager to load objects from a file... // We're not loading this as a document so it shouldn't be necessary. - LDObjectList objs = m_documents->loadFileContents (&f, nullptr, nullptr); + Model model; + m_documents->loadFileContents(&f, model, nullptr, nullptr); // If we replace the objects, delete the selection now. if (replace) @@ -339,16 +336,10 @@ // Insert the new objects currentDocument()->clearSelection(); - for (LDObject* obj : objs) + for (LDObject* object : model.objects()) { - if (not obj->isScemantic()) - { - obj->destroy(); - continue; - } - - currentDocument()->addObject (obj); - obj->select(); + if (object->isScemantic()) + currentDocument()->addObject(object); } m_window->doFullRefresh();
--- a/src/toolsets/filetoolset.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/toolsets/filetoolset.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -118,14 +118,15 @@ if (file.open(QIODevice::ReadOnly)) { // TODO: shouldn't need to go to the document manager to parse a file - LDObjectList objects = m_documents->loadFileContents(&file, nullptr, nullptr); + Model model; + m_documents->loadFileContents(&file, model, nullptr, nullptr); currentDocument()->clearSelection(); - for (LDObject* object : objects) + for (LDObject* object : model.objects()) { currentDocument()->insertObject (position, object); - object->select(); + currentDocument()->addToSelection(object); m_window->renderer()->compileObject (object); position++; }
--- a/src/toolsets/movetoolset.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/toolsets/movetoolset.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -30,7 +30,9 @@ void MoveToolset::moveSelection (bool up) { - LDObjectList objs = selectedObjects(); + // TODO: order these! + LDObjectList objs = selectedObjects().toList(); + if (objs.isEmpty()) return; @@ -39,7 +41,7 @@ int end = up ? countof(objs) : -1; int increment = up ? 1 : -1; QSet<LDObject*> objsToCompile; - LDDocument* file = objs[0]->document(); + Model* model = (*objs.begin())->model(); for (int i = start; i != end; i += increment) { @@ -48,7 +50,7 @@ int idx = obj->lineNumber(); int target = idx + (up ? -1 : 1); - if ((up and idx == 0) or (not up and idx == countof(file->objects()) - 1)) + if ((up and idx == 0) or (not up and idx == countof(model->objects()) - 1)) { // One of the objects hit the extrema. If this happens, this should be the first // object to be iterated on. Thus, nothing has changed yet and it's safe to just @@ -57,8 +59,8 @@ } objsToCompile << obj; - objsToCompile << file->getObject(target); - obj->swap(file->getObject(target)); + objsToCompile << model->getObject(target); + obj->swap(model->getObject(target)); } // The objects need to be recompiled, otherwise their pick lists are left with @@ -143,32 +145,32 @@ void MoveToolset::rotateXPos() { - math()->rotateObjects (1, 0, 0, getRotateActionAngle(), selectedObjects()); + math()->rotateObjects (1, 0, 0, getRotateActionAngle(), selectedObjects().toList().toVector()); } void MoveToolset::rotateYPos() { - math()->rotateObjects (0, 1, 0, getRotateActionAngle(), selectedObjects()); + math()->rotateObjects (0, 1, 0, getRotateActionAngle(), selectedObjects().toList().toVector()); } void MoveToolset::rotateZPos() { - math()->rotateObjects (0, 0, 1, getRotateActionAngle(), selectedObjects()); + math()->rotateObjects (0, 0, 1, getRotateActionAngle(), selectedObjects().toList().toVector()); } void MoveToolset::rotateXNeg() { - math()->rotateObjects (-1, 0, 0, getRotateActionAngle(), selectedObjects()); + math()->rotateObjects (-1, 0, 0, getRotateActionAngle(), selectedObjects().toList().toVector()); } void MoveToolset::rotateYNeg() { - math()->rotateObjects (0, -1, 0, getRotateActionAngle(), selectedObjects()); + math()->rotateObjects (0, -1, 0, getRotateActionAngle(), selectedObjects().toList().toVector()); } void MoveToolset::rotateZNeg() { - math()->rotateObjects (0, 0, -1, getRotateActionAngle(), selectedObjects()); + math()->rotateObjects (0, 0, -1, getRotateActionAngle(), selectedObjects().toList().toVector()); } void MoveToolset::configureRotationPoint()
--- a/src/toolsets/viewtoolset.cpp Sat Jan 28 17:47:06 2017 +0200 +++ b/src/toolsets/viewtoolset.cpp Sun Jan 29 15:05:14 2017 +0200 @@ -35,7 +35,7 @@ void ViewToolset::selectAll() { for (LDObject* obj : currentDocument()->objects()) - obj->select(); + currentDocument()->addToSelection(obj); } void ViewToolset::selectByColor() @@ -56,7 +56,7 @@ for (LDObject* obj : currentDocument()->objects()) { if (colors.contains (obj->color())) - obj->select(); + currentDocument()->addToSelection(obj); } } @@ -92,7 +92,7 @@ continue; } - obj->select(); + currentDocument()->addToSelection(obj); } } @@ -254,7 +254,7 @@ void ViewToolset::jumpTo() { bool ok; - int defaultValue = (countof(selectedObjects()) == 1) ? selectedObjects()[0]->lineNumber() : 0; + int defaultValue = (countof(selectedObjects()) == 1) ? (*selectedObjects().begin())->lineNumber() : 0; int index = QInputDialog::getInt (nullptr, "Go to line", "Go to line:", defaultValue, 1, currentDocument()->getObjectCount(), 1, &ok); if (ok) @@ -264,7 +264,7 @@ if (object) { currentDocument()->clearSelection(); - object->select(); + currentDocument()->addToSelection(object); m_window->updateSelection(); } }