# HG changeset patch # User Teemu Piippo # Date 1529241204 -10800 # Node ID 37fffb682d2f15e929d49f8f32557a804dab9e8b # Parent d2bf2e59a3efa2705c8362851f72ab3e79fe3b19 Circular primitive editor complete diff -r d2bf2e59a3ef -r 37fffb682d2f CMakeLists.txt --- 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 diff -r d2bf2e59a3ef -r 37fffb682d2f src/dialogs/circularprimitiveeditor.cpp --- /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 . + */ + +#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::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(reinterpret_cast(&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(&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' +} diff -r d2bf2e59a3ef -r 37fffb682d2f src/dialogs/circularprimitiveeditor.h --- /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 . + */ + +#pragma once +#include +#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; +}; diff -r d2bf2e59a3ef -r 37fffb682d2f src/dialogs/circularprimitiveeditor.ui --- /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 @@ + + + CircularPrimitiveEditor + + + + 0 + 0 + 579 + 531 + + + + Dialog + + + + + + + + Segments: + + + + + + + + + + Divisions: + + + + + + + + 8 + + + + + 16 + + + + + 48 + + + + + + + + Colour: + + + + + + + + + + + + + + + + Type + + + + + + Disc + + + + + + + Disc negati&ve + + + + + + + Circle + + + + + + + C&ylinder + + + + + + + + + + Transformation + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Reset + + + + + + + + MatrixEditor + QWidget +
widgets/matrixeditor.h
+ 1 +
+ + ColorButton + QPushButton +
widgets/colorbutton.h
+
+
+ + + + buttonBox + accepted() + CircularPrimitiveEditor + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CircularPrimitiveEditor + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff -r d2bf2e59a3ef -r 37fffb682d2f src/dialogs/cylindereditor.cpp --- 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; -} diff -r d2bf2e59a3ef -r 37fffb682d2f src/dialogs/cylindereditor.h --- 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 - -class CylinderEditor : public QDialog -{ - Q_OBJECT - -public: - explicit CylinderEditor(QWidget* parent = nullptr); - ~CylinderEditor(); - -private: - class Ui_CylinderEditor& ui; -}; diff -r d2bf2e59a3ef -r 37fffb682d2f src/dialogs/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 @@ - - - CylinderEditor - - - - 0 - 0 - 656 - 659 - - - - Dialog - - - - - 190 - 570 - 341 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 130 - 270 - 381 - 251 - - - - Transformation - - - - - - - - - - - 200 - 110 - 76 - 25 - - - - - 8 - - - - - 16 - - - - - 48 - - - - - - - 80 - 110 - 81 - 17 - - - - Divisions: - - - - - - 90 - 70 - 63 - 17 - - - - Segments: - - - - - - 200 - 60 - 47 - 27 - - - - - - - 90 - 150 - 63 - 17 - - - - Colour: - - - - - - MatrixEditor - QWidget -
widgets/matrixeditor.h
- 1 -
-
- - - - buttonBox - accepted() - CylinderEditor - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - CylinderEditor - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
diff -r d2bf2e59a3ef -r 37fffb682d2f src/guiutilities.cpp --- 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(object); + CircularPrimitiveEditor editor {primitive, parent}; + editor.exec(); + } else if (object->type() == LDObjectType::Comment) { LDComment* comment = static_cast(object); diff -r d2bf2e59a3ef -r 37fffb682d2f src/linetypes/circularprimitive.cpp --- 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) diff -r d2bf2e59a3ef -r 37fffb682d2f src/linetypes/circularprimitive.h --- 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 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; diff -r d2bf2e59a3ef -r 37fffb682d2f src/linetypes/modelobject.cpp --- 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); diff -r d2bf2e59a3ef -r 37fffb682d2f src/linetypes/modelobject.h --- 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); diff -r d2bf2e59a3ef -r 37fffb682d2f src/widgets/matrixeditor.cpp --- 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(&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(&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(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; } diff -r d2bf2e59a3ef -r 37fffb682d2f src/widgets/matrixeditor.h --- 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;