# HG changeset patch # User Teemu Piippo # Date 1598299250 -10800 # Node ID c438a7db7c52ad40207ced35a2d8489a021abeee # Parent 7abaf1d64719e30ef707e891d259481ce7b4386a# Parent 9eb5e06f34c3ae9df66563efd6fd50380e2c3507 merge commit diff -r 9eb5e06f34c3 -r c438a7db7c52 CMakeLists.txt --- a/CMakeLists.txt Mon Aug 24 22:55:37 2020 +0300 +++ b/CMakeLists.txt Mon Aug 24 23:00:50 2020 +0300 @@ -59,7 +59,13 @@ src/settingseditor/settingseditor.cpp src/types/boundingbox.cpp src/ui/canvas.cpp + src/ui/multiplyfactordialog.cpp + src/ui/objecteditor.cpp + src/ui/polygonobjecteditor.cpp src/widgets/colorbutton.cpp + src/widgets/doublespinbox.cpp + src/widgets/matrixeditor.cpp + src/widgets/vec3editor.cpp ) set (LDFORGE_HEADERS src/basics.h @@ -93,6 +99,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 @@ -101,13 +109,22 @@ src/settingseditor/settingseditor.h src/types/boundingbox.h src/ui/canvas.h + src/ui/multiplyfactordialog.h + src/ui/objecteditor.h + src/ui/polygonobjecteditor.h src/widgets/colorbutton.h + src/widgets/doublespinbox.h + src/widgets/matrixeditor.h + src/widgets/vec3editor.h ) set (LDFORGE_FORMS src/document.ui src/mainwindow.ui src/settingseditor/librarieseditor.ui src/settingseditor/settingseditor.ui + src/ui/multiplyfactordialog.ui + src/widgets/matrixeditor.ui + src/widgets/vec3editor.ui ) set(LDFORGE_LOCALES diff -r 9eb5e06f34c3 -r c438a7db7c52 locale/fi.ts --- a/locale/fi.ts Mon Aug 24 22:55:37 2020 +0300 +++ b/locale/fi.ts Mon Aug 24 23:00:50 2020 +0300 @@ -136,90 +136,134 @@ Näkymä - + Quit Lopeta - + Open… Avaa... - + Ctrl+O Ctrl+O - + New Uusi - + Ctrl+N Ctrl+N - + Preferences… Asetukset... - + Normal colours Perusvärit - + BFC color coding BFC-värikoodaus - + Random colours Satunnaiset värit - + + Pick scene colours + + + + Open model Avaa malli - + LDraw models (*.ldr *.dat) LDraw-mallit (*.ldr *.dat) - + Problem loading references Ongelma viitteiden lataamisessa - + Problem opening file Ongelma tiedoston avaamisessa - + Could not open %1: %2 Ei voitu avata %1: %2 + MatrixEditor + + + Form + + + + + + + + × + + + + + MultiplyFactorDialog + + + Multiply with a scalar + + + + + Factor: + + + + + Invert + + + + + Preview + + + + QObject - + OpenGL error: %1 OpenGL-virhe: %1 - + OpenGL error OpenGL-virhe - + Damn it Hemmetti @@ -328,4 +372,32 @@ Näppäinyhdistelmät + + Vec3Editor + + + Form + + + + + x = + + + + + y = + + + + + z = + + + + + × + + + diff -r 9eb5e06f34c3 -r c438a7db7c52 locale/sv.ts --- a/locale/sv.ts Mon Aug 24 22:55:37 2020 +0300 +++ b/locale/sv.ts Mon Aug 24 23:00:50 2020 +0300 @@ -224,6 +224,40 @@ Random colours Slumpmässiga färger + + Pick scene colours + + + + + MatrixEditor + + Form + + + + × + + + + + MultiplyFactorDialog + + Multiply with a scalar + + + + Factor: + + + + Invert + + + + Preview + + PartRenderer @@ -335,6 +369,29 @@ + Vec3Editor + + Form + + + + x = + + + + y = + + + + z = + + + + × + + + + gl::Compiler Vertex shader: diff -r 9eb5e06f34c3 -r c438a7db7c52 src/basics.h --- a/src/basics.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/basics.h Mon Aug 24 23:00:50 2020 +0300 @@ -129,6 +129,17 @@ return static_cast(x); } +/** +* @brief casts floating point values to qreal, converting non-floating point values causes an error +* @param[in] x floating point value to cast +* @returns qreal +*/ +template +auto toQreal(T x) -> std::enable_if_t, qreal> +{ + return static_cast(x); +} + template inline QPoint toQPoint(const glm::vec& vec) { diff -r 9eb5e06f34c3 -r c438a7db7c52 src/colors.h --- a/src/colors.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/colors.h Mon Aug 24 23:00:50 2020 +0300 @@ -28,9 +28,11 @@ struct ldraw::Color { - qint32 index; + qint32 index = 0; }; +Q_DECLARE_METATYPE(ldraw::Color) + class ldraw::ColorTable { public: diff -r 9eb5e06f34c3 -r c438a7db7c52 src/document.cpp --- a/src/document.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/document.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -31,13 +31,15 @@ documents{documents}, colorTable{colorTable}, renderer{new Canvas{model, documents, colorTable, this}}, - ui{*new Ui::Document} + ui{*new Ui::Document}, + objectEditor{model, ldraw::NULL_ID, this} { this->ui.setupUi(this); this->ui.listView->setModel(model); - QVBoxLayout* layout = new QVBoxLayout; - layout->addWidget(this->renderer); - this->ui.viewportFrame->setLayout(layout); + this->ui.viewportFrame->setLayout(new QVBoxLayout{this->ui.listView}); + this->ui.viewportFrame->layout()->addWidget(this->renderer); + this->ui.objectEditorFrame->setLayout(new QVBoxLayout{this->ui.objectEditorFrame}); + this->ui.objectEditorFrame->layout()->addWidget(&this->objectEditor); this->setMouseTracking(true); connect(this->ui.splitter, &QSplitter::splitterMoved, this, &Document::splitterChanged); connect(this->renderer, &Canvas::newStatusText, this, &Document::newStatusText); @@ -53,7 +55,9 @@ selection.select(index, index); } } + QSignalBlocker blocker{this}; selectionModel->select(selection, QItemSelectionModel::ClearAndSelect); + this->selectionChanged(newSelection); }); connect(this->ui.listView->selectionModel(), &QItemSelectionModel::selectionChanged, [&](const QItemSelection& selected, const QItemSelection& deselected) @@ -64,7 +68,9 @@ return fn::map>(selection.indexes(), resolveIndex); }; this->renderer->handleSelectionChange(resolve(selected), resolve(deselected)); + this->selectionChanged(resolve(this->ui.listView->selectionModel()->selection())); }); + connect(this->model, &Model::dataChanged, this->renderer, qOverload<>(&Canvas::update)); } Document::~Document() @@ -86,3 +92,15 @@ { this->renderer->setRenderPreferences(newPreferences); } + +void Document::selectionChanged(const QSet& newSelection) +{ + if (newSelection.size() == 1) + { + this->objectEditor.setObjectId(*newSelection.begin()); + } + else + { + this->objectEditor.setObjectId(ldraw::NULL_ID); + } +} diff -r 9eb5e06f34c3 -r c438a7db7c52 src/document.h --- a/src/document.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/document.h Mon Aug 24 23:00:50 2020 +0300 @@ -20,6 +20,7 @@ #include #include #include "ui/canvas.h" +#include "ui/objecteditor.h" namespace Ui { @@ -45,9 +46,11 @@ void newStatusText(const QString& newStatusText); void splitterChanged(); private: + void selectionChanged(const QSet& newSelection); Model* model; DocumentManager* const documents; const ldraw::ColorTable& colorTable; Canvas* renderer; Ui::Document& ui; + ObjectEditor objectEditor; }; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/document.ui --- a/src/document.ui Mon Aug 24 22:55:37 2020 +0300 +++ b/src/document.ui Mon Aug 24 23:00:50 2020 +0300 @@ -19,6 +19,14 @@ Qt::Horizontal + + + QFrame::StyledPanel + + + QFrame::Raised + + true @@ -30,7 +38,7 @@ QAbstractItemView::SelectRows - + QFrame::StyledPanel diff -r 9eb5e06f34c3 -r c438a7db7c52 src/geometry.h --- a/src/geometry.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/geometry.h Mon Aug 24 23:00:50 2020 +0300 @@ -26,7 +26,7 @@ template struct Polygon { - glm::vec3 points[N]; + std::array points; }; template diff -r 9eb5e06f34c3 -r c438a7db7c52 src/gl/common.h --- a/src/gl/common.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/gl/common.h Mon Aug 24 23:00:50 2020 +0300 @@ -73,8 +73,7 @@ Triangle, Quadrilateral, ConditionalEdge - }; - Type type; + } type; glm::vec3 vertices[4]; ldraw::Color color; ldraw::id_t id; @@ -113,6 +112,16 @@ namespace gl { + constexpr Polygon::Type POLYGON_TYPES[] = + { + Polygon::Type::EdgeLine, + Polygon::Type::Triangle, + Polygon::Type::Quadrilateral, + Polygon::Type::ConditionalEdge + }; + + constexpr int NUM_POLYGON_TYPES = countof(POLYGON_TYPES); + inline Polygon edgeLine(const glm::vec3& v_1, const glm::vec3& v_2, ldraw::Color color, ldraw::id_t id) { return {Polygon::EdgeLine, {v_1, v_2}, color, id}; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/gl/compiler.cpp --- a/src/gl/compiler.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/gl/compiler.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -171,9 +171,8 @@ if (not this->initialized) { this->initializeOpenGLFunctions(); - for (int i = 0; i < gl::NUM_ARRAY_CLASSES; i += 1) + for (auto& object : this->glObjects) { - auto& object = this->glObjects[i]; object.program = new QOpenGLShaderProgram; gl::buildShaders(object.program, ::vertexShaderSource, ::fragmentShaderSource); object.program->bind(); @@ -186,12 +185,12 @@ { object.program->enableAttributeArray(k); } - constexpr int stride = sizeof(gl::Vertex); - object.program->setAttributeBuffer(0, GL_FLOAT, offsetof(gl::Vertex, position), 3, stride); - object.program->setAttributeBuffer(1, GL_FLOAT, offsetof(gl::Vertex, color), 4, stride); - object.program->setAttributeBuffer(2, GL_FLOAT, offsetof(gl::Vertex, normal), 3, stride); - glVertexAttribIPointer(3, 1, GL_INT, stride, reinterpret_cast(offsetof(gl::Vertex, id))); - glVertexAttribIPointer(4, 1, GL_INT, stride, reinterpret_cast(offsetof(gl::Vertex, selected))); + constexpr int stride = sizeof(Vertex); + object.program->setAttributeBuffer(0, GL_FLOAT, offsetof(Vertex, position), 3, stride); + object.program->setAttributeBuffer(1, GL_FLOAT, offsetof(Vertex, color), 4, stride); + object.program->setAttributeBuffer(2, GL_FLOAT, offsetof(Vertex, normal), 3, stride); + glVertexAttribIPointer(3, 1, GL_INT, stride, reinterpret_cast(offsetof(Vertex, id))); + glVertexAttribIPointer(4, 1, GL_INT, stride, reinterpret_cast(offsetof(Vertex, selected))); object.vertexArray.release(); object.buffer.release(); object.program->release(); @@ -203,13 +202,13 @@ void gl::Compiler::build(Model* model, DocumentManager* context, const gl::RenderPreferences& preferences) { this->boundingBox = {}; - std::vector vboData[gl::NUM_ARRAY_CLASSES]; + std::vector vboData[gl::NUM_POLYGON_TYPES]; const std::vector polygons = model->getPolygons(context); for (const gl::Polygon& polygon : polygons) { this->buildPolygon(polygon, vboData, preferences); } - for (int arrayId = 0; arrayId < gl::NUM_ARRAY_CLASSES; arrayId += 1) + for (int arrayId = 0; arrayId < gl::NUM_POLYGON_TYPES; arrayId += 1) { auto& buffer = this->glObjects[arrayId].buffer; auto& vector = vboData[arrayId]; @@ -237,18 +236,18 @@ return gl::ArrayClass::Lines; } -ldraw::id_t gl::Compiler::idFromColor(const std::array& data) +ldraw::id_t gl::Compiler::idFromColor(const std::array& data) { return {data[0] * std::int32_t{0x10000} + data[1] * std::int32_t{0x100} + data[2]}; } void gl::Compiler::buildPolygon( gl::Polygon polygon, - std::vector* vboData, + std::vector* vboData, const gl::RenderPreferences& preferences) { const gl::ArrayClass vboClass = classifyPolygon(polygon); - std::vector& vertexBuffer = vboData[static_cast(vboClass)]; + std::vector& vertexBuffer = vboData[static_cast(vboClass)]; auto vertexRing = iter::ring(polygon.vertices, polygon.numPolygonVertices()); reserveMore(vertexBuffer, polygon.numPolygonVertices()); const QColor color = this->getColorForPolygon(polygon, preferences); @@ -258,7 +257,7 @@ const glm::vec3& v2 = vertexRing[i]; const glm::vec3& v3 = vertexRing[i + 1]; this->boundingBox.consider(polygon.vertices[i]); - gl::Vertex& vertex = vertexBuffer.emplace_back(); + Vertex& vertex = vertexBuffer.emplace_back(); vertex.position = polygon.vertices[i]; vertex.normal = glm::normalize(glm::cross(v1 - v2, v3 - v2)); vertex.color = glm::vec4{color.redF(), color.greenF(), color.blueF(), color.alphaF()}; @@ -313,16 +312,17 @@ void gl::Compiler::setSelectedObjects(const QSet ids) { - for (int i = 0; i < gl::NUM_ARRAY_CLASSES; i += 1) + for (auto& object : this->glObjects) { - auto& vector = this->glObjects[i].cachedData; - for (gl::Vertex& vertex : vector) + std::vector& vector = object.cachedData; + for (Vertex& vertex : vector) { vertex.selected = (ids.contains({vertex.id})) ? 1 : 0; } const GLsizeiptr size = static_cast(vector.size() * sizeof vector[0]); - this->glObjects[i].buffer.bind(); + object.buffer.bind(); glBufferSubData(GL_ARRAY_BUFFER, 0, size, vector.data()); + object.buffer.release(); } } diff -r 9eb5e06f34c3 -r c438a7db7c52 src/gl/compiler.h --- a/src/gl/compiler.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/gl/compiler.h Mon Aug 24 23:00:50 2020 +0300 @@ -34,15 +34,6 @@ namespace gl { class Compiler; - - struct Vertex - { - glm::vec3 position; - glm::vec4 color; - glm::vec3 normal; - glm::int32 id; - glm::int32 selected = 0; - }; } class gl::Compiler : public QObject, protected QOpenGLExtraFunctions @@ -52,7 +43,6 @@ Compiler(const ldraw::ColorTable& colorTable, QObject* parent); ~Compiler(); void build(Model* model, DocumentManager* context, const RenderPreferences& preferences); - void buildPolygon(Polygon polygon, std::vector* vboData, const gl::RenderPreferences& preferences); std::size_t vertexCount(gl::ArrayClass arrayClass) const; QColor getColorForPolygon(const gl::Polygon& polygon, const RenderPreferences& preferences); glm::vec3 modelCenter() const; @@ -63,7 +53,7 @@ void buildShaders(int arrayId); void setSelectedObjects(const QSet ids); - static ldraw::id_t idFromColor(const std::array& data); + static ldraw::id_t idFromColor(const std::array& data); template void setUniform(const char* uniformName, T&& value) @@ -86,7 +76,16 @@ this->setUniform(uniformName, *array); } private: - std::size_t storedVertexCounts[gl::NUM_ARRAY_CLASSES] = {0_z}; + struct Vertex + { + glm::vec3 position; + glm::vec4 color; + glm::vec3 normal; + glm::int32 id; + glm::int32 selected = 0; + }; + void buildPolygon(Polygon polygon, std::vector* vboData, const gl::RenderPreferences& preferences); + std::size_t storedVertexCounts[gl::NUM_POLYGON_TYPES] = {0}; bool initialized = false; BoundingBox boundingBox; const ldraw::ColorTable& colorTable; @@ -97,8 +96,8 @@ QOpenGLShaderProgram* pickSceneProgram = nullptr; QOpenGLBuffer buffer{QOpenGLBuffer::VertexBuffer}; QOpenGLVertexArrayObject vertexArray; - std::vector cachedData; - } glObjects[gl::NUM_ARRAY_CLASSES]; + std::vector cachedData; + } glObjects[gl::NUM_POLYGON_TYPES]; }; #define CHECK_GL_ERROR() { checkGLError(__FILE__, __LINE__); } diff -r 9eb5e06f34c3 -r c438a7db7c52 src/gl/partrenderer.cpp --- a/src/gl/partrenderer.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/gl/partrenderer.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -60,11 +60,7 @@ abort(); } this->compiler->initialize(); - this->compiler->build(this->model, this->documents, this->renderPreferences); - connect(this->model, &Model::dataChanged, [&]() - { - this->compiler->build(this->model, this->documents, this->renderPreferences); - }); + connect(this->model, &Model::dataChanged, this, &PartRenderer::build); this->initialized = true; this->modelQuaternion = glm::angleAxis(glm::radians(30.0f), glm::vec3{-1, 0, 0}); this->modelQuaternion *= glm::angleAxis(glm::radians(225.0f), glm::vec3{-0, 1, 0}); @@ -111,6 +107,11 @@ void PartRenderer::renderScene() { + if (this->needBuild) + { + this->compiler->build(this->model, this->documents, this->renderPreferences); + this->needBuild = false; + } this->checkForGLErrors(); if (this->renderPreferences.lineAntiAliasing && this->renderPreferences.style != gl::RenderStyle::PickScene) { @@ -211,6 +212,11 @@ { } +void PartRenderer::build() +{ + this->needBuild = true; +} + void PartRenderer::renderVao(const gl::ArrayClass arrayClass) { this->compiler->bindVertexArray(arrayClass); @@ -340,7 +346,7 @@ this->renderPreferences.style = gl::RenderStyle::PickScene; this->makeCurrent(); this->renderScene(); - std::array data; + std::array data; this->checkForGLErrors(); glReadPixels(where.x(), this->height() - where.y(), 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &data[0]); this->checkForGLErrors(); @@ -369,7 +375,7 @@ this->renderPreferences = newPreferences; if (mainColorChanged or backgroundColorChanged) { - this->compiler->build(this->model, this->documents, this->renderPreferences); + this->build(); this->setupBackgroundColor(); } emit this->renderPreferencesChanged(); diff -r 9eb5e06f34c3 -r c438a7db7c52 src/gl/partrenderer.h --- a/src/gl/partrenderer.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/gl/partrenderer.h Mon Aug 24 23:00:50 2020 +0300 @@ -57,10 +57,12 @@ void updateViewMatrix(); void updateModelMatrix(); void setupBackgroundColor(); + Q_SLOT void build(); static constexpr double MIN_ZOOM = 0.0; static constexpr double MAX_ZOOM = 3.0; double zoom = 1.0; bool initialized = false; + bool needBuild = true; void renderVao(const gl::ArrayClass arrayClass); void checkForGLErrors(); }; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/conditionaledge.cpp --- a/src/linetypes/conditionaledge.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/conditionaledge.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -1,56 +1,10 @@ #include "conditionaledge.h" -ldraw::ConditionalEdge::ConditionalEdge( - const glm::vec3& point_1, - const glm::vec3& point_2, - const glm::vec3& controlPoint_1, - const glm::vec3& controlPoint_2, - const Color color_index) : - Edge{point_1, point_2, color_index}, - controlPoint_1{controlPoint_1}, - controlPoint_2{controlPoint_2} -{ -} - -ldraw::ConditionalEdge::ConditionalEdge(const std::array& vertices, const Color color) : - Edge{vertices[0], vertices[1], color}, - controlPoint_1{vertices[2]}, - controlPoint_2{vertices[3]} -{ -} - -QVariant ldraw::ConditionalEdge::getProperty(Property property) const -{ - switch (property) - { - case Property::ControlPoint1: - return QVariant::fromValue(controlPoint_1); - case Property::ControlPoint2: - return QVariant::fromValue(controlPoint_2); - default: - return Edge::getProperty(property); - } -} - -auto ldraw::ConditionalEdge::setProperty( - Property property, - const QVariant& value) - -> SetPropertyResult -{ - switch (property) - { - case Property::ControlPoint1: - controlPoint_1 = value.value(); - case Property::ControlPoint2: - controlPoint_2 = value.value(); - default: - return Edge::setProperty(property, value); - } -} - QString ldraw::ConditionalEdge::textRepresentation() const { - return Edge::textRepresentation() + utility::format("%1 %2", - utility::vertexToStringParens(controlPoint_1), - utility::vertexToStringParens(controlPoint_2)); + return utility::format("%1 %2 %3 %4", + utility::vertexToStringParens(this->points[0]), + utility::vertexToStringParens(this->points[1]), + utility::vertexToStringParens(this->points[2]), + utility::vertexToStringParens(this->points[3])); } diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/conditionaledge.h --- a/src/linetypes/conditionaledge.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/conditionaledge.h Mon Aug 24 23:00:50 2020 +0300 @@ -1,27 +1,14 @@ #pragma once -#include "edge.h" +#include "polygonobject.h" namespace ldraw { class ConditionalEdge; } -class ldraw::ConditionalEdge : public Edge +class ldraw::ConditionalEdge : public PolygonObject<4> { public: - ConditionalEdge() = default; - ConditionalEdge( - const glm::vec3& point_1, - const glm::vec3& point_2, - const glm::vec3& controlPoint_1, - const glm::vec3& controlPoint_2, - const Color colorIndex = ldraw::edgeColor); - ConditionalEdge(const std::array& vertices, const Color color); - QVariant getProperty(Property property) const override; - SetPropertyResult setProperty( - Property property, - const QVariant& value) override; + using PolygonObject<4>::PolygonObject; QString textRepresentation() const override; - glm::vec3 controlPoint_1 = {}; - glm::vec3 controlPoint_2 = {}; }; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/edge.cpp --- a/src/linetypes/edge.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/edge.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -1,52 +1,11 @@ #include "edge.h" -ldraw::Edge::Edge( - const glm::vec3& point_1, - const glm::vec3& point_2, - const Color color_index) : - ColoredObject{color_index}, - point_1{point_1}, - point_2{point_2} {} - -ldraw::Edge::Edge(const std::array& vertices, const Color color) : - ColoredObject{color}, - point_1{vertices[0]}, - point_2{vertices[1]} -{ -} - -QVariant ldraw::Edge::getProperty(Property property) const -{ - switch (property) - { - case Property::Point1: - return QVariant::fromValue(point_1); - case Property::Point2: - return QVariant::fromValue(point_2); - default: - return BaseClass::getProperty(property); - } -} - -auto ldraw::Edge::setProperty(Property property, const QVariant& value) - -> SetPropertyResult -{ - switch (property) - { - case Property::Point1: - point_1 = value.value(); - return SetPropertyResult::Success; - case Property::Point2: - point_2 = value.value(); - return SetPropertyResult::Success; - default: - return BaseClass::setProperty(property, value); - } -} - QString ldraw::Edge::textRepresentation() const { - return utility::format("%1 %2", utility::vertexToStringParens(point_1), utility::vertexToStringParens(point_2)); + return utility::format( + "%1 %2", + utility::vertexToStringParens(this->points[0]), + utility::vertexToStringParens(this->points[1])); } void ldraw::Edge::getPolygons( @@ -54,5 +13,5 @@ GetPolygonsContext* context) const { Q_UNUSED(context) - polygons.push_back(gl::edgeLine(this->point_1, this->point_2, this->colorIndex, this->id)); + polygons.push_back(gl::edgeLine(this->points[0], this->points[1], this->colorIndex, this->id)); } diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/edge.h --- a/src/linetypes/edge.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/edge.h Mon Aug 24 23:00:50 2020 +0300 @@ -1,25 +1,15 @@ #pragma once -#include "object.h" +#include "polygonobject.h" namespace ldraw { class Edge; } -class ldraw::Edge : public ColoredObject +class ldraw::Edge : public PolygonObject<2> { public: - using BaseClass = ColoredObject; - Edge() = default; - Edge(const glm::vec3& point_1, const glm::vec3& point_2, - const Color colorIndex = ldraw::edgeColor); - Edge(const std::array& vertices, const Color color); - QVariant getProperty(Property property) const override; - SetPropertyResult setProperty( - Property property, - const QVariant& value) override; + using PolygonObject::PolygonObject; QString textRepresentation() const override; void getPolygons(std::vector& polygons, GetPolygonsContext* context) const override; - glm::vec3 point_1 = {}; - glm::vec3 point_2 = {}; }; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/errorline.cpp --- a/src/linetypes/errorline.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/errorline.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -20,22 +20,11 @@ } } -auto ldraw::ErrorLine::setProperty( - Property property, - const QVariant& value) - -> SetPropertyResult +void ldraw::ErrorLine::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) { - switch (property) - { - case Property::Text: - this->text = value.toString(); - return SetPropertyResult::Success; - case Property::ErrorMessage: - this->message = value.toString(); - return SetPropertyResult::Success; - default: - return Object::setProperty(property, value); - } + LDRAW_OBJECT_HANDLE_SET_PROPERTY(Text, {this->text = value;}); + LDRAW_OBJECT_HANDLE_SET_PROPERTY(ErrorMessage, {this->message = value;}); + BaseClass::setProperty(result, pair); } QString ldraw::ErrorLine::textRepresentation() const diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/errorline.h --- a/src/linetypes/errorline.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/errorline.h Mon Aug 24 23:00:50 2020 +0300 @@ -9,14 +9,14 @@ class ldraw::ErrorLine : public Object { public: + using BaseClass = Object; ErrorLine(QStringView text = u"", QStringView message = u""); QVariant getProperty(Property property) const override; - SetPropertyResult setProperty( - Property property, - const QVariant& value) override; QString textRepresentation() const override; QBrush textRepresentationForeground() const override; QBrush textRepresentationBackground() const override; QString text; QString message; +protected: + void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; }; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/metacommand.cpp --- a/src/linetypes/metacommand.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/metacommand.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -15,20 +15,14 @@ } } -auto ldraw::MetaCommand::setProperty(Property property, const QVariant& value) - -> SetPropertyResult +void ldraw::MetaCommand::setProperty(ldraw::Object::SetPropertyResult* result, const PropertyKeyValue& pair) { - switch (property) - { - case Property::Text: - storedText = value.toString(); - return SetPropertyResult::Success; - default: - return Object::setProperty(property, value); - } + LDRAW_OBJECT_HANDLE_SET_PROPERTY(Text, {this->storedText = value;}); + BaseClass::setProperty(result, pair); } QString ldraw::MetaCommand::textRepresentation() const { return this->storedText; } + diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/metacommand.h --- a/src/linetypes/metacommand.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/metacommand.h Mon Aug 24 23:00:50 2020 +0300 @@ -8,12 +8,12 @@ class ldraw::MetaCommand : public Object { public: + using BaseClass = Object; MetaCommand() = default; MetaCommand(QStringView text); QVariant getProperty(Property property) const override; - SetPropertyResult setProperty( - Property property, - const QVariant& value) override; QString textRepresentation() const override; QString storedText = ""; +protected: + void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; }; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/object.cpp --- a/src/linetypes/object.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/object.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -1,6 +1,8 @@ #include #include #include "object.h" +#include "widgets/vec3editor.h" +#include "modeleditcontext.h" static std::int32_t getIdForNewObject() { @@ -29,12 +31,20 @@ return {}; } -auto ldraw::Object::setProperty(Property id, const QVariant& value) - -> SetPropertyResult +void ldraw::Object::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) { - Q_UNUSED(id) - Q_UNUSED(value) - return SetPropertyResult::PropertyNotHandled; + Q_UNUSED(result) + Q_UNUSED(pair) +} + +/** + * @brief public interface to setProperty + */ +ldraw::Object::SetPropertyResult ldraw::Object::setProperty(const PropertyKeyValue& pair) +{ + SetPropertyResult result; + this->setProperty(&result, pair); + return result; } QBrush ldraw::Object::textRepresentationForeground() const @@ -58,6 +68,12 @@ Q_UNUSED(context) } +const glm::vec3& ldraw::Object::getPoint(int index) const +{ + Q_UNUSED(index); + throw BadPointIndex{}; +} + ldraw::ColoredObject::ColoredObject(const Color color_index) : colorIndex{color_index} { @@ -73,34 +89,16 @@ switch (id) { case Property::Color: - return colorIndex.index; + return QVariant::fromValue(colorIndex); default: return Object::getProperty(id); } } -auto ldraw::ColoredObject::setProperty(Property id, const QVariant& value) - -> SetPropertyResult +void ldraw::ColoredObject::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) { - switch (id) - { - case Property::Color: - { - bool ok; - const int value_int = value.toInt(&ok); - if (ok) - { - colorIndex.index = value_int; - return SetPropertyResult::Success; - } - else - { - return SetPropertyResult::InvalidValue; - } - } - default: - return Object::setProperty(id, value); - } + LDRAW_OBJECT_HANDLE_SET_PROPERTY(Color, {colorIndex = value;}); + Object::setProperty(result, pair); } QString ldraw::Empty::textRepresentation() const diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/object.h --- a/src/linetypes/object.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/object.h Mon Aug 24 23:00:50 2020 +0300 @@ -5,11 +5,13 @@ #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; @@ -23,88 +25,71 @@ ::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 - Point1, // First vertex in a polygon or edge line - Point2, // Second vertex in a polygon or edge line - Point3, // Third vertex in a polygon - Point4, // Fourth vertex in a quadrilateral - ControlPoint1, // First control point in a conditional edge line - ControlPoint2, // Second control point in a conditional edge line - 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 { using type = TYPE; }; \ - } - -namespace ldraw -{ - template - struct PropertyType - { - }; - - template - using PropertyType_t = typename PropertyType::type; -} - -LDFORGE_DEFINE_PROPERTY_TYPE(Color, int) -LDFORGE_DEFINE_PROPERTY_TYPE(Text, QString) -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(Point4, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(ControlPoint1, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(ControlPoint2, 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) - class ldraw::Object { public: enum class SetPropertyResult { Success = 0, - PropertyNotHandled, - InvalidValue + PropertyNotHandled + }; + friend bool handled(SetPropertyResult result) + { + return result == SetPropertyResult::Success; + } + class BadPointIndex : public std::exception + { }; Object(); Object(const Object&) = delete; virtual ~Object(); const id_t id; - //virtual void toString(QTextStream &out) = 0; virtual bool hasColor() const; virtual QVariant getProperty(Property id) const; - virtual SetPropertyResult setProperty(Property id, const QVariant& value); + template + SetPropertyResult setProperty(const PropertyType& value); + SetPropertyResult setProperty(const PropertyKeyValue& pair); virtual QString textRepresentation() const = 0; virtual QBrush textRepresentationForeground() const; virtual QBrush textRepresentationBackground() const; virtual QFont textRepresentationFont() const; virtual void getPolygons(std::vector& polygons, GetPolygonsContext* context) const; virtual void invert() {} + virtual int numPoints() const { return 0; } + virtual const glm::vec3& getPoint(int index) const; +protected: + template + void handle(SetPropertyResult* result, const PropertyKeyValue& pair, Function function); + virtual void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair); }; +template +ldraw::Object::SetPropertyResult ldraw::Object::setProperty(const ldraw::PropertyType& value) +{ + SetPropertyResult result = SetPropertyResult::PropertyNotHandled; + this->setProperty(&result, PropertyKeyValue{property, QVariant::fromValue(value)}); + return result; +} + +template +void ldraw::Object::handle(SetPropertyResult* result, const PropertyKeyValue& pair, Function function) +{ + if (pair.key == property) + { + function(pair.value.value>()); + *result = SetPropertyResult::Success; + } +} + class ldraw::ColoredObject : public Object { public: ColoredObject(const Color colorIndex = ldraw::mainColor); bool hasColor() const override final; QVariant getProperty(Property id) const override; - SetPropertyResult setProperty(Property id, const QVariant& value) override; Color colorIndex = ldraw::mainColor; +protected: + void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; }; /** diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/polygonobject.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/linetypes/polygonobject.h Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,70 @@ +#pragma once +#include "object.h" +#include "widgets/vec3editor.h" +#include "model.h" +#include "modeleditcontext.h" + +namespace ldraw +{ + template + class PolygonObject; +} + +template 0 and N <= 4)>> +class ldraw::PolygonObject : public ColoredObject +{ +public: + using BaseClass = ColoredObject; + PolygonObject(const std::array& 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 points; +}; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/propertygenerics.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/linetypes/propertygenerics.h Mon Aug 24 23:00:50 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 + 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 \ + { \ + using type = TYPE; \ + static constexpr std::array 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(result, pair, \ + [&](const ldraw::PropertyType& value) HANDLER);} + +// Generics +namespace ldraw +{ + template + using PropertyType = typename PropertyTraits::type; + + template + inline const char* PROPERTY_NAME = PropertyTraits::name; + + struct PropertyKeyValue + { + Property key; + QVariant value; + }; + + constexpr Property pointProperty(int n) + { + Q_ASSERT(n >= 0 and n < 4); + return static_cast(static_cast(Property::Point0) + n); + } + + struct PropertyTrait + { + ldraw::Property property; + std::array name; + int type; + }; + + namespace detail + { + template + constexpr int propertyCountHelper() + { + if constexpr (ldraw::PropertyTraits(N)>::defined) + { + return propertyCountHelper(); + } + else + { + return N; + } + } + + template + constexpr PropertyTrait getPropertyTrait() + { + constexpr auto property = static_cast(k); + using trait = ldraw::PropertyTraits; + return PropertyTrait{ + property, + trait::name, + qMetaTypeId() + }; + } + + template + auto getPropertyTraits(std::integer_sequence) + { + return std::array{getPropertyTrait()...}; + } + } + + constexpr int NUM_PROPERTIES = detail::propertyCountHelper<0>(); + inline const auto& traits() + { + static std::array result = + detail::getPropertyTraits(std::make_integer_sequence()); + return result; + } + + inline const auto& traits(ldraw::Property property) + { + return traits()[static_cast(property)]; + } + + template + constexpr auto makeIndexArray(std::index_sequence) + { + return std::array{static_cast(Ints)...}; + } + + constexpr auto ALL_PROPERTIES = makeIndexArray(std::make_index_sequence{}); + + template + bool testPropertyType(ldraw::Property property) + { + return qMetaTypeId() == ldraw::traits(property).type; + } +} diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/quadrilateral.cpp --- a/src/linetypes/quadrilateral.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/quadrilateral.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -1,70 +1,12 @@ #include "quadrilateral.h" -ldraw::Quadrilateral::Quadrilateral( - const glm::vec3& point_1, - const glm::vec3& point_2, - const glm::vec3& point_3, - const glm::vec3& point_4, - Color color_index) : - ColoredObject{color_index}, - points{point_1, point_2, point_3, point_4} -{ -} - -ldraw::Quadrilateral::Quadrilateral(const std::array& vertices, const Color color) : - ColoredObject{color}, - points{vertices[0], vertices[1], vertices[2], vertices[3]} -{ -} - -QVariant ldraw::Quadrilateral::getProperty(const Property id) const -{ - switch (id) - { - case Property::Point1: - return QVariant::fromValue(points[0]); - case Property::Point2: - return QVariant::fromValue(points[1]); - case Property::Point3: - return QVariant::fromValue(points[2]); - case Property::Point4: - return QVariant::fromValue(points[3]); - default: - return ColoredObject::getProperty(id); - } -} - -auto ldraw::Quadrilateral::setProperty( - const Property id, - const QVariant& value) - -> SetPropertyResult -{ - switch (id) - { - case Property::Point1: - points[0] = value.value(); - return SetPropertyResult::Success; - case Property::Point2: - points[1] = value.value(); - return SetPropertyResult::Success; - case Property::Point3: - points[2] = value.value(); - return SetPropertyResult::Success; - case Property::Point4: - points[3] = value.value(); - return SetPropertyResult::Success; - default: - return ColoredObject::setProperty(id, value); - } -} - QString ldraw::Quadrilateral::textRepresentation() const { return utility::format("%1 %2 %3 %4", - utility::vertexToStringParens(points[0]), - utility::vertexToStringParens(points[1]), - utility::vertexToStringParens(points[2]), - utility::vertexToStringParens(points[3])); + utility::vertexToStringParens(this->points[0]), + utility::vertexToStringParens(this->points[1]), + utility::vertexToStringParens(this->points[2]), + utility::vertexToStringParens(this->points[3])); } void ldraw::Quadrilateral::getPolygons( @@ -78,7 +20,7 @@ this->points[2], this->points[3], this->colorIndex, - this->id)); + this->id)); } void ldraw::Quadrilateral::invert() diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/quadrilateral.h --- a/src/linetypes/quadrilateral.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/quadrilateral.h Mon Aug 24 23:00:50 2020 +0300 @@ -1,26 +1,16 @@ #pragma once -#include "object.h" +#include "polygonobject.h" namespace ldraw { class Quadrilateral; } -class ldraw::Quadrilateral : public ColoredObject +class ldraw::Quadrilateral : public PolygonObject<4> { public: - Quadrilateral() = default; - Quadrilateral( - const glm::vec3 &point_1, - const glm::vec3 &point_2, - const glm::vec3 &point_3, - const glm::vec3 &point_4, - Color colorIndex = ldraw::mainColor); - Quadrilateral(const std::array& vertices, const Color color); - QVariant getProperty(Property id) const override; - SetPropertyResult setProperty(Property id, const QVariant& value) override; + using PolygonObject<4>::PolygonObject; QString textRepresentation() const override; void getPolygons(std::vector& polygons, GetPolygonsContext* context) const override; void invert() override; - glm::vec3 points[4] = {{}}; }; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/subfilereference.cpp --- a/src/linetypes/subfilereference.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/subfilereference.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -2,9 +2,12 @@ #include "documentmanager.h" #include "invert.h" -ldraw::SubfileReference::SubfileReference(const glm::mat4& transformation, +ldraw::SubfileReference::SubfileReference +( + const glm::mat4& transformation, const QString& referenceName, - const Color color) : + const Color color +) : ColoredObject{color}, transformation{transformation}, referenceName{referenceName} @@ -24,22 +27,11 @@ } } -auto ldraw::SubfileReference::setProperty( - Property property, - const QVariant& value) - -> SetPropertyResult +void ldraw::SubfileReference::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) { - switch (property) - { - case Property::Transformation: - this->transformation = value.value(); - return SetPropertyResult::Success; - case Property::ReferenceName: - this->referenceName = value.toString(); - return SetPropertyResult::Success; - default: - return ColoredObject::setProperty(property, value); - } + LDRAW_OBJECT_HANDLE_SET_PROPERTY(Transformation, {this->transformation = value;}); + LDRAW_OBJECT_HANDLE_SET_PROPERTY(ReferenceName, {this->referenceName = value;}); + ldraw::ColoredObject::setProperty(result, pair); } QString ldraw::SubfileReference::textRepresentation() const @@ -47,9 +39,11 @@ return referenceName + " " + utility::vertexToStringParens(this->position()); } -void ldraw::SubfileReference::getPolygons( +void ldraw::SubfileReference::getPolygons +( std::vector& polygons, - GetPolygonsContext* context) const + GetPolygonsContext* context +) const { Model* model = this->resolve(context->documents); if (model != nullptr) @@ -81,7 +75,7 @@ glm::vec3 ldraw::SubfileReference::position() const { - return {this->transformation[3][0], this->transformation[3][1], this->transformation[3][2]}; + return this->transformation[3]; } void ldraw::SubfileReference::invert() diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/subfilereference.h --- a/src/linetypes/subfilereference.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/subfilereference.h Mon Aug 24 23:00:50 2020 +0300 @@ -17,7 +17,6 @@ const QString &referenceName, const Color color = ldraw::mainColor); QVariant getProperty(Property property) const override; - SetPropertyResult setProperty(Property property, const QVariant& value) override; QString textRepresentation() const override; void getPolygons(std::vector& polygons, GetPolygonsContext* context) const override; glm::vec3 position() const; @@ -26,4 +25,6 @@ glm::mat4 transformation; QString referenceName; bool isInverted = false; +protected: + void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; }; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/triangle.cpp --- a/src/linetypes/triangle.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/triangle.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -1,61 +1,5 @@ #include "triangle.h" -ldraw::Triangle::Triangle( - const glm::vec3& point_1, - const glm::vec3& point_2, - const glm::vec3& point_3, - Color color_index) : - ColoredObject{color_index}, - points{point_1, point_2, point_3} -{ -} - -ldraw::Triangle::Triangle(const std::array& vertices, const Color color) : - ColoredObject{color}, - points{vertices[0], vertices[1], vertices[2]} -{ -} - -ldraw::Triangle::Triangle(const glm::vec3 (&vertices)[3], const Color color) : - ColoredObject{color}, - points{vertices[0], vertices[1], vertices[2]} -{ -} - -QVariant ldraw::Triangle::getProperty(const Property id) const -{ - switch (id) - { - case Property::Point1: - return QVariant::fromValue(points[0]); - case Property::Point2: - return QVariant::fromValue(points[1]); - case Property::Point3: - return QVariant::fromValue(points[2]); - default: - return ColoredObject::getProperty(id); - } -} - -auto ldraw::Triangle::setProperty(Property id, const QVariant& value) - -> SetPropertyResult -{ - switch (id) - { - case Property::Point1: - points[0] = value.value(); - return SetPropertyResult::Success; - case Property::Point2: - points[1] = value.value(); - return SetPropertyResult::Success; - case Property::Point3: - points[2] = value.value(); - return SetPropertyResult::Success; - default: - return ColoredObject::setProperty(id, value); - } -} - QString ldraw::Triangle::textRepresentation() const { return utility::format("%1 %2 %3", @@ -74,7 +18,7 @@ this->points[1], this->points[2], this->colorIndex, - this->id)); + this->id)); } void ldraw::Triangle::invert() diff -r 9eb5e06f34c3 -r c438a7db7c52 src/linetypes/triangle.h --- a/src/linetypes/triangle.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/linetypes/triangle.h Mon Aug 24 23:00:50 2020 +0300 @@ -1,27 +1,17 @@ #pragma once -#include "object.h" +#include "polygonobject.h" namespace ldraw { class Triangle; } -class ldraw::Triangle : public ColoredObject +class ldraw::Triangle : public PolygonObject<3> { public: - Triangle() = default; - Triangle( - const glm::vec3 &point_1, - const glm::vec3 &point_2, - const glm::vec3 &point_3, - Color colorIndex = ldraw::mainColor); - Triangle(const std::array& vertices, const Color color); - Triangle(const glm::vec3 (&vertices)[3], const Color color); - QVariant getProperty(Property id) const override; - SetPropertyResult setProperty(Property id, const QVariant& value) override; + using PolygonObject<3>::PolygonObject; QString textRepresentation() const override; void getPolygons(std::vector& polygons, GetPolygonsContext* context) const override; void invert() override; - glm::vec3 points[3] = {{}}; }; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/main.cpp --- a/src/main.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/main.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -19,6 +19,8 @@ #include #include "mainwindow.h" #include "version.h" +#include +#include int main(int argc, char *argv[]) { @@ -28,6 +30,9 @@ ::qRegisterMetaTypeStreamOperators("Library"); ::qRegisterMetaTypeStreamOperators("Libraries"); QApplication app{argc, argv}; + /* + QMessageBox::information(nullptr, "", QMetaType::typeName( qMetaTypeId() )); + */ MainWindow mainwindow; mainwindow.show(); return app.exec(); diff -r 9eb5e06f34c3 -r c438a7db7c52 src/main.h --- a/src/main.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/main.h Mon Aug 24 23:00:50 2020 +0300 @@ -74,13 +74,14 @@ using conditionaledgeid_t = Id; using subfileid_t = Id; - constexpr struct + constexpr struct NullId { template constexpr operator Id() const { return Id{0}; } + static constexpr decltype(ldraw::id_t::value) value = 0; } NULL_ID = {}; template diff -r 9eb5e06f34c3 -r c438a7db7c52 src/mainwindow.cpp --- a/src/mainwindow.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/mainwindow.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -43,6 +43,7 @@ { offsetof(Ui_MainWindow, actionRenderStyleNormal), gl::RenderStyle::Normal }, { offsetof(Ui_MainWindow, actionRenderStyleBfc), gl::RenderStyle::BfcRedGreen }, { offsetof(Ui_MainWindow, actionRenderStyleRandom), gl::RenderStyle::RandomColors }, + { offsetof(Ui_MainWindow, actionRenderStylePickScene), gl::RenderStyle::PickScene }, }; class A : public QSettings diff -r 9eb5e06f34c3 -r c438a7db7c52 src/mainwindow.ui --- a/src/mainwindow.ui Mon Aug 24 22:55:37 2020 +0300 +++ b/src/mainwindow.ui Mon Aug 24 23:00:50 2020 +0300 @@ -26,7 +26,7 @@ 0 0 800 - 22 + 24 @@ -53,6 +53,7 @@ + @@ -108,6 +109,14 @@ Random colours + + + true + + + Pick scene colours + + diff -r 9eb5e06f34c3 -r c438a7db7c52 src/model.cpp --- a/src/model.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/model.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -22,7 +22,10 @@ #include "modeleditcontext.h" Model::Model(QObject* parent) : - QAbstractListModel{parent} {} + QAbstractListModel{parent} +{ + connect(this, &Model::dataChanged, [&](){ this->needRecache = true; }); +} int Model::size() const { diff -r 9eb5e06f34c3 -r c438a7db7c52 src/model.h --- a/src/model.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/model.h Mon Aug 24 23:00:50 2020 +0300 @@ -135,5 +135,12 @@ { *index_out = index; } - return static_cast(this->objectAt(index)); + if (index.isValid()) + { + return static_cast(this->objectAt(index)); + } + else + { + return nullptr; + } } diff -r 9eb5e06f34c3 -r c438a7db7c52 src/modeleditcontext.cpp --- a/src/modeleditcontext.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/modeleditcontext.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -17,12 +17,23 @@ */ #include "modeleditcontext.h" +#include "linetypes/triangle.h" +#include "linetypes/quadrilateral.h" Model::EditContext::EditContext(Model& model) : storedModel{model} { } +Model::EditContext::~EditContext() +{ + for (ldraw::id_t id : this->modifiedObjects) + { + const QModelIndex index = this->model().lookup(id); + emit this->model().dataChanged(index, index); + } +} + ldraw::id_t Model::EditContext::append(std::unique_ptr&& object) { const ldraw::id_t id = object->id; @@ -36,15 +47,32 @@ this->model().remove(position); } -void Model::EditContext::setObjectProperty( - ldraw::id_t id, - ldraw::Property property, +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); if (object != nullptr) { - object->setProperty(property, value); + object->setProperty(ldraw::PropertyKeyValue{ldraw::pointProperty(pointId), QVariant::fromValue(value)}); + modifiedObjects.insert(id); } } @@ -63,7 +91,7 @@ return this->storedModel; } -static std::array splitTriangles(ldraw::Diagonal diagonal, const glm::vec3(&points)[4]) +static std::array splitTriangles(ldraw::Diagonal diagonal, const std::array& points) { std::array result; switch (diagonal) diff -r 9eb5e06f34c3 -r c438a7db7c52 src/modeleditcontext.h --- a/src/modeleditcontext.h Mon Aug 24 22:55:37 2020 +0300 +++ b/src/modeleditcontext.h Mon Aug 24 23:00:50 2020 +0300 @@ -18,31 +18,42 @@ #pragma once #include "model.h" -#include "linetypes/object.h" -#include "linetypes/quadrilateral.h" -#include "linetypes/triangle.h" class Model::EditContext { public: + ~EditContext(); template ldraw::Id append(Args&&... args); ldraw::id_t append(std::unique_ptr&& object); template ldraw::Id insert(int position, Args&&... args); void remove(int position); - void setObjectProperty( - ldraw::id_t object, - ldraw::Property property, - const QVariant &value); + template + void setObjectProperty(ldraw::id_t id, const ldraw::PropertyType& 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(); private: EditContext(Model& model); friend class Model; + QSet modifiedObjects; Model& storedModel; }; +template +void Model::EditContext::setObjectProperty(const ldraw::id_t id, const ldraw::PropertyType& value) +{ + ldraw::Object* object = this->model().objectAt(id); + if (object != nullptr) + { + object->setProperty(value); + modifiedObjects.insert(id); + } +} + template ldraw::Id Model::EditContext::append(Args&&... args) { diff -r 9eb5e06f34c3 -r c438a7db7c52 src/ui/canvas.cpp --- a/src/ui/canvas.cpp Mon Aug 24 22:55:37 2020 +0300 +++ b/src/ui/canvas.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -154,84 +154,87 @@ void Canvas::paintGL() { PartRenderer::paintGL(); - // Render axes - { - glLineWidth(5); - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); - this->axesProgram->draw(); - glDisable(GL_LINE_SMOOTH); - } - // Render grid - { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - this->gridProgram->draw(); - glDisable(GL_BLEND); - } - if (this->worldPosition.has_value()) + if (this->renderPreferences.style != gl::RenderStyle::PickScene) { - QPainter painter{this}; - painter.setRenderHint(QPainter::Antialiasing); - painter.setPen(Qt::black); - painter.setBrush(Qt::green); - const QPointF pos = this->modelToScreenCoordinates(*this->worldPosition); - painter.drawEllipse(pos, 5, 5); - painter.setPen(Qt::white); - painter.drawText(pos + QPointF{5, 5}, vectorToString(*this->worldPosition)); - } - { - QPainter painter{this}; - QFont font; - //font.setStyle(QFont::StyleItalic); - painter.setFont(font); - QFontMetrics fontMetrics{font}; - const auto renderText = [&](const QString& text, const geom::PointOnRectagle& intersection) + // Render axes + { + glLineWidth(5); + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + this->axesProgram->draw(); + glDisable(GL_LINE_SMOOTH); + } + // Render grid + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + this->gridProgram->draw(); + glDisable(GL_BLEND); + } + if (this->worldPosition.has_value()) { - QPointF position = toQPointF(intersection.position); - const geom::RectangleSide side = intersection.side; - switch (side) + QPainter painter{this}; + painter.setRenderHint(QPainter::Antialiasing); + painter.setPen(Qt::black); + painter.setBrush(Qt::green); + const QPointF pos = this->modelToScreenCoordinates(*this->worldPosition); + painter.drawEllipse(pos, 5, 5); + painter.setPen(Qt::white); + painter.drawText(pos + QPointF{5, 5}, vectorToString(*this->worldPosition)); + } + { + QPainter painter{this}; + QFont font; + //font.setStyle(QFont::StyleItalic); + painter.setFont(font); + QFontMetrics fontMetrics{font}; + const auto renderText = [&](const QString& text, const geom::PointOnRectagle& intersection) { - case geom::RectangleSide::Top: - position += QPointF{0, static_cast(fontMetrics.ascent())}; - break; - case geom::RectangleSide::Left: - break; - case geom::RectangleSide::Bottom: - position += QPointF{0, static_cast(-fontMetrics.descent())}; - break; - case geom::RectangleSide::Right: - position += QPointF{static_cast(-fontMetrics.width(text)), 0}; - break; - } - painter.drawText(position, text); - }; - const QRectF box { - QPointF{0, 0}, - QPointF{static_cast(this->width()), static_cast(this->height())} - }; - const QPointF p1 = this->modelToScreenCoordinates(glm::vec3{0, 0, 0}); + QPointF position = toQPointF(intersection.position); + const geom::RectangleSide side = intersection.side; + switch (side) + { + case geom::RectangleSide::Top: + position += QPointF{0, static_cast(fontMetrics.ascent())}; + break; + case geom::RectangleSide::Left: + break; + case geom::RectangleSide::Bottom: + position += QPointF{0, static_cast(-fontMetrics.descent())}; + break; + case geom::RectangleSide::Right: + position += QPointF{static_cast(-fontMetrics.width(text)), 0}; + break; + } + painter.drawText(position, text); + }; + const QRectF box { + QPointF{0, 0}, + QPointF{static_cast(this->width()), static_cast(this->height())} + }; + const QPointF p1 = this->modelToScreenCoordinates(glm::vec3{0, 0, 0}); - static const struct - { - QString text; - glm::vec3 direction; - } directions[] = - { - {"+𝑥", {1, 0, 0}}, - {"-𝑥", {-1, 0, 0}}, - {"+𝑦", {0, 1, 0}}, - {"-𝑦", {0, -1, 0}}, - {"+𝑧", {0, 0, 1}}, - {"-𝑧", {0, 0, -1}}, - }; - for (const auto& axis : directions) - { - const QPointF x_p = this->modelToScreenCoordinates(axis.direction); - const auto intersection = geom::rayRectangleIntersection(geom::rayFromPoints(toVec2(p1), toVec2(x_p)), box); - if (intersection.has_value()) + static const struct + { + QString text; + glm::vec3 direction; + } directions[] = { - renderText(axis.text, *intersection); + {"+𝑥", {1, 0, 0}}, + {"-𝑥", {-1, 0, 0}}, + {"+𝑦", {0, 1, 0}}, + {"-𝑦", {0, -1, 0}}, + {"+𝑧", {0, 0, 1}}, + {"-𝑧", {0, 0, -1}}, + }; + for (const auto& axis : directions) + { + const QPointF x_p = this->modelToScreenCoordinates(axis.direction); + const auto intersection = geom::rayRectangleIntersection(geom::rayFromPoints(toVec2(p1), toVec2(x_p)), box); + if (intersection.has_value()) + { + renderText(axis.text, *intersection); + } } } } diff -r 9eb5e06f34c3 -r c438a7db7c52 src/ui/multiplyfactordialog.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ui/multiplyfactordialog.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,94 @@ +#include "multiplyfactordialog.h" +#include "ui_multiplyfactordialog.h" + +MultiplyFactorDialog::MultiplyFactorDialog(const glm::vec3& baseVector, QWidget* parent) : + QDialog{parent}, + baseVector{baseVector}, + preview{baseVector, parent, Vec3Editor::NoMultiplyButton} +{ + ui = std::make_unique(); + ui->setupUi(this); + this->preview.setEnabled(false); + this->ui->previewGroupBox->setLayout(new QVBoxLayout{parent}); + this->ui->previewGroupBox->layout()->addWidget(&this->preview); + connect(this->ui->invert, &QCheckBox::clicked, this, &MultiplyFactorDialog::updatePreview); + connect(this->ui->factor, qOverload(&DoubleSpinBox::valueChanged), this, &MultiplyFactorDialog::updatePreview); +} + +/** + * @brief empty destructor, necessary because std::unique_ptr is used with a forward declaration + */ +MultiplyFactorDialog::~MultiplyFactorDialog() +{ +} + +/** + * @brief Computes the resulting vector + * @return the input vector multiplied by the specified vector + */ +glm::vec3 MultiplyFactorDialog::value() const +{ + glm::vec3 result = baseVector; + if (this->ui->invert->isChecked()) + { + if (qFuzzyIsNull(this->ui->factor->value())) + { + constexpr double infinity = std::numeric_limits::quiet_NaN(); + result = {infinity, infinity, infinity}; + } + else + { + result /= this->ui->factor->value(); + } + } + else + { + result *= this->ui->factor->value(); + } + return result; +} + +/** + * @brief Makes a string that is prefixed to the factor input. + * @param ui + * @return prefix string + */ +QString prefixForFactorInput(const Ui::MultiplyFactorDialog& ui) +{ + if (ui.invert->isChecked()) + { + return "1 : "; + } + else + { + return ""; + } +} + +/** + * @brief Makes a string that is suffixed to the factor input. + * @param ui + * @return prefix string + */ +QString suffixForFactorInput(const Ui::MultiplyFactorDialog& ui) +{ + if (ui.invert->isChecked()) + { + // render the actual factor that stuff gets effectively multiplied by + return " = " + QString::number(1.0 / (ui.factor->value())); + } + else + { + return ""; + } +} + +/** + * @brief Responds to changes in the value and updates previews accordingly + */ +void MultiplyFactorDialog::updatePreview() +{ + this->ui->factor->setPrefix(::prefixForFactorInput(*this->ui)); + this->ui->factor->setSuffix(::suffixForFactorInput(*this->ui)); + this->preview.setValue(this->value()); +} diff -r 9eb5e06f34c3 -r c438a7db7c52 src/ui/multiplyfactordialog.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ui/multiplyfactordialog.h Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,23 @@ +#pragma once +#include +#include "../main.h" +#include "../widgets/vec3editor.h" + +namespace Ui +{ + class MultiplyFactorDialog; +} + +class MultiplyFactorDialog : public QDialog +{ + Q_OBJECT +public: + explicit MultiplyFactorDialog(const glm::vec3& baseVector = glm::vec3{}, QWidget *parent = nullptr); + ~MultiplyFactorDialog(); + glm::vec3 value() const; +private: + Q_SLOT void updatePreview(); + std::unique_ptr ui; + const glm::vec3 baseVector; + Vec3Editor preview; +}; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/ui/multiplyfactordialog.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ui/multiplyfactordialog.ui Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,103 @@ + + + MultiplyFactorDialog + + + + 0 + 0 + 286 + 169 + + + + Multiply with a scalar + + + + + + + + Factor: + + + + + + + 1.000000000000000 + + + + + + + Invert + + + + + + + + + Preview + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + DoubleSpinBox + QDoubleSpinBox +
widgets/doublespinbox.h
+
+
+ + + + buttonBox + accepted() + MultiplyFactorDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + MultiplyFactorDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff -r 9eb5e06f34c3 -r c438a7db7c52 src/ui/objecteditor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ui/objecteditor.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,32 @@ +#include +#include "objecteditor.h" + +ObjectEditor::ObjectEditor(Model* model, const ldraw::id_t id, QWidget *parent) : + QWidget{parent}, + model{model} +{ + this->setObjectId(id); + this->setLayout(new QVBoxLayout{this}); +} + +void ObjectEditor::setObjectId(const ldraw::id_t id) +{ + this->objectId = id; + const ldraw::Object* object = this->model->get(id); + if (object != nullptr and object->numPoints() > 0) + { + if (not this->polygonEditor.has_value()) + { + this->polygonEditor.emplace(this->model, id); + this->layout()->addWidget(&*this->polygonEditor); + } + else + { + this->polygonEditor->setObjectId(id); + } + } + else + { + this->polygonEditor.reset(); + } +} diff -r 9eb5e06f34c3 -r c438a7db7c52 src/ui/objecteditor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ui/objecteditor.h Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,17 @@ +#pragma once +#include +#include "../main.h" +#include "../model.h" +#include "polygonobjecteditor.h" + +class ObjectEditor : public QWidget +{ + Q_OBJECT +public: + explicit ObjectEditor(Model* model, ldraw::id_t id = ldraw::NULL_ID, QWidget* parent = nullptr); + void setObjectId(ldraw::id_t id); +private: + Model* const model; + ldraw::id_t objectId = ldraw::NULL_ID; + std::optional polygonEditor; +}; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/ui/polygonobjecteditor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ui/polygonobjecteditor.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,77 @@ +#include +#include +#include "model.h" +#include "modeleditcontext.h" +#include "widgets/vec3editor.h" +#include "ui/polygonobjecteditor.h" + +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->splitter.emplace(Qt::Vertical, this); + this->setObjectId(id); +} + +// destructor needed for std::unique_ptr +PolygonObjectEditor::~PolygonObjectEditor() +{ +} + +ldraw::id_t PolygonObjectEditor::objectId() const +{ + return this->storedObjectId; +} + +void PolygonObjectEditor::setObjectId(ldraw::id_t id) +{ + this->storedObjectId = id; + this->buildWidgets(); +} + +void PolygonObjectEditor::buildWidgets() +{ + 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& widget : this->widgets) + { + 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 editor = std::make_unique(value.value(), 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::pointChanged(const glm::vec3& value) +{ + 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)); +} diff -r 9eb5e06f34c3 -r c438a7db7c52 src/ui/polygonobjecteditor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ui/polygonobjecteditor.h Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,23 @@ +#pragma once +#include +#include "main.h" +#include "../widgets/vec3editor.h" + +class Model; + +class PolygonObjectEditor : public QWidget +{ +public: + PolygonObjectEditor(Model* model, ldraw::id_t id, QWidget* parent = nullptr); + ~PolygonObjectEditor(); + ldraw::id_t objectId() const; + void setObjectId(ldraw::id_t id); +private: + void buildWidgets(); + void setupPointWidget(int n); + Q_SLOT void pointChanged(const glm::vec3& value); + Model* model; + ldraw::id_t storedObjectId; + std::vector> widgets; + std::optional splitter; +}; diff -r 9eb5e06f34c3 -r c438a7db7c52 src/widgets/doublespinbox.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/doublespinbox.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,58 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2020 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 "doublespinbox.h" + +/* + * Constructs a new double spin box. The locale is fixed to system default "C". + */ +DoubleSpinBox::DoubleSpinBox(QWidget* parent) : + QDoubleSpinBox {parent} +{ + this->setLocale({"C"}); + this->setRange(-1e6, 1e6); + this->setDecimals(4); +} + +/* + * Reimplementation of QDoubleSpinBox::textFromValue to remove trailing zeros. + */ +QString DoubleSpinBox::textFromValue(double value) const +{ + QString result = QDoubleSpinBox::textFromValue(value); + if (result.contains(".")) + { + // Remove trailing zeros + while (result.endsWith("0")) + result.chop(1); + // Remove trailing decimal point if we just removed all the zeros. + if (result.endsWith(".")) + result.chop(1); + } + return result; +} + +/* + * Reimplementation of QDoubleSpinBox::validate to fix the decimal point if the locale-specific + * decimal point was used. + */ +QValidator::State DoubleSpinBox::validate(QString& input, int& pos) const +{ + input.replace(QLocale().decimalPoint(), this->locale().decimalPoint()); + return QDoubleSpinBox::validate(input, pos); +} diff -r 9eb5e06f34c3 -r c438a7db7c52 src/widgets/doublespinbox.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/doublespinbox.h Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,34 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2020 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 + +/* + * A version of QDoubleSpinBox that consistently uses "." as the decimal separator + * and does not display trailing zeros. + */ +class DoubleSpinBox : public QDoubleSpinBox +{ +public: + DoubleSpinBox(QWidget* parent = nullptr); +protected: + QString textFromValue(double value) const override; + QValidator::State validate(QString& input, int& pos) const override; +}; + diff -r 9eb5e06f34c3 -r c438a7db7c52 src/widgets/matrixeditor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/matrixeditor.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,89 @@ +#include "main.h" +#include "matrixeditor.h" +#include "ui_matrixeditor.h" +#include "../ui/multiplyfactordialog.h" + +constexpr char BUTTON_COLUMN_PROPERTY[] = "_ldforge_column"; + +MatrixEditor::MatrixEditor(const glm::mat4 value, QWidget* parent) : + QWidget(parent), + ui(new Ui::MatrixEditor) +{ + ui->setupUi(this); + for (int column = 0; column < countof(this->spinboxes); column += 1) + { + for (int row = 0; row < countof(this->spinboxes[0]); row += 1) + { + const QString name = "cell"_q + QString::number(column) + QString::number(row); + QDoubleSpinBox** spinbox = &this->spinboxes[column][row]; + *spinbox = this->findChild(name); + connect(*spinbox, qOverload(&QDoubleSpinBox::valueChanged), [&]() + { + emit this->valueChanged(this->value()); + }); + Q_ASSERT(*spinbox != nullptr); + } + QAbstractButton* button = this->findChild("multiply"_q + QString::number(column)); + button->setProperty(BUTTON_COLUMN_PROPERTY, column); + connect(button, &QAbstractButton::clicked, this, &MatrixEditor::multiplyButtonPressed); + } + this->setValue(value); +} + +MatrixEditor::MatrixEditor(QWidget *parent) : + MatrixEditor{glm::mat4{1}, parent} +{ +} + +MatrixEditor::~MatrixEditor() +{ + delete ui; +} + +glm::mat4 MatrixEditor::value() const +{ + glm::mat4 result{1}; + for (int column = 0; column < countof(this->spinboxes); column += 1) + { + for (int row = 0; row < countof(this->spinboxes[0]); row += 1) + { + result[column][row] = this->spinboxes[column][row]->value(); + } + } + return result; +} + +void MatrixEditor::setValue(const glm::mat4& value) +{ + for (int column = 0; column < countof(this->spinboxes); column += 1) + { + for (int row = 0; row < countof(this->spinboxes[0]); row += 1) + { + QDoubleSpinBox* spinbox = this->spinboxes[column][row]; + QSignalBlocker blocker{spinbox}; + spinbox->setValue(value[column][row]); + } + } +} + +void MatrixEditor::multiplyButtonPressed() +{ + QAbstractButton* button = qobject_cast(this->sender()); + if (button != nullptr) + { + bool ok; + const int column = button->property(BUTTON_COLUMN_PROPERTY).toInt(&ok); + if (ok and column >= 0 and column < this->matrixSize()) + { + glm::mat4 newValue = this->value(); + MultiplyFactorDialog dialog{newValue[column], this}; + const int result = dialog.exec(); + if (result == QDialog::Accepted) + { + newValue[column] = glm::vec4{dialog.value(), (column == 3) ? 1 : 0}; + this->setValue(newValue); + emit valueChanged(newValue); + } + } + } +} diff -r 9eb5e06f34c3 -r c438a7db7c52 src/widgets/matrixeditor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/matrixeditor.h Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,30 @@ +#pragma once +#include +#include "main.h" + +namespace Ui { +class MatrixEditor; +} + +class MatrixEditor : public QWidget +{ + Q_OBJECT +public: + explicit MatrixEditor(QWidget *parent = nullptr); + explicit MatrixEditor(const glm::mat4 value, QWidget* parent = nullptr); + ~MatrixEditor(); + glm::mat4 value() const; + void setValue(const glm::mat4& value); +Q_SIGNALS: + void valueChanged(const glm::mat4& value); +private: + constexpr int matrixSize() const; + Q_SLOT void multiplyButtonPressed(); + class QDoubleSpinBox* spinboxes[4][3]; + Ui::MatrixEditor *ui; +}; + +constexpr int MatrixEditor::matrixSize() const +{ + return 4; +} diff -r 9eb5e06f34c3 -r c438a7db7c52 src/widgets/matrixeditor.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/matrixeditor.ui Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,123 @@ + + + MatrixEditor + + + + 0 + 0 + 356 + 172 + + + + Form + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + 1.000000000000000 + + + + + + + × + + + + + + + × + + + + + + + × + + + + + + + × + + + + + + + + DoubleSpinBox + QDoubleSpinBox +
widgets/doublespinbox.h
+
+
+ + +
diff -r 9eb5e06f34c3 -r c438a7db7c52 src/widgets/vec3editor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/vec3editor.cpp Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,67 @@ +#include +#include +#include +#include "vec3editor.h" +#include "ui_vec3editor.h" +#include "../ui/multiplyfactordialog.h" + +Vec3Editor::Vec3Editor(const glm::vec3& value, QWidget *parent, QFlags flags) : + QWidget{parent}, + ui{new Ui::Vec3Editor} +{ + this->ui->setupUi(this); + this->setValue(value); + if (flags.testFlag(NoMultiplyButton)) + { + this->ui->multiply->setVisible(false); + } + else + { + connect(this->ui->multiply, &QPushButton::clicked, this, &Vec3Editor::multiplyPressed); + } + for (QDoubleSpinBox* spinbox : this->spinboxes()) + { + connect(spinbox, qOverload(&QDoubleSpinBox::valueChanged), [&](double) + { + Q_EMIT this->valueChanged(this->value()); + }); + } +} + +Vec3Editor::~Vec3Editor() +{ +} + +glm::vec3 Vec3Editor::value() const +{ + auto get = [](DoubleSpinBox* spinbox){ return toFloat(spinbox->value()); }; + return {get(this->ui->x), get(this->ui->y), get(this->ui->z)}; +} + +void Vec3Editor::setValue(const glm::vec3& value) +{ + auto set = [](DoubleSpinBox* spinbox, float value) + { + QSignalBlocker blocker{spinbox}; + spinbox->setValue(toQreal(value)); + }; + set(this->ui->x, value.x); + set(this->ui->y, value.y); + set(this->ui->z, value.z); + Q_EMIT this->valueChanged(value); +} + +std::array Vec3Editor::spinboxes() +{ + return {this->ui->x, this->ui->y, this->ui->z}; +} + +void Vec3Editor::multiplyPressed() +{ + MultiplyFactorDialog dialog{this->value(), this}; + const int dialogResult = dialog.exec(); + if (dialogResult == QDialog::Accepted) + { + this->setValue(dialog.value()); + } +} diff -r 9eb5e06f34c3 -r c438a7db7c52 src/widgets/vec3editor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/vec3editor.h Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,30 @@ +#pragma once +#include +#include "main.h" + +namespace Ui +{ + class Vec3Editor; +} + +class Vec3Editor : public QWidget +{ + Q_OBJECT +public: + enum Flag + { + NoMultiplyButton = 0x1 + }; + explicit Vec3Editor(const glm::vec3& value, QWidget* parent = nullptr, QFlags flags = 0); + ~Vec3Editor(); + glm::vec3 value() const; + void setValue(const glm::vec3& value); +Q_SIGNALS: + void valueChanged(const glm::vec3& value); +private: + std::array spinboxes(); + Q_SLOT void multiplyPressed(); + std::unique_ptr ui; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags) diff -r 9eb5e06f34c3 -r c438a7db7c52 src/widgets/vec3editor.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/vec3editor.ui Mon Aug 24 23:00:50 2020 +0300 @@ -0,0 +1,83 @@ + + + Vec3Editor + + + + 0 + 0 + 613 + 46 + + + + Form + + + + + + x = + + + 4 + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + y = + + + 4 + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + z = + + + 4 + + + -1000000.000000000000000 + + + 1000000.000000000000000 + + + + + + + × + + + + + + + + DoubleSpinBox + QDoubleSpinBox +
widgets/doublespinbox.h
+
+
+ + +