src/ldObject.cpp

branch
projects
changeset 935
8d98ee0dc917
parent 931
85080f7a1c20
child 941
f895379d7fab
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ldObject.cpp	Tue Mar 03 22:29:27 2015 +0200
@@ -0,0 +1,925 @@
+/*
+ *  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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#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<long, LDObjectWeakPtr>	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<LDTrianglePtr> LDQuad::splitToTriangles()
+{
+	// Create the two triangles based on this quadrilateral:
+	// 0---3       0---3    3
+	// |   |       |  /    /|
+	// |   |  ==>  | /    / |
+	// |   |       |/    /  |
+	// 1---2       1    1---2
+	LDTrianglePtr tri1 (LDSpawn<LDTriangle> (vertex (0), vertex (1), vertex (3)));
+	LDTrianglePtr tri2 (LDSpawn<LDTriangle> (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<LDSubfile> (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<LDPolygon> LDSubfile::inlinePolygons()
+{
+	QList<LDPolygon> 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<LDBFC>()->statement() == BFCStatement::InvertNext)
+	{
+		ptr = prev.staticCast<LDBFC>();
+		return true;
+	}
+
+	return false;
+}
+
+// =============================================================================
+//
+void LDObject::move (Vertex vect)
+{
+	if (hasMatrix())
+	{
+		LDMatrixObjectPtr mo = self().toStrongRef().dynamicCast<LDMatrixObject>();
+		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<LDComment>();
+		case OBJ_BFC:			return LDSpawn<LDBFC>();
+		case OBJ_Line:			return LDSpawn<LDLine>();
+		case OBJ_CondLine:		return LDSpawn<LDCondLine>();
+		case OBJ_Subfile:		return LDSpawn<LDSubfile>();
+		case OBJ_Triangle:		return LDSpawn<LDTriangle>();
+		case OBJ_Quad:			return LDSpawn<LDQuad>();
+		case OBJ_Empty:			return LDSpawn<LDEmpty>();
+		case OBJ_Error:			return LDSpawn<LDError>();
+		case OBJ_Overlay:		return LDSpawn<LDOverlay>();
+		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<LDBFC>();
+
+		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<LDBFC> (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<LDLine>());
+
+	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<typename T>
+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<Vertex>& verts) const
+{
+	for (int i = 0; i < numVertices(); ++i)
+		verts << vertex (i);
+}
+
+void LDSubfile::getVertices (QVector<Vertex>& verts) const
+{
+	verts << fileInfo()->inlineVertices();
+}

mercurial