Major refactoring

Fri, 04 Mar 2022 11:37:50 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Fri, 04 Mar 2022 11:37:50 +0200
changeset 152
03f8e6d42e13
parent 151
e628fc2e0c72
child 153
2f79053c2e9a

Major refactoring
- Model now just stores objects
- Document contains business logic
- Model::EditContext is now ModelEditor, no longer a nested class

CMakeLists.txt file | annotate | diff | comparison | revisions
src/document.cpp file | annotate | diff | comparison | revisions
src/document.h file | annotate | diff | comparison | revisions
src/documentmanager.cpp file | annotate | diff | comparison | revisions
src/edithistory.cpp file | annotate | diff | comparison | revisions
src/edithistory.h file | annotate | diff | comparison | revisions
src/model.cpp file | annotate | diff | comparison | revisions
src/model.h file | annotate | diff | comparison | revisions
src/modeleditcontext.cpp file | annotate | diff | comparison | revisions
src/modeleditcontext.h file | annotate | diff | comparison | revisions
src/parser.cpp file | annotate | diff | comparison | revisions
src/parser.h file | annotate | diff | comparison | revisions
src/polygoncache.cpp file | annotate | diff | comparison | revisions
src/tools/basetool.cpp file | annotate | diff | comparison | revisions
src/tools/basetool.h file | annotate | diff | comparison | revisions
src/tools/drawtool.cpp file | annotate | diff | comparison | revisions
src/tools/drawtool.h file | annotate | diff | comparison | revisions
src/tools/selecttool.cpp file | annotate | diff | comparison | revisions
src/tools/selecttool.h file | annotate | diff | comparison | revisions
src/tools/transformtool.cpp file | annotate | diff | comparison | revisions
src/tools/transformtool.h file | annotate | diff | comparison | revisions
src/ui/objecteditor.cpp file | annotate | diff | comparison | revisions
src/ui/objecteditor.h file | annotate | diff | comparison | revisions
src/ui/polygonobjecteditor.cpp file | annotate | diff | comparison | revisions
src/ui/polygonobjecteditor.h file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Thu Mar 03 21:13:16 2022 +0200
+++ b/CMakeLists.txt	Fri Mar 04 11:37:50 2022 +0200
@@ -22,6 +22,7 @@
 source_group("1 Foundation code" REGULAR_EXPRESSION "src/.+\\.(cpp|h|ui)")
 source_group("4 OpenGL renderer" REGULAR_EXPRESSION "src/gl/.+\\.(cpp|h|ui)")
 source_group("5 LDraw line types" REGULAR_EXPRESSION "src/linetypes/.+\\.(cpp|h|ui)")
+source_group("5.1 LDraw algorithms" REGULAR_EXPRESSION "src/ldrawalgorithm.(cpp|h|ui)")
 source_group("3.2 Widgets" REGULAR_EXPRESSION "src/(ui|widgets)/.+\\.(cpp|h|ui)")
 source_group("3.1 Settings editor" REGULAR_EXPRESSION "src/settingseditor/.+\\.(cpp|h|ui)")
 source_group("3 User interface" REGULAR_EXPRESSION "src/(mainwindow|document|documentmanager|uiutilities)\\.(cpp|h|ui)")
@@ -35,6 +36,7 @@
 	src/edithistory.cpp
 	src/geometry.cpp
 	src/header.cpp
+	src/ldrawalgorithm.cpp
 	src/libraries.cpp
 	src/invert.cpp
 	src/main.cpp
@@ -89,6 +91,7 @@
 	src/geometry.h
 	src/header.h
 	src/invert.h
+	src/ldrawalgorithm.h
 	src/libraries.h
 	src/main.h
 	src/mainwindow.h
--- a/src/document.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/document.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -132,9 +132,13 @@
 	this->renderer->setOverpaintCallback(fn);
 }
 
-Model::EditContext Document::editModel()
+std::unique_ptr<ModelEditor> Document::editModel()
 {
-	return this->model->edit();
+	std::unique_ptr<ModelEditor> editorPointer = std::make_unique<ModelEditor>(*this->model);
+	connect(editorPointer.get(), &ModelEditor::objectModified, [&](int position){
+		this->model->emitDataChangedSignal(position);
+	});
+	return editorPointer;
 }
 
 void Document::applyToVertices(VertexMap::ApplyFunction fn) const
@@ -154,9 +158,9 @@
 {
 	this->tools.clear();
 	this->tools.reserve(3);
-	this->tools.push_back(new SelectTool{this->model, this});
-	this->tools.push_back(new DrawTool{this->model, this});
-	this->tools.push_back(new TransformTool{this->model, this});
+	this->tools.push_back(new SelectTool{this});
+	this->tools.push_back(new DrawTool{this});
+	this->tools.push_back(new TransformTool{this});
 	for (BaseTool* const toolInstance : this->tools)
 	{
 		QAction* action = new QAction{toolInstance->name(), this};
@@ -228,3 +232,8 @@
 {
 	this->renderer->adjustGridToView();
 }
+
+const Model &Document::getModel()
+{
+	return *this->model;
+}
--- a/src/document.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/document.h	Fri Mar 04 11:37:50 2022 +0200
@@ -44,10 +44,11 @@
 	void restoreSplitterState(const QByteArray& state);
 	void setRenderPreferences(const gl::RenderPreferences& newPreferences);
 	void setCanvasOverpaintCallback(Canvas::OverpaintCallback fn);
-	Model::EditContext editModel();
+	std::unique_ptr<ModelEditor> editModel();
 	void applyToVertices(VertexMap::ApplyFunction fn) const;
 	void handleKeyPress(QKeyEvent* event);
 	void adjustGridToView();
+	const Model& getModel();
 Q_SIGNALS:
 	void newStatusText(const QString& newStatusText);
 	void splitterChanged();
--- a/src/documentmanager.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/documentmanager.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -136,9 +136,8 @@
 	file.open(QFile::ReadOnly | QFile::Text);
 	std::unique_ptr<Model> newModel = std::make_unique<Model>(this);
 	QTextStream textStream{&file};
-	Model::EditContext editor = newModel->edit();
 	Parser parser{file};
-	parser.parseBody(editor);
+	parser.parseBody(*newModel);
 	std::optional<ModelId> result;
 	if (file.error() == QFile::NoError)
 	{
--- a/src/edithistory.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/edithistory.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -24,32 +24,32 @@
 	
 }
 
-void InsertHistoryEntry::undo(Model::EditContext &editContext)
+void InsertHistoryEntry::undo(ModelEditor &editContext)
 {
 	editContext.remove(this->position);
 }
 
-void InsertHistoryEntry::redo(Model::EditContext &editContext)
+void InsertHistoryEntry::redo(ModelEditor &editContext)
 {
 	
 }
 
-void DeleteHistoryEntry::undo(Model::EditContext &editContext)
+void DeleteHistoryEntry::undo(ModelEditor &editContext)
 {
 	static_cast<InsertHistoryEntry*>(this)->redo(editContext);
 }
 
-void DeleteHistoryEntry::redo(Model::EditContext &editContext)
+void DeleteHistoryEntry::redo(ModelEditor &editContext)
 {
 	static_cast<InsertHistoryEntry*>(this)->undo(editContext);
 }
 
-void EditHistoryEntry::undo(Model::EditContext &editContext)
+void EditHistoryEntry::undo(ModelEditor &editContext)
 {
 	
 }
 
-void EditHistoryEntry::redo(Model::EditContext &editContext)
+void EditHistoryEntry::redo(ModelEditor &editContext)
 {
 	
 }
--- a/src/edithistory.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/edithistory.h	Fri Mar 04 11:37:50 2022 +0200
@@ -20,11 +20,13 @@
 #include "main.h"
 #include "model.h"
 
+class ModelEditor;
+
 class AbstractHistoryEntry
 {
 public:
-	virtual void undo(Model::EditContext& editContext) = 0;
-	virtual void redo(Model::EditContext& editContext) = 0;
+	virtual void undo(ModelEditor& editContext) = 0;
+	virtual void redo(ModelEditor& editContext) = 0;
 };
 
 class InsertHistoryEntry : public AbstractHistoryEntry
@@ -33,8 +35,8 @@
 	InsertHistoryEntry(int position, const QByteArray& state) :
 		position{position},
 		state{state} {}
-	void undo(Model::EditContext& editContext) override;
-	void redo(Model::EditContext& editContext) override;
+	void undo(ModelEditor& editContext) override;
+	void redo(ModelEditor& editContext) override;
 protected:
 	int position;
 	QByteArray state;
@@ -43,8 +45,8 @@
 class DeleteHistoryEntry : public InsertHistoryEntry
 {
 public:
-	void undo(Model::EditContext& editContext) override;
-	void redo(Model::EditContext& editContext) override;
+	void undo(ModelEditor& editContext) override;
+	void redo(ModelEditor& editContext) override;
 };
 
 class EditHistoryEntry : public AbstractHistoryEntry
@@ -54,8 +56,8 @@
 		position{position},
 		stateBefore{stateBefore},
 		stateAfter{stateAfter} {}
-	void undo(Model::EditContext& editContext) override;
-	void redo(Model::EditContext& editContext) override;
+	void undo(ModelEditor& editContext) override;
+	void redo(ModelEditor& editContext) override;
 private:
 	int position;
 	QByteArray stateBefore;
--- a/src/model.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/model.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -60,17 +60,6 @@
 }
 
 /**
- * @brief Obtains an editing context for this model.
- * Editing contexts are used to perform modifications to the model.
- * @return editing context
- */
-Model::EditContext Model::edit()
-{
-	this->editCounter += 1;
-	return {*this};
-}
-
-/**
  * @brief @overload QAbstractListModel::rowCount
  * @return size
  */
@@ -158,34 +147,17 @@
 #endif
 
 /**
- * @brief Called by the editing context to signal to the model that editing is done.
- */
-void Model::editFinished()
-{
-	this->editCounter -= 1;
-}
-
-/**
- * @brief Called by the editing context to indicate that the specified object has been modified.
- * @param id ID of the object that has been modified
- */
-void Model::objectModified(ldraw::id_t id)
-{
-	const QModelIndex index = this->find(id);
-	Q_EMIT this->dataChanged(index, index);
-}
-
-/**
  * @brief Adds the given object into the model.
  * @param object r-value reference to the object
  */
-void Model::append(ModelObjectPointer&& object)
+ldraw::id_t Model::append(ModelObjectPointer&& object)
 {
 	const int position = static_cast<int>(this->body.size());
 	Q_EMIT this->beginInsertRows({}, position, position);
 	this->body.push_back(std::move(object));
 	Q_EMIT this->endInsertRows();
-	Q_EMIT this->objectAdded(position);
+	this->objectsById[object->id] = object.get();
+	return object->id;
 }
 
 /**
@@ -197,13 +169,16 @@
 	if (position >= 0 and position < signed_cast(this->body.size()))
 	{
 		Q_EMIT this->beginRemoveRows({}, position, position);
-		const ldraw::id_t id = this->body[position]->id;
 		this->body.erase(std::begin(this->body) + position);
 		Q_EMIT this->endRemoveRows();
-		Q_EMIT this->objectRemoved(id);
 	}
 }
 
+void Model::emitDataChangedSignal(int position)
+{
+	Q_EMIT this->dataChanged(this->index(position), this->index(position));
+}
+
 /**
  * @brief Gets the object pointer at the specified position
  * @param index Position of the object
@@ -279,32 +254,3 @@
 		out << object->toLDrawCode() << "\r\n";
 	});
 }
-
-/**
- * @brief Modifies the !LDRAW_ORG line so that it becomes unofficial
- */
-void makeUnofficial(Model& model)
-{
-	if (model.size() >= 4)
-	{
-		const ldraw::Object* ldrawOrgLine = model[3];
-		if (isA<ldraw::MetaCommand>(ldrawOrgLine))
-		{
-			const QString& body = ldrawOrgLine->getProperty<ldraw::Property::Text>();
-			if (body.startsWith("!LDRAW_ORG ") and not body.startsWith("!LDRAW_ORG Unofficial_"))
-			{
-				// Add Unofficial_ to part type
-				QStringList tokens = body.split(" ");
-				tokens[1] = "Unofficial_" + tokens[1];
-				// Remove the UPDATE tag if it's there
-				if (tokens.size() >= 4 && tokens[2] == "UPDATE")
-				{
-					tokens.removeAt(3);
-					tokens.removeAt(2);
-				}
-				Model::EditContext editor = model.edit();
-				editor.setObjectProperty<ldraw::Property::Text>(ldrawOrgLine->id, tokens.join(" "));
-			}
-		}
-	}
-}
--- a/src/model.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/model.h	Fri Mar 04 11:37:50 2022 +0200
@@ -34,13 +34,11 @@
 {
 	Q_OBJECT
 public:
-	class EditContext;
 	Model(QObject* parent = nullptr);
 	Model(const Model&) = delete;
 
 	int size() const;
 	ldraw::id_t at(int index) const;
-	EditContext edit();
 	int rowCount(const QModelIndex&) const override;
 	QVariant data(const QModelIndex& index, int role) const override;
 	ldraw::Object* findObjectById(const ldraw::id_t id);
@@ -59,30 +57,20 @@
 	Get2Result<R> get2(ldraw::Id<R> id) const;
 	ldraw::Object* operator[](int index);
 	const ldraw::Object* operator[](int index) const;
-Q_SIGNALS:
-	void objectAdded(int position);
-	void objectModified(int position);
-	void objectRemoved(ldraw::id_t id);
-private:
 	using ModelObjectPointer = std::unique_ptr<ldraw::Object>;
 	template<typename T, typename... Args>
 	ldraw::Id<T> append(Args&&... args);
-	void append(ModelObjectPointer&& object);
+	ldraw::id_t append(ModelObjectPointer&& object);
 	template<typename T, typename... Args>
 	ldraw::Id<T> insert(std::size_t position, Args&&... args);
 	void remove(int position);
-	void editFinished();
-	void objectModified(ldraw::id_t id);
+	void emitDataChangedSignal(int position);
+private:
 	bool modified = false;
 	std::vector<ModelObjectPointer> body;
 	std::map<ldraw::id_t, ldraw::Object*> objectsById;
-	/**
-	 * @brief Amount of model edit contexts active
-	 */
-	int editCounter = 0;
 };
 
-void makeUnofficial(Model& model);
 void save(const Model& model, QIODevice *device);
 
 /**
@@ -112,7 +100,6 @@
 	this->body.push_back(std::make_unique<T>(args...));
 	ldraw::Object* pointer = this->body.back().get();
 	this->objectsById[pointer->id] = pointer;
-	Q_EMIT objectAdded(static_cast<int>(this->body.size() - 1));
 	Q_EMIT endInsertRows();
 	return ldraw::Id<T>{pointer->id.value};
 }
@@ -124,7 +111,6 @@
 	this->body.insert(std::begin(this->body) + position, std::make_unique<T>(args...));
 	ldraw::Object* pointer = this->body[position].get();
 	this->objectsById[pointer->id] = pointer;
-	Q_EMIT objectAdded(static_cast<int>(position));
 	Q_EMIT endInsertRows();
 	return ldraw::Id<T>{pointer->id.value};
 }
--- a/src/modeleditcontext.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/modeleditcontext.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -20,40 +20,42 @@
 #include "linetypes/triangle.h"
 #include "linetypes/quadrilateral.h"
 
-Model::EditContext::EditContext(Model& model) :
+ModelEditor::ModelEditor(Model& model) :
 	storedModel{model}
 {
 }
 
-Model::EditContext::~EditContext()
+ModelEditor::~ModelEditor()
 {
 	for (ldraw::id_t id : this->modifiedObjects)
 	{
-		this->model().objectModified(id);
+		const QModelIndex index = this->model().find(id);
+		if (index.isValid())
+		{
+			Q_EMIT this->objectModified(index.row());
+		}
 	}
-	this->model().editFinished();
 }
 
-ldraw::id_t Model::EditContext::append(std::unique_ptr<ldraw::Object>&& object)
+ldraw::id_t ModelEditor::append(std::unique_ptr<ldraw::Object>&& object)
 {
-	const ldraw::id_t id = object->id;
-	this->model().objectsById[id] = object.get();
-	this->model().append(std::move(object));
-	return id;
+	this->storedModel.append(std::move(object));
+	Q_EMIT this->objectAdded(this->model().size() - 1);
+	return object->id;
 }
 
-void Model::EditContext::remove(int position)
+void ModelEditor::remove(int position)
 {
-	this->model().remove(position);
+	this->storedModel.remove(position);
 }
 
-auto Model::EditContext::setObjectProperty(
+auto ModelEditor::setObjectProperty(
 	const ldraw::id_t id,
 	const ldraw::Property property,
 	const QVariant& value)
 	-> ldraw::Object::SetPropertyResult
 {
-	ldraw::Object* const object = this->model().findObjectById(id);
+	ldraw::Object* const object = this->storedModel.findObjectById(id);
 	if (object != nullptr)
 	{
 		const ldraw::Object::SetPropertyResult result = object->setProperty(ldraw::PropertyKeyValue{property, value});
@@ -66,9 +68,9 @@
 	}
 }
 
-void Model::EditContext::setObjectPoint(ldraw::id_t id, int pointId, const glm::vec3& value)
+void ModelEditor::setObjectPoint(ldraw::id_t id, int pointId, const glm::vec3& value)
 {
-	ldraw::Object* object = this->model().findObjectById(id);
+	ldraw::Object* object = this->storedModel.findObjectById(id);
 	if (object != nullptr)
 	{
 		object->setProperty(ldraw::PropertyKeyValue{ldraw::pointProperty(pointId), QVariant::fromValue(value)});
@@ -76,53 +78,8 @@
 	}
 }
 
-void Model::EditContext::invertObject(ldraw::id_t id)
-{
-	auto it = this->model().objectsById.find(id);
-	if (it != this->model().objectsById.end())
-	{
-		ldraw::Object* object = it->second;
-		object->invert();
-	}
-}
-
-Model& Model::EditContext::model()
+const Model &ModelEditor::model()
 {
 	return this->storedModel;
 }
 
-static std::array<geom::Triangle, 2> splitTriangles(ldraw::Diagonal diagonal, const std::array<glm::vec3, 4>& points)
-{
-	std::array<geom::Triangle, 2> result;
-	switch (diagonal)
-	{
-	case ldraw::Diagonal::Diagonal_13:
-		result = {geom::Triangle{points[0], points[1], points[2]}, {points[0], points[2], points[3]}};
-		break;
-	case ldraw::Diagonal::Diagonal_24:
-		result = {geom::Triangle{points[0], points[1], points[3]}, {points[1], points[2], points[3]}};
-		break;
-	}
-	return result;
-}
-
-auto ldraw::splitQuadrilateral(
-	Model::EditContext& editor,
-	ldraw::quadrilateralid_t quadrilateral_id,
-	ldraw::Diagonal splitType
-) -> std::optional<std::pair<ldraw::triangleid_t, ldraw::triangleid_t>>
-{
-	std::optional<std::pair<ldraw::triangleid_t, ldraw::triangleid_t>> result;
-	const auto resolved = editor.model().get2(quadrilateral_id);
-	if (resolved.object != nullptr)
-	{
-		const ldraw::Color color = resolved.object->colorIndex;
-		const std::array<geom::Triangle, 2> split = splitTriangles(splitType, resolved.object->points);
-		const int position = resolved.index.row();
-		editor.remove(position);
-		result = std::make_pair(
-			editor.insert<ldraw::Triangle>(position, split[0].points, color),
-			editor.insert<ldraw::Triangle>(position, split[1].points, color));
-	}
-	return result;
-}
--- a/src/modeleditcontext.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/modeleditcontext.h	Fri Mar 04 11:37:50 2022 +0200
@@ -19,10 +19,16 @@
 #pragma once
 #include "model.h"
 
-class Model::EditContext
+/**
+ * @brief Provides an interface for editing a model such that signals are emitted for each edit done.
+ * User edits to models should always be done through this class.
+ */
+class ModelEditor : public QObject
 {
+	Q_OBJECT
 public:
-	~EditContext();
+	ModelEditor(Model& model);
+	~ModelEditor();
 	template<typename T, typename... Args>
 	ldraw::Id<T> append(Args&&... args);
 	ldraw::id_t append(std::unique_ptr<ldraw::Object>&& object);
@@ -34,51 +40,59 @@
 	auto setObjectProperty(ldraw::id_t id, ldraw::Property property, const QVariant& value)
 		-> ldraw::Object::SetPropertyResult;
 	void setObjectPoint(ldraw::id_t id, int pointId, const glm::vec3& value);
-	void invertObject(ldraw::id_t id);
-	Model& model();
+	template<typename T, typename Fn>
+	bool modifyObject(ldraw::Id<T> id, Fn&& function);
+	template<typename T, typename Fn>
+	bool modifyObjectAt(int position, Fn&& function);
+	const Model& model();
+Q_SIGNALS:
+	void objectAdded(int position);
+	void objectModified(int position);
+	void objectRemoved(ldraw::id_t id);
 private:
-	EditContext(Model& model);
-	friend class Model;
 	QSet<ldraw::id_t> modifiedObjects;
 	Model& storedModel;
 };
 
 template<ldraw::Property Property>
-void Model::EditContext::setObjectProperty(const ldraw::id_t id, const ldraw::PropertyType<Property>& value)
+void ModelEditor::setObjectProperty(const ldraw::id_t id, const ldraw::PropertyType<Property>& value)
 {
-	ldraw::Object* object = this->model().findObjectById(id);
-	if (object != nullptr)
-	{
+	this->modifyObject(id, [&](ldraw::Object* object){
 		object->setProperty<Property>(value);
-		modifiedObjects.insert(id);
-	}
+	});
 }
 
 template<typename T, typename... Args>
-ldraw::Id<T> Model::EditContext::append(Args&&... args)
+ldraw::Id<T> ModelEditor::append(Args&&... args)
 {
 	return this->storedModel.append<T>(args...);
 }
 
 template<typename T, typename... Args>
-ldraw::Id<T> Model::EditContext::insert(int position, Args&&... args)
+ldraw::Id<T> ModelEditor::insert(int position, Args&&... args)
 {
 	return this->storedModel.insert<T>(position, args...);
 }
 
-namespace ldraw
+template<typename T, typename Fn>
+bool ModelEditor::modifyObject(ldraw::Id<T> id, Fn&& function)
 {
-	/// Determines how quadrilaterals are split into triangles
-	enum class Diagonal
+	QModelIndex index = this->model().find(id);
+	return this->modifyObjectAt<T>(index.row(), function);
+}
+
+template<typename T, typename Fn>
+bool ModelEditor::modifyObjectAt(int position, Fn&& function)
+{
+	if (position >= 0 and position < this->model().size())
 	{
-		Diagonal_13,
-		Diagonal_24
-	};
-
-	// Splits the specified quadrilateral into triangles.
-	// If it is not a quadrilateral then no action is performed
-	auto splitQuadrilateral(Model::EditContext& editor,
-		quadrilateralid_t quadrilateral_id,
-		Diagonal splitType = Diagonal::Diagonal_13
-	) -> std::optional<std::pair<triangleid_t, triangleid_t>>;
-}
+		T* object = dynamic_cast<T*>(this->storedModel[position]);
+		if (object != nullptr)
+		{
+			Q_EMIT this->objectModified(position);
+			function(object);
+			return true;
+		}
+	}
+	return false;
+}
\ No newline at end of file
--- a/src/parser.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/parser.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -233,7 +233,7 @@
  * @brief Parses the model body into the given model.
  * @param editor Handle to model edit context
  */
-void Parser::parseBody(Model::EditContext& editor)
+void Parser::parseBody(Model& model)
 {
 	bool invertNext = false;
 	while (not this->device.atEnd())
@@ -249,10 +249,10 @@
 			continue;
 		}
 		std::unique_ptr<ldraw::Object> object = parseFromString(line);
-		auto id = editor.append(std::move(object));
+		model.append(std::move(object));
 		if (invertNext)
 		{
-			editor.invertObject(id);
+			model[model.size() - 1]->invert();
 		}
 		invertNext = false;
 	}
--- a/src/parser.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/parser.h	Fri Mar 04 11:37:50 2022 +0200
@@ -30,7 +30,7 @@
 	enum { EndOfModel = -1 };
 	Parser(QIODevice& device, QObject* parent = nullptr);
 	LDHeader parseHeader(Winding& winding);
-	void parseBody(Model::EditContext& editor);
+	void parseBody(Model &model);
 	static std::unique_ptr<ldraw::Object> parseFromString(QString line);
 private:
 	enum HeaderParseResult {ParseSuccess, ParseFailure, StopParsing};
--- a/src/polygoncache.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/polygoncache.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -6,8 +6,8 @@
 {
 	const auto mark = [this](){ this->needRecache = true; };
 	connect(model, &Model::dataChanged, mark);
-	connect(model, &Model::objectAdded, mark);
-	connect(model, &Model::objectRemoved, mark);
+	connect(model, &Model::rowsInserted, mark);
+	connect(model, &Model::rowsRemoved, mark);
 }
 
 /**
--- a/src/tools/basetool.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/tools/basetool.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -1,5 +1,9 @@
 #include "basetool.h"
+#include "document.h"
 
-BaseTool::BaseTool(Model*, QWidget *parent) :
-	QObject{parent},
-	parentWidget{parent} {}
+BaseTool::BaseTool(Document *document) :
+	QObject{document},
+	parentWidget{document},
+	document{document}
+{
+}
--- a/src/tools/basetool.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/tools/basetool.h	Fri Mar 04 11:37:50 2022 +0200
@@ -10,7 +10,7 @@
 	Q_OBJECT
 
 public:
-	BaseTool(Model* model, QWidget* parent = nullptr);
+	BaseTool(Document* document);
 
 	virtual QString name() const = 0;
 	virtual QString toolTip() const = 0;
@@ -24,5 +24,6 @@
 	virtual void overpaint(Canvas*, QPainter*) const {}
 protected:
 	QWidget* const parentWidget;
+	Document* const document;
 };
 
--- a/src/tools/drawtool.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/tools/drawtool.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -12,8 +12,10 @@
 static const QBrush polygonBrush = {QColor{64, 255, 128, 192}};
 static const QBrush badPolygonBrush = {QColor{255, 96, 96, 192}};
 
-DrawTool::DrawTool(Model *model, QWidget *parent) :
-	BaseTool{model, parent} {}
+DrawTool::DrawTool(Document* document) :
+	BaseTool{document}
+{
+}
 
 QString DrawTool::name() const
 {
@@ -142,17 +144,17 @@
 {
 	if (this->polygon.size() >= 2 and this->polygon.size() <= 4)
 	{
-		Model::EditContext edit = document->editModel();
+		std::unique_ptr<ModelEditor> modelEditor = document->editModel();
 		switch (this->polygon.size())
 		{
 		case 2:
-			edit.append<ldraw::Edge>(vectorToArray<2>(this->polygon), ldraw::EDGE_COLOR);
+			modelEditor->append<ldraw::Edge>(vectorToArray<2>(this->polygon), ldraw::EDGE_COLOR);
 			break;
 		case 3:
-			edit.append<ldraw::Triangle>(vectorToArray<3>(this->polygon), ldraw::MAIN_COLOR);
+			modelEditor->append<ldraw::Triangle>(vectorToArray<3>(this->polygon), ldraw::MAIN_COLOR);
 			break;
 		case 4:
-			edit.append<ldraw::Quadrilateral>(vectorToArray<4>(this->polygon), ldraw::MAIN_COLOR);
+			modelEditor->append<ldraw::Quadrilateral>(vectorToArray<4>(this->polygon), ldraw::MAIN_COLOR);
 			break;
 		}
 	}
--- a/src/tools/drawtool.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/tools/drawtool.h	Fri Mar 04 11:37:50 2022 +0200
@@ -6,7 +6,7 @@
 	Q_OBJECT
 
 public:
-	Q_INVOKABLE DrawTool(Model* model, QWidget* parent = nullptr);
+	Q_INVOKABLE DrawTool(Document* document);
 
 	QString name() const override;
 	QString toolTip() const override;
--- a/src/tools/selecttool.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/tools/selecttool.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -1,8 +1,10 @@
 #include "selecttool.h"
 
-SelectTool::SelectTool(Model* model, QWidget* parent) :
-	BaseTool{model, parent},
-	objectEditor{new ObjectEditor{model, ldraw::NULL_ID, parent}} {}
+SelectTool::SelectTool(Document* document) :
+	BaseTool{document},
+	objectEditor{new ObjectEditor{document, ldraw::NULL_ID}}
+{
+}
 
 QString SelectTool::name() const
 {
--- a/src/tools/selecttool.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/tools/selecttool.h	Fri Mar 04 11:37:50 2022 +0200
@@ -7,7 +7,7 @@
 	Q_OBJECT
 
 public:
-	Q_INVOKABLE SelectTool(Model* model, QWidget* parent = nullptr);
+	Q_INVOKABLE SelectTool(Document *document);
 	QString name() const override;
 	QString toolTip() const override;
 	bool mouseClick(Document*, Canvas*, QMouseEvent*) override;
--- a/src/tools/transformtool.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/tools/transformtool.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -3,13 +3,13 @@
 #include "modeleditcontext.h"
 #include "linetypes/object.h"
 #include "transformtool.h"
+#include "document.h"
 
-TransformTool::TransformTool(Model* model, QWidget* parent) :
-	BaseTool{model, parent},
-	model{model},
-	matrixEditor{new MatrixEditor{parent}},
-	button{new QPushButton{"Apply", parent}},
-	widget{new QWidget{parent}}
+TransformTool::TransformTool(Document* document) :
+	BaseTool{document},
+	matrixEditor{new MatrixEditor{document}},
+	button{new QPushButton{"Apply", document}},
+	widget{new QWidget{document}}
 {
 	widget->setLayout(new QHBoxLayout{widget});
 	widget->layout()->addWidget(this->matrixEditor);
@@ -39,25 +39,22 @@
 
 void TransformTool::applyButtonClicked()
 {
-	Model::EditContext editcontext = this->model->edit();
+	std::unique_ptr<ModelEditor> editor = this->document->editModel();
 	const glm::mat4 matrix = this->matrixEditor->value();
 	for (ldraw::id_t id : this->selection)
 	{
-		const ldraw::Object* object = model->get(id);
-		for (int i = 0; i < object->numPoints(); i += 1)
-		{
-			const ldraw::Property property = ldraw::pointProperty(i);
-			const glm::vec3& vec = matrix * glm::vec4{object->getPoint(i), 1};
-			editcontext.setObjectProperty(id, property, QVariant::fromValue(vec));
-		}
-		QVariant transformMatrix = object->getProperty(ldraw::Property::Transformation);
-		if (not transformMatrix.isNull())
-		{
-			editcontext.setObjectProperty(
-				id,
-				ldraw::Property::Transformation,
-				QVariant::fromValue(matrix * transformMatrix.value<glm::mat4>())
-			);
-		}
+		editor->modifyObject(id, [&](ldraw::Object* object){
+			for (int i = 0; i < object->numPoints(); i += 1)
+			{
+				const ldraw::Property property = ldraw::pointProperty(i);
+				const glm::vec3& vec = matrix * glm::vec4{object->getPoint(i), 1};
+				object->setProperty({property, QVariant::fromValue(vec)});
+			}
+			QVariant transformMatrix = object->getProperty(ldraw::Property::Transformation);
+			if (not transformMatrix.isNull())
+			{
+				object->setProperty<ldraw::Property::Transformation>(matrix * transformMatrix.value<glm::mat4>());
+			}
+		});
 	}
 }
--- a/src/tools/transformtool.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/tools/transformtool.h	Fri Mar 04 11:37:50 2022 +0200
@@ -7,14 +7,13 @@
 {
 	Q_OBJECT
 public:
-	Q_INVOKABLE TransformTool(Model *model, QWidget *parent = nullptr);
+	Q_INVOKABLE TransformTool(Document *document);
 	virtual QString name() const override;
 	virtual QString toolTip() const override;
 	void selectionChanged(const QSet<ldraw::id_t> &newSelection) override;
 	QWidget *toolWidget() override;
 private:
 	Q_SLOT void applyButtonClicked();
-	Model* const model;
 	MatrixEditor* matrixEditor;
 	QPushButton* button;
 	QWidget* widget;
--- a/src/ui/objecteditor.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/ui/objecteditor.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -1,9 +1,10 @@
 #include <QVBoxLayout>
 #include "objecteditor.h"
+#include "document.h"
 
-ObjectEditor::ObjectEditor(Model* model, const ldraw::id_t id, QWidget *parent) :
-	QWidget{parent},
-	model{model}
+ObjectEditor::ObjectEditor(Document* document, const ldraw::id_t id) :
+	QWidget{document},
+	document{document}
 {
 	this->setObjectId(id);
 	this->setLayout(new QVBoxLayout{this});
@@ -12,12 +13,12 @@
 void ObjectEditor::setObjectId(const ldraw::id_t id)
 {
 	this->objectId = id;
-	const ldraw::Object* object = this->model->get(id);
+	const ldraw::Object* object = this->document->getModel().get(id);
 	if (object != nullptr and object->numPoints() > 0)
 	{
 		if (not this->polygonEditor.has_value())
 		{
-			this->polygonEditor.emplace(this->model, id);
+			this->polygonEditor.emplace(this->document, id);
 			this->layout()->addWidget(&*this->polygonEditor);
 		}
 		else
--- a/src/ui/objecteditor.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/ui/objecteditor.h	Fri Mar 04 11:37:50 2022 +0200
@@ -4,14 +4,16 @@
 #include "../model.h"
 #include "polygonobjecteditor.h"
 
+class Document;
+
 class ObjectEditor : public QWidget
 {
 	Q_OBJECT
 public:
-	explicit ObjectEditor(Model* model, ldraw::id_t id = ldraw::NULL_ID, QWidget* parent = nullptr);
+	explicit ObjectEditor(Document* document, ldraw::id_t id = ldraw::NULL_ID);
 	void setObjectId(ldraw::id_t id);
 private:
-	Model* const model;
+	Document* const document;
 	ldraw::id_t objectId = ldraw::NULL_ID;
 	std::optional<PolygonObjectEditor> polygonEditor;
 };
--- a/src/ui/polygonobjecteditor.cpp	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/ui/polygonobjecteditor.cpp	Fri Mar 04 11:37:50 2022 +0200
@@ -1,6 +1,6 @@
 #include <QFormLayout>
 #include <QSplitter>
-#include "model.h"
+#include "document.h"
 #include "modeleditcontext.h"
 #include "widgets/vec3editor.h"
 #include "ui/polygonobjecteditor.h"
@@ -8,9 +8,9 @@
 static constexpr char INDEX_NAME[] = "_ldforge_index";
 static constexpr char LABEL_NAME[] = "_ldforge_label";
 
-PolygonObjectEditor::PolygonObjectEditor(Model* model, ldraw::id_t id, QWidget* parent) :
-	QWidget{parent},
-	model{model},
+PolygonObjectEditor::PolygonObjectEditor(Document* document, ldraw::id_t id) :
+	QWidget{document},
+	document{document},
 	storedObjectId{ldraw::NULL_ID.value}
 {
 	this->splitter.emplace(Qt::Vertical, this);
@@ -53,23 +53,27 @@
 
 void PolygonObjectEditor::setupPointWidget(int n)
 {
-	const ldraw::Object* const object = this->model->get(this->objectId());
-	const ldraw::Property property = ldraw::pointProperty(n);
-	const QVariant value = object->getProperty(property);
-	if (value.isValid())
+	const QModelIndex index = this->document->getModel().find(this->objectId());
+	if (index.isValid())
 	{
-		std::unique_ptr<Vec3Editor> editor = std::make_unique<Vec3Editor>(value.value<glm::vec3>(), this);
-		QObject::connect(editor.get(), &Vec3Editor::valueChanged, this, &PolygonObjectEditor::pointChanged);
-		editor->setProperty(INDEX_NAME, QVariant::fromValue(n));
-		editor->setProperty(LABEL_NAME, &ldraw::traits(property).name[0]);
-		this->widgets.push_back(std::move(editor));
+		const ldraw::Object* const object = this->document->getModel()[index.row()];
+		const ldraw::Property property = ldraw::pointProperty(n);
+		const QVariant value = object->getProperty(property);
+		if (value.isValid())
+		{
+			std::unique_ptr<Vec3Editor> editor = std::make_unique<Vec3Editor>(value.value<glm::vec3>(), this);
+			QObject::connect(editor.get(), &Vec3Editor::valueChanged, this, &PolygonObjectEditor::pointChanged);
+			editor->setProperty(INDEX_NAME, QVariant::fromValue(n));
+			editor->setProperty(LABEL_NAME, &ldraw::traits(property).name[0]);
+			this->widgets.push_back(std::move(editor));
+		}
 	}
 }
 
 void PolygonObjectEditor::pointChanged(const glm::vec3& value)
 {
-	Model::EditContext editcontext = this->model->edit();
+	std::unique_ptr<ModelEditor> modelEditor = this->document->editModel();
 	const int n = this->sender()->property(INDEX_NAME).toInt();
 	const ldraw::Property property = ldraw::pointProperty(n);
-	editcontext.setObjectProperty(this->objectId(), property, QVariant::fromValue(value));
+	modelEditor->setObjectProperty(this->objectId(), property, QVariant::fromValue(value));
 }
--- a/src/ui/polygonobjecteditor.h	Thu Mar 03 21:13:16 2022 +0200
+++ b/src/ui/polygonobjecteditor.h	Fri Mar 04 11:37:50 2022 +0200
@@ -4,12 +4,12 @@
 #include "main.h"
 #include "../widgets/vec3editor.h"
 
-class Model;
+class Document;
 
 class PolygonObjectEditor : public QWidget
 {
 public:
-	PolygonObjectEditor(Model* model, ldraw::id_t id, QWidget* parent = nullptr);
+	PolygonObjectEditor(Document* document, ldraw::id_t id);
 	~PolygonObjectEditor();
 	ldraw::id_t objectId() const;
 	void setObjectId(ldraw::id_t id);
@@ -17,7 +17,7 @@
 	void buildWidgets();
 	void setupPointWidget(int n);
 	Q_SLOT void pointChanged(const glm::vec3& value);
-	Model* model;
+	Document* document;
 	ldraw::id_t storedObjectId;
 	std::vector<std::unique_ptr<QWidget>> widgets;
 	std::optional<QSplitter> splitter;

mercurial