Circular primitive editor complete

Sun, 17 Jun 2018 16:13:24 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Sun, 17 Jun 2018 16:13:24 +0300
changeset 1406
37fffb682d2f
parent 1405
d2bf2e59a3ef
child 1407
22bc5862cb56

Circular primitive editor complete

CMakeLists.txt file | annotate | diff | comparison | revisions
src/dialogs/circularprimitiveeditor.cpp file | annotate | diff | comparison | revisions
src/dialogs/circularprimitiveeditor.h file | annotate | diff | comparison | revisions
src/dialogs/circularprimitiveeditor.ui file | annotate | diff | comparison | revisions
src/dialogs/cylindereditor.cpp file | annotate | diff | comparison | revisions
src/dialogs/cylindereditor.h file | annotate | diff | comparison | revisions
src/dialogs/cylindereditor.ui file | annotate | diff | comparison | revisions
src/guiutilities.cpp file | annotate | diff | comparison | revisions
src/linetypes/circularprimitive.cpp file | annotate | diff | comparison | revisions
src/linetypes/circularprimitive.h file | annotate | diff | comparison | revisions
src/linetypes/modelobject.cpp file | annotate | diff | comparison | revisions
src/linetypes/modelobject.h file | annotate | diff | comparison | revisions
src/widgets/matrixeditor.cpp file | annotate | diff | comparison | revisions
src/widgets/matrixeditor.h file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Sun Jun 17 14:06:03 2018 +0300
+++ b/CMakeLists.txt	Sun Jun 17 16:13:24 2018 +0300
@@ -62,7 +62,7 @@
 	src/algorithms/invert.cpp
 	src/dialogs/colorselector.cpp
 	src/dialogs/configdialog.cpp
-	src/dialogs/cylindereditor.cpp
+	src/dialogs/circularprimitiveeditor.cpp
 	src/dialogs/externalprogrampathdialog.cpp
 	src/dialogs/generateprimitivedialog.cpp
 	src/dialogs/ldrawpathdialog.cpp
@@ -135,7 +135,7 @@
 	src/algorithms/invert.h
 	src/dialogs/colorselector.h
 	src/dialogs/configdialog.h
-	src/dialogs/cylindereditor.h
+	src/dialogs/circularprimitiveeditor.h
 	src/dialogs/externalprogrampathdialog.h
 	src/dialogs/generateprimitivedialog.h
 	src/dialogs/ldrawpathdialog.h
@@ -188,7 +188,7 @@
 	src/dialogs/colorselector.ui
 	src/dialogs/configdialog.ui
 	src/dialogs/covererdialog.ui
-	src/dialogs/cylindereditor.ui
+	src/dialogs/circularprimitiveeditor.ui
 	src/dialogs/edger2dialog.ui
 	src/dialogs/editrawdialog.ui
 	src/dialogs/externalprogrampathdialog.ui
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dialogs/circularprimitiveeditor.cpp	Sun Jun 17 16:13:24 2018 +0300
@@ -0,0 +1,186 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013 - 2018 Teemu Piippo
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "circularprimitiveeditor.h"
+#include "ui_circularprimitiveeditor.h"
+#include "../primitives.h"
+
+// offsetof doesn't work if this doesn't hold true
+static_assert(std::is_standard_layout<Ui_CircularPrimitiveEditor>::value, "UI type is not of standard layout");
+
+// Contains offsets to radio buttons and the choice they represent.
+static const struct
+{
+	std::ptrdiff_t offset;
+	PrimitiveModel::Type primitiveType;
+
+	QRadioButton* resolve(Ui_CircularPrimitiveEditor& ui) const
+	{
+		// Find the radio button by pointer arithmetic
+		return *reinterpret_cast<QRadioButton**>(reinterpret_cast<char*>(&ui) + offset);
+	}
+} radioButtonMap[] = {
+#define MAP_RADIO_BUTTON(widget, type) { offsetof(Ui_CircularPrimitiveEditor, widget), PrimitiveModel::type }
+	MAP_RADIO_BUTTON(circle, Circle),
+	MAP_RADIO_BUTTON(cylinder, Cylinder),
+	MAP_RADIO_BUTTON(disc, Disc),
+	MAP_RADIO_BUTTON(discNegative, DiscNegative),
+#undef MAP_RADIO_BUTTON
+};
+
+/*
+ * Constructs a new circular primitive editor and sets up connections.
+ */
+CircularPrimitiveEditor::CircularPrimitiveEditor(LDCircularPrimitive* primitive, QWidget* parent) :
+	QDialog {parent},
+	ui {*new Ui_CircularPrimitiveEditor},
+	primitive {primitive}
+{
+	ui.setupUi(this);
+
+	// Set the initial values of the dialog
+	updateWidgets();
+
+	if (primitive)
+	{
+		// Store the original state of the object. If the user presses "Reset" then the object is restored
+		// from this archive.
+		Serializer serializer {originalState, Serializer::Store};
+		primitive->serialize(serializer);
+	}
+
+	for (const auto& mapping : ::radioButtonMap)
+	{
+		QRadioButton* button = mapping.resolve(ui);
+
+		// If the radio button gets checked, update the type of the circular primitive.
+		connect(
+			button,
+			&QRadioButton::toggled,
+			[&](bool checked)
+			{
+				if (checked and this->primitive)
+					this->primitive->setPrimitiveType(mapping.primitiveType);
+			}
+		);
+	}
+
+	// Connect various widgets so that changing them changes the primitive object.
+	connect(
+		ui.segments,
+		qOverload<int>(&QSpinBox::valueChanged),
+		[&](int newSegments)
+		{
+			if (this->primitive)
+				this->primitive->setSegments(newSegments);
+		}
+	);
+	connect(
+		ui.divisions,
+		&QComboBox::currentTextChanged,
+		[&](const QString& newDivisions)
+		{
+			if (this->primitive)
+				this->primitive->setDivisions(newDivisions.toInt());
+		}
+	);
+	connect(
+		ui.color,
+		&ColorButton::colorChanged,
+		[&](LDColor newColor)
+		{
+			if (this->primitive)
+				this->primitive->setColor(newColor);
+		}
+	);
+	connect(
+		ui.matrix,
+		&MatrixEditor::matrixChanged,
+		[&](const QMatrix4x4& newMatrix)
+		{
+			if (this->primitive)
+				this->primitive->setTransformationMatrix(newMatrix);
+		}
+	);
+	// Connect the reset button, "reset button" here meaning any button with the reset role.
+	connect(
+		ui.buttonBox,
+		&QDialogButtonBox::clicked,
+		[&](QAbstractButton* button)
+		{
+			if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::ResetRole)
+				reset();
+		}
+	);
+
+	if (this->primitive)
+	{
+		// If the primitive is changed by some other thing (e.g. by resetting it), update the widgets.
+		connect(this->primitive, &LDObject::modified, this, &CircularPrimitiveEditor::updateWidgets);
+
+		// If the object is deleted, then destroy the dialog.
+		connect(this->primitive, &LDObject::destroyed, this, &QDialog::reject);
+	}
+}
+
+/*
+ * Frees the user interface memory after the dialog is destroyed.
+ */
+CircularPrimitiveEditor::~CircularPrimitiveEditor()
+{
+	delete &ui;
+}
+
+/*
+ * Updates the widgets of the editor to reflect the properites of the object being modified.
+ */
+void CircularPrimitiveEditor::updateWidgets()
+{
+	setEnabled(primitive != nullptr);
+
+	if (primitive)
+	{
+		for (const auto& mapping : ::radioButtonMap)
+		{
+			// Choose the correct radio button
+			QRadioButton* button = mapping.resolve(ui);
+			withSignalsBlocked(button, [&]()
+			{
+				button->setChecked(primitive->primitiveType() == mapping.primitiveType);
+			});
+		}
+
+		// Set the values of the form.
+		withSignalsBlocked(ui.segments, [&](){ ui.segments->setValue(primitive->segments()); });
+		withSignalsBlocked(ui.divisions, [&]()
+		{
+			ui.divisions->setCurrentText(QString::number(primitive->divisions()));
+		});
+		withSignalsBlocked(ui.color, [&](){ ui.color->setColor(primitive->color()); });
+		withSignalsBlocked(ui.matrix, [&](){ ui.matrix->setMatrix(primitive->transformationMatrix()); });
+	}
+}
+
+/*
+ * Resets the object being modified. The object will emit a signal that is connected to updateWidgets.
+ */
+void CircularPrimitiveEditor::reset()
+{
+	if (primitive)
+		primitive->restore(originalState); // Restoring does not change 'originalState'
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dialogs/circularprimitiveeditor.h	Sun Jun 17 16:13:24 2018 +0300
@@ -0,0 +1,38 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013 - 2018 Teemu Piippo
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <QDialog>
+#include "../linetypes/circularprimitive.h"
+
+class CircularPrimitiveEditor : public QDialog
+{
+	Q_OBJECT
+
+public:
+	explicit CircularPrimitiveEditor(LDCircularPrimitive* primitive = nullptr, QWidget* parent = nullptr);
+	~CircularPrimitiveEditor();
+
+private:
+	Q_SLOT void updateWidgets();
+	Q_SLOT void reset();
+
+	class Ui_CircularPrimitiveEditor& ui;
+	LDCircularPrimitive* primitive;
+	LDObjectState originalState;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dialogs/circularprimitiveeditor.ui	Sun Jun 17 16:13:24 2018 +0300
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CircularPrimitiveEditor</class>
+ <widget class="QDialog" name="CircularPrimitiveEditor">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>579</width>
+    <height>531</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,1,0">
+   <item>
+    <layout class="QFormLayout" name="formLayout_2">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Segments:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QSpinBox" name="segments"/>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Divisions:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QComboBox" name="divisions">
+       <item>
+        <property name="text">
+         <string>8</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>16</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>48</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Colour:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="ColorButton" name="color">
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Type</string>
+     </property>
+     <layout class="QFormLayout" name="formLayout">
+      <item row="3" column="0">
+       <widget class="QRadioButton" name="disc">
+        <property name="text">
+         <string>Disc</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1">
+       <widget class="QRadioButton" name="discNegative">
+        <property name="text">
+         <string>Disc negati&amp;ve</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QRadioButton" name="circle">
+        <property name="text">
+         <string>Circle</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QRadioButton" name="cylinder">
+        <property name="text">
+         <string>C&amp;ylinder</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Transformation</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="MatrixEditor" name="matrix" native="true"/>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Close|QDialogButtonBox::Reset</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>MatrixEditor</class>
+   <extends>QWidget</extends>
+   <header>widgets/matrixeditor.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>ColorButton</class>
+   <extends>QPushButton</extends>
+   <header>widgets/colorbutton.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>CircularPrimitiveEditor</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>CircularPrimitiveEditor</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- a/src/dialogs/cylindereditor.cpp	Sun Jun 17 14:06:03 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#include "cylindereditor.h"
-#include "ui_cylindereditor.h"
-
-CylinderEditor::CylinderEditor(QWidget* parent) :
-	QDialog {parent},
-	ui {*new Ui_CylinderEditor}
-{
-	ui.setupUi(this);
-}
-
-CylinderEditor::~CylinderEditor()
-{
-	delete &ui;
-}
--- a/src/dialogs/cylindereditor.h	Sun Jun 17 14:06:03 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-#pragma once
-#include <QDialog>
-
-class CylinderEditor : public QDialog
-{
-	Q_OBJECT
-
-public:
-	explicit CylinderEditor(QWidget* parent = nullptr);
-	~CylinderEditor();
-
-private:
-	class Ui_CylinderEditor& ui;
-};
--- a/src/dialogs/cylindereditor.ui	Sun Jun 17 14:06:03 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,168 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>CylinderEditor</class>
- <widget class="QDialog" name="CylinderEditor">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>656</width>
-    <height>659</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Dialog</string>
-  </property>
-  <widget class="QDialogButtonBox" name="buttonBox">
-   <property name="geometry">
-    <rect>
-     <x>190</x>
-     <y>570</y>
-     <width>341</width>
-     <height>32</height>
-    </rect>
-   </property>
-   <property name="orientation">
-    <enum>Qt::Horizontal</enum>
-   </property>
-   <property name="standardButtons">
-    <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
-   </property>
-  </widget>
-  <widget class="QGroupBox" name="groupBox">
-   <property name="geometry">
-    <rect>
-     <x>130</x>
-     <y>270</y>
-     <width>381</width>
-     <height>251</height>
-    </rect>
-   </property>
-   <property name="title">
-    <string>Transformation</string>
-   </property>
-   <layout class="QVBoxLayout" name="verticalLayout">
-    <item>
-     <widget class="MatrixEditor" name="matrixEditor" native="true"/>
-    </item>
-   </layout>
-  </widget>
-  <widget class="QComboBox" name="divisions">
-   <property name="geometry">
-    <rect>
-     <x>200</x>
-     <y>110</y>
-     <width>76</width>
-     <height>25</height>
-    </rect>
-   </property>
-   <item>
-    <property name="text">
-     <string>8</string>
-    </property>
-   </item>
-   <item>
-    <property name="text">
-     <string>16</string>
-    </property>
-   </item>
-   <item>
-    <property name="text">
-     <string>48</string>
-    </property>
-   </item>
-  </widget>
-  <widget class="QLabel" name="label">
-   <property name="geometry">
-    <rect>
-     <x>80</x>
-     <y>110</y>
-     <width>81</width>
-     <height>17</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>Divisions:</string>
-   </property>
-  </widget>
-  <widget class="QLabel" name="label_2">
-   <property name="geometry">
-    <rect>
-     <x>90</x>
-     <y>70</y>
-     <width>63</width>
-     <height>17</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>Segments:</string>
-   </property>
-  </widget>
-  <widget class="QSpinBox" name="segments">
-   <property name="geometry">
-    <rect>
-     <x>200</x>
-     <y>60</y>
-     <width>47</width>
-     <height>27</height>
-    </rect>
-   </property>
-  </widget>
-  <widget class="QLabel" name="label_3">
-   <property name="geometry">
-    <rect>
-     <x>90</x>
-     <y>150</y>
-     <width>63</width>
-     <height>17</height>
-    </rect>
-   </property>
-   <property name="text">
-    <string>Colour:</string>
-   </property>
-  </widget>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>MatrixEditor</class>
-   <extends>QWidget</extends>
-   <header>widgets/matrixeditor.h</header>
-   <container>1</container>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections>
-  <connection>
-   <sender>buttonBox</sender>
-   <signal>accepted()</signal>
-   <receiver>CylinderEditor</receiver>
-   <slot>accept()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>248</x>
-     <y>254</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>157</x>
-     <y>274</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>buttonBox</sender>
-   <signal>rejected()</signal>
-   <receiver>CylinderEditor</receiver>
-   <slot>reject()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>316</x>
-     <y>260</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>286</x>
-     <y>274</y>
-    </hint>
-   </hints>
-  </connection>
- </connections>
-</ui>
--- a/src/guiutilities.cpp	Sun Jun 17 14:06:03 2018 +0300
+++ b/src/guiutilities.cpp	Sun Jun 17 16:13:24 2018 +0300
@@ -27,7 +27,9 @@
 #include "mainwindow.h"
 #include "linetypes/modelobject.h"
 #include "linetypes/comment.h"
+#include "linetypes/circularprimitive.h"
 #include "dialogs/subfilereferenceeditor.h"
+#include "dialogs/circularprimitiveeditor.h"
 #include "widgets/vertexobjecteditor.h"
 
 GuiUtilities::GuiUtilities (QObject* parent) :
@@ -181,6 +183,12 @@
 		editor.setPrimitivesTree(parent->primitives());
 		editor.exec();
 	}
+	else if (object->type() == LDObjectType::CircularPrimitive)
+	{
+		LDCircularPrimitive* primitive = static_cast<LDCircularPrimitive*>(object);
+		CircularPrimitiveEditor editor {primitive, parent};
+		editor.exec();
+	}
 	else if (object->type() == LDObjectType::Comment)
 	{
 		LDComment* comment = static_cast<LDComment*>(object);
--- a/src/linetypes/circularprimitive.cpp	Sun Jun 17 14:06:03 2018 +0300
+++ b/src/linetypes/circularprimitive.cpp	Sun Jun 17 16:13:24 2018 +0300
@@ -179,6 +179,36 @@
 	return result;
 }
 
+PrimitiveModel::Type LDCircularPrimitive::primitiveType() const
+{
+	return m_type;
+}
+
+void LDCircularPrimitive::setPrimitiveType(PrimitiveModel::Type newType)
+{
+	changeProperty(&m_type, newType);
+}
+
+int LDCircularPrimitive::segments() const
+{
+	return m_segments;
+}
+
+void LDCircularPrimitive::setSegments(int newSegments)
+{
+	changeProperty(&m_segments, newSegments);
+}
+
+int LDCircularPrimitive::divisions() const
+{
+	return m_divisions;
+}
+
+void LDCircularPrimitive::setDivisions(int newDivisions)
+{
+	changeProperty(&m_divisions, newDivisions);
+}
+
 int LDCircularPrimitive::triangleCount(DocumentManager*) const
 {
 	switch (m_type)
--- a/src/linetypes/circularprimitive.h	Sun Jun 17 14:06:03 2018 +0300
+++ b/src/linetypes/circularprimitive.h	Sun Jun 17 16:13:24 2018 +0300
@@ -28,6 +28,12 @@
 	) override;
 	QVector<LDPolygon> rasterizePolygons(DocumentManager* context, Winding parentWinding) override;
 	QString objectListText() const override;
+	PrimitiveModel::Type primitiveType() const;
+	void setPrimitiveType(PrimitiveModel::Type newType);
+	int segments() const;
+	void setSegments(int newSegments);
+	int divisions() const;
+	void setDivisions(int newDivisions);
 	int triangleCount(DocumentManager*) const override;
 	QString iconName() const override;
 	void serialize(class Serializer& serializer) override;
--- a/src/linetypes/modelobject.cpp	Sun Jun 17 14:06:03 2018 +0300
+++ b/src/linetypes/modelobject.cpp	Sun Jun 17 16:13:24 2018 +0300
@@ -596,6 +596,14 @@
 	serializer << m_coords[3];
 }
 
+void LDObject::restore(LDObjectState& archive)
+{
+	Serializer restorer {archive, Serializer::Restore};
+	Serializer::Archive before = Serializer::store(this);
+	serialize(restorer);
+	emit modified(before, Serializer::store(this));
+}
+
 void LDMatrixObject::serialize(Serializer& serializer)
 {
 	LDObject::serialize(serializer);
--- a/src/linetypes/modelobject.h	Sun Jun 17 14:06:03 2018 +0300
+++ b/src/linetypes/modelobject.h	Sun Jun 17 16:13:24 2018 +0300
@@ -89,6 +89,7 @@
 	virtual QString iconName() const = 0;
 	const Vertex& vertex (int i) const;
 	virtual void serialize(class Serializer& serializer);
+	void restore(LDObjectState& archive);
 
 	static LDObject* newFromType(LDObjectType type);
 
--- a/src/widgets/matrixeditor.cpp	Sun Jun 17 14:06:03 2018 +0300
+++ b/src/widgets/matrixeditor.cpp	Sun Jun 17 16:13:24 2018 +0300
@@ -15,7 +15,7 @@
 			matrixCell(i, j),
 			qOverload<double>(&QDoubleSpinBox::valueChanged),
 			this,
-			&MatrixEditor::matrixChanged
+			&MatrixEditor::matrix3x3Changed
 		);
 	}
 
@@ -28,6 +28,15 @@
 			&MatrixEditor::scalingChanged
 		);
 	}
+
+	for (QDoubleSpinBox* spinbox : {ui.positionX, ui.positionY, ui.positionZ})
+	{
+		connect(
+			spinbox,
+			qOverload<double>(&QDoubleSpinBox::valueChanged),
+			[&](){ emit matrixChanged(this->matrix()); }
+		);
+	}
 }
 
 MatrixEditor::MatrixEditor(QWidget* parent) :
@@ -109,6 +118,7 @@
 					{
 						cellWidget->setValue(cellValue);
 					});
+					emit matrixChanged(this->matrix());
 				}
 			}
 
@@ -135,7 +145,7 @@
 /*
  * Updates the appropriate scaling vector element when a matrix cell is changed.
  */
-void MatrixEditor::matrixChanged()
+void MatrixEditor::matrix3x3Changed()
 {
 	QDoubleSpinBox* cellWidget = static_cast<QDoubleSpinBox*>(this->sender());
 
@@ -147,6 +157,7 @@
 		{
 			spinbox->setValue(this->matrixScaling(column));
 		});
+		emit matrixChanged(this->matrix());
 	}
 	catch (const std::out_of_range&) {}
 }
@@ -161,7 +172,13 @@
 		transformationMatrix(i, j) = this->matrixCell(i, j)->value();
 	}
 
-	transformationMatrix.translate(ui.positionX->value(), ui.positionY->value(), ui.positionZ->value());
+	QVector4D translation {
+		(float) ui.positionX->value(),
+		(float) ui.positionY->value(),
+		(float) ui.positionZ->value(),
+		0.0f
+	};
+	transformationMatrix.setColumn(3, translation);
 	return transformationMatrix;
 }
 
--- a/src/widgets/matrixeditor.h	Sun Jun 17 14:06:03 2018 +0300
+++ b/src/widgets/matrixeditor.h	Sun Jun 17 16:13:24 2018 +0300
@@ -17,9 +17,12 @@
 	QMatrix4x4 matrix() const;
 	void setMatrix(const QMatrix4x4& matrix);
 
+signals:
+	void matrixChanged(const QMatrix4x4& matrix);
+
 private slots:
 	void scalingChanged();
-	void matrixChanged();
+	void matrix3x3Changed();
 
 private:
 	QDoubleSpinBox* matrixCell(int row, int column) const;

mercurial