Mon, 04 Jun 2018 23:12:40 +0300
added work done on cylinders
--- a/CMakeLists.txt Wed May 30 22:31:06 2018 +0300 +++ b/CMakeLists.txt Mon Jun 04 23:12:40 2018 +0300 @@ -79,6 +79,7 @@ src/geometry/plane.cpp src/linetypes/comment.cpp src/linetypes/conditionaledge.cpp + src/linetypes/cylinder.cpp src/linetypes/edgeline.cpp src/linetypes/empty.cpp src/linetypes/modelobject.cpp @@ -96,6 +97,7 @@ src/types/vertex.cpp src/widgets/doublespinbox.cpp src/widgets/headeredit.cpp + src/widgets/matrixeditor.cpp src/widgets/vertexobjecteditor.cpp ) @@ -157,6 +159,7 @@ src/geometry/plane.h src/linetypes/comment.h src/linetypes/conditionaledge.h + src/linetypes/cylinder.h src/linetypes/edgeline.h src/linetypes/empty.h src/linetypes/modelobject.h @@ -175,6 +178,7 @@ src/types/vertex.h src/widgets/doublespinbox.h src/widgets/headeredit.h + src/widgets/matrixeditor.h src/widgets/vertexobjecteditor.h ) @@ -202,6 +206,7 @@ src/partdownloader.ui src/widgets/vertexobjecteditor.ui src/widgets/headeredit.ui + src/widgets/matrixeditor.ui ) set (LDFORGE_OTHER_FILES @@ -234,6 +239,7 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG") endif() + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Wpedantic -Werror=switch") endif() qt5_add_resources (LDFORGE_QRC ${LDFORGE_RESOURCES})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dialogs/cylindereditor.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -0,0 +1,14 @@ +#include "cylindereditor.h" +#include "ui_cylindereditor.h" + +CylinderEditor::CylinderEditor(QWidget *parent) : + QDialog(parent), + ui(new Ui::CylinderEditor) +{ + ui->setupUi(this); +} + +CylinderEditor::~CylinderEditor() +{ + delete ui; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dialogs/cylindereditor.h Mon Jun 04 23:12:40 2018 +0300 @@ -0,0 +1,22 @@ +#ifndef CYLINDEREDITOR_H +#define CYLINDEREDITOR_H + +#include <QDialog> + +namespace Ui { +class CylinderEditor; +} + +class CylinderEditor : public QDialog +{ + Q_OBJECT + +public: + explicit CylinderEditor(QWidget *parent = 0); + ~CylinderEditor(); + +private: + Ui::CylinderEditor *ui; +}; + +#endif // CYLINDEREDITOR_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dialogs/cylindereditor.ui Mon Jun 04 23:12:40 2018 +0300 @@ -0,0 +1,68 @@ +<?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>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="geometry"> + <rect> + <x>30</x> + <y>240</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> + <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/dialogs/subfilereferenceeditor.cpp Wed May 30 22:31:06 2018 +0300 +++ b/src/dialogs/subfilereferenceeditor.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -30,11 +30,10 @@ { this->ui.setupUi(this); this->ui.referenceName->setText(reference->referenceName()); + this->ui.matrixEditor->setPosition(reference->position()); + this->ui.matrixEditor->setMatrix(reference->transformationMatrix()); this->color = reference->color(); ::setColorButton(this->ui.colorButton, this->color); - this->ui.positionX->setValue(reference->position().x); - this->ui.positionY->setValue(reference->position().y); - this->ui.positionZ->setValue(reference->position().z); connect( this->ui.colorButton, &QPushButton::clicked, @@ -44,22 +43,6 @@ ::setColorButton(this->ui.colorButton, this->color); } ); - for (int i : {0, 1, 2}) - for (int j : {0, 1, 2}) - { - QLayoutItem* item = this->ui.matrixLayout->itemAtPosition(i, j); - QDoubleSpinBox* spinbox = item ? qobject_cast<QDoubleSpinBox*>(item->widget()) : nullptr; - withSignalsBlocked(spinbox, [&]() - { - spinbox->setValue(reference->transformationMatrix()(i, j)); - }); - connect( - spinbox, - qOverload<double>(&QDoubleSpinBox::valueChanged), - this, - &SubfileReferenceEditor::matrixChanged - ); - } connect( this->ui.primitivesTreeView, &QTreeView::clicked, @@ -72,26 +55,6 @@ this->ui.referenceName->setText(primitiveName.toString()); } ); - - for (QDoubleSpinBox* spinbox : {this->ui.scalingX, this->ui.scalingY, this->ui.scalingZ}) - { - connect( - spinbox, - qOverload<double>(&QDoubleSpinBox::valueChanged), - this, - &SubfileReferenceEditor::scalingChanged - ); - } - - // Fill in the initial scaling values - for (int column : {0, 1, 2}) - { - QDoubleSpinBox* spinbox = this->vectorElement(column); - withSignalsBlocked(spinbox, [&]() - { - spinbox->setValue(this->matrixScaling(column)); - }); - } } SubfileReferenceEditor::~SubfileReferenceEditor() @@ -99,61 +62,12 @@ delete &this->ui; } -/* - * Returns a spinbox from the matrix grid at position (row, column). - * Row and column must be within [0, 2]. - */ -QDoubleSpinBox* SubfileReferenceEditor::matrixCell(int row, int column) const -{ - if (qBound(0, row, 2) != row or qBound(0, column, 2) != column) - { - throw std::out_of_range {"bad row and column values"}; - } - else - { - QLayoutItem* item = this->ui.matrixLayout->itemAtPosition(row, column); - return item ? qobject_cast<QDoubleSpinBox*>(item->widget()) : nullptr; - } -} - -/* - * Returns a spinbox for the vector element at the given position - * Index must be within [0, 2] - */ -QDoubleSpinBox* SubfileReferenceEditor::vectorElement(int index) -{ - switch (index) - { - case 0: - return this->ui.scalingX; - - case 1: - return this->ui.scalingY; - - case 2: - return this->ui.scalingZ; - - default: - throw std::out_of_range {"bad index"}; - } -} - void SubfileReferenceEditor::accept() { this->reference->setReferenceName(this->ui.referenceName->text()); - Matrix transformationMatrix; - for (int i : {0, 1, 2}) - for (int j : {0, 1, 2}) - { - transformationMatrix(i, j) = this->matrixCell(i, j)->value(); - } - this->reference->setTransformationMatrix(transformationMatrix); - this->reference->setPosition({ - this->ui.positionX->value(), - this->ui.positionY->value(), - this->ui.positionZ->value() - }); this->reference->setColor(this->color); + this->reference->setTransformationMatrix(this->ui.matrixEditor->matrix()); + this->reference->setPosition(this->ui.matrixEditor->position()); QDialog::accept(); } @@ -161,77 +75,3 @@ { this->ui.primitivesTreeView->setModel(primitives); } - -double SubfileReferenceEditor::matrixScaling(int column) const -{ - return sqrt( - pow(this->matrixCell(0, column)->value(), 2) + - pow(this->matrixCell(1, column)->value(), 2) + - pow(this->matrixCell(2, column)->value(), 2) - ); -} - -/* - * Updates the appropriate matrix column when a scaling vector element is changed. - */ -void SubfileReferenceEditor::scalingChanged() -{ - for (int column : {0, 1, 2}) - { - if (this->sender() == this->vectorElement(column)) - { - double oldScaling = this->matrixScaling(column); - double newScaling = static_cast<QDoubleSpinBox*>(this->sender())->value(); - - if (not qFuzzyCompare(newScaling, 0.0)) - { - for (int row : {0, 1, 2}) - { - double cellValue = this->matrixCell(row, column)->value(); - cellValue *= newScaling / oldScaling; - QDoubleSpinBox* cellWidget = this->matrixCell(row, column); - withSignalsBlocked(cellWidget, [&]() - { - cellWidget->setValue(cellValue); - }); - } - } - - break; - } - } -} - -/* - * Finds the position for the given cell widget. - */ -QPair<int, int> SubfileReferenceEditor::cellPosition(QDoubleSpinBox* cellWidget) -{ - for (int row : {0, 1, 2}) - for (int column : {0, 1, 2}) - { - if (this->matrixCell(row, column) == cellWidget) - return {row, column}; - } - - throw std::out_of_range {"widget is not in the matrix"}; -} - -/* - * Updates the appropriate scaling vector element when a matrix cell is changed. - */ -void SubfileReferenceEditor::matrixChanged() -{ - QDoubleSpinBox* cellWidget = static_cast<QDoubleSpinBox*>(this->sender()); - - try - { - int column = this->cellPosition(cellWidget).second; - QDoubleSpinBox* spinbox = this->vectorElement(column); - withSignalsBlocked(spinbox, [&]() - { - spinbox->setValue(this->matrixScaling(column)); - }); - } - catch (const std::out_of_range&) {} -}
--- a/src/dialogs/subfilereferenceeditor.h Wed May 30 22:31:06 2018 +0300 +++ b/src/dialogs/subfilereferenceeditor.h Mon Jun 04 23:12:40 2018 +0300 @@ -33,16 +33,7 @@ void accept() override; void setPrimitivesTree(class PrimitiveManager* primitives); -private slots: - void scalingChanged(); - void matrixChanged(); - private: - QDoubleSpinBox* matrixCell(int row, int column) const; - double matrixScaling(int column) const; - QPair<int, int> cellPosition(QDoubleSpinBox* cellWidget); - QDoubleSpinBox* vectorElement(int index); - class Ui_SubfileReferenceEditor& ui; class LDSubfileReference* const reference; LDColor color;
--- a/src/dialogs/subfilereferenceeditor.ui Wed May 30 22:31:06 2018 +0300 +++ b/src/dialogs/subfilereferenceeditor.ui Mon Jun 04 23:12:40 2018 +0300 @@ -13,7 +13,7 @@ <property name="windowTitle"> <string>Dialog</string> </property> - <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0"> + <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,0"> <item> <widget class="QTreeView" name="primitivesTreeView"/> </item> @@ -60,269 +60,21 @@ </item> </layout> </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Position:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="DoubleSpinBox" name="positionX"> - <property name="prefix"> - <string>𝑥 = </string> - </property> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item> - <widget class="DoubleSpinBox" name="positionY"> - <property name="prefix"> - <string>𝑦 = </string> - </property> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item> - <widget class="DoubleSpinBox" name="positionZ"> - <property name="prefix"> - <string>𝑧 = </string> - </property> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - </layout> - </item> - <item row="3" column="1"> - <widget class="Line" name="line"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Transformation matrix:</string> - </property> - </widget> - </item> - <item row="4" column="1"> - <layout class="QGridLayout" name="matrixLayout"> - <item row="1" column="1"> - <widget class="DoubleSpinBox" name="matrixE"> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="DoubleSpinBox" name="matrixA"> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="DoubleSpinBox" name="matrixG"> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="DoubleSpinBox" name="matrixH"> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item row="2" column="2"> - <widget class="DoubleSpinBox" name="matrixI"> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="DoubleSpinBox" name="matrixF"> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="DoubleSpinBox" name="matrixC"> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="DoubleSpinBox" name="matrixD"> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="DoubleSpinBox" name="matrixB"> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>-10000.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - </layout> - </item> - <item row="6" column="0"> - <widget class="QLabel" name="label_5"> - <property name="text"> - <string>Scaling vector:</string> - </property> - </widget> - </item> - <item row="6" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_3"> - <item> - <widget class="DoubleSpinBox" name="scalingX"> - <property name="prefix"> - <string>𝑥 × </string> - </property> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>0.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item> - <widget class="DoubleSpinBox" name="scalingY"> - <property name="prefix"> - <string>𝑦 × </string> - </property> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>0.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - <item> - <widget class="DoubleSpinBox" name="scalingZ"> - <property name="prefix"> - <string>𝑧 × </string> - </property> - <property name="decimals"> - <number>5</number> - </property> - <property name="minimum"> - <double>0.000000000000000</double> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - </widget> - </item> - </layout> - </item> - <item row="5" column="1"> - <widget class="Line" name="line_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> </layout> </item> <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Transformation</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="MatrixEditor" name="matrixEditor" native="true"/> + </item> + </layout> + </widget> + </item> + <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -336,30 +88,16 @@ </widget> <customwidgets> <customwidget> - <class>DoubleSpinBox</class> - <extends>QDoubleSpinBox</extends> - <header>widgets/doublespinbox.h</header> + <class>MatrixEditor</class> + <extends>QWidget</extends> + <header>widgets/matrixeditor.h</header> + <container>1</container> </customwidget> </customwidgets> <tabstops> <tabstop>primitivesTreeView</tabstop> <tabstop>referenceName</tabstop> <tabstop>colorButton</tabstop> - <tabstop>positionX</tabstop> - <tabstop>positionY</tabstop> - <tabstop>positionZ</tabstop> - <tabstop>matrixA</tabstop> - <tabstop>matrixB</tabstop> - <tabstop>matrixC</tabstop> - <tabstop>matrixD</tabstop> - <tabstop>matrixE</tabstop> - <tabstop>matrixF</tabstop> - <tabstop>matrixG</tabstop> - <tabstop>matrixH</tabstop> - <tabstop>matrixI</tabstop> - <tabstop>scalingX</tabstop> - <tabstop>scalingY</tabstop> - <tabstop>scalingZ</tabstop> </tabstops> <resources/> <connections>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/generics/vectormodel.h Mon Jun 04 23:12:40 2018 +0300 @@ -0,0 +1,4 @@ +#ifndef VECTORMODEL_H +#define VECTORMODEL_H + +#endif // VECTORMODEL_H
--- a/src/glcompiler.cpp Wed May 30 22:31:06 2018 +0300 +++ b/src/glcompiler.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -373,20 +373,6 @@ break; } - // TODO: try use interfaces to remove these special treatments? - case LDObjectType::SubfileReference: - { - LDSubfileReference* subfileReference = static_cast<LDSubfileReference*>(object); - auto data = subfileReference->inlinePolygons( - m_documents, - m_renderer->model()->winding() - ); - - for (LDPolygon& poly : data) - compilePolygon (poly, index, info); - break; - } - case LDObjectType::BezierCurve: { LDBezierCurve* curve = static_cast<LDBezierCurve*>(object); @@ -396,6 +382,13 @@ break; default: + if (object->isRasterizable()) + { + auto data = object->rasterizePolygons(m_documents, m_renderer->model()->winding()); + + for (LDPolygon& poly : data) + compilePolygon(poly, index, info); + } break; }
--- a/src/glcompiler.h Wed May 30 22:31:06 2018 +0300 +++ b/src/glcompiler.h Mon Jun 04 23:12:40 2018 +0300 @@ -64,7 +64,7 @@ const QModelIndex& polygonOwnerIndex, ObjectVboData& objectInfo ); - Q_SLOT void compileObject (const QModelIndex &index); + Q_SLOT void compileObject(const QModelIndex &index); QColor getColorForPolygon( LDPolygon& poly, const QModelIndex& polygonOwnerIndex,
--- a/src/glrenderer.cpp Wed May 30 22:31:06 2018 +0300 +++ b/src/glrenderer.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -247,6 +247,12 @@ void GLRenderer::initializeGL() { initializeOpenGLFunctions(); + + if (glGetError() != GL_NO_ERROR) + { + abort(); + } + setBackground(); glLineWidth (config::lineThickness()); glLineStipple (1, 0x6666);
--- a/src/lddocument.cpp Wed May 30 22:31:06 2018 +0300 +++ b/src/lddocument.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -105,7 +105,7 @@ m_tabIndex = value; } -const QList<LDPolygon>& LDDocument::polygonData() const +const QVector<LDPolygon>& LDDocument::polygonData() const { return m_polygonData; } @@ -505,7 +505,7 @@ // ============================================================================= // -QList<LDPolygon> LDDocument::inlinePolygons() +QVector<LDPolygon> LDDocument::inlinePolygons() { initializeCachedData(); return polygonData(); @@ -534,7 +534,7 @@ if (deep and object->type() == LDObjectType::SubfileReference) { LDSubfileReference* reference = static_cast<LDSubfileReference*>(object); - reference->inlineContents( + reference->rasterize( documentManager(), this->winding(), model,
--- a/src/lddocument.h Wed May 30 22:31:06 2018 +0300 +++ b/src/lddocument.h Mon Jun 04 23:12:40 2018 +0300 @@ -100,13 +100,13 @@ EditHistory* history() const; void initializeCachedData(); void inlineContents(Model& model, bool deep, bool renderinline); - QList<LDPolygon> inlinePolygons(); + QVector<LDPolygon> inlinePolygons(); const QSet<Vertex>& inlineVertices(); bool isFrozen() const; bool isSafeToClose(); QString name() const; void objectRemoved(LDObject* object, int index); - const QList<LDPolygon>& polygonData() const; + const QVector<LDPolygon>& polygonData() const; void recountTriangles(); void redo(); void redoVertices(); @@ -139,7 +139,7 @@ long m_savePosition; int m_tabIndex; int m_triangleCount; - QList<LDPolygon> m_polygonData; + QVector<LDPolygon> m_polygonData; QMap<LDObject*, QSet<Vertex>> m_objectVertices; QSet<Vertex> m_vertices; DocumentManager* m_manager;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/linetypes/cylinder.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -0,0 +1,133 @@ +#include "../algorithms/geometry.h" +#include "../glShared.h" +#include "../model.h" +#include "../algorithms/invert.h" +#include "cylinder.h" +#include "quadrilateral.h" +#include "primitives.h" + +QString LDCylinder::buildFilename() const +{ + int numerator = this->m_segments; + int denominator = this->m_divisions; + QString prefix; + + if (m_divisions != MediumResolution) + prefix = QString::number(m_divisions) + '\\'; + + simplify(numerator, denominator); + return format("%1%2-%3cyli.dat", prefix, numerator, denominator); +} + +LDCylinder::LDCylinder( + int segments, + int divisions, + const Matrix& transformationMatrix, + const Vertex& position +) : + LDMatrixObject {transformationMatrix, position}, + m_segments {segments}, + m_divisions {divisions} {} + +QString LDCylinder::asText() const +{ + return LDSubfileReference(buildFilename(), transformationMatrix(), position()).asText(); +} + +void LDCylinder::getVertices(DocumentManager* /* context */, QSet<Vertex>& vertices) const +{ + int endSegment = (m_segments == m_divisions) ? m_segments : m_segments + 1; + + for (int i = 0; i < endSegment; i += 1) + { + QPointF point2d = pointOnLDrawCircumference(i, m_divisions); + + for (double y_value : {0.0, 1.0}) + { + Vertex vertex {point2d.x(), y_value, point2d.y()}; + vertex.transform(transformationMatrix(), position()); + vertices.insert(vertex); + } + } +} + +void LDCylinder::rasterize( + DocumentManager* context, + Winding /* parentWinding */, + Model& model, + bool /* deep */, + bool /* render */ +) { + Model cylinderBody {context}; + buildPrimitiveBody(cylinderBody); + + for (LDObject* object : cylinderBody.objects()) + { + for (int i = 0; i < object->numVertices(); i += 1) + { + Vertex vertex = object->vertex(i); + vertex.transform(transformationMatrix(), position()); + object->setVertex(i, vertex); + } + } + + model.merge(cylinderBody); +} + +QVector<LDPolygon> LDCylinder::rasterizePolygons(DocumentManager* context, Winding winding) +{ + Model cylinderBody {context}; + buildPrimitiveBody(cylinderBody, winding); + QVector<LDPolygon> result; + + for (LDObject* object : cylinderBody.objects()) + { + for (int i = 0; i < object->numVertices(); i += 1) + { + Vertex vertex = object->vertex(i); + vertex.transform(transformationMatrix(), position()); + object->setVertex(i, vertex); + } + + LDPolygon* polygon = object->getPolygon(); + + if (polygon) + { + if (shouldInvert(winding, context)) + invertPolygon(*polygon); + + result.append(*polygon); + } + + delete polygon; + } + + return result; +} + +void LDCylinder::buildPrimitiveBody(Model& model, Winding winding) const +{ + PrimitiveModel primitive; + primitive.type = PrimitiveModel::Cylinder; + primitive.segments = m_segments; + primitive.divisions = m_divisions; + primitive.ringNumber = 0; + primitive.generateCylinder(model, winding); +} + +QString LDCylinder::objectListText() const +{ + QString result = format("Cylinder %1 %2, (", m_segments / m_divisions, position().toString(true)); + + for (int i = 0; i < 9; ++i) + result += format("%1%2", transformationMatrix().value(i), (i != 8) ? " " : ""); + + result += ')'; + return result; +} + +void LDCylinder::serialize(class Serializer& serializer) +{ + LDMatrixObject::serialize(serializer); + serializer << m_segments << m_divisions; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/linetypes/cylinder.h Mon Jun 04 23:12:40 2018 +0300 @@ -0,0 +1,39 @@ +#pragma once +#include "modelobject.h" + +class LDCylinder : public LDMatrixObject +{ +public: + static const LDObjectType SubclassType = LDObjectType::Cylinder; + + LDCylinder() = default; + LDCylinder(int segments, int divisions, const Matrix& transformationMatrix, const Vertex& position); + + virtual LDObjectType type() const override + { + return SubclassType; + } + + virtual QString asText() const override; + virtual void getVertices(DocumentManager *context, QSet<Vertex>& verts) const override; + bool isRasterizable() const override { return true; } + void rasterize( + DocumentManager* context, + Winding parentWinding, + Model& model, + bool deep, + bool render + ) override; + QVector<LDPolygon> rasterizePolygons(DocumentManager* context, Winding parentWinding) override; + QString objectListText() const override; + int triangleCount(DocumentManager*) const override { return 2 * m_segments; } + QString typeName() const override { return "cylinder"; } + void serialize(class Serializer& serializer) override; + +private: + QString buildFilename() const; + void buildPrimitiveBody(Model& model, Winding winding = CounterClockwise) const; + + int m_segments; + int m_divisions; +};
--- a/src/linetypes/modelobject.cpp Wed May 30 22:31:06 2018 +0300 +++ b/src/linetypes/modelobject.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -31,6 +31,7 @@ #include "conditionaledge.h" #include "comment.h" #include "empty.h" +#include "cylinder.h" // List of all LDObjects QMap<qint32, LDObject*> g_allObjects; @@ -146,18 +147,41 @@ obj->setColor (parentcolor); } -bool shouldInvert(LDSubfileReference* reference, Winding winding, DocumentManager* context) +/* + * Returns whether or not a compound object should be inverted. + */ +bool LDMatrixObject::shouldInvert(Winding winding, DocumentManager* context) { bool result = false; - result ^= (reference->isInverted()); - result ^= (reference->transformationMatrix().determinant() < 0); - result ^= (reference->fileInfo(context)->winding() != winding); + result ^= (isInverted()); + result ^= (transformationMatrix().determinant() < 0); + result ^= (nativeWinding(context) != winding); return result; } +/* + * The winding used by the object's geometry. By default it's CCW but some documents referenced by + * a subfile reference may use CW geometry. + * + * Since the native winding of a subfile reference depends on the actual document it references, + * determining the winding requires the libraries for reference. + */ +Winding LDObject::nativeWinding(DocumentManager* /*context*/) const +{ + return CounterClockwise; +} + +/* + * Reimplementation of LDObject::nativeWinding for subfile references + */ +Winding LDSubfileReference::nativeWinding(DocumentManager* context) const +{ + return fileInfo(context)->winding(); +} + // ============================================================================= // ----------------------------------------------------------------------------- -void LDSubfileReference::inlineContents( +void LDSubfileReference::rasterize( DocumentManager* context, Winding parentWinding, Model& model, @@ -174,7 +198,7 @@ // Transform the objects for (LDObject* object : inlined) { - if (::shouldInvert(this, parentWinding, context)) + if (shouldInvert(parentWinding, context)) ::invert(object, context); TransformObject(object, transformationMatrix(), position(), color()); @@ -230,20 +254,20 @@ // ============================================================================= // -QList<LDPolygon> LDSubfileReference::inlinePolygons(DocumentManager* context, Winding parentWinding) +QVector<LDPolygon> LDSubfileReference::rasterizePolygons(DocumentManager* context, Winding parentWinding) { LDDocument* file = fileInfo(context); if (file) { - QList<LDPolygon> data = fileInfo(context)->inlinePolygons(); + QVector<LDPolygon> data = fileInfo(context)->inlinePolygons(); for (LDPolygon& entry : data) { for (int i = 0; i < entry.numVertices(); ++i) entry.vertices[i].transform (transformationMatrix(), position()); - if (::shouldInvert(this, parentWinding, context)) + if (shouldInvert(parentWinding, context)) ::invertPolygon(entry); } @@ -332,6 +356,9 @@ case LDObjectType::BezierCurve: return new LDBezierCurve {}; + case LDObjectType::Cylinder: + return new LDCylinder {}; + case LDObjectType::_End: break; } @@ -518,6 +545,11 @@ } } +QVector<LDPolygon> LDObject::rasterizePolygons(DocumentManager*, Winding) +{ + return {}; +} + QString LDError::objectListText() const { return "ERROR: " + asText();
--- a/src/linetypes/modelobject.h Wed May 30 22:31:06 2018 +0300 +++ b/src/linetypes/modelobject.h Mon Jun 04 23:12:40 2018 +0300 @@ -40,6 +40,7 @@ Error, // Object is the result of failed parsing Empty, // Object represents an empty line BezierCurve, // Object represents a Bézier curve + Cylinder, _End }; @@ -69,12 +70,15 @@ virtual bool hasMatrix() const; // Does this object have a matrix and position? (see LDMatrixObject) virtual bool isColored() const; bool isHidden() const; + virtual bool isRasterizable() const { return false; } // Can this object be rasterized? virtual bool isScemantic() const; // Does this object have meaning in the part model? void move (const QVector3D vector); virtual int numVertices() const; virtual int numPolygonVertices() const; virtual QString objectListText() const; QColor randomColor() const; + virtual void rasterize(DocumentManager*, Winding, Model&, bool, bool) {} + virtual QVector<LDPolygon> rasterizePolygons(DocumentManager*, Winding); void setColor (LDColor color); void setHidden (bool value); void setVertex (int i, const Vertex& vert); @@ -92,6 +96,7 @@ void modified(const LDObjectState& before, const LDObjectState& after); protected: + virtual Winding nativeWinding(DocumentManager*) const; template<typename T> void changeProperty(T* property, const T& value); @@ -114,6 +119,7 @@ LDMatrixObject() = default; LDMatrixObject(const Matrix& transformationMatrix, const Vertex& pos); + bool hasMatrix() const override { return true; } const Vertex& position() const; void setCoordinate (const Axis ax, double value); void setPosition (const Vertex& a); @@ -121,6 +127,9 @@ const Matrix& transformationMatrix() const; void serialize(class Serializer& serializer) override; +protected: + bool shouldInvert(Winding winding, DocumentManager* context); + private: Vertex m_position; Matrix m_transformationMatrix = Matrix::identity; @@ -171,23 +180,26 @@ virtual QString asText() const override; LDDocument* fileInfo(DocumentManager *context) const; + bool isRasterizable() const override { return true; } virtual void getVertices(DocumentManager *context, QSet<Vertex>& verts) const override; - void inlineContents( + void rasterize( DocumentManager* context, Winding parentWinding, Model& model, bool deep, bool render - ); - QList<LDPolygon> inlinePolygons(DocumentManager* context, Winding parentWinding); + ) override; + QVector<LDPolygon> rasterizePolygons(DocumentManager* context, Winding parentWinding) override; QString objectListText() const override; QString referenceName() const; int triangleCount(DocumentManager *context) const override; - bool hasMatrix() const override { return true; } QString typeName() const override { return "subfilereference"; } void serialize(class Serializer& serializer) override; void setReferenceName(const QString& newReferenceName); +protected: + Winding nativeWinding(DocumentManager* context) const override; + private: QString m_referenceName; };
--- a/src/parser.cpp Wed May 30 22:31:06 2018 +0300 +++ b/src/parser.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -24,6 +24,7 @@ #include "linetypes/empty.h" #include "linetypes/quadrilateral.h" #include "linetypes/triangle.h" +#include "linetypes/cylinder.h" /* * Constructs an LDraw parser @@ -381,13 +382,33 @@ CheckTokenCount (tokens, 15); CheckTokenNumbers (tokens, 1, 13); - Vertex referncePosition = parseVertex (tokens, 2); // 2 - 4 + Vertex displacement = parseVertex (tokens, 2); // 2 - 4 Matrix transform; + QString referenceName = tokens[14]; for (int i = 0; i < 9; ++i) transform.value(i) = tokens[i + 5].toDouble(); // 5 - 13 - LDSubfileReference* obj = model.emplaceAt<LDSubfileReference>(position, tokens[14], transform, referncePosition); + static const QRegExp cylinderRegexp {R"((?:(\d+)\\)?(\d+)-(\d+)cyli\.dat)"}; + LDObject* obj; + + if (cylinderRegexp.exactMatch(referenceName)) + { + int resolution = MediumResolution; + + if (not cylinderRegexp.capturedTexts()[1].isEmpty()) + resolution = cylinderRegexp.capturedTexts()[1].toInt(); + + int numerator = cylinderRegexp.capturedTexts()[2].toInt(); + int denominator = cylinderRegexp.capturedTexts()[3].toInt(); + int segments = (numerator * resolution) / denominator; + obj = model.emplaceAt<LDCylinder>(position, segments, resolution, transform, displacement); + } + else + { + obj = model.emplaceAt<LDSubfileReference>(position, referenceName, transform, displacement); + } + obj->setColor (tokens[1].toInt(nullptr, 0)); return obj; }
--- a/src/primitives.cpp Wed May 30 22:31:06 2018 +0300 +++ b/src/primitives.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -263,7 +263,7 @@ // This actual value is given by: hypot(0.0761, 0.3827) static const double chordLength = 0.3901929010117944; -void PrimitiveModel::generateCylinder(Model& model) const +void PrimitiveModel::generateCylinder(Model& model, Winding winding) const { Q_ASSERT(this->type == Cylinder); auto circle = makeCircle(this->segments, this->divisions, 1); @@ -289,12 +289,14 @@ double x1 = circle[i].x2(); double z0 = circle[i].y1(); double z1 = circle[i].y2(); + double y1 = (winding == CounterClockwise) ? 0.0 : 1.0; + double y2 = (winding == CounterClockwise) ? 1.0 : 0.0; LDQuadrilateral* quad = model.emplace<LDQuadrilateral>( - Vertex {x1, 0.0, z1}, - Vertex {x0, 0.0, z0}, - Vertex {x0, 1.0, z0}, - Vertex {x1, 1.0, z1} + Vertex {x1, y1, z1}, + Vertex {x0, y1, z0}, + Vertex {x0, y2, z0}, + Vertex {x1, y2, z1} ); quad->setColor(MainColor); }
--- a/src/primitives.h Wed May 30 22:31:06 2018 +0300 +++ b/src/primitives.h Mon Jun 04 23:12:40 2018 +0300 @@ -57,7 +57,7 @@ QString typeName() const; void generateBody(Model& model) const; - void generateCylinder(Model& model) const; + void generateCylinder(Model& model, Winding winding = CounterClockwise) const; static QString typeName(Type type); QString makeFileName(FilenameStyle style) const; };
--- a/src/toolsets/basictoolset.cpp Wed May 30 22:31:06 2018 +0300 +++ b/src/toolsets/basictoolset.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -104,34 +104,37 @@ void BasicToolset::doInline (bool deep) { - for (LDSubfileReference* reference : filterByType<LDSubfileReference> (selectedObjects())) + for (LDObject* object : selectedObjects()) { - // Get the index of the subfile so we know where to insert the - // inlined contents. - QPersistentModelIndex referenceIndex = currentDocument()->indexOf(reference); - int row = referenceIndex.row(); - - if (referenceIndex.isValid()) + if (object->isRasterizable()) { - Model inlined {m_documents}; - reference->inlineContents( - m_documents, - currentDocument()->winding(), - inlined, - deep, - false - ); + // Get the index of the subfile so we know where to insert the + // inlined contents. + QPersistentModelIndex referenceIndex = currentDocument()->indexOf(object); + int row = referenceIndex.row(); - // Merge in the inlined objects - for (LDObject* inlinedObject : inlined.objects()) + if (referenceIndex.isValid()) { - currentDocument()->insertCopy(row, inlinedObject); - mainWindow()->select(currentDocument()->index(row)); - row += 1; + Model inlined {m_documents}; + object->rasterize( + m_documents, + currentDocument()->winding(), + inlined, + deep, + false + ); + + // Merge in the inlined objects + for (LDObject* inlinedObject : inlined.objects()) + { + currentDocument()->insertCopy(row, inlinedObject); + mainWindow()->select(currentDocument()->index(row)); + row += 1; + } + + // Delete the subfile now as it's been inlined. + currentDocument()->removeRow(referenceIndex.row()); } - - // Delete the subfile now as it's been inlined. - currentDocument()->removeRow(referenceIndex.row()); } }
--- a/src/toolsets/extprogramtoolset.cpp Wed May 30 22:31:06 2018 +0300 +++ b/src/toolsets/extprogramtoolset.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -152,11 +152,10 @@ { for (LDObject* obj : objects) { - if (obj->type() == LDObjectType::SubfileReference) + if (obj->isRasterizable()) { - LDSubfileReference* ref = static_cast<LDSubfileReference*> (obj); Model model {m_documents}; - ref->inlineContents(m_documents, CounterClockwise, model, true, false); + obj->rasterize(m_documents, CounterClockwise, model, true, false); writeObjects(model.objects(), f); } else if (obj->type() == LDObjectType::BezierCurve)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/matrixeditor.cpp Mon Jun 04 23:12:40 2018 +0300 @@ -0,0 +1,202 @@ +#include "matrixeditor.h" +#include "ui_matrixeditor.h" + +MatrixEditor::MatrixEditor(const Matrix& matrix, const Vertex& position, QWidget *parent) : + QWidget {parent}, + ui {*new Ui_MatrixEditor} +{ + this->ui.setupUi(this); + this->setPosition(position); + this->setMatrix(matrix); + + for (int i : {0, 1, 2}) + for (int j : {0, 1, 2}) + { + connect( + matrixCell(i, j), + qOverload<double>(&QDoubleSpinBox::valueChanged), + this, + &MatrixEditor::matrixChanged + ); + } + + for (QDoubleSpinBox* spinbox : {this->ui.scalingX, this->ui.scalingY, this->ui.scalingZ}) + { + connect( + spinbox, + qOverload<double>(&QDoubleSpinBox::valueChanged), + this, + &MatrixEditor::scalingChanged + ); + } +} + +MatrixEditor::MatrixEditor(QWidget* parent) : + MatrixEditor {Matrix::identity, {0, 0, 0}, parent} {} + +MatrixEditor::~MatrixEditor() +{ + delete &this->ui; +} + +/* + * Returns a spinbox from the matrix grid at position (row, column). + * Row and column must be within [0, 2]. + */ +QDoubleSpinBox* MatrixEditor::matrixCell(int row, int column) const +{ + if (qBound(0, row, 2) != row or qBound(0, column, 2) != column) + { + throw std::out_of_range {"bad row and column values"}; + } + else + { + QLayoutItem* item = this->ui.matrixLayout->itemAtPosition(row, column); + return item ? qobject_cast<QDoubleSpinBox*>(item->widget()) : nullptr; + } +} + +/* + * Returns a spinbox for the vector element at the given position + * Index must be within [0, 2] + */ +QDoubleSpinBox* MatrixEditor::vectorElement(int index) +{ + switch (index) + { + case 0: + return this->ui.scalingX; + + case 1: + return this->ui.scalingY; + + case 2: + return this->ui.scalingZ; + + default: + throw std::out_of_range {"bad index"}; + } +} + +double MatrixEditor::matrixScaling(int column) const +{ + return sqrt( + pow(this->matrixCell(0, column)->value(), 2) + + pow(this->matrixCell(1, column)->value(), 2) + + pow(this->matrixCell(2, column)->value(), 2) + ); +} + +/* + * Updates the appropriate matrix column when a scaling vector element is changed. + */ +void MatrixEditor::scalingChanged() +{ + for (int column : {0, 1, 2}) + { + if (this->sender() == this->vectorElement(column)) + { + double oldScaling = this->matrixScaling(column); + double newScaling = static_cast<QDoubleSpinBox*>(this->sender())->value(); + + if (not qFuzzyCompare(newScaling, 0.0)) + { + for (int row : {0, 1, 2}) + { + double cellValue = this->matrixCell(row, column)->value(); + cellValue *= newScaling / oldScaling; + QDoubleSpinBox* cellWidget = this->matrixCell(row, column); + withSignalsBlocked(cellWidget, [&]() + { + cellWidget->setValue(cellValue); + }); + } + } + + break; + } + } +} + +/* + * Finds the position for the given cell widget. + */ +QPair<int, int> MatrixEditor::cellPosition(QDoubleSpinBox* cellWidget) +{ + for (int row : {0, 1, 2}) + for (int column : {0, 1, 2}) + { + if (this->matrixCell(row, column) == cellWidget) + return {row, column}; + } + + throw std::out_of_range {"widget is not in the matrix"}; +} + +/* + * Updates the appropriate scaling vector element when a matrix cell is changed. + */ +void MatrixEditor::matrixChanged() +{ + QDoubleSpinBox* cellWidget = static_cast<QDoubleSpinBox*>(this->sender()); + + try + { + int column = this->cellPosition(cellWidget).second; + QDoubleSpinBox* spinbox = this->vectorElement(column); + withSignalsBlocked(spinbox, [&]() + { + spinbox->setValue(this->matrixScaling(column)); + }); + } + catch (const std::out_of_range&) {} +} + +Vertex MatrixEditor::position() const +{ + return { + this->ui.positionX->value(), + this->ui.positionY->value(), + this->ui.positionZ->value() + }; +} + +Matrix MatrixEditor::matrix() const +{ + Matrix transformationMatrix; + + for (int i : {0, 1, 2}) + for (int j : {0, 1, 2}) + { + transformationMatrix(i, j) = this->matrixCell(i, j)->value(); + } + + return transformationMatrix; +} + +void MatrixEditor::setPosition(const Vertex& position) +{ + this->ui.positionX->setValue(position.x); + this->ui.positionY->setValue(position.y); + this->ui.positionZ->setValue(position.z); +} + +void MatrixEditor::setMatrix(const Matrix& matrix) +{ + for (int i : {0, 1, 2}) + for (int j : {0, 1, 2}) + { + QDoubleSpinBox* spinbox = matrixCell(i, j); + withSignalsBlocked(spinbox, [&](){ spinbox->setValue(matrix(i, j)); }); + } + + // Fill in the initial scaling values + for (int column : {0, 1, 2}) + { + QDoubleSpinBox* spinbox = this->vectorElement(column); + withSignalsBlocked(spinbox, [&]() + { + spinbox->setValue(this->matrixScaling(column)); + }); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/matrixeditor.h Mon Jun 04 23:12:40 2018 +0300 @@ -0,0 +1,37 @@ +#pragma once +#include <QWidget> +#include "../linetypes/modelobject.h" + +class QDoubleSpinBox; +class Ui_MatrixEditor; + +class MatrixEditor : public QWidget +{ + Q_OBJECT + +public: + MatrixEditor( + const Matrix& matrix = Matrix::identity, + const Vertex& position = {0, 0, 0}, + QWidget* parent = nullptr + ); + MatrixEditor(QWidget* parent); + ~MatrixEditor(); + + Vertex position() const; + Matrix matrix() const; + void setPosition(const Vertex& position); + void setMatrix(const Matrix& matrix); + +private slots: + void scalingChanged(); + void matrixChanged(); + +private: + QDoubleSpinBox* matrixCell(int row, int column) const; + double matrixScaling(int column) const; + QPair<int, int> cellPosition(QDoubleSpinBox* cellWidget); + QDoubleSpinBox* vectorElement(int index); + + Ui_MatrixEditor& ui; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/matrixeditor.ui Mon Jun 04 23:12:40 2018 +0300 @@ -0,0 +1,274 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MatrixEditor</class> + <widget class="QWidget" name="MatrixEditor"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>697</width> + <height>231</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="5" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Scaling vector:</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="DoubleSpinBox" name="scalingX"> + <property name="prefix"> + <string>𝑥 × </string> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>0.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item> + <widget class="DoubleSpinBox" name="scalingY"> + <property name="prefix"> + <string>𝑦 × </string> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>0.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item> + <widget class="DoubleSpinBox" name="scalingZ"> + <property name="prefix"> + <string>𝑧 × </string> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>0.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + </layout> + </item> + <item row="4" column="1"> + <layout class="QGridLayout" name="matrixLayout"> + <item row="1" column="2"> + <widget class="DoubleSpinBox" name="matrixF"> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="DoubleSpinBox" name="matrixI"> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="DoubleSpinBox" name="matrixA"> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="DoubleSpinBox" name="matrixC"> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="DoubleSpinBox" name="matrixG"> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="DoubleSpinBox" name="matrixH"> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="DoubleSpinBox" name="matrixB"> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="DoubleSpinBox" name="matrixD"> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="DoubleSpinBox" name="matrixE"> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="DoubleSpinBox" name="positionX"> + <property name="prefix"> + <string>𝑥 = </string> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item> + <widget class="DoubleSpinBox" name="positionY"> + <property name="prefix"> + <string>𝑦 = </string> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + <item> + <widget class="DoubleSpinBox" name="positionZ"> + <property name="prefix"> + <string>𝑧 = </string> + </property> + <property name="decimals"> + <number>5</number> + </property> + <property name="minimum"> + <double>-10000.000000000000000</double> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Position:</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Transformation matrix:</string> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>DoubleSpinBox</class> + <extends>QDoubleSpinBox</extends> + <header>widgets/doublespinbox.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui>