object editing

Mon, 11 May 2020 12:18:59 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Mon, 11 May 2020 12:18:59 +0300
changeset 89
7abaf1d64719
parent 88
14e51640c189
child 92
c438a7db7c52

object editing

CMakeLists.txt file | annotate | diff | comparison | revisions
src/colors.h file | annotate | diff | comparison | revisions
src/linetypes/conditionaledge.h file | annotate | diff | comparison | revisions
src/linetypes/edge.h file | annotate | diff | comparison | revisions
src/linetypes/object.cpp file | annotate | diff | comparison | revisions
src/linetypes/object.h file | annotate | diff | comparison | revisions
src/linetypes/polygonobject.h file | annotate | diff | comparison | revisions
src/linetypes/propertygenerics.h file | annotate | diff | comparison | revisions
src/linetypes/quadrilateral.h file | annotate | diff | comparison | revisions
src/linetypes/triangle.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/modeleditcontext.cpp file | annotate | diff | comparison | revisions
src/modeleditcontext.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	Mon May 11 12:18:04 2020 +0300
+++ b/CMakeLists.txt	Mon May 11 12:18:59 2020 +0300
@@ -94,6 +94,8 @@
 	src/linetypes/errorline.h
 	src/linetypes/metacommand.h
 	src/linetypes/object.h
+	src/linetypes/polygonobject.h
+	src/linetypes/propertygenerics.h
 	src/linetypes/quadrilateral.h
 	src/linetypes/subfilereference.h
 	src/linetypes/triangle.h
--- a/src/colors.h	Mon May 11 12:18:04 2020 +0300
+++ b/src/colors.h	Mon May 11 12:18:59 2020 +0300
@@ -28,9 +28,11 @@
 
 struct ldraw::Color
 {
-	qint32 index;
+	qint32 index = 0;
 };
 
+Q_DECLARE_METATYPE(ldraw::Color)
+
 class ldraw::ColorTable
 {
 public:
--- a/src/linetypes/conditionaledge.h	Mon May 11 12:18:04 2020 +0300
+++ b/src/linetypes/conditionaledge.h	Mon May 11 12:18:59 2020 +0300
@@ -1,5 +1,5 @@
 #pragma once
-#include "edge.h"
+#include "polygonobject.h"
 
 namespace ldraw
 {
--- a/src/linetypes/edge.h	Mon May 11 12:18:04 2020 +0300
+++ b/src/linetypes/edge.h	Mon May 11 12:18:59 2020 +0300
@@ -1,5 +1,5 @@
 #pragma once
-#include "object.h"
+#include "polygonobject.h"
 
 namespace ldraw
 {
--- a/src/linetypes/object.cpp	Mon May 11 12:18:04 2020 +0300
+++ b/src/linetypes/object.cpp	Mon May 11 12:18:59 2020 +0300
@@ -1,6 +1,8 @@
 #include <QBrush>
 #include <QFont>
 #include "object.h"
+#include "widgets/vec3editor.h"
+#include "modeleditcontext.h"
 
 static std::int32_t getIdForNewObject()
 {
@@ -87,7 +89,7 @@
 	switch (id)
 	{
 	case Property::Color:
-		return colorIndex.index;
+		return QVariant::fromValue<Color>(colorIndex);
 	default:
 		return Object::getProperty(id);
 	}
@@ -95,7 +97,7 @@
 
 void ldraw::ColoredObject::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair)
 {
-	LDRAW_OBJECT_HANDLE_SET_PROPERTY(Color, {colorIndex.index = value;});
+	LDRAW_OBJECT_HANDLE_SET_PROPERTY(Color, {colorIndex = value;});
 	Object::setProperty(result, pair);
 }
 
--- a/src/linetypes/object.h	Mon May 11 12:18:04 2020 +0300
+++ b/src/linetypes/object.h	Mon May 11 12:18:59 2020 +0300
@@ -5,17 +5,17 @@
 #include "main.h"
 #include "colors.h"
 #include "gl/common.h"
+#include "linetypes/propertygenerics.h"
+
+class Model;
 
 namespace ldraw
 {
 	struct GetPolygonsContext;
-	enum class Property;
 	class Object;
 	class ColoredObject;
 	class Empty;
 	class UnhandledProperty;
-	template<int N>
-	class PolygonObject;
 }
 
 class DocumentManager;
@@ -25,67 +25,6 @@
 	::DocumentManager* documents;
 };
 
-/**
- * @brief Different properties that can be queried with getProperty
- */
-enum class ldraw::Property
-{
-	Color, // Color of the object
-	Text, // Text contained in a comment
-	Point0, // First vertex in a polygon or edge line
-	Point1, // Second vertex in a polygon or edge line
-	Point2, // Third vertex in a polygon
-	Point3, // Fourth vertex in a quadrilateral
-	Transformation, // 4x4 transformation matrix of a subfile reference
-	ReferenceName, // Subfile reference name
-	IsInverted, // Whether or not the object has been inverted with BFC INVERTNEXT
-	ErrorMessage // For error lines, why parsing failed
-};
-
-// Mapping of properties to types
-#define LDFORGE_DEFINE_PROPERTY_TYPE(PROPERTY, TYPE) \
-	namespace ldraw { \
-		template<> struct PropertyType<ldraw::Property::PROPERTY> { using type = TYPE; }; \
-	}
-
-namespace ldraw
-{
-	template<ldraw::Property property>
-	struct PropertyType
-	{
-	};
-
-	template<ldraw::Property property>
-	using PropertyType_t = typename PropertyType<property>::type;
-
-	struct PropertyKeyValue
-	{
-		Property key;
-		QVariant value;
-	};
-
-	constexpr Property pointProperty(int n)
-	{
-		Q_ASSERT(n >= 0 and n < 4);
-		return static_cast<Property>(static_cast<int>(Property::Point0) + n);
-	}
-}
-
-LDFORGE_DEFINE_PROPERTY_TYPE(Color, int)
-LDFORGE_DEFINE_PROPERTY_TYPE(Text, QString)
-LDFORGE_DEFINE_PROPERTY_TYPE(Point0, glm::vec3)
-LDFORGE_DEFINE_PROPERTY_TYPE(Point1, glm::vec3)
-LDFORGE_DEFINE_PROPERTY_TYPE(Point2, glm::vec3)
-LDFORGE_DEFINE_PROPERTY_TYPE(Point3, glm::vec3)
-LDFORGE_DEFINE_PROPERTY_TYPE(Transformation, glm::mat4)
-LDFORGE_DEFINE_PROPERTY_TYPE(ReferenceName, QString)
-LDFORGE_DEFINE_PROPERTY_TYPE(IsInverted, bool)
-LDFORGE_DEFINE_PROPERTY_TYPE(ErrorMessage, QString)
-
-#define LDRAW_OBJECT_HANDLE_SET_PROPERTY(PROPERTY, HANDLER) \
-	{this->handle<ldraw::Property::PROPERTY>(result, pair, \
-		[&](const ldraw::PropertyType_t<ldraw::Property::PROPERTY>& value) HANDLER);}
-
 class ldraw::Object
 {
 public:
@@ -108,7 +47,7 @@
 	virtual bool hasColor() const;
 	virtual QVariant getProperty(Property id) const;
 	template<ldraw::Property property>
-	SetPropertyResult setProperty(const PropertyType_t<property>& value);
+	SetPropertyResult setProperty(const PropertyType<property>& value);
 	SetPropertyResult setProperty(const PropertyKeyValue& pair);
 	virtual QString textRepresentation() const = 0;
 	virtual QBrush textRepresentationForeground() const;
@@ -125,7 +64,7 @@
 };
 
 template<ldraw::Property property>
-ldraw::Object::SetPropertyResult ldraw::Object::setProperty(const PropertyType_t<property>& value)
+ldraw::Object::SetPropertyResult ldraw::Object::setProperty(const ldraw::PropertyType<property>& value)
 {
 	SetPropertyResult result = SetPropertyResult::PropertyNotHandled;
 	this->setProperty(&result, PropertyKeyValue{property, QVariant::fromValue(value)});
@@ -137,7 +76,7 @@
 {
 	if (pair.key == property)
 	{
-		function(pair.value.value<ldraw::PropertyType_t<property>>());
+		function(pair.value.value<ldraw::PropertyType<property>>());
 		*result = SetPropertyResult::Success;
 	}
 }
@@ -153,64 +92,6 @@
 	void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override;
 };
 
-template<int N>
-class ldraw::PolygonObject : public ColoredObject
-{
-public:
-	PolygonObject(const std::array<glm::vec3, N>& points, const Color color) :
-		ColoredObject{color},
-		points{points} {}
-	int numPoints() const override
-	{
-		return N;
-	}
-	const glm::vec3& getPoint(int index) const override
-	{
-		Q_ASSERT(index >= 0 and index < N);
-		return this->points[index];
-	}
-	QVariant getProperty(const Property id) const override
-	{
-		switch (id)
-		{
-		case Property::Point0:
-			return QVariant::fromValue(points[0]);
-		case Property::Point1:
-			return QVariant::fromValue(points[1]);
-		case Property::Point2:
-			if (N >= 3)
-			{
-				return QVariant::fromValue(points[2]);
-			}
-			break;
-		case Property::Point3:
-			if (N >= 4)
-			{
-				return QVariant::fromValue(points[3]);
-			}
-			break;
-		default:
-			break;
-		}
-		return ColoredObject::getProperty(id);
-	}
-	void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair)
-	{
-		LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point0, {points[0] = value;})
-		LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point1, {points[1] = value;})
-		if constexpr (N >= 3)
-		{
-			LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point2, {points[2] = value;})
-		}
-		if constexpr (N >= 4)
-		{
-			LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point3, {points[2] = value;})
-		}
-		ColoredObject::setProperty(result, pair);
-	}
-	std::array<std::enable_if_t<(N > 0 and N <= 4), glm::vec3>, N> points;
-};
-
 /**
  * @brief Represents an empty line.
  */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/linetypes/polygonobject.h	Mon May 11 12:18:59 2020 +0300
@@ -0,0 +1,70 @@
+#pragma once
+#include "object.h"
+#include "widgets/vec3editor.h"
+#include "model.h"
+#include "modeleditcontext.h"
+
+namespace ldraw
+{
+	template<int N, typename>
+	class PolygonObject;
+}
+
+template<int N, typename = std::enable_if_t<(N > 0 and N <= 4)>>
+class ldraw::PolygonObject : public ColoredObject
+{
+public:
+	using BaseClass = ColoredObject;
+	PolygonObject(const std::array<glm::vec3, N>& points, const Color color) :
+		ColoredObject{color},
+		points{points} {}
+	int numPoints() const override
+	{
+		return N;
+	}
+	const glm::vec3& getPoint(int index) const override
+	{
+		Q_ASSERT(index >= 0 and index < N);
+		return this->points[index];
+	}
+	QVariant getProperty(const Property id) const override
+	{
+		switch (id)
+		{
+		case Property::Point0:
+			return QVariant::fromValue(points[0]);
+		case Property::Point1:
+			return QVariant::fromValue(points[1]);
+		case Property::Point2:
+			if (N >= 3)
+			{
+				return QVariant::fromValue(points[2]);
+			}
+			break;
+		case Property::Point3:
+			if (N >= 4)
+			{
+				return QVariant::fromValue(points[3]);
+			}
+			break;
+		default:
+			break;
+		}
+		return BaseClass::getProperty(id);
+	}
+	void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair)
+	{
+		LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point0, {points[0] = value;})
+		LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point1, {points[1] = value;})
+		if constexpr (N >= 3)
+		{
+			LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point2, {points[2] = value;})
+		}
+		if constexpr (N >= 4)
+		{
+			LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point3, {points[3] = value;})
+		}
+		ColoredObject::setProperty(result, pair);
+	}
+	std::array<glm::vec3, N> points;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/linetypes/propertygenerics.h	Mon May 11 12:18:59 2020 +0300
@@ -0,0 +1,153 @@
+#pragma once
+#include "main.h"
+#include "colors.h"
+
+class Vec3Editor;
+class MatrixEditor;
+
+namespace ldraw
+{
+	enum class Property;
+	struct PropertyKeyValue;
+	template<Property property>
+	struct PropertyTraits
+	{
+		static constexpr bool defined = false;
+	};
+}
+
+/**
+ * Different properties
+ */
+enum class ldraw::Property
+{
+	Color, // Color of the object
+	Text, // Text contained in a comment
+	Point0, // First vertex in a polygon or edge line
+	Point1, // Second vertex in a polygon or edge line
+	Point2, // Third vertex in a polygon
+	Point3, // Fourth vertex in a quadrilateral
+	Transformation, // 4x4 transformation matrix of a subfile reference
+	ReferenceName, // Subfile reference name
+	IsInverted, // Whether or not the object has been inverted with BFC INVERTNEXT
+	ErrorMessage // For error lines, why parsing failed
+};
+
+Q_DECLARE_METATYPE(ldraw::Property)
+
+// Mapping of properties to types
+#define LDFORGE_DEFINE_PROPERTY_TYPE(PROPERTY, TYPE) \
+	namespace ldraw \
+	{ \
+		template<> struct PropertyTraits<ldraw::Property::PROPERTY> \
+		{ \
+			using type = TYPE; \
+			static constexpr std::array<char, 256> name{#PROPERTY}; \
+			static constexpr bool defined = true; \
+		}; \
+	}
+
+LDFORGE_DEFINE_PROPERTY_TYPE(Color, ldraw::Color)
+LDFORGE_DEFINE_PROPERTY_TYPE(Text, QString)
+LDFORGE_DEFINE_PROPERTY_TYPE(Point0, glm::vec3)
+LDFORGE_DEFINE_PROPERTY_TYPE(Point1, glm::vec3)
+LDFORGE_DEFINE_PROPERTY_TYPE(Point2, glm::vec3)
+LDFORGE_DEFINE_PROPERTY_TYPE(Point3, glm::vec3)
+LDFORGE_DEFINE_PROPERTY_TYPE(Transformation, glm::mat4)
+LDFORGE_DEFINE_PROPERTY_TYPE(ReferenceName, QString)
+LDFORGE_DEFINE_PROPERTY_TYPE(IsInverted, bool)
+LDFORGE_DEFINE_PROPERTY_TYPE(ErrorMessage, QString)
+
+#define LDRAW_OBJECT_HANDLE_SET_PROPERTY(PROPERTY, HANDLER) \
+	{this->handle<ldraw::Property::PROPERTY>(result, pair, \
+		[&](const ldraw::PropertyType<ldraw::Property::PROPERTY>& value) HANDLER);}
+
+// Generics
+namespace ldraw
+{
+	template<ldraw::Property property>
+	using PropertyType = typename PropertyTraits<property>::type;
+
+	template<ldraw::Property property>
+	inline const char* PROPERTY_NAME = PropertyTraits<property>::name;
+
+	struct PropertyKeyValue
+	{
+		Property key;
+		QVariant value;
+	};
+
+	constexpr Property pointProperty(int n)
+	{
+		Q_ASSERT(n >= 0 and n < 4);
+		return static_cast<Property>(static_cast<int>(Property::Point0) + n);
+	}
+
+	struct PropertyTrait
+	{
+		ldraw::Property property;
+		std::array<char, 256> name;
+		int type;
+	};
+
+	namespace detail
+	{
+		template<int N>
+		constexpr int propertyCountHelper()
+		{
+			if constexpr (ldraw::PropertyTraits<static_cast<Property>(N)>::defined)
+			{
+				return propertyCountHelper<N + 1>();
+			}
+			else
+			{
+				return N;
+			}
+		}
+
+		template<int k>
+		constexpr PropertyTrait getPropertyTrait()
+		{
+			constexpr auto property = static_cast<ldraw::Property>(k);
+			using trait = ldraw::PropertyTraits<property>;
+			return PropertyTrait{
+				property,
+				trait::name,
+				qMetaTypeId<typename trait::type>()
+			};
+		}
+
+		template<int... Ints>
+		auto getPropertyTraits(std::integer_sequence<int, Ints...>)
+		{
+			return std::array<PropertyTrait, sizeof...(Ints)>{getPropertyTrait<Ints>()...};
+		}
+	}
+
+	constexpr int NUM_PROPERTIES = detail::propertyCountHelper<0>();
+	inline const auto& traits()
+	{
+		static std::array<PropertyTrait, NUM_PROPERTIES> result =
+			detail::getPropertyTraits(std::make_integer_sequence<int, NUM_PROPERTIES>());
+		return result;
+	}
+
+	inline const auto& traits(ldraw::Property property)
+	{
+		return traits()[static_cast<int>(property)];
+	}
+
+	template<typename T, std::size_t... Ints>
+	constexpr auto makeIndexArray(std::index_sequence<Ints...>)
+	{
+		return std::array{static_cast<T>(Ints)...};
+	}
+
+	constexpr auto ALL_PROPERTIES = makeIndexArray<Property>(std::make_index_sequence<NUM_PROPERTIES>{});
+
+	template<typename T>
+	bool testPropertyType(ldraw::Property property)
+	{
+		return qMetaTypeId<T>() == ldraw::traits(property).type;
+	}
+}
--- a/src/linetypes/quadrilateral.h	Mon May 11 12:18:04 2020 +0300
+++ b/src/linetypes/quadrilateral.h	Mon May 11 12:18:59 2020 +0300
@@ -1,5 +1,5 @@
 #pragma once
-#include "object.h"
+#include "polygonobject.h"
 
 namespace ldraw
 {
--- a/src/linetypes/triangle.h	Mon May 11 12:18:04 2020 +0300
+++ b/src/linetypes/triangle.h	Mon May 11 12:18:59 2020 +0300
@@ -1,5 +1,5 @@
 #pragma once
-#include "object.h"
+#include "polygonobject.h"
 
 namespace ldraw
 {
--- a/src/main.cpp	Mon May 11 12:18:04 2020 +0300
+++ b/src/main.cpp	Mon May 11 12:18:59 2020 +0300
@@ -20,6 +20,8 @@
 #include <GL/glut.h>
 #include "mainwindow.h"
 #include "version.h"
+#include <iostream>
+#include <QMessageBox>
 
 int main(int argc, char *argv[])
 {
@@ -30,6 +32,9 @@
 	::qRegisterMetaTypeStreamOperators<Library>("Library");
 	::qRegisterMetaTypeStreamOperators<Libraries>("Libraries");
 	QApplication app{argc, argv};
+	/*
+	QMessageBox::information(nullptr, "", QMetaType::typeName( qMetaTypeId<ldraw::Color>() ));
+	*/
 	MainWindow mainwindow;
 	mainwindow.show();
 	return app.exec();
--- a/src/modeleditcontext.cpp	Mon May 11 12:18:04 2020 +0300
+++ b/src/modeleditcontext.cpp	Mon May 11 12:18:59 2020 +0300
@@ -17,6 +17,8 @@
  */
 
 #include "modeleditcontext.h"
+#include "linetypes/triangle.h"
+#include "linetypes/quadrilateral.h"
 
 Model::EditContext::EditContext(Model& model) :
 	storedModel{model}
@@ -45,6 +47,25 @@
 	this->model().remove(position);
 }
 
+auto Model::EditContext::setObjectProperty(
+	const ldraw::id_t id,
+	const ldraw::Property property,
+	const QVariant& value)
+	-> ldraw::Object::SetPropertyResult
+{
+	ldraw::Object* const object = this->model().objectAt(id);
+	if (object != nullptr)
+	{
+		const ldraw::Object::SetPropertyResult result = object->setProperty(ldraw::PropertyKeyValue{property, value});
+		modifiedObjects.insert(id);
+		return result;
+	}
+	else
+	{
+		return ldraw::Object::SetPropertyResult::PropertyNotHandled;
+	}
+}
+
 void Model::EditContext::setObjectPoint(ldraw::id_t id, int pointId, const glm::vec3& value)
 {
 	ldraw::Object* object = this->model().objectAt(id);
--- a/src/modeleditcontext.h	Mon May 11 12:18:04 2020 +0300
+++ b/src/modeleditcontext.h	Mon May 11 12:18:59 2020 +0300
@@ -18,9 +18,6 @@
 
 #pragma once
 #include "model.h"
-#include "linetypes/object.h"
-#include "linetypes/quadrilateral.h"
-#include "linetypes/triangle.h"
 
 class Model::EditContext
 {
@@ -33,15 +30,9 @@
 	ldraw::Id<T> insert(int position, Args&&... args);
 	void remove(int position);
 	template<ldraw::Property property>
-	void setObjectProperty(ldraw::id_t id, const ldraw::PropertyType_t<property>& value)
-	{
-		ldraw::Object* object = this->model().objectAt(id);
-		if (object != nullptr)
-		{
-			object->setProperty<property>(value);
-			modifiedObjects.insert(id);
-		}
-	}
+	void setObjectProperty(ldraw::id_t id, const ldraw::PropertyType<property>& value);
+	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();
@@ -52,6 +43,17 @@
 	Model& storedModel;
 };
 
+template<ldraw::Property Property>
+void Model::EditContext::setObjectProperty(const ldraw::id_t id, const ldraw::PropertyType<Property>& value)
+{
+	ldraw::Object* object = this->model().objectAt(id);
+	if (object != nullptr)
+	{
+		object->setProperty<Property>(value);
+		modifiedObjects.insert(id);
+	}
+}
+
 template<typename T, typename... Args>
 ldraw::Id<T> Model::EditContext::append(Args&&... args)
 {
--- a/src/ui/polygonobjecteditor.cpp	Mon May 11 12:18:04 2020 +0300
+++ b/src/ui/polygonobjecteditor.cpp	Mon May 11 12:18:59 2020 +0300
@@ -1,27 +1,21 @@
-#include <QVBoxLayout>
+#include <QFormLayout>
 #include <QSplitter>
 #include "model.h"
 #include "modeleditcontext.h"
 #include "widgets/vec3editor.h"
 #include "ui/polygonobjecteditor.h"
 
-constexpr char COLUMN_PROPERTY[] = "_ldforge_column";
+static constexpr char INDEX_NAME[] = "_ldforge_index";
+static constexpr char PROPERTY_NAME[] = "_ldforge_property";
+static constexpr char OBJECT_ID_NAME[] = "_ldforge_id";
+static constexpr char LABEL_NAME[] = "_ldforge_label";
 
 PolygonObjectEditor::PolygonObjectEditor(Model* model, ldraw::id_t id, QWidget* parent) :
 	QWidget{parent},
 	model{model},
 	storedObjectId{ldraw::NULL_ID.value}
 {
-	this->setLayout(new QVBoxLayout{this});
-	for (int i = 0; i < countof(this->vec3Editors); i += 1)
-	{
-		std::optional<Vec3Editor>& editorPointer = this->vec3Editors[i];
-		editorPointer.emplace(glm::vec3{}, this);
-		editorPointer->setProperty(COLUMN_PROPERTY, QVariant::fromValue(i));
-		this->layout()->addWidget(&*editorPointer);
-		connect(&*editorPointer, &Vec3Editor::valueChanged, this, &PolygonObjectEditor::vectorChanged);
-	}
-	this->layout()->addWidget(new QSplitter{Qt::Vertical, this});
+	this->splitter.emplace(Qt::Vertical, this);
 	this->setObjectId(id);
 }
 
@@ -38,37 +32,46 @@
 void PolygonObjectEditor::setObjectId(ldraw::id_t id)
 {
 	this->storedObjectId = id;
-	this->updateNumRows();
+	this->buildWidgets();
 }
 
-void PolygonObjectEditor::vectorChanged(const glm::vec3& value)
+void PolygonObjectEditor::buildWidgets()
 {
-	bool ok;
-	const int num = sender()->property(COLUMN_PROPERTY).toInt(&ok);
-	if (ok and num >= 0 and num < countof(this->vec3Editors))
+	this->widgets.clear();
+	delete this->layout();
+	QFormLayout* layout = new QFormLayout{this};
+	this->setLayout(layout);
+	for (int n : {0, 1, 2, 3})
+	{
+		this->setupPointWidget(n);
+	}
+	for (std::unique_ptr<QWidget>& widget : this->widgets)
 	{
-		Model::EditContext editor = this->model->edit();
-		editor.setObjectPoint(this->objectId(), num, value);
+		const QString label = widget->property(LABEL_NAME).toString();
+		layout->addRow(label, widget.get());
+	}
+	layout->addRow("", &*this->splitter);
+}
+
+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())
+	{
+		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::updateNumRows()
+void PolygonObjectEditor::pointChanged(const glm::vec3& value)
 {
-	const ldraw::Object* object = model->get(this->storedObjectId);
-	const int numPoints = object != nullptr ? object->numPoints() : 0;
-	for (int i = 0; i < countof(this->vec3Editors); i += 1)
-	{
-		Vec3Editor& editor = *this->vec3Editors[i];
-		QSignalBlocker blocker{&editor};
-		if (i < numPoints)
-		{
-			editor.setVisible(true);
-			editor.setValue(object->getPoint(i));
-		}
-		else
-		{
-			editor.setVisible(false);
-			editor.setValue(glm::vec3{});
-		}
-	}
+	Model::EditContext editcontext = this->model->edit();
+	const int n = this->sender()->property(INDEX_NAME).toInt();
+	const ldraw::Property property = ldraw::pointProperty(n);
+	editcontext.setObjectProperty(this->objectId(), property, QVariant::fromValue(value));
 }
--- a/src/ui/polygonobjecteditor.h	Mon May 11 12:18:04 2020 +0300
+++ b/src/ui/polygonobjecteditor.h	Mon May 11 12:18:59 2020 +0300
@@ -13,9 +13,11 @@
 	ldraw::id_t objectId() const;
 	void setObjectId(ldraw::id_t id);
 private:
-	Q_SLOT void vectorChanged(const glm::vec3& value);
-	void updateNumRows();
+	void buildWidgets();
+	void setupPointWidget(int n);
+	Q_SLOT void pointChanged(const glm::vec3& value);
 	Model* model;
 	ldraw::id_t storedObjectId;
-	std::optional<Vec3Editor> vec3Editors[4];
+	std::vector<std::unique_ptr<QWidget>> widgets;
+	std::optional<class QSplitter> splitter;
 };

mercurial