Experiment to delete the Model class and rely solely on text documents

Mon, 04 Jul 2022 19:53:13 +0300

author
Teemu Piippo <teemu.s.piippo@gmail.com>
date
Mon, 04 Jul 2022 19:53:13 +0300
changeset 333
07e65a4c6611
parent 332
ae7f7fbb9cda
child 334
083b0d8e220a
child 339
4787d05e9c89

Experiment to delete the Model class and rely solely on text documents

src/basics.h file | annotate | diff | comparison | revisions
src/documentmanager.cpp file | annotate | diff | comparison | revisions
src/documentmanager.h file | annotate | diff | comparison | revisions
src/gl/compiler.cpp file | annotate | diff | comparison | revisions
src/gl/compiler.h file | annotate | diff | comparison | revisions
src/gl/partrenderer.cpp file | annotate | diff | comparison | revisions
src/gl/partrenderer.h file | annotate | diff | comparison | revisions
src/layers/edittools.cpp file | annotate | diff | comparison | revisions
src/layers/edittools.h file | annotate | diff | comparison | revisions
src/ldrawalgorithm.cpp file | annotate | diff | comparison | revisions
src/ldrawalgorithm.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/model.cpp file | annotate | diff | comparison | revisions
src/model.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/polygoncache.h file | annotate | diff | comparison | revisions
src/vertexmap.cpp file | annotate | diff | comparison | revisions
src/vertexmap.h file | annotate | diff | comparison | revisions
--- a/src/basics.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/basics.h	Mon Jul 04 19:53:13 2022 +0300
@@ -60,6 +60,17 @@
 	constexpr auto operator<=>(const ModelId&) const = default;
 };
 
+struct ElementId
+{
+	std::int32_t value;
+	constexpr auto operator<=>(const ElementId& other) const = default;
+};
+
+constexpr auto qHash(ElementId id)
+{
+	return qHash(id.value);
+}
+
 //! \brief count the amount of elements in a basic array
 template<typename T, int N>
 constexpr int countof(T(&)[N])
--- a/src/documentmanager.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/documentmanager.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -111,6 +111,7 @@
  * @param openType rationale behind opening this file
  * @returns model id, or no value on error
  */
+#include <QPlainTextDocumentLayout>
 std::optional<ModelId> DocumentManager::openModel(
 	const QString& path,
 	QTextStream& errorStream,
@@ -119,10 +120,9 @@
 	QFile file{path};
 	const QString name = pathToName(QFileInfo{path});
 	file.open(QFile::ReadOnly | QFile::Text);
-	std::unique_ptr<Model> newModel = std::make_unique<Model>(nullptr);
-	QTextStream textStream{&file};
-	Parser parser{textStream};
-	parser.parseBody(*newModel);
+	std::unique_ptr<QTextDocument> newModel = std::make_unique<QTextDocument>(nullptr);
+	newModel->setDocumentLayout(new QPlainTextDocumentLayout{newModel.get()});
+	newModel->setPlainText(file.readAll());
 	std::optional<ModelId> result;
 	if (file.error() == QFile::NoError)
 	{
@@ -195,8 +195,7 @@
 		QSaveFile file{info->path};
 		file.setDirectWriteFallback(true);
 		if (file.open(QSaveFile::WriteOnly)) {
-			QTextStream stream{&file};
-			::save(*info->model.get(), &stream);
+			file.write(info->model->toPlainText().toUtf8());
 			const bool commitSucceeded = file.commit();
 			if (not commitSucceeded) {
 				errors << QObject::tr("Could not save: %1").arg(file.errorString());
@@ -332,10 +331,7 @@
 				}
 			}
 		};
-		QObject::connect(model, &Model::dataChanged, modelModified);
-		QObject::connect(model, &Model::rowsInserted, modelModified);
-		QObject::connect(model, &Model::rowsRemoved, modelModified);
-		QObject::connect(model, &Model::modelReset, modelModified);
+		QObject::connect(model, &QTextDocument::contentsChanged, modelModified);
 	}
 }
 
@@ -359,9 +355,12 @@
 static std::set<QString> referenceNames(const Model* model)
 {
 	std::set<QString> result;
-	iterate<Colored<SubfileReference>>(*model, [&result](const SubfileReference& ref){
-		result.insert(ref.name);
-	});
+	for (const QString& line : model->toPlainText().split("\n")) {
+		const opt<ParsedLine> parsed = parse(line);
+		if (parsed.has_value() and std::holds_alternative<LineType1>(*parsed)) {
+			result.insert(std::get<LineType1>(*parsed).value.name);
+		}
+	}
 	return result;
 }
 
--- a/src/documentmanager.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/documentmanager.h	Mon Jul 04 19:53:13 2022 +0300
@@ -35,7 +35,7 @@
 public:
 	struct ModelInfo
 	{
-		std::unique_ptr<Model> model;
+		std::unique_ptr<QTextDocument> model;
 		ModelId id;
 		QString path;
 		OpenType opentype;
--- a/src/gl/compiler.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/gl/compiler.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -295,8 +295,8 @@
 			vertex.position = point;
 			vertex.normal = normal;
 			vertex.color = glm::vec4{color.redF(), color.greenF(), color.blueF(), color.alphaF()};
-			vertex.id = polygon.id.value;
-			vertex.pickcolor = idToColor(polygon.id.value);
+			vertex.id = polygon.linenumber;
+			vertex.pickcolor = idToColor(polygon.linenumber);
 		});
 	});
 	for (gl::ModelShaders::ShaderObject& shader : shaders->shaderObjects)
@@ -309,13 +309,12 @@
 	}
 }
 
-ElementId gl::idFromUcharColor(const std::array<GLubyte, 3>& data)
+int gl::idFromUcharColor(const std::array<GLubyte, 3>& data)
 {
-	return {
+	return
 		static_cast<std::int32_t>(data[0]) |
 		static_cast<std::int32_t>(data[1]) << 8 |
-		static_cast<std::int32_t>(data[2]) << 16
-	};
+		static_cast<std::int32_t>(data[2]) << 16;
 }
 
 void gl::bindModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass)
@@ -332,6 +331,7 @@
 	shaderObject.vertexArray.release();
 }
 
+/*
 void gl::setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ElementId> &ids)
 {
 	for (ModelShaders::ShaderObject& object : shaders->shaderObjects)
@@ -347,6 +347,7 @@
 		object.buffer.release();
 	}
 }
+*/
 
 std::size_t gl::vertexCount(const gl::ModelShaders* shaders, const gl::ArrayClass arrayClass)
 {
--- a/src/gl/compiler.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/gl/compiler.h	Mon Jul 04 19:53:13 2022 +0300
@@ -27,7 +27,6 @@
 #include "src/model.h"
 #include "src/gl/common.h"
 
-class Model;
 class DocumentManager;
 
 namespace gl
@@ -80,9 +79,9 @@
 	void initializeModelShaders(ModelShaders* modelShaders);
 	void bindModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass);
 	void releaseModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass);
-	void setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ElementId>& ids);
+	//void setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ElementId>& ids);
 	std::size_t vertexCount(const ModelShaders *shaders, gl::ArrayClass arrayClass);
-	ElementId idFromUcharColor(const std::array<GLubyte, 3>& data);
+	int idFromUcharColor(const std::array<GLubyte, 3>& data);
 
 	template<typename... Ts>
 	void setShaderUniform(gl::ModelShaders* shaders, const char* uniformName, Ts&&... args)
--- a/src/gl/partrenderer.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/gl/partrenderer.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -50,10 +50,7 @@
 	surfaceFormat.setSamples(8);
 	this->setFormat(surfaceFormat);
 	const auto setNeedBuild = [&]{this->needBuild = true;};
-	connect(model, &Model::rowsInserted, setNeedBuild);
-	connect(model, &Model::rowsRemoved, setNeedBuild);
-	connect(model, &Model::dataChanged, setNeedBuild);
-	connect(model, &Model::modelReset, setNeedBuild);
+	connect(model, &QTextDocument::contentsChange, setNeedBuild);
 	const auto updateLayerMvpMatrix = [this]{
 		const glm::mat4 newMvpMatrix = this->projectionMatrix * this->viewMatrix * this->modelMatrix;
 		for (RenderLayer* layer : this->activeRenderLayers) {
@@ -82,7 +79,7 @@
 	for (RenderLayer* layer : this->inactiveRenderLayers) {
 		layer->initializeGL();
 	}
-	connect(this->model, &Model::dataChanged, this, &PartRenderer::build);
+	connect(this->model, &QTextDocument::contentsChanged, this, &PartRenderer::build);
 	this->initialized = true;
 	this->modelQuaternion = glm::angleAxis(glm::radians(30.0f), glm::vec3{-1, 0, 0});
 	this->modelQuaternion *= glm::angleAxis(glm::radians(225.0f), glm::vec3{-0, 1, 0});
@@ -437,7 +434,7 @@
 		viewportVector);
 }
 
-ElementId PartRenderer::pick(QPoint where)
+int32_t PartRenderer::pick(QPoint where)
 {
 	// y is flipped, take that into account
 	where.setY(this->height() - where.y());
@@ -494,11 +491,13 @@
 	return this->highlighted;
 }
 
-void PartRenderer::setSelection(const QSet<ElementId>& selection)
+void PartRenderer::setSelection(const QSet<int32_t>& selection)
 {
+#if 0
 	Q_ASSERT(not selection.contains({0}));
 	gl::setModelShaderSelectedObjects(&this->shaders, selection);
 	this->update();
+#endif
 }
 
 glm::vec3 PartRenderer::cameraVector(const QPointF& point) const
--- a/src/gl/partrenderer.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/gl/partrenderer.h	Mon Jul 04 19:53:13 2022 +0300
@@ -44,8 +44,8 @@
 	std::optional<glm::vec3> screenToModelCoordinates(const QPointF& point, const Plane& plane) const;
 	QPointF modelToScreenCoordinates(const glm::vec3& point) const;
 	bool isDark() const;
-	ElementId pick(QPoint where);
-	void setSelection(const QSet<ElementId>& selectedIds);
+	std::int32_t pick(QPoint where);
+	void setSelection(const QSet<std::int32_t>& selectedIds);
 	glm::vec3 cameraVector(const QPointF& point) const;
 	Line<3> cameraLine(const QPointF& point) const;
 Q_SIGNALS:
--- a/src/layers/edittools.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/layers/edittools.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -292,7 +292,7 @@
 	switch(this->mode) {
 	case SelectMode:
 		if (event->button() == Qt::LeftButton) {
-			const ElementId highlighted = this->renderer->pick(event->pos());
+			const std::int32_t highlighted = this->renderer->pick(event->pos());
 			Q_EMIT this->select({highlighted}, false);
 		}
 		break;
--- a/src/layers/edittools.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/layers/edittools.h	Mon Jul 04 19:53:13 2022 +0300
@@ -62,7 +62,7 @@
 Q_SIGNALS:
 	void newStatusText(const QString& newStatusText);
 	void modelAction(const ModelAction& action);
-	void select(const QSet<ElementId>& ids, bool retain);
+	void select(const QSet<std::int32_t>& ids, bool retain);
 	void suggestCursor(const QCursor& cursor);
 protected:
 	void mvpMatrixChanged(const glm::mat4& matrix) override;
--- a/src/ldrawalgorithm.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/ldrawalgorithm.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -20,6 +20,7 @@
 std::vector<ModelAction> ldraw::makeUnofficial(const Model* model)
 {
 	std::vector<ModelAction> actions;
+#if 0
 	if (model->size() >= 4) {
 		if (const Comment* comment = std::get_if<Comment>(&(*model)[3])) {
 			const QString& body = comment->text;
@@ -41,28 +42,6 @@
 			}
 		}
 	}
+#endif
 	return actions;
 }
-
-ModelElement inverted(const ModelElement& element)
-{
-	return std::visit(overloaded{
-		[](Colored<SubfileReference> ref) -> ModelElement {
-			ref.inverted = not ref.inverted;
-			return ref;
-		},
-		[](Colored<CircularPrimitive> circ) -> ModelElement {
-			circ.inverted = not circ.inverted;
-			return circ;
-		},
-		[](Colored<Triangle> triangle) -> ModelElement {
-			std::swap(triangle.p1, triangle.p2);
-			return triangle;
-		},
-		[](Colored<Quadrilateral> quad) -> ModelElement {
-			std::swap(quad.p2, quad.p4);
-			return quad;
-		},
-		[](const ModelElement& x) { return x; }
-	}, element);
-}
--- a/src/ldrawalgorithm.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/ldrawalgorithm.h	Mon Jul 04 19:53:13 2022 +0300
@@ -58,5 +58,3 @@
 		});
 	}
 }
-
-ModelElement inverted(const ModelElement &element);
--- a/src/main.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/main.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -52,7 +52,6 @@
 	std::unique_ptr<EditTools> tools;
 	std::unique_ptr<AxesLayer> axesLayer;
 	std::unique_ptr<GridLayer> gridLayer;
-	std::unique_ptr<QTextDocument> textbuffer;
 	std::unique_ptr<QTextCursor> textcursor;
 	Model* model;
 };
@@ -484,6 +483,7 @@
 			},
 		}, action);
 		*/
+		
 	};
 	const auto restoreSettings = [&]{
 		recentlyOpenedFiles = setting<Setting::RecentFiles>();
@@ -523,15 +523,8 @@
 			data->canvas->setLayerEnabled(data->axesLayer.get(), setting<Setting::DrawAxes>());
 			data->canvas->addRenderLayer(data->gridLayer.get());
 			data->canvas->addRenderLayer(data->tools.get());
-			QString modeltext;
-			QTextStream stream{&modeltext};
-			::save(*model, &stream);
-			data->textbuffer = std::make_unique<QTextDocument>();
-			data->textbuffer->setPlainText(modeltext);
-			data->textbuffer->setDefaultFont(monospace());
-			data->textbuffer->setDocumentLayout(new QPlainTextDocumentLayout(data->textbuffer.get()));
-			new LDrawSyntaxHighlighter{data->textbuffer.get()};
-			data->textcursor = std::make_unique<QTextCursor>(data->textbuffer.get());
+			new LDrawSyntaxHighlighter{model};
+			data->textcursor = std::make_unique<QTextCursor>(model);
 			documents.setModelPayload(modelId, data);
 			QObject::connect(
 				data->tools.get(),
@@ -716,7 +709,7 @@
 		if (modelSubWindow != nullptr) {
 			if (ModelData* data = documents.findPayload<ModelData>(modelSubWindow->modelId)) {
 				checkEditingModeAction(data->tools->currentEditingMode());
-				ui.modelEdit->setDocument(data->textbuffer.get());
+				ui.modelEdit->setDocument(data->model);
 				ui.modelEdit->setTextCursor(*data->textcursor);
 			}
 		}
@@ -775,12 +768,6 @@
 		&QPlainTextEdit::textChanged,
 		[&]{
 			if (ModelData* data = currentModelData(&ui, &documents)) {
-				Model* const model = data->model;
-				model->clear();
-				QString text = ui.modelEdit->toPlainText();
-				QTextStream stream{&text};
-				Parser parser(stream);
-				parser.parseBody(*data->model);
 				documents.loadDependenciesForAllModels(libraries);
 				data->canvas->update();
 			}
--- a/src/model.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/model.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -113,9 +113,6 @@
 	return std::visit(overloaded{
 		[](const Colored<SubfileReference>& ref) {
 			QString result;
-			if (ref.inverted) {
-				result += QStringLiteral("0 BFC INVERTNEXT\r\n");
-			}
 			result += QStringLiteral("1 %1 %2 %3")
 				.arg(ref.color.index)
 				.arg(transformToString(ref.transformation))
@@ -169,50 +166,6 @@
 	}, element);
 }
 
-Model::Model(QObject *parent) :
-	QAbstractListModel{parent}
-{
-}
-
-Model::~Model()
-{
-}
-
-ElementId Model::append(const ModelElement &value)
-{
-	const std::size_t position = this->size();
-	const ElementId id = this->runningId;
-	this->runningId.value += 1;
-	const int row = narrow<int>(signed_cast(this->size()));
-	Q_EMIT this->beginInsertRows({}, row, row);
-	this->body.push_back({value, id});
-	this->positions[id] = position;
-	Q_EMIT this->endInsertRows();
-	return id;
-}
-
-const ModelElement &Model::at(std::size_t position) const
-{
-	return this->body[position].data;
-}
-
-ElementId Model::idAt(std::size_t position) const
-{
-	return this->body[position].id;
-}
-
-void Model::assignAt(std::size_t position, const ModelElement &element)
-{
-	this->body[position].data = element;
-	const QModelIndex index = this->index(narrow<int>(signed_cast(position)));
-	Q_EMIT this->dataChanged(index, index);
-}
-
-std::optional<std::size_t> Model::find(ElementId id) const
-{
-	return pointerToOptional(findInMap(this->positions, id));
-}
-
 template<typename K, typename V>
 void removeFromMap(std::map<K, V>& map, const K& key)
 {
@@ -222,72 +175,9 @@
 	}
 }
 
-void Model::remove(const std::size_t index)
-{
-	if (index <  this->body.size()) {
-		const int row = narrow<int>(signed_cast(index));
-		Q_EMIT this->beginRemoveRows({}, row, row);
-		removeFromMap(this->positions, this->body[index].id);
-		this->body.erase(this->body.begin() + row);
-		for (std::size_t i = index; i < this->body.size(); ++i) {
-			this->positions[this->body[i].id] = i;
-		}
-		Q_EMIT this->endRemoveRows();
-	}
-}
-
-int Model::rowCount(const QModelIndex &) const
-{
-	return narrow<int>(signed_cast(this->size()));
-}
-
-QVariant Model::data(const QModelIndex &index, int role) const
-{
-	const std::size_t i = unsigned_cast(index.row());
-	const ModelElement& element = this->body[i].data;
-	switch(role)
-	{
-	case Qt::DecorationRole:
-		return iconForElement(element);
-	case Qt::DisplayRole:
-		return modelElementToString(element);
-	/*
-	case Qt::ForegroundRole:
-		return object->textRepresentationForeground();
-	case Qt::BackgroundRole:
-		return object->textRepresentationBackground();
-	case Qt::FontRole:
-		return object->textRepresentationFont();
-	*/
-	default:
-		return {};
-	}
-}
-
-const ModelElement &Model::operator[](std::size_t index) const
-{
-	return this->body[index].data;
-}
-
-std::size_t Model::size() const
-{
-	return this->body.size();
-}
-
-void Model::clear()
-{
-	this->beginResetModel();
-	this->body.clear();
-	this->positions.clear();
-	this->runningId = {1};
-	this->endResetModel();
-}
-
 void save(const Model &model, QTextStream* stream)
 {
-	for (std::size_t i = 0; i < model.size(); ++i) {
-		(*stream) << modelElementToString(model[i]) << "\r\n";
-	}
+	*stream << model.toPlainText();
 }
 
 /**
@@ -296,6 +186,7 @@
  */
 void updateHeaderNameField(Model& model, const QString &name)
 {
+#if 0
 	// Update the "Name: 1234.dat" comment
 	if (model.size() >= 2) {
 		if (const Comment* nameObject = std::get_if<Comment>(&model[1])) {
@@ -304,4 +195,5 @@
 			}
 		}
 	}
+#endif
 }
--- a/src/model.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/model.h	Mon Jul 04 19:53:13 2022 +0300
@@ -22,11 +22,13 @@
 #include "src/basics.h"
 #include "src/colors.h"
 
+#include <QTextDocument>
+using Model = QTextDocument;
+
 struct SubfileReference
 {
 	QString name;
 	glm::mat4 transformation;
-	bool inverted = false;
 };
 
 template<typename T>
@@ -116,34 +118,6 @@
 
 using PolygonElement = Colored<PlainPolygonElement>;
 
-template<typename T>
-struct remove_color {};
-
-template<typename T>
-struct remove_color<Colored<T>> { using type = T; };
-
-template<typename T>
-struct remove_color<Colored<T>&> { using type = T&; };
-
-template<typename T>
-struct remove_color<const Colored<T>&> { using type = const T&; };
-
-template<typename T>
-struct remove_color<Colored<T>&&> { using type = T&&; };
-
-template<typename T>
-using remove_color_t = typename remove_color<T>::type;
-
-static_assert(std::is_same_v<remove_color_t<Colored<Triangle>>, Triangle>);
-static_assert(std::is_same_v<remove_color_t<Colored<Triangle>&>, Triangle&>);
-static_assert(std::is_same_v<remove_color_t<const Colored<Triangle>&>, const Triangle&>);
-
-template<typename T>
-constexpr remove_color_t<T&&> extract_colored(T&& x)
-{
-	return static_cast<remove_color_t<T&&>>(x);
-}
-
 template<typename Ret, typename Fn1, typename Fn2, typename Fn3, typename Fn4, typename T>
 constexpr auto visitPolygon(Fn1&& f1, Fn2&& f2, Fn3&& f3, Fn4&& f4, T&& element)
 {
@@ -193,74 +167,6 @@
 		element);
 }
 
-QString modelElementToString(const ModelElement& element);
-struct ElementId
-{
-	std::int32_t value;
-	constexpr auto operator<=>(const ElementId& other) const = default;
-};
-
-constexpr auto qHash(ElementId id)
-{
-	return qHash(id.value);
-}
-
-class Model : public QAbstractListModel
-{
-	Q_OBJECT
-	struct Entry {
-		ModelElement data;
-		ElementId id;
-	};
-	std::vector<Entry> body;
-	std::map<ElementId, std::size_t> positions;
-	ElementId runningId = {1};
-public:
-	explicit Model(QObject* parent);
-	virtual ~Model();
-	ElementId append(const ModelElement& value);
-	const ModelElement& at(std::size_t position) const;
-	ElementId idAt(std::size_t position) const;
-	void assignAt(std::size_t position, const ModelElement& element);
-	std::optional<std::size_t> find(ElementId id) const;
-	void remove(std::size_t index);
-	int rowCount(const QModelIndex&) const override;
-	QVariant data(const QModelIndex& index, int role) const override;
-	const ModelElement& operator[](std::size_t index) const;
-	std::size_t size() const;
-	void clear();
-	auto operator[](const std::size_t index) {
-		struct {
-			Model& model;
-			const std::size_t index;
-			operator const ModelElement&() {
-				return model.at(index);
-			}
-			auto& operator=(const ModelElement& newData) {
-				model.assignAt(index, newData);
-				return *this;
-			}
-			const auto* operator&() {
-				return &(this->operator const ModelElement&());
-			}
-		} result{*this, index};
-		return result;
-	}
-};
-
-void save(const Model& model, QTextStream* stream);
-void updateHeaderNameField(Model& model, const QString &name);
-
-template<typename T>
-void iterate(const Model& model, std::function<void(const T&)> fn)
-{
-	for (std::size_t i = 0; i < model.size(); ++i) {
-		if (std::holds_alternative<T>(model[i])) {
-			fn(std::get<T>(model[i]));
-		}
-	}
-}
-
 constexpr Colored<LineSegment> edge(const glm::vec3& p1, const glm::vec3& p2)
 {
 	return Colored<LineSegment>{{.p1 = p1, .p2 = p2}, EDGE_COLOR};
--- a/src/parser.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/parser.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -16,238 +16,175 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <QRegExp>
+#include <QRegularExpression>
 #include <QIODevice>
 #include "src/ldrawalgorithm.h"
 #include "src/model.h"
 #include "src/parser.h"
 
-struct BodyParseError
-{
-	QString message;
-};
+#define NUMBER_REGEX R"([+-]?(?:(?:\d+\.?\d*)|(?:\.\d+)))"
+#define SPACE_REGEX R"(\s+)"
+#define VEC3_REGEX "(" NUMBER_REGEX SPACE_REGEX NUMBER_REGEX SPACE_REGEX NUMBER_REGEX ")"
+#define TWO_VECTORS VEC3_REGEX SPACE_REGEX VEC3_REGEX
+#define THREE_VECTORS TWO_VECTORS SPACE_REGEX VEC3_REGEX
+#define FOUR_VECTORS THREE_VECTORS SPACE_REGEX VEC3_REGEX
 
-/*
- * Constructs an LDraw parser
- */
-Parser::Parser(QTextStream& stream, QObject* parent) :
-	QObject {parent},
-	stream {stream} {}
-
-/*
- * Reads a single line from the device.
- */
-QString Parser::readLine()
+static const auto& exprs()
 {
-	return this->stream.readLine().trimmed();
+	static const struct
+	{
+		QRegularExpression subfileRe{QStringLiteral(
+			R"(^\s*(1)\s+(\d+)\s+)" FOUR_VECTORS SPACE_REGEX R"(([^ ]+)\s*$)"
+		)};
+		QRegularExpression edgeRe{QStringLiteral(
+			R"(^\s*(2)\s+(\d+)\s+)" TWO_VECTORS R"(\s*$)"
+		)};
+		QRegularExpression triangleRe{QStringLiteral(
+			R"(^\s*(3)\s+(\d+)\s+)" THREE_VECTORS R"(\s*$)"
+		)};
+		QRegularExpression quadRe{QStringLiteral(
+			R"(^\s*(4)\s+(\d+)\s+)" FOUR_VECTORS R"(\s*$)"
+		)};
+		QRegularExpression cedgeRe{QStringLiteral(
+			R"(^\s*(5)\s+(\d+)\s+)" FOUR_VECTORS R"(\s*$)"
+		)};
+#if 0
+		QRegularExpression bfcRe{QStringLiteral(
+			R"(^\s*(0) (BFC (?:CERTIFY CCW|CERTIFY CW|NOCERTIFY|INVERTNEXT|CLIP|NOCLIP))\s*$)"
+		)};
+#endif
+		QRegularExpression commentRe{QStringLiteral(
+			R"(^\s*(0)\s+(.+)$)"
+		)};
+	} result;
+	return result;
+}
+template<Attribute Attrib, typename T, Attribute... Attribs>
+QString attrib(const LineType<T, Attribs...>& parsed)
+{
+	const int index = attribIndex<LineType<T, Attribs...>, Attrib>;
+	const TextRange& range = parsed.positions[index];
+	return parsed.content.mid(range.start, range.length);
 }
 
-/**
- * @brief Parses the model body into the given model.
- * @param editor Handle to model edit context
- */
-void Parser::parseBody(Model& model)
+template<Attribute X, Attribute Y, Attribute Z, typename T, Attribute... Attribs>
+glm::vec3 vectorAttrib(const LineType<T, Attribs...>& parsed)
 {
-	bool invertNext = false;
-	while (not this->stream.atEnd())
-	{
-		// Some LDraw parts such as 53588.dat can contain "BFC  INVERTNEXT" with multiple inner whitespaces.
-		// So we need to pass the string through QString::simplified to catch these cases.
-		const QString line = this->readLine().trimmed();
-		if (line == "0 BFC INVERTNEXT" or line == "0 BFC CERTIFY INVERTNEXT")
-		{
-			invertNext = true;
-			continue;
-		}
-		ModelElement element = parseLDrawLine(line);
-		if (invertNext)
-		{
-			element = inverted(element);
-		}
-		model.append(element);
-		invertNext = false;
-	}
+	return glm::vec3{
+		attrib<X>(parsed).toFloat(),
+		attrib<Y>(parsed).toFloat(),
+		attrib<Z>(parsed).toFloat(),
+	};
+}
+
+template<typename T>
+ColorIndex colorAttrib(T* parsed)
+{
+	return ColorIndex{attrib<Attribute::Color>(*parsed).toInt()};
 }
 
-static ldraw::Color colorFromString(const QString& colorString)
+opt<ParsedLine> parse(const QString& line)
 {
-	bool colorSucceeded;
-	const ldraw::Color color = {colorString.toInt(&colorSucceeded)};
-	if (colorSucceeded)
-	{
-		return color;
-	}
-	else
-	{
-		throw BodyParseError{"colour was not an integer value"};
-	}
-}
-
-static glm::vec3 vertexFromStrings(
-	const QStringList& tokens,
-	const int startingPosition)
-{
-	bool ok_x;
-	const float x = tokens[startingPosition].toFloat(&ok_x);
-	bool ok_y;
-	const float y = tokens[startingPosition + 1].toFloat(&ok_y);
-	bool ok_z;
-	const float z = tokens[startingPosition + 2].toFloat(&ok_z);
-	if (not ok_x or not ok_y or not ok_z)
-	{
-		throw BodyParseError{"vertex contained illegal co-ordinates"};
+	const auto tryRe = [&line](const QRegularExpression& re){
+		opt<QRegularExpressionMatch> result = re.match(line);
+		if (not result->hasMatch()) {
+			result.reset();
+		}
+		return result;
+	};
+	opt<ParsedLine> result;
+	//! \brief Put value into result, add matched ranges to it and return a pointer
+	const auto init = [&]<typename T>(T&& value, const QRegularExpressionMatch& match){
+		result = value;
+		T* parsed = &std::get<T>(*result);
+		for (int i = 0; i < countof(T::attributes); ++i) {
+			parsed->positions[i] = {
+				.start = match.capturedStart(i + 1),
+				.length = match.capturedLength(i + 1),
+			};
+		}
+		return parsed;
+	};
+	if (auto line1Match = tryRe(exprs().subfileRe)) {
+		LineType1* const parsed = init(LineType1{}, *line1Match);
+		parsed->value = {
+			SubfileReference{
+				.name = attrib<Attribute::Name>(*parsed),
+				.transformation = glm::mat4{
+					glm::vec4{vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed), 0},
+					glm::vec4{vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed), 0},
+					glm::vec4{vectorAttrib<Attribute::X4, Attribute::Y4, Attribute::Z4>(*parsed), 0},
+					glm::vec4{vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed), 0}
+				},
+			},
+			colorAttrib(parsed),
+		};
 	}
-	return {x, y, z};
-}
-
-static glm::mat4 matrixFromStrings(
-	const QStringList& tokens,
-	const int startingPosition,
-	const int positionStartingIndex)
-{
-	glm::mat4 result = glm::mat4{1};
-	for (int i = 0; i < 9; i += 1)
-	{
-		const int row = i / 3;
-		const int column = i % 3;
-		const int index = i + startingPosition;
-		if (index >= tokens.size())
-		{
-			throw BodyParseError{"too few tokens available"};
-		}
-		bool ok;
-		// note that glm::mat4 is column-major
-		result[column][row] = tokens[index].toFloat(&ok);
-		if (not ok)
-		{
-			throw BodyParseError{"non-numeric values for matrix"};
-		}
+	else if (auto line2Match = tryRe(exprs().edgeRe)) {
+		LineType2* const parsed = init(LineType2{}, *line2Match);
+		parsed->value = {
+			LineSegment{
+				.p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed),
+				.p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed),
+			},
+			colorAttrib(parsed),
+		};
+	}
+	else if (auto line3Match = tryRe(exprs().triangleRe)) {
+		LineType3* const parsed = init(LineType3{}, *line3Match);
+		parsed->value = {
+			Triangle{
+				.p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed),
+				.p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed),
+				.p3 = vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed),
+			},
+			colorAttrib(parsed),
+		};
 	}
-	for (int i = 0; i < 3; i += 1)
-	{
-		bool ok;
-		const auto value = tokens[i + positionStartingIndex].toFloat(&ok);
-		result[3][i] = value;
-		if (not ok)
-		{
-			throw BodyParseError{"non-numeric values for matrix"};
-		}
+	else if (auto line4Match = tryRe(exprs().quadRe)) {
+		LineType4* const parsed = init(LineType4{}, *line4Match);
+		parsed->value = {
+			Quadrilateral{
+				.p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed),
+				.p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed),
+				.p3 = vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed),
+				.p4 = vectorAttrib<Attribute::X4, Attribute::Y4, Attribute::Z4>(*parsed),
+			},
+			colorAttrib(parsed),
+		};
+	}
+	else if (auto line5Match = tryRe(exprs().cedgeRe)) {
+		LineType5* const parsed = init(LineType5{}, *line5Match);
+		parsed->value = {
+			ConditionalEdge{
+				.p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed),
+				.p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed),
+				.c1 = vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed),
+				.c2 = vectorAttrib<Attribute::X4, Attribute::Y4, Attribute::Z4>(*parsed),
+			},
+			colorAttrib(parsed),
+		};
 	}
 	return result;
 }
 
-static Comment parseType0Line(const QString& line)
-{
-	return {line.mid(1).trimmed()};
-}
-
-static ModelElement parseType1Line(const QStringList& tokens)
-{
-	constexpr int colorPosition = 1;
-	constexpr int positionPosition = 2; // 2..4
-	constexpr int transformPosition = 5; // 5..13
-	constexpr int namePosition = 14;
-	if (tokens.size() != 15)
-	{
-		throw BodyParseError{"wrong amount of tokens in a type-1 line"};
-	}
-	const ldraw::Color color = colorFromString(tokens[colorPosition]);
-	const glm::mat4 transform = matrixFromStrings(tokens, transformPosition, positionPosition);
-	const QString& name = tokens[namePosition];
-	static QRegExp re{R"((?:(\d+)\\)?(\d+)-(\d)+([a-z]+)\.dat)"};
-	if (re.exactMatch(name)) {
-		const auto p = std::find(std::begin(circularPrimitiveStems), std::end(circularPrimitiveStems), re.cap(4));
-		const unsigned int divisions = (re.cap(1).isEmpty()) ? 16 : re.cap(1).toUInt();
-		const unsigned int segments = re.cap(2).toUInt() * divisions / re.cap(3).toUInt();
-		if (p != std::end(circularPrimitiveStems)) {
-			const auto type = static_cast<CircularPrimitive::Type>(p - std::begin(circularPrimitiveStems));
-			return Colored<CircularPrimitive>{
-				CircularPrimitive{
-					.type = type,
-					.fraction = {segments, divisions},
-					.transformation = transform,
-				},
-				color,
-			};
-		}
-	}
-	return Colored<SubfileReference>{
-		{
-			.name = name,
-			.transformation = transform,
-		},
-		color,
-	};
-}
-
-template<int NumVertices>
-static auto parsePolygon(const QStringList& tokens)
-{
-	constexpr int colorPosition = 1;
-	auto vertexPosition = [](int n) { return 2 + 3*n; };
-	if (tokens.size() != 2 + 3 * NumVertices)
-	{
-		throw BodyParseError{"wrong amount of tokens"};
-	}
-	const ldraw::Color color = colorFromString(tokens[colorPosition]);
-	std::array<glm::vec3, NumVertices> vertices;
-	for (int i = 0; i < NumVertices; i += 1)
-	{
-		vertices[unsigned_cast(i)] = vertexFromStrings(tokens, vertexPosition(i));
-	}
-	return std::make_pair(vertices, color);
-}
-
-ModelElement parseLDrawLine(QString line)
-{
-	try
-	{
-		const QStringList tokens = line.simplified().split(" ");
-		if (tokens.empty() or tokens == QStringList{{""}})
-		{
-			return Empty{};
-		}
-		bool ok_code;
-		const int code = tokens[0].toInt(&ok_code);
-		if (not ok_code)
-		{
-			throw BodyParseError{QObject::tr("line type was not an integer")};
-		}
-		switch (code)
-		{
-		case 0:
-			return parseType0Line(line);
-		case 1:
-			return parseType1Line(tokens);
-		case 2:
-		{
-			const auto pair = parsePolygon<2>(tokens);
-			return Colored<LineSegment>{{pair.first[0], pair.first[1]}, pair.second};
-		}
-		case 3:
-		{
-			const auto pair = parsePolygon<3>(tokens);
-			return Colored<Triangle>{{pair.first[0], pair.first[1], pair.first[2]}, pair.second
-			};
-		}
-		case 4:
-		{
-			const auto pair = parsePolygon<4>(tokens);
-			const Quadrilateral quad{pair.first[0], pair.first[1], pair.first[2], pair.first[3]};
-			return Colored<Quadrilateral>{quad, pair.second};
-		}
-		case 5:
-		{
-			const auto pair = parsePolygon<4>(tokens);
-			const ConditionalEdge cedge{pair.first[0], pair.first[1], pair.first[2], pair.first[3]};
-			return Colored<ConditionalEdge>{cedge, pair.second};
-		}
-		default:
-			throw BodyParseError{QObject::tr("bad line type '%1'").arg(code)};
-		}
-	}
-	catch(const BodyParseError& error)
-	{
-		return ParseError{line};
+#if 0
+static QRegExp re{R"((?:(\d+)\\)?(\d+)-(\d)+([a-z]+)\.dat)"};
+if (re.exactMatch(name)) {
+	const auto p = std::find(std::begin(circularPrimitiveStems), std::end(circularPrimitiveStems), re.cap(4));
+	const unsigned int divisions = (re.cap(1).isEmpty()) ? 16 : re.cap(1).toUInt();
+	const unsigned int segments = re.cap(2).toUInt() * divisions / re.cap(3).toUInt();
+	if (p != std::end(circularPrimitiveStems)) {
+		const auto type = static_cast<CircularPrimitive::Type>(p - std::begin(circularPrimitiveStems));
+		return Colored<CircularPrimitive>{
+			CircularPrimitive{
+				.type = type,
+				.fraction = {segments, divisions},
+				.transformation = transform,
+			},
+			color,
+		};
 	}
 }
+#endif
+
--- a/src/parser.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/parser.h	Mon Jul 04 19:53:13 2022 +0300
@@ -20,15 +20,124 @@
 #include "src/basics.h"
 #include "src/model.h"
 
-class Parser : public QObject
+struct TextRange
 {
-	Q_OBJECT
-public:
-	Parser(QTextStream& stream, QObject* parent = nullptr);
-	void parseBody(Model &model);
-private:
-	QString readLine();
-	QTextStream& stream;
+	int start;
+	int length;
+};
+
+
+enum class Attribute {
+	LineType,
+	Color,
+	X1,
+	Y1,
+	Z1,
+	X2,
+	Y2,
+	Z2,
+	X3,
+	Y3,
+	Z3,
+	X4,
+	Y4,
+	Z4,
+	Name,
+	Text
+};
+
+template<typename T, Attribute... Attribs>
+struct LineType
+{
+	static constexpr Attribute attributes[] = {Attribs...};
+	QString content;
+	TextRange positions[countof(attributes)];
+	T value;
 };
 
-ModelElement parseLDrawLine(QString line);
+using LineType0 = LineType<Comment, Attribute::Text>;
+using LineType1 = LineType<Colored<SubfileReference>,
+	Attribute::LineType,
+	Attribute::Color,
+	Attribute::X1,
+	Attribute::Y1,
+	Attribute::Z1,
+	Attribute::X2,
+	Attribute::Y2,
+	Attribute::Z2,
+	Attribute::X3,
+	Attribute::Y3,
+	Attribute::Z3,
+	Attribute::X4,
+	Attribute::Y4,
+	Attribute::Z4,
+	Attribute::Name>;
+using LineType2 = LineType<Colored<LineSegment>,
+	Attribute::LineType,
+	Attribute::Color,
+	Attribute::X1,
+	Attribute::Y1,
+	Attribute::Z1,
+	Attribute::X2,
+	Attribute::Y2,
+	Attribute::Z2>;
+using LineType3 = LineType<Colored<Triangle>,
+	Attribute::LineType,
+	Attribute::Color,
+	Attribute::X1,
+	Attribute::Y1,
+	Attribute::Z1,
+	Attribute::X2,
+	Attribute::Y2,
+	Attribute::Z2,
+	Attribute::X3,
+	Attribute::Y3,
+	Attribute::Z3>;
+using LineType4 = LineType<Colored<Quadrilateral>,
+	Attribute::LineType,
+	Attribute::Color,
+	Attribute::X1,
+	Attribute::Y1,
+	Attribute::Z1,
+	Attribute::X2,
+	Attribute::Y2,
+	Attribute::Z2,
+	Attribute::X3,
+	Attribute::Y3,
+	Attribute::Z3,
+	Attribute::X4,
+	Attribute::Y4,
+	Attribute::Z4>;
+using LineType5 = LineType<Colored<ConditionalEdge>,
+	Attribute::LineType,
+	Attribute::Color,
+	Attribute::X1,
+	Attribute::Y1,
+	Attribute::Z1,
+	Attribute::X2,
+	Attribute::Y2,
+	Attribute::Z2,
+	Attribute::X3,
+	Attribute::Y3,
+	Attribute::Z3,
+	Attribute::X4,
+	Attribute::Y4,
+	Attribute::Z4>;
+
+template<typename T, Attribute Attrib>
+constexpr int findAttributeIndex()
+{
+	constexpr auto it = std::find(
+		std::begin(T::attributes),
+		std::end(T::attributes),
+		Attrib
+	);
+	static_assert(it != std::end(T::attributes), "Attribute not found");
+	return it - std::begin(T::attributes);
+}
+
+template<typename T, Attribute Attrib>
+constexpr int attribIndex = findAttributeIndex<T, Attrib>();
+static_assert(attribIndex<LineType3, Attribute::X1> == 2);
+using ParsedLine = std::variant<LineType0, LineType1, LineType2, LineType3, LineType4, LineType5>;
+opt<ParsedLine> parse(const QString& line);
--- a/src/polygoncache.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/polygoncache.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -2,6 +2,7 @@
 #include "src/documentmanager.h"
 #include "src/invert.h"
 #include "src/polygoncache.h"
+#include "src/parser.h"
 
 Model* resolve(const QString& name, const ModelId callingModelId, DocumentManager* documents)
 {
@@ -40,16 +41,26 @@
 	return a xor n_xor(rest...);
 }
 
+namespace {
+struct GetPolygonsContext
+{
+	bool invertnext = false;
+	ModelId modelId;
+	class DocumentManager* documents;
+};
+}
+
 template<typename Fn, typename Fn2>
 static void inlineSubfileReference(
 	const PolygonCache::vector_type& polygons,
 	const Colored<SubfileReference>& ref,
+	GetPolygonsContext* context,
 	Fn&& add,
 	Fn2&& reserve)
 {
 	const bool needInverting = 0
 		^ (glm::determinant(ref.transformation) < 0)
-		^ (ref.inverted);
+		^ (context->invertnext);
 	reserve(polygons.size());
 	for (const PolygonElement& cacheElement : polygons) {
 		PolygonElement polygon = transformed(cacheElement, ref.transformation);
@@ -63,42 +74,60 @@
 	}
 }
 
-Model* findDependency(const SubfileReference& ref, GetPolygonsContext* context)
+static Model* findDependency(const SubfileReference& ref, GetPolygonsContext* context)
 {
 	return context->documents->findDependencyByName(context->modelId, ref.name);
 }
 
 template<typename Fn, typename Fn2>
 static void collectPolygons(
-	const ModelElement& element,
+	const ParsedLine& element,
 	Winding& winding,
 	GetPolygonsContext* context,
 	Fn&& add,
 	Fn2&& reserve)
 {
+	bool foundinvertnext = false;
 	std::visit<void>(overloaded{
-		[&](const Colored<LineSegment>& edge) {
-			add({edge, edge.color});
-		},
-		[&](const Colored<Triangle>& triangle) {
-			add({triangle, triangle.color});
+		[&](const LineType0& line0) {
+			const QString text = line0.value.text.simplified();
+			if (text == QStringLiteral("BFC INVERTNEXT")) {
+				context->invertnext = true;
+				foundinvertnext = true;
+			}
+			else if (text == QStringLiteral("BFC CERTIFY CW")) {
+				winding = Clockwise;
+			}
+			else if (text == QStringLiteral("BFC CERTIFY CCW")) {
+				winding = Anticlockwise;
+			}
+			else if (text == QStringLiteral("BFC NOCERTIFY")) {
+				winding = NoWinding;
+			}
 		},
-		[&](const Colored<Quadrilateral>& quad) {
-			add({quad, quad.color});
+		[&](const LineType2& line2) {
+			add({line2.value, line2.value.color});
+		},
+		[&](const LineType3& line3) {
+			add({line3.value, line3.value.color});
 		},
-		[&](const Colored<ConditionalEdge>& cedge) {
-			add({cedge, cedge.color});
+		[&](const LineType4& line4) {
+			add({line4.value, line4.value.color});
 		},
-		[&add, context, &reserve](const Colored<SubfileReference>& ref) {
-			Model* const dependency = findDependency(ref, context);
+		[&](const LineType5& line5) {
+			add({line5.value, line5.value.color});
+		},
+		[&add, context, &reserve](const LineType1& line1) {
+			Model* const dependency = findDependency(line1.value, context);
 			if (PolygonCache* cache = (dependency != nullptr)
 				? findPolygonCacheForModel(dependency, context->documents)
 				: nullptr
 			) {
 				recacheIfNeeded(cache, dependency, context->documents);
-				inlineSubfileReference(cache->polygons, ref, add, reserve);
+				inlineSubfileReference(cache->polygons, line1.value, context, add, reserve);
 			}
 		},
+	#if 0
 		[&add](const Colored<CircularPrimitive>& circ) {
 			rasterize(circ, [&](const PlainPolygonElement& polygon, ColorIndex color){
 				if (color == MAIN_COLOR) {
@@ -107,19 +136,11 @@
 				add(PolygonElement{polygon, color});
 			});
 		},
-		[&winding](const Comment& comment) {
-			if (comment.text == QStringLiteral("BFC CERTIFY CW")) {
-				winding = Clockwise;
-			} 
-			else if (comment.text == QStringLiteral("BFC CERTIFY CCW")) {
-				winding = Anticlockwise;
-			}
-			else if (comment.text == QStringLiteral("BFC NOCERTIFY")) {
-				winding = NoWinding;
-			}
-		},
-		[](const ModelElement&) {}
+	#endif
 	}, element);
+	if (not foundinvertnext) {
+		context->invertnext = false;
+	}
 }
 
 static std::vector<WithId<PolygonElement>> inlinePolygons(
@@ -128,19 +149,22 @@
 {
 	Winding winding = NoWinding;
 	std::vector<WithId<PolygonElement>> result;
-	for (std::size_t i = 0; i < model->size(); i += 1)
-	{
-		const ModelElement& element = (*model)[i];
-		const ElementId id = model->idAt(i);
-		collectPolygons(element, winding, context,
-			[&result, winding, id](const PolygonElement& poly){
-				result.push_back({poly, id});
-				if (winding == Winding::Clockwise) {
-					gl::invert(result.back());
-				}
-			}, [&result](std::size_t incomingsize){
-				reserveMore(result, incomingsize);
-			});
+	int i = 0;
+	const auto add = [&result, winding, i](const PolygonElement& poly){
+		result.push_back({poly, i});
+		if (winding == Winding::Clockwise) {
+			gl::invert(result.back());
+		}
+	};
+	const auto reserve = [&result](std::size_t incomingsize){
+		reserveMore(result, incomingsize);
+	};
+	for (const QString& line : model->toPlainText().split("\n")) {
+		const opt<ParsedLine> parsedline = parse(line);
+		if (parsedline.has_value()) {
+			collectPolygons(*parsedline, winding, context, add, reserve);
+		}
+		++i;
 	}
 	return result;
 }
@@ -153,7 +177,11 @@
 		const std::optional<ModelId> modelId = documents->findIdForModel(model);
 		if (modelId.has_value())
 		{
-			GetPolygonsContext context{modelId.value(), documents};
+			GetPolygonsContext context{
+				.invertnext = false,
+				.modelId = *modelId,
+				.documents = documents,
+			};
 			cache->polygons = inlinePolygons(model, &context);
 		}
 		cache->needRecache = false;
--- a/src/polygoncache.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/polygoncache.h	Mon Jul 04 19:53:13 2022 +0300
@@ -1,19 +1,13 @@
 #pragma once
+#include <QTextDocument>
 #include "src/basics.h"
 #include "src/model.h"
 #include "src/gl/common.h"
-#include "src/documentmanager.h"
 
 template<typename T>
 struct WithId : T
 {
-	ElementId id;
-};
-
-struct GetPolygonsContext
-{
-	ModelId modelId;
-	class DocumentManager* documents;
+	std::int32_t linenumber;
 };
 
 struct PolygonCache
@@ -27,4 +21,4 @@
 	PolygonCache* cache,
 	Model* model,
 	class DocumentManager* documents);
-PolygonCache* findPolygonCacheForModel(Model* model, DocumentManager* context);
+PolygonCache* findPolygonCacheForModel(QTextDocument* model, DocumentManager* context);
--- a/src/vertexmap.cpp	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/vertexmap.cpp	Mon Jul 04 19:53:13 2022 +0300
@@ -13,24 +13,6 @@
 VertexMap::VertexMap(const Model *model) :
 	model{model}
 {
-	connect(
-		model,
-		&Model::dataChanged,
-		this,
-		&VertexMap::build
-	);
-	connect(
-		model,
-		&Model::rowsInserted,
-		this,
-		&VertexMap::build
-	);
-	connect(
-		model,
-		&Model::rowsRemoved,
-		this,
-		&VertexMap::build
-	);
 	this->build();
 }
 
@@ -116,6 +98,7 @@
 	this->map.clear();
 	this->vertices.clear();
 	this->vertexHashes.clear();
+#if 0
 	for (std::size_t i = 0; i < this->model->size(); ++i)
 	{
 		const ModelElement& element = this->model->at(i);
@@ -173,6 +156,7 @@
 		}
 		info.transform = glm::scale(info.transform, glm::vec3{scale, scale, scale});
 	}
+#endif
 	Q_EMIT this->verticesChanged();
 }
 
--- a/src/vertexmap.h	Mon Jul 04 15:37:22 2022 +0300
+++ b/src/vertexmap.h	Mon Jul 04 19:53:13 2022 +0300
@@ -14,7 +14,7 @@
 	struct VertexInfo
 	{
 		glm::vec3 point;
-		std::set<ElementId> objects;
+		//std::set<ElementId> objects;
 		bool transformSet = false;
 		glm::mat4 transform;
 	};

mercurial