diff -r ab77deb851fa -r 8d98ee0dc917 src/ldObject.cc
--- a/src/ldObject.cc Tue Mar 03 16:50:39 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,925 +0,0 @@
-/*
- * LDForge: LDraw parts authoring CAD
- * Copyright (C) 2013 - 2015 Teemu Piippo
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-
-#include "main.h"
-#include "ldObject.h"
-#include "ldDocument.h"
-#include "miscallenous.h"
-#include "mainWindow.h"
-#include "editHistory.h"
-#include "glRenderer.h"
-#include "colors.h"
-#include "glCompiler.h"
-
-CFGENTRY (String, DefaultName, "")
-CFGENTRY (String, DefaultUser, "")
-CFGENTRY (Bool, UseCALicense, true)
-
-// List of all LDObjects
-QMap g_allObjects;
-static int32 g_idcursor = 1; // 0 shalt be null
-static constexpr int32 g_maxID = (1 << 24);
-
-#define LDOBJ_DEFAULT_CTOR(T,BASE) \
- T :: T (LDObjectPtr* selfptr) : \
- BASE (selfptr) {}
-
-// =============================================================================
-// LDObject constructors
-//
-LDObject::LDObject (LDObjectPtr* selfptr) :
- m_isHidden (false),
- m_isSelected (false),
- m_isDestructed (false),
- qObjListEntry (null)
-{
- *selfptr = LDObjectPtr (this, [](LDObject* obj){ obj->finalDelete(); });
- memset (m_coords, 0, sizeof m_coords);
- m_self = selfptr->toWeakRef();
- chooseID();
- g_allObjects[id()] = self();
- setRandomColor (QColor::fromHsv (rand() % 360, rand() % 256, rand() % 96 + 128));
-}
-
-LDSubfile::LDSubfile (LDObjectPtr* selfptr) :
- LDMatrixObject (selfptr) {}
-
-LDOBJ_DEFAULT_CTOR (LDEmpty, LDObject)
-LDOBJ_DEFAULT_CTOR (LDError, LDObject)
-LDOBJ_DEFAULT_CTOR (LDLine, LDObject)
-LDOBJ_DEFAULT_CTOR (LDTriangle, LDObject)
-LDOBJ_DEFAULT_CTOR (LDCondLine, LDLine)
-LDOBJ_DEFAULT_CTOR (LDQuad, LDObject)
-LDOBJ_DEFAULT_CTOR (LDOverlay, LDObject)
-LDOBJ_DEFAULT_CTOR (LDBFC, LDObject)
-LDOBJ_DEFAULT_CTOR (LDComment, LDObject)
-
-// =============================================================================
-//
-void LDObject::chooseID()
-{
- // If this is the first pass we can just use a global ID counter for each
- // unique object. Let's hope that nobody goes to create 17 million objects
- // anytime soon.
- if (g_idcursor < g_maxID)
- {
- setID (g_idcursor++);
- return;
- }
-
- // In case someone does, we cannot really continue execution. We must abort,
- // give the user a chance to save their documents though.
- Critical ("Created too many objects. Execution cannot continue. You have a "
- "chance to save any changes to documents, then restart.");
- (void) IsSafeToCloseAll();
- Exit();
-}
-
-// =============================================================================
-//
-void LDObject::setVertexCoord (int i, Axis ax, double value)
-{
- Vertex v = vertex (i);
- v.setCoordinate (ax, value);
- setVertex (i, v);
-}
-
-// =============================================================================
-//
-QString LDComment::asText() const
-{
- return format ("0 %1", text());
-}
-
-// =============================================================================
-//
-QString LDSubfile::asText() const
-{
- QString val = format ("1 %1 %2 ", color(), position());
- val += transform().toString();
- val += ' ';
- val += fileInfo()->name();
- return val;
-}
-
-// =============================================================================
-//
-QString LDLine::asText() const
-{
- QString val = format ("2 %1", color());
-
- for (int i = 0; i < 2; ++i)
- val += format (" %1", vertex (i));
-
- return val;
-}
-
-// =============================================================================
-//
-QString LDTriangle::asText() const
-{
- QString val = format ("3 %1", color());
-
- for (int i = 0; i < 3; ++i)
- val += format (" %1", vertex (i));
-
- return val;
-}
-
-// =============================================================================
-//
-QString LDQuad::asText() const
-{
- QString val = format ("4 %1", color());
-
- for (int i = 0; i < 4; ++i)
- val += format (" %1", vertex (i));
-
- return val;
-}
-
-// =============================================================================
-//
-QString LDCondLine::asText() const
-{
- QString val = format ("5 %1", color());
-
- // Add the coordinates
- for (int i = 0; i < 4; ++i)
- val += format (" %1", vertex (i));
-
- return val;
-}
-
-// =============================================================================
-//
-QString LDError::asText() const
-{
- return contents();
-}
-
-// =============================================================================
-//
-QString LDEmpty::asText() const
-{
- return "";
-}
-
-// =============================================================================
-//
-const char* LDBFC::StatementStrings[] =
-{
- "CERTIFY CCW",
- "CCW",
- "CERTIFY CW",
- "CW",
- "NOCERTIFY",
- "INVERTNEXT",
- "CLIP",
- "CLIP CCW",
- "CLIP CW",
- "NOCLIP",
-};
-
-QString LDBFC::asText() const
-{
- return format ("0 BFC %1", StatementStrings[int (m_statement)]);
-}
-
-// =============================================================================
-//
-QList LDQuad::splitToTriangles()
-{
- // Create the two triangles based on this quadrilateral:
- // 0---3 0---3 3
- // | | | / /|
- // | | ==> | / / |
- // | | |/ / |
- // 1---2 1 1---2
- LDTrianglePtr tri1 (LDSpawn (vertex (0), vertex (1), vertex (3)));
- LDTrianglePtr tri2 (LDSpawn (vertex (1), vertex (2), vertex (3)));
-
- // The triangles also inherit the quad's color
- tri1->setColor (color());
- tri2->setColor (color());
-
- return {tri1, tri2};
-}
-
-// =============================================================================
-//
-void LDObject::replace (LDObjectPtr other)
-{
- long idx = lineNumber();
- assert (idx != -1);
-
- // Replace the instance of the old object with the new object
- document().toStrongRef()->setObject (idx, other);
-
- // Remove the old object
- destroy();
-}
-
-// =============================================================================
-//
-void LDObject::swap (LDObjectPtr other)
-{
- assert (document() == other->document());
- document().toStrongRef()->swapObjects (self(), other);
-}
-
-// =============================================================================
-//
-LDLine::LDLine (LDObjectPtr* selfptr, Vertex v1, Vertex v2) :
- LDObject (selfptr)
-{
- setVertex (0, v1);
- setVertex (1, v2);
-}
-
-// =============================================================================
-//
-LDTriangle::LDTriangle (LDObjectPtr* selfptr, const Vertex& v1, const Vertex& v2, const Vertex& v3) :
- LDObject (selfptr)
-{
- setVertex (0, v1);
- setVertex (1, v2);
- setVertex (2, v3);
-}
-
-// =============================================================================
-//
-LDQuad::LDQuad (LDObjectPtr* selfptr, const Vertex& v1, const Vertex& v2,
- const Vertex& v3, const Vertex& v4) :
- LDObject (selfptr)
-{
- setVertex (0, v1);
- setVertex (1, v2);
- setVertex (2, v3);
- setVertex (3, v4);
-}
-
-// =============================================================================
-//
-LDCondLine::LDCondLine (LDObjectPtr* selfptr, const Vertex& v0, const Vertex& v1,
- const Vertex& v2, const Vertex& v3) :
- LDLine (selfptr)
-{
- setVertex (0, v0);
- setVertex (1, v1);
- setVertex (2, v2);
- setVertex (3, v3);
-}
-
-// =============================================================================
-//
-LDObject::~LDObject() {}
-
-// =============================================================================
-//
-void LDObject::destroy()
-{
- // Don't bother during program termination
- if (IsExiting() or isDestructed())
- return;
-
- // If this object was selected, unselect it now
- if (isSelected() and document() != null)
- deselect();
-
- // If this object was associated to a file, remove it off it now
- if (document() != null)
- document().toStrongRef()->forgetObject (self());
-
- // Delete the GL lists
- if (g_win != null)
- g_win->R()->forgetObject (self());
-
- // Remove this object from the list of LDObjects
- g_allObjects.erase (g_allObjects.find (id()));
- setDestructed (true);
-}
-
-//
-// Deletes the object. Only the shared pointer is to call this!
-//
-void LDObject::finalDelete()
-{
- if (not isDestructed())
- destroy();
-
- delete this;
-}
-
-// =============================================================================
-//
-void LDObject::setDocument (const LDDocumentWeakPtr& a)
-{
- m_document = a;
-
- if (a == null)
- setSelected (false);
-}
-
-// =============================================================================
-//
-static void TransformObject (LDObjectPtr obj, Matrix transform, Vertex pos, LDColor parentcolor)
-{
- switch (obj->type())
- {
- case OBJ_Line:
- case OBJ_CondLine:
- case OBJ_Triangle:
- case OBJ_Quad:
- for (int i = 0; i < obj->numVertices(); ++i)
- {
- Vertex v = obj->vertex (i);
- v.transform (transform, pos);
- obj->setVertex (i, v);
- }
-
- break;
-
- case OBJ_Subfile:
- {
- LDSubfilePtr ref = qSharedPointerCast (obj);
- Matrix newMatrix = transform * ref->transform();
- Vertex newpos = ref->position();
- newpos.transform (transform, pos);
- ref->setPosition (newpos);
- ref->setTransform (newMatrix);
- break;
- }
-
- default:
- break;
- }
-
- if (obj->color() == MainColor())
- obj->setColor (parentcolor);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-LDObjectList LDSubfile::inlineContents (bool deep, bool render)
-{
- LDObjectList objs = fileInfo()->inlineContents (deep, render);
-
- // Transform the objects
- for (LDObjectPtr obj : objs)
- {
- // assert (obj->type() != OBJ_Subfile);
- // Set the parent now so we know what inlined the object.
- obj->setParent (self());
- TransformObject (obj, transform(), position(), color());
- }
-
- return objs;
-}
-
-// =============================================================================
-//
-LDPolygon* LDObject::getPolygon()
-{
- LDObjectType ot = type();
- int num = (ot == OBJ_Line) ? 2
- : (ot == OBJ_Triangle) ? 3
- : (ot == OBJ_Quad) ? 4
- : (ot == OBJ_CondLine) ? 5
- : 0;
-
- if (num == 0)
- return null;
-
- LDPolygon* data = new LDPolygon;
- data->id = id();
- data->num = num;
- data->color = color().index();
-
- for (int i = 0; i < data->numVertices(); ++i)
- data->vertices[i] = vertex (i);
-
- return data;
-}
-
-// =============================================================================
-//
-QList LDSubfile::inlinePolygons()
-{
- QList data = fileInfo()->inlinePolygons();
-
- for (LDPolygon& entry : data)
- {
- for (int i = 0; i < entry.numVertices(); ++i)
- entry.vertices[i].transform (transform(), position());
- }
-
- return data;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-long LDObject::lineNumber() const
-{
- assert (document() != null);
-
- for (int i = 0; i < document().toStrongRef()->getObjectCount(); ++i)
- {
- if (document().toStrongRef()->getObject (i) == this)
- return i;
- }
-
- return -1;
-}
-
-// =============================================================================
-//
-void LDObject::moveObjects (LDObjectList objs, const bool up)
-{
- if (objs.isEmpty())
- return;
-
- // If we move down, we need to iterate the array in reverse order.
- long const start = up ? 0 : (objs.size() - 1);
- long const end = up ? objs.size() : -1;
- long const incr = up ? 1 : -1;
- LDObjectList objsToCompile;
- LDDocumentPtr file = objs[0]->document();
-
- for (long i = start; i != end; i += incr)
- {
- LDObjectPtr obj = objs[i];
-
- long const idx = obj->lineNumber();
- long const target = idx + (up ? -1 : 1);
-
- if ((up and idx == 0) or (not up and idx == (long) file->objects().size() - 1l))
- {
- // 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
- // abort the entire operation.
- assert (i == start);
- return;
- }
-
- objsToCompile << obj;
- objsToCompile << file->getObject (target);
-
- obj->swap (file->getObject (target));
- }
-
- RemoveDuplicates (objsToCompile);
-
- // The objects need to be recompiled, otherwise their pick lists are left with
- // the wrong index colors which messes up selection.
- for (LDObjectPtr obj : objsToCompile)
- g_win->R()->compileObject (obj);
-}
-
-// =============================================================================
-//
-QString LDObject::typeName (LDObjectType type)
-{
- return LDObject::getDefault (type)->typeName();
-}
-
-// =============================================================================
-//
-QString LDObject::describeObjects (const LDObjectList& objs)
-{
- QString text;
-
- if (objs.isEmpty())
- return "nothing"; // :)
-
- for (LDObjectType objType = OBJ_FirstType; objType < OBJ_NumTypes; ++objType)
- {
- int count = 0;
-
- for (LDObjectPtr obj : objs)
- {
- if (obj->type() == objType)
- count++;
- }
-
- if (count == 0)
- continue;
-
- if (not text.isEmpty())
- text += ", ";
-
- QString noun = format ("%1%2", typeName (objType), Plural (count));
- text += format ("%1 %2", count, noun);
- }
-
- return text;
-}
-
-// =============================================================================
-//
-LDObjectPtr LDObject::topLevelParent()
-{
- if (parent() == null)
- return self();
-
- LDObjectWeakPtr it (self());
-
- while (it.toStrongRef()->parent() != null)
- it = it.toStrongRef()->parent();
-
- return it.toStrongRef();
-}
-
-// =============================================================================
-//
-LDObjectPtr LDObject::next() const
-{
- long idx = lineNumber();
- assert (idx != -1);
-
- if (idx == (long) document().toStrongRef()->getObjectCount() - 1)
- return LDObjectPtr();
-
- return document().toStrongRef()->getObject (idx + 1);
-}
-
-// =============================================================================
-//
-LDObjectPtr LDObject::previous() const
-{
- long idx = lineNumber();
- assert (idx != -1);
-
- if (idx == 0)
- return LDObjectPtr();
-
- return document().toStrongRef()->getObject (idx - 1);
-}
-
-// =============================================================================
-//
-bool LDObject::previousIsInvertnext (LDBFCPtr& ptr)
-{
- LDObjectPtr prev (previous());
-
- if (prev != null and prev->type() == OBJ_BFC and
- prev.staticCast()->statement() == BFCStatement::InvertNext)
- {
- ptr = prev.staticCast();
- return true;
- }
-
- return false;
-}
-
-// =============================================================================
-//
-void LDObject::move (Vertex vect)
-{
- if (hasMatrix())
- {
- LDMatrixObjectPtr mo = self().toStrongRef().dynamicCast();
- mo->setPosition (mo->position() + vect);
- }
- else
- {
- for (int i = 0; i < numVertices(); ++i)
- setVertex (i, vertex (i) + vect);
- }
-}
-
-// =============================================================================
-//
-LDObjectPtr LDObject::getDefault (const LDObjectType type)
-{
- switch (type)
- {
- case OBJ_Comment: return LDSpawn();
- case OBJ_BFC: return LDSpawn();
- case OBJ_Line: return LDSpawn();
- case OBJ_CondLine: return LDSpawn();
- case OBJ_Subfile: return LDSpawn();
- case OBJ_Triangle: return LDSpawn();
- case OBJ_Quad: return LDSpawn();
- case OBJ_Empty: return LDSpawn();
- case OBJ_Error: return LDSpawn();
- case OBJ_Overlay: return LDSpawn();
- case OBJ_NumTypes: assert (false);
- }
- return LDObjectPtr();
-}
-
-// =============================================================================
-//
-void LDObject::invert() {}
-void LDBFC::invert() {}
-void LDEmpty::invert() {}
-void LDComment::invert() {}
-void LDError::invert() {}
-
-// =============================================================================
-//
-void LDTriangle::invert()
-{
- // Triangle goes 0 -> 1 -> 2, reversed: 0 -> 2 -> 1.
- // Thus, we swap 1 and 2.
- Vertex tmp = vertex (1);
- setVertex (1, vertex (2));
- setVertex (2, tmp);
-
- return;
-}
-
-// =============================================================================
-//
-void LDQuad::invert()
-{
- // Quad: 0 -> 1 -> 2 -> 3
- // rev: 0 -> 3 -> 2 -> 1
- // Thus, we swap 1 and 3.
- Vertex tmp = vertex (1);
- setVertex (1, vertex (3));
- setVertex (3, tmp);
-}
-
-// =============================================================================
-//
-void LDSubfile::invert()
-{
- if (document() == null)
- return;
-
- // Check whether subfile is flat
- int axisSet = (1 << X) | (1 << Y) | (1 << Z);
- LDObjectList objs = fileInfo()->inlineContents (true, false);
-
- for (LDObjectPtr obj : objs)
- {
- for (int i = 0; i < obj->numVertices(); ++i)
- {
- Vertex const& vrt = obj->vertex (i);
-
- if (axisSet & (1 << X) and vrt.x() != 0.0)
- axisSet &= ~(1 << X);
-
- if (axisSet & (1 << Y) and vrt.y() != 0.0)
- axisSet &= ~(1 << Y);
-
- if (axisSet & (1 << Z) and vrt.z() != 0.0)
- axisSet &= ~(1 << Z);
- }
-
- if (axisSet == 0)
- break;
- }
-
- if (axisSet != 0)
- {
- // Subfile has all vertices zero on one specific plane, so it is flat.
- // Let's flip it.
- Matrix matrixModifier = IdentityMatrix;
-
- if (axisSet & (1 << X))
- matrixModifier[0] = -1;
-
- if (axisSet & (1 << Y))
- matrixModifier[4] = -1;
-
- if (axisSet & (1 << Z))
- matrixModifier[8] = -1;
-
- setTransform (transform() * matrixModifier);
- return;
- }
-
- // Subfile is not flat. Resort to invertnext.
- int idx = lineNumber();
-
- if (idx > 0)
- {
- LDBFCPtr bfc = previous().dynamicCast();
-
- if (not bfc.isNull() and bfc->statement() == BFCStatement::InvertNext)
- {
- // This is prefixed with an invertnext, thus remove it.
- bfc->destroy();
- return;
- }
- }
-
- // Not inverted, thus prefix it with a new invertnext.
- document().toStrongRef()->insertObj (idx, LDSpawn (BFCStatement::InvertNext));
-}
-
-// =============================================================================
-//
-void LDLine::invert()
-{
- // For lines, we swap the vertices.
- Vertex tmp = vertex (0);
- setVertex (0, vertex (1));
- setVertex (1, tmp);
-}
-
-// =============================================================================
-//
-void LDCondLine::invert()
-{
- // I don't think that a conditional line's control points need to be
- // swapped, do they?
- Vertex tmp = vertex (0);
- setVertex (0, vertex (1));
- setVertex (1, tmp);
-}
-
-// =============================================================================
-//
-LDLinePtr LDCondLine::toEdgeLine()
-{
- LDLinePtr replacement (LDSpawn());
-
- for (int i = 0; i < replacement->numVertices(); ++i)
- replacement->setVertex (i, vertex (i));
-
- replacement->setColor (color());
-
- replace (replacement);
- return replacement;
-}
-
-// =============================================================================
-//
-LDObjectPtr LDObject::fromID (int id)
-{
- auto it = g_allObjects.find (id);
-
- if (it != g_allObjects.end())
- return *it;
-
- return LDObjectPtr();
-}
-
-// =============================================================================
-//
-QString LDOverlay::asText() const
-{
- return format ("0 !LDFORGE OVERLAY %1 %2 %3 %4 %5 %6",
- fileName(), camera(), x(), y(), width(), height());
-}
-
-void LDOverlay::invert() {}
-
-// =============================================================================
-//
-// Hook the set accessors of certain properties to this changeProperty function.
-// It takes care of history management so we can capture low-level changes, this
-// makes history stuff work out of the box.
-//
-template
-static void changeProperty (LDObjectPtr obj, T* ptr, const T& val)
-{
- long idx;
-
- if (*ptr == val)
- return;
-
- if (obj->document() != null and (idx = obj->lineNumber()) != -1)
- {
- QString before = obj->asText();
- *ptr = val;
- QString after = obj->asText();
-
- if (before != after)
- {
- obj->document().toStrongRef()->addToHistory (new EditHistory (idx, before, after));
- g_win->R()->compileObject (obj);
- CurrentDocument()->redoVertices();
- }
- }
- else
- *ptr = val;
-}
-
-// =============================================================================
-//
-void LDObject::setColor (LDColor const& val)
-{
- changeProperty (self(), &m_color, val);
-}
-
-// =============================================================================
-//
-const Vertex& LDObject::vertex (int i) const
-{
- return m_coords[i];
-}
-
-// =============================================================================
-//
-void LDObject::setVertex (int i, const Vertex& vert)
-{
- changeProperty (self(), &m_coords[i], vert);
-}
-
-// =============================================================================
-//
-void LDMatrixObject::setPosition (const Vertex& a)
-{
- changeProperty (self(), &m_position, a);
-}
-
-// =============================================================================
-//
-void LDMatrixObject::setTransform (const Matrix& val)
-{
- changeProperty (self(), &m_transform, val);
-}
-
-// =============================================================================
-//
-void LDObject::select()
-{
- assert (document() != null);
- document().toStrongRef()->addToSelection (self());
-
- // If this object is inverted with INVERTNEXT, pick the INVERTNEXT as well.
- /*
- LDBFCPtr invertnext;
-
- if (previousIsInvertnext (invertnext))
- invertnext->select();
- */
-}
-
-// =============================================================================
-//
-void LDObject::deselect()
-{
- assert (document() != null);
- document().toStrongRef()->removeFromSelection (self());
-
- // If this object is inverted with INVERTNEXT, deselect the INVERTNEXT as well.
- LDBFCPtr invertnext;
-
- if (previousIsInvertnext (invertnext))
- invertnext->deselect();
-}
-
-// =============================================================================
-//
-QString PreferredLicenseText()
-{
- return (cfg::UseCALicense ? CALicenseText : "");
-}
-
-// =============================================================================
-//
-LDObjectPtr LDObject::createCopy() const
-{
- LDObjectPtr copy = ParseLine (asText());
- return copy;
-}
-
-// =============================================================================
-//
-void LDSubfile::setFileInfo (const LDDocumentPtr& a)
-{
- changeProperty (self(), &m_fileInfo, a);
-
- // If it's an immediate subfile reference (i.e. this subfile belongs in an
- // explicit file), we need to pre-compile the GL polygons for the document
- // if they don't exist already.
- if (a != null and
- a->isImplicit() == false and
- a->polygonData().isEmpty())
- {
- a->initializeCachedData();
- }
-};
-
-void LDObject::getVertices (QVector& verts) const
-{
- for (int i = 0; i < numVertices(); ++i)
- verts << vertex (i);
-}
-
-void LDSubfile::getVertices (QVector& verts) const
-{
- verts << fileInfo()->inlineVertices();
-}