Sun, 29 Jan 2017 21:02:11 +0200
More rework on model stuff, removals of LDSpawn calls. Most importantly, the LDraw code parsing function was moved to Model.
--- a/src/basics.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/basics.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -138,35 +138,6 @@ // ============================================================================= // -void BoundingBox::calcObject (LDObject* obj) -{ - switch (obj->type()) - { - case OBJ_Line: - case OBJ_Triangle: - case OBJ_Quad: - case OBJ_CondLine: - for (int i = 0; i < obj->numVertices(); ++i) - calcVertex (obj->vertex (i)); - break; - - case OBJ_SubfileReference: - { - Model model; - static_cast<LDSubfileReference*>(obj)->inlineContents(model, true, false); - - for (LDObject* it : model.objects()) - calcObject(it); - } - break; - - default: - break; - } -} - -// ============================================================================= -// BoundingBox& BoundingBox::operator<< (const Vertex& v) { calcVertex (v); @@ -175,14 +146,6 @@ // ============================================================================= // -BoundingBox& BoundingBox::operator<< (LDObject* obj) -{ - calcObject (obj); - return *this; -} - -// ============================================================================= -// void BoundingBox::calcVertex (const Vertex& vertex) { m_vertex0.setX (qMin (vertex.x(), m_vertex0.x()));
--- a/src/basics.h Sun Jan 29 15:49:36 2017 +0200 +++ b/src/basics.h Sun Jan 29 21:02:11 2017 +0200 @@ -107,7 +107,6 @@ public: BoundingBox(); - void calcObject (LDObject* obj); void calcVertex (const Vertex& vertex); Vertex center() const; bool isEmpty() const; @@ -116,7 +115,6 @@ const Vertex& vertex0() const; const Vertex& vertex1() const; - BoundingBox& operator<< (LDObject* obj); BoundingBox& operator<< (const Vertex& v); private:
--- a/src/documentloader.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/documentloader.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -114,7 +114,7 @@ while (line.endsWith ("\n") or line.endsWith ("\r")) line.chop (1); - LDObject* obj = ParseLine (line); + LDObject* obj = _model->addFromString(line); // Check for parse errors and warn about them if (obj->type() == OBJ_Error) @@ -122,8 +122,6 @@ print ("Couldn't parse line #%1: %2", progress() + 1, static_cast<LDError*> (obj)->reason()); ++m_warningCount; } - - _model->addObject(obj); } m_progress = i;
--- a/src/documentmanager.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/documentmanager.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -329,7 +329,7 @@ int numWarnings; bool ok; - Model model; + Model model {this}; loadFileContents(fp, model, &numWarnings, &ok); load->merge(model); fp->close();
--- a/src/editHistory.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/editHistory.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -171,7 +171,7 @@ void AddHistoryEntry::redo() const { - parent()->document()->insertObject (m_index, ParseLine (m_code)); + parent()->document()->insertFromString(m_index, m_code); } // @@ -203,15 +203,13 @@ void EditHistoryEntry::undo() const { LDObject* object = parent()->document()->getObject (m_index); - LDObject* newObject = ParseLine (m_oldCode); - parent()->document()->replace(object, newObject); + parent()->document()->replaceWithFromString(object, m_oldCode); } void EditHistoryEntry::redo() const { LDObject* object = parent()->document()->getObject (m_index); - LDObject* newObject = ParseLine (m_newCode); - parent()->document()->replace(object, newObject); + parent()->document()->replaceWithFromString(object, m_newCode); } //
--- a/src/editmodes/circleMode.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/editmodes/circleMode.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -86,7 +86,7 @@ void CircleMode::endDraw() { - Model model; + Model model {m_documents}; PrimitiveModel primitiveModel; primitiveModel.segments = m_window->ringToolSegments(); primitiveModel.divisions = m_window->ringToolHiRes() ? HighResolution : LowResolution;
--- a/src/editmodes/curvemode.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/editmodes/curvemode.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -86,7 +86,7 @@ { if (countof(m_drawedVerts) == 4) { - Model model; + Model model {m_documents}; model.emplace<LDBezierCurve>(m_drawedVerts[0], m_drawedVerts[1], m_drawedVerts[2], m_drawedVerts[3]); finishDraw(model); }
--- a/src/editmodes/drawMode.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/editmodes/drawMode.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -65,16 +65,14 @@ { // Clean the selection and create the object QList<Vertex>& verts = m_drawedVerts; - Model model; + Model model {m_documents}; switch (countof(verts)) { - case 2: - { + case 2: // 2 verts - make a line - LDLine* obj = model.emplace<LDLine>(verts[0], verts[1]); - break; - } + model.emplace<LDLine>(verts[0], verts[1]); + break; case 3: case 4:
--- a/src/editmodes/linePathMode.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/editmodes/linePathMode.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -80,10 +80,10 @@ void LinePathMode::endDraw() { - Model model; + Model model {m_documents}; for (int i = 0; i < countof(m_drawedVerts) - 1; ++i) - LDLine* line = model.emplace<LDLine>(m_drawedVerts[i], m_drawedVerts[i + 1]); + model.emplace<LDLine>(m_drawedVerts[i], m_drawedVerts[i + 1]); finishDraw(model); }
--- a/src/editmodes/rectangleMode.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/editmodes/rectangleMode.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -41,7 +41,7 @@ { if (countof(m_drawedVerts) == 2) { - Model model; + Model model {m_documents}; LDQuad* quad = model.emplace<LDQuad>(); updateRectVerts();
--- a/src/glRenderer.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/glRenderer.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -1438,68 +1438,71 @@ continue; LDGLOverlay& meta = currentDocumentData().overlays[camera]; - LDOverlay* ovlobj = findOverlayObject (camera); + LDOverlay* overlayObject = findOverlayObject (camera); - if (meta.image == nullptr and ovlobj) + if (meta.image == nullptr and overlayObject) { // If this is the last overlay image, we need to remove the empty space after it as well. - LDObject* nextobj = ovlobj->next(); + LDObject* nextobj = overlayObject->next(); if (nextobj and nextobj->type() == OBJ_Empty) document()->remove(nextobj); // If the overlay object was there and the overlay itself is // not, remove the object. - document()->remove(ovlobj); + document()->remove(overlayObject); + overlayObject = nullptr; } - else if (meta.image and ovlobj == nullptr) + else if (meta.image and overlayObject == nullptr) { // Inverse case: image is there but the overlay object is // not, thus create the object. - ovlobj = LDSpawn<LDOverlay>(); - + // // Find a suitable position to place this object. We want to place // this into the header, which is everything up to the first scemantic // object. If we find another overlay object, place this object after // the last one found. Otherwise, place it before the first schemantic // object and put an empty object after it (though don't do this if // there was no schemantic elements at all) - int i, lastOverlay = -1; + int i; + int lastOverlayPosition = -1; bool found = false; for (i = 0; i < document()->getObjectCount(); ++i) { - LDObject* obj = document()->getObject (i); + LDObject* object = document()->getObject (i); - if (obj->isScemantic()) + if (object->isScemantic()) { found = true; break; } - if (obj->type() == OBJ_Overlay) - lastOverlay = i; + if (object->type() == OBJ_Overlay) + lastOverlayPosition = i; } - if (lastOverlay != -1) - document()->insertObject (lastOverlay + 1, ovlobj); + if (lastOverlayPosition != -1) + { + overlayObject = document()->emplaceAt<LDOverlay>(lastOverlayPosition + 1); + } else { - document()->insertObject (i, ovlobj); + overlayObject = document()->emplaceAt<LDOverlay>(i); if (found) document()->emplaceAt<LDEmpty>(i + 1); } } - if (meta.image and ovlobj) + if (meta.image and overlayObject) { - ovlobj->setCamera (camera); - ovlobj->setFileName (meta.fileName); - ovlobj->setX (meta.offsetX); - ovlobj->setY (meta.offsetY); - ovlobj->setWidth (meta.width); - ovlobj->setHeight (meta.height); + overlayObject->setCamera (camera); + overlayObject->setFileName (meta.fileName); + overlayObject->setX (meta.offsetX); + overlayObject->setY (meta.offsetY); + overlayObject->setWidth (meta.width); + overlayObject->setHeight (meta.height); } }
--- a/src/ldDocument.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/ldDocument.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -36,6 +36,7 @@ LDDocument::LDDocument (DocumentManager* parent) : QObject (parent), + Model {parent}, HierarchyElement (parent), m_history (new EditHistory (this)), m_flags(IsCache | VerticesOutdated | NeedsVertexMerge | NeedsRecache), @@ -291,248 +292,6 @@ // ============================================================================= // -static void CheckTokenCount (const QStringList& tokens, int num) -{ - if (countof(tokens) != num) - throw QString (format ("Bad amount of tokens, expected %1, got %2", num, countof(tokens))); -} - -// ============================================================================= -// -static void CheckTokenNumbers (const QStringList& tokens, int min, int max) -{ - bool ok; - QRegExp scientificRegex ("\\-?[0-9]+\\.[0-9]+e\\-[0-9]+"); - - for (int i = min; i <= max; ++i) - { - // Check for floating point - tokens[i].toDouble (&ok); - if (ok) - return; - - // Check hex - if (tokens[i].startsWith ("0x")) - { - tokens[i].mid (2).toInt (&ok, 16); - - if (ok) - return; - } - - // Check scientific notation, e.g. 7.99361e-15 - if (scientificRegex.exactMatch (tokens[i])) - return; - - throw QString (format ("Token #%1 was `%2`, expected a number (matched length: %3)", - (i + 1), tokens[i], scientificRegex.matchedLength())); - } -} - -// ============================================================================= -// -static Vertex ParseVertex (QStringList& s, const int n) -{ - Vertex v; - v.apply ([&] (Axis ax, double& a) { a = s[n + ax].toDouble(); }); - return v; -} - -static int32 StringToNumber (QString a, bool* ok = nullptr) -{ - int base = 10; - - if (a.startsWith ("0x")) - { - a.remove (0, 2); - base = 16; - } - - return a.toLong (ok, base); -} - -// ============================================================================= -// This is the LDraw code parser function. It takes in a string containing LDraw -// code and returns the object parsed from it. parseLine never returns null, -// the object will be LDError if it could not be parsed properly. -// ============================================================================= -LDObject* ParseLine (QString line) -{ - try - { - QStringList tokens = line.split (" ", QString::SkipEmptyParts); - - if (countof(tokens) <= 0) - { - // Line was empty, or only consisted of whitespace - return LDSpawn<LDEmpty>(); - } - - if (countof(tokens[0]) != 1 or not tokens[0][0].isDigit()) - throw QString ("Illogical line code"); - - int num = tokens[0][0].digitValue(); - - switch (num) - { - case 0: - { - // Comment - QString commentText (line.mid (line.indexOf ("0") + 2)); - QString commentTextSimplified (commentText.simplified()); - - // Handle BFC statements - if (countof(tokens) > 2 and tokens[1] == "BFC") - { - for (BfcStatement statement : iterateEnum<BfcStatement>()) - { - if (commentTextSimplified == format("BFC %1", LDBfc::statementToString (statement))) - return LDSpawn<LDBfc>(statement); - } - - // MLCAD is notorious for stuffing these statements in parts it - // creates. The above block only handles valid statements, so we - // need to handle MLCAD-style invertnext, clip and noclip separately. - if (commentTextSimplified == "BFC CERTIFY INVERTNEXT") - return LDSpawn<LDBfc> (BfcStatement::InvertNext); - else if (commentTextSimplified == "BFC CERTIFY CLIP") - return LDSpawn<LDBfc> (BfcStatement::Clip); - else if (commentTextSimplified == "BFC CERTIFY NOCLIP") - return LDSpawn<LDBfc> (BfcStatement::NoClip); - } - - if (countof(tokens) > 2 and tokens[1] == "!LDFORGE") - { - // Handle LDForge-specific types, they're embedded into comments too - if (tokens[2] == "OVERLAY") - { - CheckTokenCount (tokens, 9); - CheckTokenNumbers (tokens, 5, 8); - - LDOverlay* obj = LDSpawn<LDOverlay>(); - obj->setFileName (tokens[3]); - obj->setCamera (tokens[4].toLong()); - obj->setX (tokens[5].toLong()); - obj->setY (tokens[6].toLong()); - obj->setWidth (tokens[7].toLong()); - obj->setHeight (tokens[8].toLong()); - return obj; - } - else if (tokens[2] == "BEZIER_CURVE") - { - CheckTokenCount (tokens, 16); - CheckTokenNumbers (tokens, 3, 15); - LDBezierCurve* obj = LDSpawn<LDBezierCurve>(); - obj->setColor (StringToNumber (tokens[3])); - - for (int i = 0; i < 4; ++i) - obj->setVertex (i, ParseVertex (tokens, 4 + (i * 3))); - - return obj; - } - } - - // Just a regular comment: - LDComment* obj = LDSpawn<LDComment>(); - obj->setText (commentText); - return obj; - } - - case 1: - { - // Subfile - CheckTokenCount (tokens, 15); - CheckTokenNumbers (tokens, 1, 13); - LDDocument* document = g_win->documents()->getDocumentByName (tokens[14]); - - // If we cannot open the file, mark it an error. Note we cannot use LDParseError - // here because the error object needs the document reference. - if (not document) - { - LDError* obj = LDSpawn<LDError> (line, format ("Could not open %1", tokens[14])); - obj->setFileReferenced (tokens[14]); - return obj; - } - - LDSubfileReference* obj = LDSpawn<LDSubfileReference>(); - obj->setColor (StringToNumber (tokens[1])); - obj->setPosition (ParseVertex (tokens, 2)); // 2 - 4 - - Matrix transform; - - for (int i = 0; i < 9; ++i) - transform.value(i) = tokens[i + 5].toDouble(); // 5 - 13 - - obj->setTransformationMatrix (transform); - obj->setFileInfo (document); - return obj; - } - - case 2: - { - CheckTokenCount (tokens, 8); - CheckTokenNumbers (tokens, 1, 7); - - // Line - LDLine* obj (LDSpawn<LDLine>()); - obj->setColor (StringToNumber (tokens[1])); - - for (int i = 0; i < 2; ++i) - obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 7 - - return obj; - } - - case 3: - { - CheckTokenCount (tokens, 11); - CheckTokenNumbers (tokens, 1, 10); - - // Triangle - LDTriangle* obj (LDSpawn<LDTriangle>()); - obj->setColor (StringToNumber (tokens[1])); - - for (int i = 0; i < 3; ++i) - obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 10 - - return obj; - } - - case 4: - case 5: - { - CheckTokenCount (tokens, 14); - CheckTokenNumbers (tokens, 1, 13); - - // Quadrilateral / Conditional line - LDObject* obj; - - if (num == 4) - obj = LDSpawn<LDQuad>(); - else - obj = LDSpawn<LDCondLine>(); - - obj->setColor (StringToNumber (tokens[1])); - - for (int i = 0; i < 4; ++i) - obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 13 - - return obj; - } - - default: - throw QString ("Unknown line code number"); - } - } - catch (QString& e) - { - // Strange line we couldn't parse - return LDSpawn<LDError> (line, e); - } -} - -// ============================================================================= -// void LDDocument::reloadAllSubfiles() { print ("Reloading subfiles of %1", getDisplayName()); @@ -554,7 +313,7 @@ // Reparse gibberish files. It could be that they are invalid because // of loading errors. Circumstances may be different now. if (obj->type() == OBJ_Error) - obj->replace (ParseLine (static_cast<LDError*> (obj)->contents())); + replaceWithFromString(obj, static_cast<LDError*> (obj)->contents()); } m_flags |= NeedsRecache; @@ -660,7 +419,7 @@ if (checkFlag(NeedsRecache)) { m_vertices.clear(); - Model model; + Model model {m_documents}; inlineContents(model, true, true); for (LDObject* obj : model.objects()) @@ -688,7 +447,7 @@ if (checkFlag(VerticesOutdated)) { m_objectVertices.clear(); - Model model; + Model model {m_documents}; inlineContents(model, true, false); for (LDObject* obj : model) @@ -740,7 +499,7 @@ if (deep and object->type() == OBJ_SubfileReference) static_cast<LDSubfileReference*>(object)->inlineContents(model, deep, renderinline); else - model.addObject(object->createCopy()); + model.addFromString(object->asText()); } }
--- a/src/ldObject.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/ldObject.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -347,7 +347,7 @@ // ----------------------------------------------------------------------------- void LDSubfileReference::inlineContents(Model& model, bool deep, bool render) { - Model inlined; + Model inlined {this->model()->documentManager()}; fileInfo()->inlineContents(inlined, deep, render); // Transform the objects @@ -545,7 +545,7 @@ // Check whether subfile is flat int axisSet = (1 << X) | (1 << Y) | (1 << Z); - Model model; + Model model {this->model()->documentManager()}; fileInfo()->inlineContents(model, true, false); for (LDObject* obj : model.objects()) @@ -960,14 +960,6 @@ return result; } -// ============================================================================= -// -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},
--- a/src/ldObject.h Sun Jan 29 15:49:36 2017 +0200 +++ b/src/ldObject.h Sun Jan 29 21:02:11 2017 +0200 @@ -98,8 +98,7 @@ LDObject (Model* model = nullptr); virtual QString asText() const = 0; // This object as LDraw code - LDColor color() const; - LDObject* createCopy() const; + LDColor color() const; virtual LDColor defaultColor() const = 0; // What color does the object default to? Model* model() const; LDPolygon* getPolygon();
--- a/src/mathfunctions.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/mathfunctions.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -95,9 +95,14 @@ for (LDObject* obj : objs) { if (obj->hasMatrix()) + { box << static_cast<LDMatrixObject*> (obj)->position(); + } else - box << obj; + { + for (int i = 0; i < obj->numVertices(); ++i) + box << obj->vertex(i); + } } return box.center();
--- a/src/model.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/model.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -1,7 +1,9 @@ #include "model.h" #include "ldObject.h" +#include "documentmanager.h" -Model::Model() {} +Model::Model(DocumentManager* manager) : + _manager {manager} {} Model::~Model() { @@ -197,3 +199,262 @@ { return _objects.isEmpty(); } + +DocumentManager* Model::documentManager() const +{ + return _manager; +} + +// ============================================================================= +// +static void CheckTokenCount (const QStringList& tokens, int num) +{ + if (countof(tokens) != num) + throw QString (format ("Bad amount of tokens, expected %1, got %2", num, countof(tokens))); +} + +// ============================================================================= +// +static void CheckTokenNumbers (const QStringList& tokens, int min, int max) +{ + bool ok; + QRegExp scientificRegex ("\\-?[0-9]+\\.[0-9]+e\\-[0-9]+"); + + for (int i = min; i <= max; ++i) + { + // Check for floating point + tokens[i].toDouble (&ok); + if (ok) + return; + + // Check hex + if (tokens[i].startsWith ("0x")) + { + tokens[i].mid (2).toInt (&ok, 16); + + if (ok) + return; + } + + // Check scientific notation, e.g. 7.99361e-15 + if (scientificRegex.exactMatch (tokens[i])) + return; + + throw QString (format ("Token #%1 was `%2`, expected a number (matched length: %3)", + (i + 1), tokens[i], scientificRegex.matchedLength())); + } +} + +// ============================================================================= +// +static Vertex ParseVertex (QStringList& s, const int n) +{ + Vertex v; + v.apply ([&] (Axis ax, double& a) { a = s[n + ax].toDouble(); }); + return v; +} + +static int32 StringToNumber (QString a, bool* ok = nullptr) +{ + int base = 10; + + if (a.startsWith ("0x")) + { + a.remove (0, 2); + base = 16; + } + + return a.toLong (ok, base); +} + +// ============================================================================= +// This is the LDraw code parser function. It takes in a string containing LDraw +// code and returns the object parsed from it. parseLine never returns null, +// the object will be LDError if it could not be parsed properly. +// ============================================================================= +LDObject* Model::insertFromString(int position, QString line) +{ + try + { + QStringList tokens = line.split(" ", QString::SkipEmptyParts); + + if (countof(tokens) <= 0) + { + // Line was empty, or only consisted of whitespace + return emplaceAt<LDEmpty>(position); + } + + if (countof(tokens[0]) != 1 or not tokens[0][0].isDigit()) + throw QString ("Illogical line code"); + + int num = tokens[0][0].digitValue(); + + switch (num) + { + case 0: + { + // Comment + QString commentText = line.mid (line.indexOf ("0") + 2); + QString commentTextSimplified = commentText.simplified(); + + // Handle BFC statements + if (countof(tokens) > 2 and tokens[1] == "BFC") + { + for (BfcStatement statement : iterateEnum<BfcStatement>()) + { + if (commentTextSimplified == format("BFC %1", LDBfc::statementToString (statement))) + return emplaceAt<LDBfc>(position, statement); + } + + // MLCAD is notorious for stuffing these statements in parts it + // creates. The above block only handles valid statements, so we + // need to handle MLCAD-style invertnext, clip and noclip separately. + if (commentTextSimplified == "BFC CERTIFY INVERTNEXT") + return emplaceAt<LDBfc>(position, BfcStatement::InvertNext); + else if (commentTextSimplified == "BFC CERTIFY CLIP") + return emplaceAt<LDBfc>(position, BfcStatement::Clip); + else if (commentTextSimplified == "BFC CERTIFY NOCLIP") + return emplaceAt<LDBfc>(position, BfcStatement::NoClip); + } + + if (countof(tokens) > 2 and tokens[1] == "!LDFORGE") + { + // Handle LDForge-specific types, they're embedded into comments too + if (tokens[2] == "OVERLAY") + { + CheckTokenCount (tokens, 9); + CheckTokenNumbers (tokens, 5, 8); + + LDOverlay* obj = emplaceAt<LDOverlay>(position); + obj->setFileName (tokens[3]); + obj->setCamera (tokens[4].toLong()); + obj->setX (tokens[5].toLong()); + obj->setY (tokens[6].toLong()); + obj->setWidth (tokens[7].toLong()); + obj->setHeight (tokens[8].toLong()); + return obj; + } + else if (tokens[2] == "BEZIER_CURVE") + { + CheckTokenCount (tokens, 16); + CheckTokenNumbers (tokens, 3, 15); + LDBezierCurve* obj = emplaceAt<LDBezierCurve>(position); + obj->setColor (StringToNumber (tokens[3])); + + for (int i = 0; i < 4; ++i) + obj->setVertex (i, ParseVertex (tokens, 4 + (i * 3))); + + return obj; + } + } + + // Just a regular comment: + return emplaceAt<LDComment>(position, commentText); + } + + case 1: + { + // Subfile + CheckTokenCount (tokens, 15); + CheckTokenNumbers (tokens, 1, 13); + LDDocument* document = _manager->getDocumentByName(tokens[14]); + + // If we cannot open the file, mark it an error. Note we cannot use LDParseError + // here because the error object needs the document reference. + if (not document) + { + LDError* obj = emplaceAt<LDError>(position, line, format ("Could not open %1", tokens[14])); + obj->setFileReferenced (tokens[14]); + return obj; + } + + Vertex referncePosition = ParseVertex (tokens, 2); // 2 - 4 + Matrix transform; + + for (int i = 0; i < 9; ++i) + transform.value(i) = tokens[i + 5].toDouble(); // 5 - 13 + + LDSubfileReference* obj = emplaceAt<LDSubfileReference>(position, document, transform, referncePosition); + obj->setColor (StringToNumber (tokens[1])); + return obj; + } + + case 2: + { + CheckTokenCount (tokens, 8); + CheckTokenNumbers (tokens, 1, 7); + + // Line + LDLine* obj = emplaceAt<LDLine>(position); + obj->setColor (StringToNumber (tokens[1])); + + for (int i = 0; i < 2; ++i) + obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 7 + + return obj; + } + + case 3: + { + CheckTokenCount (tokens, 11); + CheckTokenNumbers (tokens, 1, 10); + + // Triangle + LDTriangle* obj = emplaceAt<LDTriangle>(position); + obj->setColor (StringToNumber (tokens[1])); + + for (int i = 0; i < 3; ++i) + obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 10 + + return obj; + } + + case 4: + case 5: + { + CheckTokenCount (tokens, 14); + CheckTokenNumbers (tokens, 1, 13); + + // Quadrilateral / Conditional line + LDObject* obj; + + if (num == 4) + obj = emplaceAt<LDQuad>(position); + else + obj = emplaceAt<LDCondLine>(position); + + obj->setColor (StringToNumber (tokens[1])); + + for (int i = 0; i < 4; ++i) + obj->setVertex (i, ParseVertex (tokens, 2 + (i * 3))); // 2 - 13 + + return obj; + } + + default: + throw QString {"Unknown line code number"}; + } + } + catch (QString& errorMessage) + { + // Strange line we couldn't parse + return emplaceAt<LDError>(position, line, errorMessage); + } +} + +LDObject* Model::addFromString(QString line) +{ + return insertFromString(size(), line); +} + +LDObject* Model::replaceWithFromString(LDObject* object, QString line) +{ + if (object and object->model() == this) + { + int position = object->lineNumber(); + removeAt(position); + return insertFromString(position, line); + } + else + return nullptr; +}
--- a/src/model.h Sun Jan 29 15:49:36 2017 +0200 +++ b/src/model.h Sun Jan 29 21:02:11 2017 +0200 @@ -5,7 +5,7 @@ class Model { public: - Model(); + Model(DocumentManager* manager); Model(const Model& other) = delete; ~Model(); @@ -27,6 +27,10 @@ QVector<LDObject*>::iterator begin(); QVector<LDObject*>::iterator end(); bool isEmpty() const; + DocumentManager* documentManager() const; + LDObject* insertFromString(int position, QString line); + LDObject* addFromString(QString line); + LDObject* replaceWithFromString(LDObject* object, QString line); template<typename T, typename... Args> T* emplace(Args&& ...args) @@ -85,6 +89,7 @@ virtual LDObject* withdrawAt(int position); QVector<LDObject*> _objects; + DocumentManager* _manager; mutable int _triangleCount = 0; mutable bool _needsTriangleRecount; };
--- a/src/toolsets/algorithmtoolset.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/toolsets/algorithmtoolset.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -110,8 +110,7 @@ return; // Reinterpret it from the text of the input field - LDObject* newobj = ParseLine (ui.code->text()); - obj->replace (newobj); + currentDocument()->replaceWithFromString(obj, ui.code->text()); } void AlgorithmToolset::makeBorders() @@ -125,7 +124,7 @@ if (type != OBJ_Quad and type != OBJ_Triangle) continue; - Model lines; + Model lines {m_documents}; if (type == OBJ_Quad) { @@ -395,7 +394,7 @@ if (not isOneOf (obj->type(), OBJ_Line, OBJ_CondLine)) continue; - Model segments; + Model segments {m_documents}; for (int i = 0; i < numSegments; ++i) { @@ -536,7 +535,7 @@ subfile->setFullPath(fullsubname); subfile->setName(LDDocument::shortenName(fullsubname)); - Model header; + Model header {m_documents}; header.emplace<LDComment>(subtitle); header.emplace<LDComment>("Name: "); // This gets filled in when the subfile is saved header.emplace<LDComment>(format("Author: %1 [%2]", m_config->defaultName(), m_config->defaultUser())); @@ -552,7 +551,7 @@ // Copy the body over to the new document for (LDObject* object : selectedObjects()) - subfile->addObject(object->createCopy()); + subfile->addFromString(object->asText()); // Try save it if (m_window->save(subfile, true))
--- a/src/toolsets/basictoolset.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/toolsets/basictoolset.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -72,17 +72,16 @@ const QString clipboardText = qApp->clipboard()->text(); int idx = m_window->suggestInsertPoint(); currentDocument()->clearSelection(); - int num = 0; + int count = 0; - for (QString line : clipboardText.split ("\n")) + for (QString line : clipboardText.split("\n")) { - LDObject* pasted = ParseLine (line); - currentDocument()->insertObject (idx++, pasted); + LDObject* pasted = currentDocument()->insertFromString(idx++, line); currentDocument()->addToSelection(pasted); - ++num; + ++count; } - print (tr ("%1 objects pasted"), num); + print(tr("%1 objects pasted"), count); m_window->refresh(); m_window->scrollToSelection(); } @@ -103,7 +102,7 @@ if (idx != -1) { - Model inlined; + Model inlined {m_documents}; reference->inlineContents(inlined, deep, false); // Merge in the inlined objects @@ -120,7 +119,7 @@ for (LDBezierCurve* curve : filterByType<LDBezierCurve> (selectedObjects())) { - Model curveModel; + Model curveModel {m_documents}; curve->rasterize(curveModel, grid()->bezierCurveSegments()); currentDocument()->replace(curve, curveModel); } @@ -164,7 +163,7 @@ void BasicToolset::insertRaw() { - int idx = m_window->suggestInsertPoint(); + int position = m_window->suggestInsertPoint(); QDialog* const dlg = new QDialog; QVBoxLayout* const layout = new QVBoxLayout; @@ -185,11 +184,9 @@ for (QString line : QString (inputbox->toPlainText()).split ("\n")) { - LDObject* obj = ParseLine (line); - - currentDocument()->insertObject (idx, obj); - currentDocument()->addToSelection(obj); - idx++; + LDObject* object = currentDocument()->insertFromString(position, line); + currentDocument()->addToSelection(object); + position += 1; } m_window->refresh();
--- a/src/toolsets/extprogramtoolset.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/toolsets/extprogramtoolset.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -170,14 +170,14 @@ if (obj->type() == OBJ_SubfileReference) { LDSubfileReference* ref = static_cast<LDSubfileReference*> (obj); - Model model; + Model model {m_documents}; ref->inlineContents(model, true, false); writeObjects(model.objects().toList(), f); } else if (obj->type() == OBJ_BezierCurve) { LDBezierCurve* curve = static_cast<LDBezierCurve*> (obj); - Model model; + Model model {m_documents}; curve->rasterize(model, grid()->bezierCurveSegments()); writeObjects(model.objects().toList(), f); } @@ -323,7 +323,7 @@ // 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. - Model model; + Model model {m_documents}; m_documents->loadFileContents(&f, model, nullptr, nullptr); // If we replace the objects, delete the selection now.
--- a/src/toolsets/filetoolset.cpp Sun Jan 29 15:49:36 2017 +0200 +++ b/src/toolsets/filetoolset.cpp Sun Jan 29 21:02:11 2017 +0200 @@ -117,8 +117,7 @@ if (file.open(QIODevice::ReadOnly)) { - // TODO: shouldn't need to go to the document manager to parse a file - Model model; + Model model {m_documents}; m_documents->loadFileContents(&file, model, nullptr, nullptr); currentDocument()->clearSelection();