Mon, 06 Jun 2022 22:01:22 +0300
Giant refactor
--- a/CMakeLists.txt Wed May 25 20:36:34 2022 +0300 +++ b/CMakeLists.txt Mon Jun 06 22:01:22 2022 +0300 @@ -25,7 +25,6 @@ src/colors.cpp src/document.cpp src/documentmanager.cpp - src/edithistory.cpp src/geometry.cpp src/header.cpp src/ldrawalgorithm.cpp @@ -34,7 +33,6 @@ src/main.cpp src/mainwindow.cpp src/model.cpp - src/modeleditor.cpp src/parser.cpp src/polygoncache.cpp src/uiutilities.cpp @@ -46,16 +44,6 @@ src/gl/gridprogram.cpp src/gl/partrenderer.cpp src/gl/vertexprogram.cpp - src/linetypes/circularprimitive.cpp - src/linetypes/compoundobject.cpp - src/linetypes/conditionaledge.cpp - src/linetypes/edge.cpp - src/linetypes/errorline.cpp - src/linetypes/metacommand.cpp - src/linetypes/object.cpp - src/linetypes/quadrilateral.cpp - src/linetypes/subfilereference.cpp - src/linetypes/triangle.cpp src/settingseditor/keyboardshortcutseditor.cpp src/settingseditor/librarieseditor.cpp src/settingseditor/settingseditor.cpp @@ -75,7 +63,6 @@ src/colors.h src/document.h src/documentmanager.h - src/edithistory.h src/functional.h src/geometry.h src/header.h @@ -85,7 +72,6 @@ src/main.h src/mainwindow.h src/model.h - src/modeleditor.h src/parser.h src/polygoncache.h src/ring.h @@ -100,18 +86,6 @@ src/gl/gridprogram.h src/gl/partrenderer.h src/gl/vertexprogram.h - src/linetypes/circularprimitive.h - src/linetypes/compoundobject.h - src/linetypes/conditionaledge.h - src/linetypes/edge.h - 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 src/settingseditor/keyboardshortcutseditor.h src/settingseditor/librarieseditor.h src/settingseditor/settingseditor.h
--- a/src/document.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/document.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -21,13 +21,9 @@ #include "document.h" #include "ui_document.h" #include "model.h" -#include "modeleditor.h" #include "ui/objecteditor.h" -#include "linetypes/edge.h" -#include "linetypes/triangle.h" -#include "linetypes/quadrilateral.h" -Document::Document( +EditorTabWidget::EditorTabWidget( Model* model, DocumentManager* documents, const ldraw::ColorTable& colorTable, @@ -39,8 +35,7 @@ documents{documents}, vertexMap{model}, ui{*new Ui_Document}, - toolsBar{new QToolBar{this}}, - objectEditor{new ObjectEditor{this}} + toolsBar{new QToolBar{this}} { this->ui.setupUi(this); const int listWidth = static_cast<int>(this->width() / 3); @@ -52,17 +47,17 @@ this->ui.viewportFrame->layout()->addWidget(this->canvas); this->toolsBar->setOrientation(Qt::Vertical); this->setMouseTracking(true); - connect(this->ui.viewportListSplitter, &QSplitter::splitterMoved, this, &Document::splitterChanged); - connect(this->canvas, &Canvas::mouseClick, this, &Document::canvasMouseClick); - connect(this->canvas, &Canvas::mouseMove, this, &Document::canvasMouseMove); - connect(this->canvas, &Canvas::newStatusText, this, &Document::newStatusText); + connect(this->ui.viewportListSplitter, &QSplitter::splitterMoved, this, &EditorTabWidget::splitterChanged); + connect(this->canvas, &Canvas::mouseClick, this, &EditorTabWidget::canvasMouseClick); + connect(this->canvas, &Canvas::mouseMove, this, &EditorTabWidget::canvasMouseMove); + connect(this->canvas, &Canvas::newStatusText, this, &EditorTabWidget::newStatusText); connect(this->ui.listView->selectionModel(), &QItemSelectionModel::selectionChanged, [&](const QItemSelection& selected, const QItemSelection& deselected) { - auto resolveIndex = [this](const QModelIndex& index){ return (*this->model)[index.row()]->id; }; + auto resolveIndex = [this](const QModelIndex& index){ return this->model->idAt(index.row()); }; auto resolve = [resolveIndex](const QItemSelection& selection) { - return fn::map<QSet<ldraw::id_t>>(selection.indexes(), resolveIndex); + return fn::map<QSet<ModelId>>(selection.indexes(), resolveIndex); }; this->canvas->handleSelectionChange(resolve(selected), resolve(deselected)); }); @@ -75,38 +70,29 @@ this->initializeTools(); } -Document::~Document() +EditorTabWidget::~EditorTabWidget() { delete &this->ui; } -QByteArray Document::saveSplitterState() const +QByteArray EditorTabWidget::saveSplitterState() const { return this->ui.viewportListSplitter->saveState(); } -void Document::restoreSplitterState(const QByteArray& state) +void EditorTabWidget::restoreSplitterState(const QByteArray& state) { this->ui.viewportListSplitter->restoreState(state); } -std::unique_ptr<ModelEditor> Document::editModel() -{ - std::unique_ptr<ModelEditor> editorPointer = std::make_unique<ModelEditor>(*this->model); - connect(editorPointer.get(), &ModelEditor::objectModified, [&](int position){ - this->model->emitDataChangedSignal(position); - }); - return editorPointer; -} - -void Document::applyToVertices(VertexMap::ApplyFunction fn) const +void EditorTabWidget::applyToVertices(VertexMap::ApplyFunction fn) const { this->vertexMap.apply(fn); } const char INDEX_PROPERTY[] = "_editing_mode_index"; -void Document::initializeTools() +void EditorTabWidget::initializeTools() { const struct { @@ -118,7 +104,7 @@ .name = tr("Select"), .tooltip = tr("Select elements from the model."), .icon = {":/icons/navigate-outline.png"}, - .widget = this->objectEditor, + //.widget = this->objectEditor, }, { .name = tr("Draw"), @@ -142,12 +128,12 @@ } this->ui.toolWidgetStack->addWidget(widget); this->toolActions.push_back(action); - connect(action, &QAction::triggered, this, &Document::editingModeTriggered); + connect(action, &QAction::triggered, this, &EditorTabWidget::editingModeTriggered); } this->ui.listView->selectAll(); } -void Document::editingModeTriggered() +void EditorTabWidget::editingModeTriggered() { QAction* triggeredAction = qobject_cast<QAction*>(this->sender()); if (triggeredAction != nullptr) @@ -186,16 +172,16 @@ return any(points, std::bind(geom::isclose, std::placeholders::_1, pos)); } -void Document::canvasMouseClick(QMouseEvent *event) +void EditorTabWidget::canvasMouseClick(QMouseEvent *event) { switch(this->drawState.mode) { case SelectMode: if (event->button() == Qt::LeftButton) { - const ldraw::id_t highlighted = this->canvas->getHighlightedObject(); - QSet<ldraw::id_t> selected; - if (highlighted != ldraw::NULL_ID) { + const ModelId highlighted = this->canvas->getHighlightedObject(); + QSet<ModelId> selected; + if (highlighted != ModelId{0}) { selected.insert(highlighted); } this->select(selected); @@ -229,7 +215,7 @@ } } -void Document::canvasMouseMove(QMouseEvent *event) +void EditorTabWidget::canvasMouseMove(QMouseEvent *event) { switch(this->drawState.mode) { @@ -247,52 +233,59 @@ } } -void Document::select(const QSet<ldraw::id_t> &selected) +void EditorTabWidget::select(const QSet<ModelId> &selected) { QItemSelectionModel* selectionModel = this->ui.listView->selectionModel(); QItemSelection itemSelection; - for (const ldraw::id_t id : selected) + for (const ModelId id : selected) { - QModelIndex index = this->model->find(id); - if (index != QModelIndex{}) + const std::optional<int> row = this->model->find(id); + if (row.has_value()) { - itemSelection.select(index, index); + const QModelIndex qindex = this->model->index(*row); + itemSelection.select(qindex, qindex); } } selectionModel->select(itemSelection, QItemSelectionModel::ClearAndSelect); } -const Model &Document::getModel() const -{ - return *this->model; -} - -const QSet<ldraw::id_t> Document::selectedObjects() const +const QSet<ModelId> EditorTabWidget::selectedObjects() const { return this->canvas->selectedObjects(); } -void Document::closeShape() +void EditorTabWidget::closeShape() { if (this->drawState.polygon.size() >= 2 and this->drawState.polygon.size() <= 4) { - std::unique_ptr<ModelEditor> modelEditor = this->editModel(); switch (this->drawState.polygon.size()) { case 2: - modelEditor->append<ldraw::Edge>( - vectorToArray<2>(this->drawState.polygon), - ldraw::EDGE_COLOR); + this->model->append(Colored<LineSegment>{ + LineSegment{ + .p1 = this->drawState.polygon[0], + .p2 = this->drawState.polygon[1], + }, + ldraw::EDGE_COLOR}); break; case 3: - modelEditor->append<ldraw::Triangle>( - vectorToArray<3>(this->drawState.polygon), - ldraw::MAIN_COLOR); + this->model->append(Colored<Triangle>{ + Triangle{ + .p1 = this->drawState.polygon[0], + .p2 = this->drawState.polygon[1], + .p3 = this->drawState.polygon[2], + }, + ldraw::MAIN_COLOR}); break; case 4: - modelEditor->append<ldraw::Quadrilateral>( - vectorToArray<4>(this->drawState.polygon), - ldraw::MAIN_COLOR); + this->model->append(Colored<Quadrilateral>{ + Quadrilateral{ + .p1 = this->drawState.polygon[0], + .p2 = this->drawState.polygon[1], + .p3 = this->drawState.polygon[2], + .p4 = this->drawState.polygon[3], + }, + ldraw::MAIN_COLOR}); break; } }
--- a/src/document.h Wed May 25 20:36:34 2022 +0300 +++ b/src/document.h Mon Jun 06 22:01:22 2022 +0300 @@ -23,46 +23,38 @@ #include "ui/canvas.h" #include "model.h" #include "vertexmap.h" -#include "edithistory.h" -class Document : public QWidget +class EditorTabWidget : public QWidget { Q_OBJECT public: - explicit Document( + explicit EditorTabWidget( Model* model, DocumentManager* documents, const ldraw::ColorTable& colorTable, QWidget *parent = nullptr); - ~Document() override; + ~EditorTabWidget() override; QByteArray saveSplitterState() const; void restoreSplitterState(const QByteArray& state); - std::unique_ptr<ModelEditor> editModel(); void applyToVertices(VertexMap::ApplyFunction fn) const; - const Model& getModel() const; - const QSet<ldraw::id_t> selectedObjects() const; + const QSet<ModelId> selectedObjects() const; const ldraw::ColorTable& colorTable; Canvas* const canvas; + Model* const model; Q_SLOT void editingModeTriggered(); Q_SLOT void canvasMouseClick(QMouseEvent* event); Q_SLOT void canvasMouseMove(QMouseEvent* event); - void select(const QSet<ldraw::id_t> &selected); - DrawState drawState; + void select(const QSet<ModelId> &selected); Q_SIGNALS: void newStatusText(const QString& newStatusText); void splitterChanged(); private: void initializeTools(); void closeShape(); - Model* model; + DrawState drawState; DocumentManager* const documents; VertexMap vertexMap; class Ui_Document& ui; QToolBar* toolsBar; std::vector<QAction*> toolActions; - class ObjectEditor* objectEditor; - /** - * @brief History information of edits to this model - */ - // EditHistory editHistory; };
--- a/src/documentmanager.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/documentmanager.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -21,8 +21,6 @@ #include <QFileInfo> #include <QSaveFile> #include "documentmanager.h" -#include "modeleditor.h" -#include "linetypes/subfilereference.h" #include "parser.h" /** @@ -43,7 +41,7 @@ const ModelId modelId{++this->modelIdCounter}; const QString name = makeNewModelName(); this->openModels[modelId] = ModelInfo{ - .model = std::make_unique<Model>(), + .model = std::make_unique<Model>(this), .id = modelId, .opentype = OpenType::ManuallyOpened, }; @@ -400,6 +398,16 @@ return referencedFilePath; } +template<typename T> +void iterate(const Model& model, std::function<void(const T&)> fn) +{ + for (int i = 0; i < model.size(); ++i) { + if (std::holds_alternative<T>(model[i])) { + fn(std::get<T>(model[i])); + } + } +} + void DocumentManager::loadDependenciesForModel( const ModelId modelId, const QString &path, @@ -418,9 +426,8 @@ } ModelInfo& modelInfo = this->openModels[modelId]; modelInfo.dependencies.clear(); - for (int i = 0; i < modelInfo.model->size(); i += 1) - { - const QString referenceName = (*modelInfo.model)[i]->getProperty(ldraw::Property::ReferenceName).toString(); + iterate<Colored<SubfileReference>>(*modelInfo.model, [&](const SubfileReference& ref) { + const QString referenceName = ref.name; if (not referenceName.isEmpty() and modelInfo.dependencies.count(referenceName) == 0 and not failedToOpen.contains(referenceName)) @@ -458,5 +465,5 @@ bag.missing.append(referenceName); } } - } + }); }
--- a/src/edithistory.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -#include "edithistory.h" -#include "modeleditor.h" - -EditHistory::EditHistory() -{ - -} - -void InsertHistoryEntry::undo(ModelEditor &editContext) -{ - editContext.remove(this->position); -} - -void InsertHistoryEntry::redo(ModelEditor &editContext) -{ - -} - -void DeleteHistoryEntry::undo(ModelEditor &editContext) -{ - static_cast<InsertHistoryEntry*>(this)->redo(editContext); -} - -void DeleteHistoryEntry::redo(ModelEditor &editContext) -{ - static_cast<InsertHistoryEntry*>(this)->undo(editContext); -} - -void EditHistoryEntry::undo(ModelEditor &editContext) -{ - -} - -void EditHistoryEntry::redo(ModelEditor &editContext) -{ - -}
--- a/src/edithistory.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,102 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -#pragma once -#include "main.h" -#include "model.h" - -class ModelEditor; - -class AbstractHistoryEntry -{ -public: - virtual void undo(ModelEditor& editContext) = 0; - virtual void redo(ModelEditor& editContext) = 0; -}; - -class InsertHistoryEntry : public AbstractHistoryEntry -{ -public: - InsertHistoryEntry(int position, const QByteArray& state) : - position{position}, - state{state} {} - void undo(ModelEditor& editContext) override; - void redo(ModelEditor& editContext) override; -protected: - int position; - QByteArray state; -}; - -class DeleteHistoryEntry : public InsertHistoryEntry -{ -public: - void undo(ModelEditor& editContext) override; - void redo(ModelEditor& editContext) override; -}; - -class EditHistoryEntry : public AbstractHistoryEntry -{ -public: - EditHistoryEntry(int position, const QByteArray& stateBefore, const QByteArray& stateAfter) : - position{position}, - stateBefore{stateBefore}, - stateAfter{stateAfter} {} - void undo(ModelEditor& editContext) override; - void redo(ModelEditor& editContext) override; -private: - int position; - QByteArray stateBefore; - QByteArray stateAfter; -}; - -class EditHistory -{ -public: - using Changeset = std::vector<std::unique_ptr<AbstractHistoryEntry>>; - EditHistory(); - /** - * @brief Adds a new entry into the edit history. Creates a new changeset if there is not one already open. - * If behind in history, deletes all history entries in front. - */ - template<typename T, typename... Rs> - void add(Rs&&... args) - { - if (not this->changesetOpen) - { - while (this->position < this->changesets.size()) - { - this->changesets.erase(this->changesets.end() - 1); - } - if (this->changesets.empty()) - { - this->changesets.emplace_back(); - } - this->changesetOpen = true; - } - this->changesets.back().emplace_back(args...); - } - void commit() - { - this->changesetOpen = false; - this->position += 1; - } -private: - std::vector<Changeset> changesets; - std::size_t position = 0; - bool changesetOpen = false; -}; \ No newline at end of file
--- a/src/geometry.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/geometry.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -33,7 +33,7 @@ */ geom::Plane geom::planeFromTriangle(const geom::Triangle& triangle) { - return geom::Plane{normalVector(triangle), triangle.points[0]}; + return geom::Plane{normalVector(triangle), triangle.p1}; } /** @@ -45,8 +45,8 @@ { return glm::normalize( glm::cross( - triangle.points[1] - triangle.points[0], - triangle.points[2] - triangle.points[0])); + triangle.p2 - triangle.p1, + triangle.p3 - triangle.p1)); } /** @@ -107,7 +107,7 @@ { std::optional<glm::vec2> result = lineLineIntersection( rayToLine(ray), - lineFromPoints(line.points[0], line.points[1])); + lineFromPoints(line.p1, line.p2)); if (result.has_value()) { const float d1 = glm::dot(*result - ray.anchor, ray.direction); @@ -117,7 +117,7 @@ } else { - const float d2 = glm::dot(*result - line.points[0], *result - line.points[1]); + const float d2 = glm::dot(*result - line.p1, *result - line.p2); if (d2 > 0) { result.reset();
--- a/src/geometry.h Wed May 25 20:36:34 2022 +0300 +++ b/src/geometry.h Mon Jun 06 22:01:22 2022 +0300 @@ -24,24 +24,44 @@ glm::vec<N, T, Q> anchor; }; - template<int N> - struct Polygon - { - std::array<glm::vec3, N> points; - }; - - template<int N> - struct Polygon2D - { - glm::vec2 points[N]; - }; - inline const glm::vec3 origin = {0, 0, 0}; inline const Plane XY = {{0, 0, 1}, origin}; inline const Plane XZ = {{0, 1, 0}, origin}; inline const Plane YZ = {{1, 0, 0}, origin}; - using Triangle = Polygon<3>; - using LineSegment2D = Polygon2D<2>; + + struct LineSegment + { + glm::vec3 p1, p2; + }; + struct Triangle + { + glm::vec3 p1, p2, p3; + }; + struct Quadrilateral + { + glm::vec3 p1, p2, p3, p4; + }; + struct ConditionalEdge + { + glm::vec3 p1, p2; + glm::vec3 c1, c2; + }; + struct LineSegment2D + { + glm::vec2 p1, p2; + }; + + // get polygon type from amount of points + template<int N> + struct PolygonType {}; + template<> + struct PolygonType<2> { using type = LineSegment; }; + template<> + struct PolygonType<3> { using type = Triangle; }; + template<> + struct PolygonType<4> { using type = Quadrilateral; }; + template<int N> + using Polygon = typename PolygonType<N>::type; /** * @brief Computes a line from two points @@ -154,3 +174,4 @@ glm::vec3 pointOnCurve(const BezierCurve& curve, float t); glm::vec3 derivativeOnCurve(const BezierCurve& curve, float t); } +using namespace geom;
--- a/src/gl/common.h Wed May 25 20:36:34 2022 +0300 +++ b/src/gl/common.h Mon Jun 06 22:01:22 2022 +0300 @@ -27,6 +27,7 @@ #include <glm/gtc/type_ptr.hpp> #include "basics.h" #include "colors.h" +#include "model.h" namespace gl { @@ -77,7 +78,7 @@ } type; glm::vec3 vertices[4]; ldraw::Color color; - ldraw::id_t id; + ModelId id; /** * @return amount of vertices used for geometry @@ -124,41 +125,44 @@ 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) + inline Polygon edgeLine(const Colored<LineSegment>& seg, ModelId id) { - return {Polygon::EdgeLine, {v_1, v_2}, color, id}; - } - - inline Polygon triangle( - const glm::vec3& v_1, - const glm::vec3& v_2, - const glm::vec3& v_3, - ldraw::Color color, - ldraw::id_t id) - { - return {Polygon::Triangle, {v_1, v_2, v_3}, color, id}; + return Polygon{ + .type = Polygon::EdgeLine, + .vertices = {seg.p1, seg.p2}, + .color = seg.color, + .id = id, + }; } - inline Polygon quadrilateral( - const glm::vec3& v_1, - const glm::vec3& v_2, - const glm::vec3& v_3, - const glm::vec3& v_4, - ldraw::Color color, - ldraw::id_t id) + inline Polygon triangle(const Colored<Triangle>& tri, ModelId id) { - return {Polygon::Quadrilateral, {v_1, v_2, v_3, v_4}, color, id}; + return Polygon{ + .type = Polygon::Triangle, + .vertices = {tri.p1, tri.p2, tri.p3}, + .color = tri.color, + .id = id, + }; } - inline Polygon conditionalEdge( - const glm::vec3& v_1, - const glm::vec3& v_2, - const glm::vec3& control_1, - const glm::vec3& control_2, - ldraw::Color color, - ldraw::id_t id) + inline Polygon quadrilateral(const Colored<Quadrilateral>& quad, ModelId id) { - return {Polygon::ConditionalEdge, {v_1, v_2, control_1, control_2}, color, id}; + return Polygon{ + .type = Polygon::Quadrilateral, + .vertices = {quad.p1, quad.p2, quad.p3, quad.p4}, + .color = quad.color, + .id = id, + }; + } + + inline Polygon conditionalEdge(const Colored<ConditionalEdge>& cedge, ModelId id) + { + return Polygon{ + .type = Polygon::ConditionalEdge, + .vertices = {cedge.p1, cedge.p2, cedge.c1, cedge.c2}, + .color = cedge.color, + .id = id, + }; } // Vbo names
--- a/src/gl/compiler.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/gl/compiler.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -311,7 +311,7 @@ } } -ldraw::id_t gl::idFromColor(const std::array<GLubyte, 3>& data) +ModelId gl::idFromColor(const std::array<GLubyte, 3>& data) { return {data[0] * std::int32_t{0x10000} + data[1] * std::int32_t{0x100} + data[2]}; } @@ -330,7 +330,7 @@ shaderObject.vertexArray.release(); } -void gl::setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ldraw::id_t>& ids) +void gl::setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ModelId> &ids) { for (ModelShaders::ShaderObject& object : shaders->shaderObjects) {
--- a/src/gl/compiler.h Wed May 25 20:36:34 2022 +0300 +++ b/src/gl/compiler.h Mon Jun 06 22:01:22 2022 +0300 @@ -27,6 +27,7 @@ #include <QOpenGLBuffer> #include <QOpenGLShaderProgram> #include <QOpenGLExtraFunctions> +#include "model.h" class Model; class DocumentManager; @@ -64,9 +65,9 @@ void initializeModelShaders(ModelShaders* modelShaders); void bindModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass); void releaseModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass); - void setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ldraw::id_t>& ids); + void setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ModelId>& ids); std::size_t vertexCount(const ModelShaders *shaders, gl::ArrayClass arrayClass); - ldraw::id_t idFromColor(const std::array<GLubyte, 3>& data); + ModelId idFromColor(const std::array<GLubyte, 3>& data); template<typename T> void setShaderUniform(gl::ModelShaders* shaders, const char* uniformName, T&& value)
--- a/src/gl/partrenderer.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/gl/partrenderer.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -25,6 +25,7 @@ #include "geometry.h" #include "partrenderer.h" #include "model.h" +#include "gl/compiler.h" static constexpr double MIN_ZOOM = -3.0; static constexpr double MAX_ZOOM = 3.0; @@ -352,7 +353,7 @@ viewportVector); } -ldraw::id_t PartRenderer::pick(QPoint where) +ModelId PartRenderer::pick(QPoint where) { // y is flipped, take that into account where.setY(this->height() - where.y()); @@ -402,7 +403,7 @@ /** * @return the currently highlighted object */ -ldraw::id_t PartRenderer::getHighlightedObject() const +ModelId PartRenderer::getHighlightedObject() const { return this->highlighted; }
--- a/src/gl/partrenderer.h Wed May 25 20:36:34 2022 +0300 +++ b/src/gl/partrenderer.h Mon Jun 06 22:01:22 2022 +0300 @@ -1,15 +1,10 @@ #pragma once #include <QOpenGLWidget> -#include <QOpenGLFunctions> -#include <QQuaternion> -#include <QOpenGLVertexArrayObject> -#include <QOpenGLBuffer> -#include <QOpenGLShader> -#include <QOpenGLShaderProgram> -#include <glm/glm.hpp> #include "main.h" #include "gl/common.h" #include "gl/compiler.h" +#include "documentmanager.h" +#include "types/boundingbox.h" class PartRenderer : public QOpenGLWidget { @@ -22,9 +17,9 @@ QWidget* parent = nullptr); ~PartRenderer() override; void setRenderPreferences(const gl::RenderPreferences& newPreferences); - ldraw::id_t getHighlightedObject() const; + ModelId getHighlightedObject() const; protected: - ldraw::id_t pick(QPoint where); + ModelId pick(QPoint where); void initializeGL() override; void resizeGL(int width, int height) override; void paintGL() override; @@ -35,7 +30,7 @@ const ldraw::ColorTable& colorTable; BoundingBox boundingBox; gl::ModelShaders shaders; - ldraw::id_t highlighted = ldraw::NULL_ID; + ModelId highlighted = {0}; std::optional<glm::vec3> screenToModelCoordinates(const QPoint& point, const geom::Plane& plane) const; QPointF modelToScreenCoordinates(const glm::vec3& point) const; geom::Line<3> cameraLine(const QPoint& point) const;
--- a/src/gl/vertexprogram.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/gl/vertexprogram.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -120,7 +120,7 @@ this->fragmentStyle = newFragmentStyle; } -void VertexProgram::build(const Document *document) +void VertexProgram::build(const EditorTabWidget *document) { constexpr glm::vec3 color = {0.0, 1.0, 1.0}; this->data.clear();
--- a/src/gl/vertexprogram.h Wed May 25 20:36:34 2022 +0300 +++ b/src/gl/vertexprogram.h Mon Jun 06 22:01:22 2022 +0300 @@ -1,7 +1,7 @@ #ifndef VERTEXPROGRAM_H #define VERTEXPROGRAM_H #include "basicshaderprogram.h" -class Document; +class EditorTabWidget; class VertexProgram : public AbstractBasicShaderProgram { @@ -17,7 +17,7 @@ glm::vec3 color; }; VertexProgram(QObject* parent = nullptr); - void build(const Document* document); + void build(const EditorTabWidget* document); protected: const char* vertexShaderSource() const override; const char* fragmentShaderSource() const override;
--- a/src/invert.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/invert.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -21,7 +21,6 @@ #include "gl/common.h" #include "invert.h" #include "documentmanager.h" -#include "modeleditor.h" /* * Returns a matrix that causes a flip on the given dimension.
--- a/src/ldrawalgorithm.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/ldrawalgorithm.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -1,53 +1,26 @@ #include "ldrawalgorithm.h" -#include "linetypes/quadrilateral.h" -#include "linetypes/triangle.h" -void ldraw::invert(ModelEditor& editor, ldraw::id_t id, GetPolygonsContext* context) +std::pair<Triangle, Triangle> splitTriangles( + const Quadrilateral& q, + ldraw::Diagonal diagonal) { - editor.modifyObject(id, [context](ldraw::Object* object){ - object->invert(context); - }); -} - -static std::array<geom::Triangle, 2> splitTriangles(ldraw::Diagonal diagonal, const std::array<glm::vec3, 4>& points) -{ - std::array<geom::Triangle, 2> result; + std::pair<Triangle, Triangle> result; switch (diagonal) { case ldraw::Diagonal::Diagonal_13: - result = {geom::Triangle{points[0], points[1], points[2]}, {points[0], points[2], points[3]}}; + result = {Triangle{q.p1, q.p2, q.p3}, {q.p1, q.p3, q.p4}}; break; case ldraw::Diagonal::Diagonal_24: - result = {geom::Triangle{points[0], points[1], points[3]}, {points[1], points[2], points[3]}}; + result = {Triangle{q.p1, q.p2, q.p3}, {q.p2, q.p3, q.p4}}; break; } return result; } -auto ldraw::splitQuadrilateral( - ModelEditor& editor, - ldraw::quadrilateralid_t quadrilateral_id, - ldraw::Diagonal splitType -) -> std::optional<std::pair<ldraw::triangleid_t, ldraw::triangleid_t>> -{ - std::optional<std::pair<ldraw::triangleid_t, ldraw::triangleid_t>> result; - const auto resolved = editor.model().get2(quadrilateral_id); - if (resolved.object != nullptr) - { - const ldraw::Color color = resolved.object->colorIndex; - const std::array<geom::Triangle, 2> split = splitTriangles(splitType, resolved.object->points); - const int position = resolved.index.row(); - editor.remove(position); - result = std::make_pair( - editor.insert<ldraw::Triangle>(position, split[0].points, color), - editor.insert<ldraw::Triangle>(position, split[1].points, color)); - } - return result; -} - /** * @brief Modifies the !LDRAW_ORG line so that it becomes unofficial */ +/* void ldraw::makeUnofficial(ModelEditor& editor) { if (editor.model().size() >= 4) @@ -71,4 +44,24 @@ } } } -} \ No newline at end of file +} +*/ + +ModelElement inverted(const ModelElement& element) +{ + return std::visit(overloaded{ + [](Colored<SubfileReference> ref) -> ModelElement { + ref.inverted = not ref.inverted; + return ref; + }, + [](Colored<Triangle> triangle) -> ModelElement { + std::swap(triangle.p1, triangle.p2); + return triangle; + }, + [](Colored<Quadrilateral> quad) -> ModelElement { + std::swap(quad.p2, quad.p4); + return quad; + }, + [](const ModelElement& x) { return x; } + }, element); +}
--- a/src/ldrawalgorithm.h Wed May 25 20:36:34 2022 +0300 +++ b/src/ldrawalgorithm.h Mon Jun 06 22:01:22 2022 +0300 @@ -1,5 +1,6 @@ #pragma once -#include "modeleditor.h" +#include "main.h" +#include "model.h" namespace ldraw { @@ -10,15 +11,13 @@ Diagonal_24 }; - // Splits the specified quadrilateral into triangles. - // If it is not a quadrilateral then no action is performed - auto splitQuadrilateral(ModelEditor& editor, - quadrilateralid_t quadrilateral_id, - Diagonal splitType = Diagonal::Diagonal_13 - ) -> std::optional<std::pair<triangleid_t, triangleid_t>>; + std::pair<Triangle, Triangle> splitTriangles( + const Quadrilateral& q, + ldraw::Diagonal diagonal); - void invert(ModelEditor& editor, ldraw::id_t id, GetPolygonsContext *context); + /* void makeUnofficial(ModelEditor &editor); + */ template<typename Fn> void circle(int segments, int divisions, Fn&& fn) @@ -34,3 +33,5 @@ } } } + +ModelElement inverted(const ModelElement &element);
--- a/src/linetypes/circularprimitive.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ -#include "circularprimitive.h" - -ldraw::CircularPrimitive::CircularPrimitive(CircularPrimitiveType type, int segments, int divisions) : - type{type}, - segments{segments}, - divisions{divisions} -{ -} - -QVariant ldraw::CircularPrimitive::getProperty(Property property) const -{ - switch (property) - { - case Property::Segments: - return this->segments; - case Property::Divisions: - return this->divisions; - case Property::CircularPrimitiveType: - return this->type; - default: - return BaseClass::getProperty(property); - } -} - -QString ldraw::CircularPrimitive::textRepresentation() const -{ - return circularPrimitiveTypeName(this->type) + " " + QString::number(this->fraction()); -} - -QString ldraw::CircularPrimitive::circularPrimitiveTypeName(CircularPrimitiveType type) -{ - switch (type) - { - case Circle: - return QObject::tr("Circle"); - case Disc: - return QObject::tr("Disc"); - } -} - -ldraw::Object::Type ldraw::CircularPrimitive::typeIdentifier() const -{ - return ldraw::Object::Type::CircularPrimitive; -} - -QDataStream &ldraw::CircularPrimitive::serialize(QDataStream &stream) const -{ - return BaseClass::serialize(stream) << this->type << this->segments << this->divisions; -} - -QDataStream &ldraw::CircularPrimitive::deserialize(QDataStream &stream) -{ - return BaseClass::deserialize(stream) >> this->type >> this->segments >> this->divisions; -} - -QString ldraw::CircularPrimitive::toLDrawCode() const -{ - return utility::format( - "0 !LDFORGE CIRCULAR_PRIMITIVE %1 %2 %3 %4", - static_cast<int>(this->type), - this->segments, - this->divisions, - this->transformToBareString()); -} - -QString ldraw::CircularPrimitive::iconName() const -{ - return ":/icons/linetype-circularprimitive.png"; -} - -QString ldraw::CircularPrimitive::typeName() const -{ - return QObject::tr("circular primitive"); -} - -void ldraw::CircularPrimitive::getPolygons(std::vector<gl::Polygon> &polygons, GetPolygonsContext *) const -{ - for (int i = 0; i < this->segments; i += 1) - { - const float ang_1 = (2 * pi<> * i) / this->divisions; - const float ang_2 = (2 * pi<> * (i + 1)) / this->divisions; - const glm::vec3 p_1 = {std::sin(ang_1), 0, std::cos(ang_1)}; - const glm::vec3 p_2 = {std::sin(ang_2), 0, std::cos(ang_2)}; - switch (this->type) - { - case Circle: - polygons.push_back(gl::edgeLine( - p_1, - p_2, - this->colorIndex, - this->id)); - break; - case Disc: - polygons.push_back(gl::triangle( - {0, 0, 0}, - p_1, - p_2, - this->colorIndex, - this->id)); - break; - } - } -} - -float ldraw::CircularPrimitive::fraction() const -{ - return static_cast<float>(this->segments) / static_cast<float>(this->divisions); -}
--- a/src/linetypes/circularprimitive.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -#pragma once -#include "compoundobject.h" -#include "propertygenerics.h" - -namespace ldraw -{ - class CircularPrimitive; -} - -class ldraw::CircularPrimitive : public CompoundObject -{ -public: - using BaseClass = CompoundObject; - CircularPrimitive() = default; - CircularPrimitive(CircularPrimitiveType type, int segments, int divisions); - QVariant getProperty(Property property) const override; - QString textRepresentation() const override; - static QString circularPrimitiveTypeName(CircularPrimitiveType type); - Type typeIdentifier() const override; - QDataStream& serialize(QDataStream& stream) const override; - QDataStream& deserialize(QDataStream& stream) override; - QString toLDrawCode() const override; - QString iconName() const override; - QString typeName() const override; - void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext*) const override; - float fraction() const; - CircularPrimitiveType type = Circle; - int segments = 16; - int divisions = 16; -}; -
--- a/src/linetypes/compoundobject.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ -#include "compoundobject.h" -#include "documentmanager.h" -#include "invert.h" -#include "polygoncache.h" - -ldraw::CompoundObject::CompoundObject -( - const glm::mat4& transformation, - const Color color -) : - ColoredObject{color}, - transformation{transformation} -{ -} - -QVariant ldraw::CompoundObject::getProperty(Property property) const -{ - switch (property) - { - case Property::Transformation: - return QVariant::fromValue(this->transformation); - case Property::IsInverted: - return this->isInverted; - default: - return ColoredObject::getProperty(property); - } -} - -void ldraw::CompoundObject::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) -{ - LDRAW_OBJECT_HANDLE_SET_PROPERTY(Transformation, {this->transformation = value;}); - LDRAW_OBJECT_HANDLE_SET_PROPERTY(IsInverted, {this->isInverted = value;}); - ldraw::ColoredObject::setProperty(result, pair); -} - -glm::vec3 ldraw::CompoundObject::position() const -{ - return this->transformation[3]; -} - -void ldraw::CompoundObject::invert(GetPolygonsContext *context) -{ - const std::optional<Axis> flatDimension = context ? this->flatDimension(context) : std::optional<Axis>{}; - if (flatDimension.has_value()) - { - glm::mat4 matrix = glm::identity<glm::mat4>(); - matrix[*flatDimension][*flatDimension] = -1.0f; - this->transformation *= matrix; - } - else - { - this->isInverted = not this->isInverted; - } -} - - -QDataStream& ldraw::CompoundObject::serialize(QDataStream &stream) const -{ - return ColoredObject::serialize(stream) << this->transformation << this->isInverted; -} - -QDataStream& ldraw::CompoundObject::deserialize(QDataStream &stream) -{ - return ColoredObject::deserialize(stream) >> this->transformation >> this->isInverted; -} - -/** - * @brief Finds out in which dimension the object is flat in. In that dimension all vertices have a value of 0. - * If the object is not flat, this does not return a value. - * @param model Model to find out flatness of - * @param documents Where to look for subfiles - * @returns dimension the model is flat in, if such dimension exists, no value otherwise. - */ -std::optional<Axis> ldraw::CompoundObject::flatDimension(GetPolygonsContext *context) const -{ - // The dimensions that this model is potentially flat in. - QVector<Axis> dimensions = {X, Y, Z}; - std::vector<gl::Polygon> polygons; - this->getPolygons(polygons, context); - for (const gl::Polygon& polygon : polygons) - { - for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) - { - const glm::vec3& v_i = polygon.vertices[i]; - if (not qFuzzyCompare(v_i.x, 0.0f)) - { - dimensions.removeOne(X); - } - if (not qFuzzyCompare(v_i.y, 0.0f)) - { - dimensions.removeOne(Y); - } - if (not qFuzzyCompare(v_i.z, 0.0f)) - { - dimensions.removeOne(Z); - } - } - // If there are no more dimensions left, we can exit the loop. - if (dimensions.isEmpty()) - { - break; - } - } - if (dimensions.size() == 1) - { - // The model is flat in one dimension, return that. - // If the model is flat in two or three dimensions, it's not really a valid model. - return dimensions[0]; - } - else - { - // The model is not flat. - return {}; - } -} - -QString ldraw::CompoundObject::transformToBareString() const -{ - return utility::format( - "%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12", - this->transformation[3][0], - this->transformation[3][1], - this->transformation[3][2], - this->transformation[0][0], - this->transformation[1][0], - this->transformation[2][0], - this->transformation[0][1], - this->transformation[1][1], - this->transformation[2][1], - this->transformation[0][2], - this->transformation[1][2], - this->transformation[2][2]); -}
--- a/src/linetypes/compoundobject.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -#pragma once -#include "object.h" - -class Model; - -namespace ldraw -{ - class CompoundObject; -} - -/** - * @brief An abstract object that has a transformation mmatrix and can be inverted. - * It can be inlined into multiple objects. - */ -class ldraw::CompoundObject : public ColoredObject -{ -public: - CompoundObject() = default; - CompoundObject( - const glm::mat4& transformation, - const Color color = ldraw::MAIN_COLOR); - QVariant getProperty(Property property) const override; - glm::vec3 position() const; - void invert(GetPolygonsContext*context) override; - Model* resolve(const ModelId callingModelId, DocumentManager* documents) const; - QDataStream& serialize(QDataStream& stream) const override; - QDataStream& deserialize(QDataStream& stream) override; - std::optional<Axis> flatDimension(GetPolygonsContext *context) const; - glm::mat4 transformation; - QString transformToBareString() const; - bool isInverted = false; -protected: - void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; -};
--- a/src/linetypes/conditionaledge.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -#include "conditionaledge.h" - -QString ldraw::ConditionalEdge::textRepresentation() const -{ - 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])); -} - -ldraw::Object::Type ldraw::ConditionalEdge::typeIdentifier() const -{ - return Type::ConditionalEdge; -} - -QString ldraw::ConditionalEdge::toLDrawCode() const -{ - return utility::format( - "5 %1 %2 %3 %4 %5", - this->colorIndex.index, - utility::vertexToString(this->points[0]), - utility::vertexToString(this->points[1]), - utility::vertexToString(this->points[2]), - utility::vertexToString(this->points[3])); -} - -QString ldraw::ConditionalEdge::iconName() const -{ - return ":/icons/linetype-conditionaledge.png"; -} - -QString ldraw::ConditionalEdge::typeName() const -{ - return QObject::tr("conditional edge"); -}
--- a/src/linetypes/conditionaledge.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -#pragma once -#include "polygonobject.h" - -namespace ldraw -{ - class ConditionalEdge; -} - -class ldraw::ConditionalEdge : public PolygonObject<4> -{ -public: - using PolygonObject<4>::PolygonObject; - QString textRepresentation() const override; - Type typeIdentifier() const override; - QString toLDrawCode() const override; - QString iconName() const override; - QString typeName() const override; -};
--- a/src/linetypes/edge.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -#include "edge.h" - -QString ldraw::Edge::textRepresentation() const -{ - return utility::format( - "%1 %2", - utility::vertexToStringParens(this->points[0]), - utility::vertexToStringParens(this->points[1])); -} - -void ldraw::Edge::getPolygons( - std::vector<gl::Polygon>& polygons, - GetPolygonsContext* context) const -{ - Q_UNUSED(context) - polygons.push_back(gl::edgeLine(this->points[0], this->points[1], this->colorIndex, this->id)); -} - -ldraw::Object::Type ldraw::Edge::typeIdentifier() const -{ - return Type::EdgeLine; -} - -QString ldraw::Edge::toLDrawCode() const -{ - return utility::format( - "2 %1 %2 %3", - this->colorIndex.index, - utility::vertexToString(this->points[0]), - utility::vertexToString(this->points[1])); -} - -QString ldraw::Edge::iconName() const -{ - return ":/icons/linetype-edgeline.png"; -} - -QString ldraw::Edge::typeName() const -{ - return QObject::tr("edge"); -}
--- a/src/linetypes/edge.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -#pragma once -#include "polygonobject.h" - -namespace ldraw -{ - class Edge; -} - -class ldraw::Edge : public PolygonObject<2> -{ -public: - using PolygonObject::PolygonObject; - QString textRepresentation() const override; - void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override; - Type typeIdentifier() const override; - QString toLDrawCode() const override; - QString iconName() const override; - QString typeName() const override; -};
--- a/src/linetypes/errorline.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -#include <QBrush> -#include "errorline.h" - -ldraw::ErrorLine::ErrorLine(QStringView text, QStringView message) : - text{text.toString()}, - message{message.toString()} -{ -} - -QVariant ldraw::ErrorLine::getProperty(Property property) const -{ - switch (property) - { - case Property::Text: - return this->text; - case Property::ErrorMessage: - return this->message; - default: - return Object::getProperty(property); - } -} - -void ldraw::ErrorLine::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) -{ - 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 -{ - return this->text; -} - -QBrush ldraw::ErrorLine::textRepresentationForeground() const -{ - return QBrush{Qt::yellow}; -} - -QBrush ldraw::ErrorLine::textRepresentationBackground() const -{ - return QBrush{Qt::red}; -} - -ldraw::Object::Type ldraw::ErrorLine::typeIdentifier() const -{ - return Type::ErrorLine; -} - -QDataStream &ldraw::ErrorLine::serialize(QDataStream &stream) const -{ - return ldraw::Object::serialize(stream) << this->text; -} - -QDataStream &ldraw::ErrorLine::deserialize(QDataStream &stream) -{ - return ldraw::Object::deserialize(stream) >> this->text; -} - -QString ldraw::ErrorLine::toLDrawCode() const -{ - return this->text; -} - -QString ldraw::ErrorLine::iconName() const -{ - return ":/icons/linetype-errorline.png"; -} - -QString ldraw::ErrorLine::typeName() const -{ - return QObject::tr("error line"); -}
--- a/src/linetypes/errorline.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -#pragma once -#include "object.h" - -namespace ldraw -{ - class ErrorLine; -} - -class ldraw::ErrorLine : public Object -{ -public: - using BaseClass = Object; - ErrorLine(QStringView text = u"", QStringView message = u""); - QVariant getProperty(Property property) const override; - QString textRepresentation() const override; - QBrush textRepresentationForeground() const override; - QBrush textRepresentationBackground() const override; - Type typeIdentifier() const override; - QDataStream& serialize(QDataStream& stream) const override; - QDataStream& deserialize(QDataStream& stream) override; - QString toLDrawCode() const override; - QString iconName() const override; - QString typeName() const override; - QString text; - QString message; -protected: - void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; -};
--- a/src/linetypes/metacommand.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -#include "metacommand.h" - -ldraw::MetaCommand::MetaCommand(QStringView text) : - Object{}, - storedText{text.toString()} {} - -QVariant ldraw::MetaCommand::getProperty(Property property) const -{ - switch (property) - { - case Property::Text: - return storedText; - default: - return Object::getProperty(property); - } -} - -void ldraw::MetaCommand::setProperty(ldraw::Object::SetPropertyResult* result, const PropertyKeyValue& pair) -{ - LDRAW_OBJECT_HANDLE_SET_PROPERTY(Text, {this->storedText = value;}); - BaseClass::setProperty(result, pair); -} - -QString ldraw::MetaCommand::textRepresentation() const -{ - return this->storedText; -} - -ldraw::Object::Type ldraw::MetaCommand::typeIdentifier() const -{ - return Type::MetaCommand; -} - -/** - * @brief @overload ldraw::Object::serialize - * @param stream - * @return stream - */ -QDataStream& ldraw::MetaCommand::serialize(QDataStream& stream) const -{ - return ldraw::Object::serialize(stream) << this->storedText; -} - -/** - * @brief @overload ldraw::Object::deserialize - * @param stream - * @return stream - */ -QDataStream& ldraw::MetaCommand::deserialize(QDataStream& stream) -{ - return ldraw::Object::deserialize(stream) >> this->storedText; -} - -QString ldraw::MetaCommand::toLDrawCode() const -{ - return "0 " + this->storedText; -} - -QString ldraw::MetaCommand::iconName() const -{ - return ":/icons/chatbubble-ellipses-outline.png"; -} - -QString ldraw::MetaCommand::typeName() const -{ - return QObject::tr("comment"); -} -
--- a/src/linetypes/metacommand.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -#pragma once -#include "object.h" - -namespace ldraw -{ - class MetaCommand; -} -class ldraw::MetaCommand : public Object -{ -public: - using BaseClass = Object; - MetaCommand() = default; - MetaCommand(QStringView text); - QVariant getProperty(Property property) const override; - QString textRepresentation() const override; - QString storedText = ""; - Type typeIdentifier() const override; - QDataStream& serialize(QDataStream& stream) const override; - QDataStream& deserialize(QDataStream& stream) override; - QString toLDrawCode() const override; - QString iconName() const override; - QString typeName() const override; -protected: - void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; -};
--- a/src/linetypes/object.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +0,0 @@ -#include <QBrush> -#include <QFont> -#include "object.h" -#include "widgets/vec3editor.h" -#include "modeleditor.h" - -static std::int32_t getIdForNewObject() -{ - static std::int32_t id = 0; - id += 1; - return id; -} - -ldraw::Object::Object() : - id {getIdForNewObject()} -{ -} - -ldraw::Object::~Object() -{ -} - -bool ldraw::Object::hasColor() const -{ - return false; -} - -QVariant ldraw::Object::getProperty(Property id) const -{ - Q_UNUSED(id); - return {}; -} - -void ldraw::Object::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) -{ - 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 -{ - return {}; -} - -QBrush ldraw::Object::textRepresentationBackground() const -{ - return {}; -} - -QFont ldraw::Object::textRepresentationFont() const -{ - return {}; -} - -void ldraw::Object::getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const -{ - Q_UNUSED(polygons) - Q_UNUSED(context) -} - -const glm::vec3& ldraw::Object::getPoint(int index) const -{ - Q_UNUSED(index); - throw BadPointIndex{}; -} - -/** - * @brief Serializes the object into a stream of bytes for internal storage. - * @param stream Data stream to serialize into - */ -QDataStream& ldraw::Object::serialize(QDataStream &stream) const -{ - return stream << static_cast<int>(this->typeIdentifier()); -} - -/** - * @brief Deserializes the object from a stream of bytes coming from internal storage. - * @param stream Data stream to serialize from. - * @note @c ldraw::Object::serialize will insert a type enumerator. Before calling this function, - * this enumerator is assumed to have been handled as the object class needs to be initialized based - * on the value of this enumerator. - */ -QDataStream& ldraw::Object::deserialize(QDataStream &stream) -{ - return stream; -} - -QString ldraw::Object::iconName() const -{ - return ""; -} - -ldraw::ColoredObject::ColoredObject(const Color color_index) : - colorIndex{color_index} -{ -} - -bool ldraw::ColoredObject::hasColor() const -{ - return true; -} - -QVariant ldraw::ColoredObject::getProperty(Property id) const -{ - switch (id) - { - case Property::Color: - return QVariant::fromValue<Color>(colorIndex); - default: - return Object::getProperty(id); - } -} - -void ldraw::ColoredObject::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) -{ - LDRAW_OBJECT_HANDLE_SET_PROPERTY(Color, {colorIndex = value;}); - Object::setProperty(result, pair); -} - -/** - * @brief @overload @c ldraw::Object::serialize - * @param stream - * @return stream - */ -QDataStream& ldraw::ColoredObject::serialize(QDataStream& stream) const -{ - return Object::serialize(stream) << this->colorIndex; -} - -/** - * @brief @overload @c ldraw::Object::deserialize - * @param stream - * @return stream - */ -QDataStream& ldraw::ColoredObject::deserialize(QDataStream& stream) -{ - return Object::deserialize(stream) >> this->colorIndex; -} - -QString ldraw::Empty::textRepresentation() const -{ - return ""; -} - -ldraw::Object::Type ldraw::Empty::typeIdentifier() const -{ - return Type::Empty; -} - -QString ldraw::Empty::toLDrawCode() const -{ - return ""; -} - -QString ldraw::Empty::typeName() const -{ - return QObject::tr("empty"); -}
--- a/src/linetypes/object.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +0,0 @@ -#pragma once -#include <QPointF> -#include <QString> -#include <QStringView> -#include "main.h" -#include "colors.h" -#include "gl/common.h" -#include "linetypes/propertygenerics.h" - -class Model; - -namespace ldraw -{ - struct GetPolygonsContext; - class Object; - class ColoredObject; - class Empty; - class UnhandledProperty; -} - -class DocumentManager; - -struct ldraw::GetPolygonsContext -{ - ::ModelId modelId; - ::DocumentManager* documents; -}; - -class ldraw::Object -{ -public: - enum class SetPropertyResult - { - Success = 0, - PropertyNotHandled - }; - /** - * @brief Enumerates different object types - */ - enum class Type - { - Empty, - Comment, - MetaCommand, - ErrorLine, - SubfileReference, - EdgeLine, - ConditionalEdge, - Triangle, - Quadrilateral, - CircularPrimitive, - }; - 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 bool hasColor() const; - virtual QVariant getProperty(Property id) const; - template<ldraw::Property property> - PropertyType<property> getProperty() const; - template<ldraw::Property property> - SetPropertyResult setProperty(const PropertyType<property>& 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<gl::Polygon>& polygons, GetPolygonsContext* context) const; - virtual void invert(GetPolygonsContext*) {} - virtual int numPoints() const { return 0; } - virtual const glm::vec3& getPoint(int index) const; - virtual QDataStream& serialize(QDataStream& stream) const; - virtual QDataStream& deserialize(QDataStream& stream); - virtual Type typeIdentifier() const = 0; - virtual QString toLDrawCode() const = 0; - virtual QString iconName() const; - virtual QString typeName() const = 0; - -protected: - template<Property property, typename Function> - void handle(SetPropertyResult* result, const PropertyKeyValue& pair, Function function); - virtual void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair); -}; - -/** - * @brief Tests whether the object is exactly of the specified type - * @tparam R Type of LDraw line type object to test for - * @param object Object to test - * @returns whether the type of the object specified by @c id is the same type as R. Returns false if it is a subclass. - */ -template<typename R> -bool isA(const ldraw::Object* object) -{ - const std::type_info& a = typeid(*object); - const std::type_info& b = typeid(R); - return a == b; -} - -template<ldraw::Property property> -ldraw::Object::SetPropertyResult ldraw::Object::setProperty(const ldraw::PropertyType<property>& value) -{ - SetPropertyResult result = SetPropertyResult::PropertyNotHandled; - this->setProperty(&result, PropertyKeyValue{property, QVariant::fromValue(value)}); - return result; -} - -template<ldraw::Property property, typename Function> -void ldraw::Object::handle(SetPropertyResult* result, const PropertyKeyValue& pair, Function function) -{ - if (pair.key == property) - { - function(pair.value.value<ldraw::PropertyType<property>>()); - *result = SetPropertyResult::Success; - } -} - -template<ldraw::Property property> -ldraw::PropertyType<property> ldraw::Object::getProperty() const -{ - return this->getProperty(property).value<ldraw::PropertyType<property>>(); -} - -class ldraw::ColoredObject : public Object -{ -public: - ColoredObject(const Color colorIndex = ldraw::MAIN_COLOR); - bool hasColor() const override final; - QVariant getProperty(Property id) const override; - QDataStream &serialize(QDataStream& stream) const override; - QDataStream& deserialize(QDataStream& stream) override; - Color colorIndex = ldraw::MAIN_COLOR; -protected: - void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; -}; - -/** - * @brief Represents an empty line. - */ -class ldraw::Empty : public Object -{ - QString textRepresentation() const override; - Type typeIdentifier() const override; - QString toLDrawCode() const override; - QString typeName() const override; -};
--- a/src/linetypes/polygonobject.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -#pragma once -#include "object.h" -#include "widgets/vec3editor.h" -#include "model.h" -#include "modeleditor.h" - -namespace ldraw -{ - template<int N, typename> - class PolygonObject; -} - -template<int N, typename = std::enable_if_t<(N > 0 and N <= 4)>> -class ldraw::PolygonObject : public ColoredObject -{ -public: - using BaseClass = ColoredObject; - PolygonObject(const std::array<glm::vec3, N>& points, const Color color) : - ColoredObject{color}, - points{points} {} - int numPoints() const override - { - return N; - } - const glm::vec3& getPoint(int index) const override - { - Q_ASSERT(index >= 0 and index < N); - return this->points[index]; - } - QVariant getProperty(const Property id) const override - { - switch (id) - { - case Property::Point0: - return QVariant::fromValue(points[0]); - case Property::Point1: - return QVariant::fromValue(points[1]); - case Property::Point2: - if (N >= 3) - { - return QVariant::fromValue(points[2]); - } - break; - case Property::Point3: - if (N >= 4) - { - return QVariant::fromValue(points[3]); - } - break; - default: - break; - } - return BaseClass::getProperty(id); - } - void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override - { - 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); - } - QDataStream &serialize(QDataStream& stream) const override - { - ColoredObject::serialize(stream); - for (const glm::vec3& point : this->points) - { - stream << point; - } - return stream; - } - QDataStream& deserialize(QDataStream& stream) override - { - ColoredObject::deserialize(stream); - for (glm::vec3& point : this->points) - { - stream >> point; - } - return stream; - } - std::array<glm::vec3, N> points; -};
--- a/src/linetypes/propertygenerics.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +0,0 @@ -#pragma once -#include "main.h" -#include "colors.h" - -class Vec3Editor; -class MatrixEditor; - -namespace ldraw -{ - enum CircularPrimitiveType - { - Circle, - Disc - }; - enum class Property; - struct PropertyKeyValue; - template<Property property> - struct PropertyTraits - { - static constexpr bool defined = false; - }; -} - -Q_DECLARE_METATYPE(ldraw::CircularPrimitiveType) - -/** - * 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 - CircularPrimitiveType, // Type of circular primitive (circle, disc, ...) - Segments, // Amount of circular segments this primitive covers (numerator) - Divisions, // Amount of segments in the circle this primitive fits in (8, 16, 48, ...) -}; - -Q_DECLARE_METATYPE(ldraw::Property) - -// Mapping of properties to types -#define LDFORGE_DEFINE_PROPERTY_TYPE(PROPERTY, TYPE) \ - namespace ldraw \ - { \ - template<> struct PropertyTraits<ldraw::Property::PROPERTY> \ - { \ - using type = TYPE; \ - static constexpr std::array<char, 256> name{#PROPERTY}; \ - static constexpr bool defined = true; \ - }; \ - } - -LDFORGE_DEFINE_PROPERTY_TYPE(Color, ldraw::Color) -LDFORGE_DEFINE_PROPERTY_TYPE(Text, QString) -LDFORGE_DEFINE_PROPERTY_TYPE(Point0, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(Point1, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(Point2, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(Point3, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(Transformation, glm::mat4) -LDFORGE_DEFINE_PROPERTY_TYPE(ReferenceName, QString) -LDFORGE_DEFINE_PROPERTY_TYPE(IsInverted, bool) -LDFORGE_DEFINE_PROPERTY_TYPE(ErrorMessage, QString) -LDFORGE_DEFINE_PROPERTY_TYPE(CircularPrimitiveType, ldraw::CircularPrimitiveType) -LDFORGE_DEFINE_PROPERTY_TYPE(Segments, int) -LDFORGE_DEFINE_PROPERTY_TYPE(Divisions, int) - -#define LDRAW_OBJECT_HANDLE_SET_PROPERTY(PROPERTY, HANDLER) \ - {this->handle<ldraw::Property::PROPERTY>(result, pair, \ - [&](const ldraw::PropertyType<ldraw::Property::PROPERTY>& value) HANDLER);} - -// Generics -namespace ldraw -{ - template<ldraw::Property property> - using PropertyType = typename PropertyTraits<property>::type; - - template<ldraw::Property property> - inline const char* PROPERTY_NAME = PropertyTraits<property>::name; - - constexpr int MAX_POINTS = 4; - - struct PropertyKeyValue - { - Property key; - QVariant value; - }; - - constexpr Property pointProperty(int n) - { - Q_ASSERT(n >= 0 and n < MAX_POINTS); - return static_cast<Property>(static_cast<int>(Property::Point0) + n); - } - - struct PropertyTrait - { - ldraw::Property property; - std::array<char, 256> name; - int type; - }; - - namespace detail - { - template<int N> - constexpr int propertyCountHelper() - { - if constexpr (ldraw::PropertyTraits<static_cast<Property>(N)>::defined) - { - return propertyCountHelper<N + 1>(); - } - else - { - return N; - } - } - - template<int k> - constexpr PropertyTrait getPropertyTrait() - { - constexpr auto property = static_cast<ldraw::Property>(k); - using trait = ldraw::PropertyTraits<property>; - return PropertyTrait{ - property, - trait::name, - qMetaTypeId<typename trait::type>() - }; - } - - template<int... Ints> - auto getPropertyTraits(std::integer_sequence<int, Ints...>) - { - return std::array<PropertyTrait, sizeof...(Ints)>{getPropertyTrait<Ints>()...}; - } - } - - constexpr int NUM_PROPERTIES = detail::propertyCountHelper<0>(); - inline const auto& traits() - { - static std::array<PropertyTrait, NUM_PROPERTIES> result = - detail::getPropertyTraits(std::make_integer_sequence<int, NUM_PROPERTIES>()); - return result; - } - - inline const auto& traits(ldraw::Property property) - { - return traits()[static_cast<int>(property)]; - } - - template<typename T, std::size_t... Ints> - constexpr auto makeIndexArray(std::index_sequence<Ints...>) - { - return std::array{static_cast<T>(Ints)...}; - } - - constexpr auto ALL_PROPERTIES = makeIndexArray<Property>(std::make_index_sequence<NUM_PROPERTIES>{}); - - template<typename T> - bool testPropertyType(ldraw::Property property) - { - return qMetaTypeId<T>() == ldraw::traits(property).type; - } -}
--- a/src/linetypes/quadrilateral.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -#include "quadrilateral.h" - -QString ldraw::Quadrilateral::textRepresentation() const -{ - 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])); -} - -void ldraw::Quadrilateral::getPolygons( - std::vector<gl::Polygon>& polygons, - GetPolygonsContext* context) const -{ - Q_UNUSED(context) - polygons.push_back(gl::quadrilateral( - this->points[0], - this->points[1], - this->points[2], - this->points[3], - this->colorIndex, - this->id)); -} - -void ldraw::Quadrilateral::invert(GetPolygonsContext *) -{ - // 0 1 2 3 - // -> 2 1 0 3 - std::swap(this->points[0], this->points[2]); -} - -ldraw::Object::Type ldraw::Quadrilateral::typeIdentifier() const -{ - return Type::Quadrilateral; -} - -QString ldraw::Quadrilateral::toLDrawCode() const -{ - return utility::format( - "4 %1 %2 %3 %4 %5", - this->colorIndex.index, - utility::vertexToString(this->points[0]), - utility::vertexToString(this->points[1]), - utility::vertexToString(this->points[2]), - utility::vertexToString(this->points[3])); -} - -QString ldraw::Quadrilateral::iconName() const -{ - return ":/icons/linetype-quadrilateral.png"; -} - -QString ldraw::Quadrilateral::typeName() const -{ - return QObject::tr("quadrilateral"); -}
--- a/src/linetypes/quadrilateral.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -#pragma once -#include "polygonobject.h" - -namespace ldraw -{ - class Quadrilateral; -} - -class ldraw::Quadrilateral : public PolygonObject<4> -{ -public: - using PolygonObject<4>::PolygonObject; - QString textRepresentation() const override; - void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override; - void invert(GetPolygonsContext *) override; - Type typeIdentifier() const override; - QString toLDrawCode() const override; - QString iconName() const override; - QString typeName() const override; -};
--- a/src/linetypes/subfilereference.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,128 +0,0 @@ -#include "subfilereference.h" -#include "documentmanager.h" -#include "invert.h" -#include "polygoncache.h" - -ldraw::SubfileReference::SubfileReference -( - const glm::mat4& transformation, - const QString& referenceName, - const Color color -) : - CompoundObject{transformation, color}, - referenceName{referenceName} -{ -} - -QVariant ldraw::SubfileReference::getProperty(Property property) const -{ - switch (property) - { - case Property::ReferenceName: - return this->referenceName; - default: - return CompoundObject::getProperty(property); - } -} - -void ldraw::SubfileReference::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) -{ - LDRAW_OBJECT_HANDLE_SET_PROPERTY(ReferenceName, {this->referenceName = value;}); - ldraw::CompoundObject::setProperty(result, pair); -} - -QString ldraw::SubfileReference::textRepresentation() const -{ - return this->referenceName + " " + utility::vertexToStringParens(this->position()); -} - -void ldraw::SubfileReference::getPolygons -( - std::vector<gl::Polygon>& polygons, - GetPolygonsContext* context -) const -{ - Model* dependency = this->resolve(context->modelId, context->documents); - PolygonCache* cache = nullptr; - if (dependency != nullptr) - { - const auto dependencyModelId = context->documents->findIdForModel(dependency); - if (dependencyModelId.has_value()) - { - cache = context->documents->getPolygonCacheForModel(dependencyModelId.value()); - } - } - if (cache != nullptr) - { - const bool needInverting = glm::determinant(this->transformation) < 0; - const std::vector<gl::Polygon> modelPolygons = getCachedPolygons( - cache, - dependency, - context->documents); - polygons.reserve(polygons.size() + modelPolygons.size()); - for (gl::Polygon polygon : modelPolygons) - { - for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) - { - glm::vec4 vertex {polygon.vertices[i], 1}; - vertex = this->transformation * vertex; - polygon.vertices[i] = vertex; - } - if (needInverting != this->isInverted) - { - gl::invert(polygon); - } - if (polygon.color == ldraw::MAIN_COLOR) - { - polygon.color = this->colorIndex; - } - polygon.id = this->id; - polygons.push_back(polygon); - } - } -} - -Model* ldraw::SubfileReference::resolve(const ModelId callingModelId, DocumentManager* documents) const -{ - return documents->findDependencyByName(callingModelId, this->referenceName); -} - -ldraw::Object::Type ldraw::SubfileReference::typeIdentifier() const -{ - return Type::SubfileReference; -} - -QDataStream& ldraw::SubfileReference::serialize(QDataStream &stream) const -{ - return CompoundObject::serialize(stream) << this->referenceName; -} - -QDataStream& ldraw::SubfileReference::deserialize(QDataStream &stream) -{ - return CompoundObject::deserialize(stream) >> this->referenceName; -} - -QString ldraw::SubfileReference::toLDrawCode() const -{ - QString result; - if (this->isInverted) - { - result += "0 BFC INVERTNEXT\r\n"; - } - result += utility::format( - "1 %1 %2 %3", - this->colorIndex.index, - this->transformToBareString(), - this->referenceName); - return result; -} - -QString ldraw::SubfileReference::iconName() const -{ - return ":/icons/linetype-subfile.png"; -} - -QString ldraw::SubfileReference::typeName() const -{ - return QObject::tr("subfile reference"); -}
--- a/src/linetypes/subfilereference.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -#pragma once -#include "compoundobject.h" -#include "invert.h" - -class Model; - -namespace ldraw -{ - class SubfileReference; -} - -class ldraw::SubfileReference : public CompoundObject -{ -public: - SubfileReference() = default; - SubfileReference( - const glm::mat4& transformation, - const QString &referenceName, - const Color color = ldraw::MAIN_COLOR); - QVariant getProperty(Property property) const override; - QString textRepresentation() const override; - void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override; - Model* resolve(const ModelId callingModelId, DocumentManager* documents) const; - Type typeIdentifier() const override; - QDataStream& serialize(QDataStream& stream) const override; - QDataStream& deserialize(QDataStream& stream) override; - QString toLDrawCode() const override; - QString iconName() const override; - QString typeName() const override; - QString referenceName; -protected: - void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; -};
--- a/src/linetypes/triangle.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -#include "triangle.h" - -QString ldraw::Triangle::textRepresentation() const -{ - return utility::format("%1 %2 %3", - utility::vertexToStringParens(points[0]), - utility::vertexToStringParens(points[1]), - utility::vertexToStringParens(points[2])); -} - -void ldraw::Triangle::getPolygons( - std::vector<gl::Polygon>& polygons, - GetPolygonsContext* context) const -{ - Q_UNUSED(context) - polygons.push_back(gl::triangle( - this->points[0], - this->points[1], - this->points[2], - this->colorIndex, - this->id)); -} - -void ldraw::Triangle::invert(GetPolygonsContext *) -{ - // 0 1 2 - // -> 1 0 2 - std::swap(this->points[0], this->points[1]); -} - -ldraw::Object::Type ldraw::Triangle::typeIdentifier() const -{ - return Type::Triangle; -} - -QString ldraw::Triangle::toLDrawCode() const -{ - return utility::format( - "3 %1 %2 %3 %4", - this->colorIndex.index, - utility::vertexToString(this->points[0]), - utility::vertexToString(this->points[1]), - utility::vertexToString(this->points[2])); -} - -QString ldraw::Triangle::iconName() const -{ - return ":/icons/linetype-triangle.png"; -} - -QString ldraw::Triangle::typeName() const -{ - return QObject::tr("triangle"); -}
--- a/src/linetypes/triangle.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -#pragma once -#include "polygonobject.h" - -namespace ldraw -{ - class Triangle; -} - -class ldraw::Triangle : public PolygonObject<3> -{ -public: - using PolygonObject<3>::PolygonObject; - QString textRepresentation() const override; - void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override; - void invert(GetPolygonsContext*) override; - Type typeIdentifier() const override; - QString toLDrawCode() const override; - QString iconName() const override; - QString typeName() const override; -}; -
--- a/src/main.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/main.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -19,30 +19,21 @@ #include <QApplication> #include "mainwindow.h" #include "version.h" -#include <iostream> -#include <QMessageBox> -#include <QLabel> -#include <QImageReader> -#include <QPushButton> -int main(int argc, char *argv[]) +static void doQtRegistrations() { QCoreApplication::setApplicationName(::appName); QCoreApplication::setOrganizationName("hecknology.net"); QCoreApplication::setOrganizationDomain("hecknology.net"); - ::qRegisterMetaTypeStreamOperators<Library>("Library"); - ::qRegisterMetaTypeStreamOperators<Libraries>("Libraries"); + qRegisterMetaTypeStreamOperators<Library>("Library"); + qRegisterMetaTypeStreamOperators<Libraries>("Libraries"); +} + +int main(int argc, char *argv[]) +{ + doQtRegistrations(); QApplication app{argc, argv}; MainWindow mainwindow; mainwindow.show(); return app.exec(); } - -QDataStream& operator<<(QDataStream& stream, const glm::vec3& vec) -{ - return stream << vec.x << vec.y << vec.z; -} -QDataStream& operator>>(QDataStream& stream, glm::vec3& vec) -{ - return stream >> vec.x >> vec.y >> vec.z; -}
--- a/src/main.h Wed May 25 20:36:34 2022 +0300 +++ b/src/main.h Mon Jun 06 22:01:22 2022 +0300 @@ -34,78 +34,6 @@ constexpr char mainwindow[] = "mainwindow"; } -namespace ldraw -{ - class Object; - - // Uniquely identifies a model body object - template<typename T> - struct Id - { - std::int32_t value; - template<typename A, typename B> - static constexpr bool is_base_or_base_of = std::disjunction_v<std::is_base_of<A, B>, std::is_base_of<B, A>>; - template<typename R, typename = std::enable_if_t<is_base_or_base_of<T, R>>> - constexpr bool operator<(ldraw::Id<R> other) const - { - return this->value < other.value; - } - friend constexpr unsigned int qHash(ldraw::Id<T> id) - { - return qHash(id.value); - } - // Allow comparing ids as long as they are related - template<typename R, typename = std::enable_if_t<is_base_or_base_of<T, R>>> - friend bool operator==(ldraw::Id<T> one, ldraw::Id<R> other) - { - return one.value == other.value; - } - // Allow upcasting - template<typename R, typename = std::enable_if_t<std::is_base_of_v<R, T>>> - constexpr operator Id<R>() const - { - return Id<R>{this->value}; - } - }; - - using id_t = Id<Object>; - using triangleid_t = Id<class Triangle>; - using quadrilateralid_t = Id<class Quadrilateral>; - using edgeid_t = Id<class EdgeLine>; - using conditionaledgeid_t = Id<class ConditionalEdge>; - using subfileid_t = Id<class SubfileReference>; - using commentid_t = Id<class Comment>; - using metacommandid_t = Id<class MetaCommand>; - - constexpr struct NullId - { - template<typename T> - constexpr operator Id<T>() const - { - return Id<T>{0}; - } - static constexpr decltype(ldraw::id_t::value) value = 0; - } NULL_ID = {}; - - template<typename T> - inline bool operator==(Id<T> one, decltype(NULL_ID)) - { - return one.value == 0; - } - - template<typename T> - inline bool operator!=(Id<T> one, decltype(NULL_ID)) - { - return one.value != 0; - } - - template<typename T> - inline bool operator<(Id<T> one, decltype(NULL_ID)) - { - return one.value < 0; - } -} - constexpr std::size_t operator""_z(const unsigned long long int x) { return static_cast<std::size_t>(x); @@ -262,8 +190,6 @@ return qHash(value.value); } -using ModelId = TypeValue<int, struct TypeValueModelId>; - /** * Iterates a @c glm::mat */ @@ -279,8 +205,15 @@ } } -QDataStream& operator<<(QDataStream&, const glm::vec3&); -QDataStream& operator>>(QDataStream&, glm::vec3&); +inline QDataStream& operator<<(QDataStream& stream, const glm::vec3& vec) +{ + return stream << vec.x << vec.y << vec.z; +} + +inline QDataStream& operator>>(QDataStream& stream, glm::vec3& vec) +{ + return stream >> vec.x >> vec.y >> vec.z; +} template<int X, int Y, typename T, glm::qualifier Q> QDataStream& operator<<(QDataStream& stream, const glm::mat<X, Y, T, Q>& mat) @@ -312,3 +245,27 @@ } return result; } + +template<typename T> +std::optional<T> pointerToOptional(const T* p) +{ + std::optional<T> result; + if (p != nullptr) { + result = *p; + } + return result; +} + +template<typename T, typename R> +void removeFromMap(std::map<T, R>& map, T&& key) +{ + const auto it = map.find(key); + if (it != map.end()) { + map.erase(it); + } +} + +// some magic code from https://en.cppreference.com/w/cpp/utility/variant/visit +// for use with std::visit +template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; +template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
--- a/src/mainwindow.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/mainwindow.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -28,7 +28,6 @@ #include "document.h" #include "uiutilities.h" #include "widgets/colorselectdialog.h" -#include "modeleditor.h" template<typename BaseType, typename MemberType, typename DataType> struct MemberData @@ -193,9 +192,13 @@ void MainWindow::openModelForEditing(const ModelId modelId) { - Document* document = new Document{this->documents.getModelById(modelId), &this->documents, this->colorTable}; + EditorTabWidget* document = new EditorTabWidget{ + this->documents.getModelById(modelId), + &this->documents, + this->colorTable, + }; document->canvas->setRenderPreferences(this->renderPreferences); - connect(document, &Document::newStatusText, [&](const QString& newStatusText) + connect(document, &EditorTabWidget::newStatusText, [&](const QString& newStatusText) { this->statusBar()->showMessage(newStatusText); }); @@ -220,25 +223,25 @@ } } -Document* MainWindow::currentDocument() +EditorTabWidget* MainWindow::currentDocument() { - return qobject_cast<Document*>(this->ui->tabs->currentWidget()); + return qobject_cast<EditorTabWidget*>(this->ui->tabs->currentWidget()); } -const Document* MainWindow::currentDocument() const +const EditorTabWidget* MainWindow::currentDocument() const { - return qobject_cast<const Document*>(this->ui->tabs->currentWidget()); + return qobject_cast<const EditorTabWidget*>(this->ui->tabs->currentWidget()); } void MainWindow::handleDocumentSplitterChange() { - Document* currentDocument = this->currentDocument(); + EditorTabWidget* currentDocument = this->currentDocument(); if (currentDocument != nullptr) { this->documentSplitterState = currentDocument->saveSplitterState(); for (int i = 0; i < this->ui->tabs->count(); i += 1) { - Document* document = qobject_cast<Document*>(this->ui->tabs->widget(i)); + EditorTabWidget* document = qobject_cast<EditorTabWidget*>(this->ui->tabs->widget(i)); if (document != nullptr and document != currentDocument) { document->restoreSplitterState(this->documentSplitterState); @@ -363,7 +366,8 @@ */ void MainWindow::actionDelete() { - Document* document = this->currentDocument(); + /* + EditorTabWidget* document = this->currentDocument(); if (document != nullptr) { std::unique_ptr<ModelEditor> modelEditor = document->editModel(); @@ -377,6 +381,7 @@ } } } + */ } /** @@ -384,7 +389,8 @@ */ void MainWindow::actionInvert() { - Document* document = this->currentDocument(); + /* + EditorTabWidget* document = this->currentDocument(); if (document != nullptr) { // TODO: simplify @@ -405,6 +411,7 @@ } } } + */ } /** @@ -415,7 +422,7 @@ { if (tabIndex >= 0 and tabIndex < this->ui->tabs->count()) { - Document* document = qobject_cast<Document*>(this->ui->tabs->widget(tabIndex)); + EditorTabWidget* document = qobject_cast<EditorTabWidget*>(this->ui->tabs->widget(tabIndex)); if (document != nullptr) { this->closeDocument(document); @@ -427,9 +434,9 @@ * @brief Closes the specified document * @param document */ -void MainWindow::closeDocument(Document *document) +void MainWindow::closeDocument(EditorTabWidget *document) { - std::optional<ModelId> modelId = this->documents.findIdForModel(&document->getModel()); + std::optional<ModelId> modelId = this->documents.findIdForModel(document->model); if (modelId.has_value()) { this->documents.closeDocument(modelId.value()); @@ -439,10 +446,10 @@ std::optional<ModelId> MainWindow::findCurrentModelId() const { - const Document* document = this->currentDocument(); + const EditorTabWidget* document = this->currentDocument(); if (document != nullptr) { - return this->documents.findIdForModel(&document->getModel()); + return this->documents.findIdForModel(document->model); } else { @@ -492,7 +499,7 @@ { for (int i = 0; i < this->ui->tabs->count(); i += 1) { - Document* document = qobject_cast<Document*>(this->ui->tabs->widget(i)); + EditorTabWidget* document = qobject_cast<EditorTabWidget*>(this->ui->tabs->widget(i)); if (document != nullptr) { document->canvas->setRenderPreferences(this->renderPreferences);
--- a/src/mainwindow.h Wed May 25 20:36:34 2022 +0300 +++ b/src/mainwindow.h Mon Jun 06 22:01:22 2022 +0300 @@ -27,7 +27,7 @@ #include "uiutilities.h" #include "ui/canvas.h" -class Document; +class EditorTabWidget; class MainWindow : public QMainWindow { @@ -78,8 +78,8 @@ void openModelForEditing(const ModelId modelId); static QString pathToTranslation(const QString& localeCode); void loadColors(); - Document *currentDocument(); - const Document *currentDocument() const; - void closeDocument(Document* document); + EditorTabWidget *currentDocument(); + const EditorTabWidget *currentDocument() const; + void closeDocument(EditorTabWidget* document); std::optional<ModelId> findCurrentModelId() const; };
--- a/src/model.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/model.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -16,254 +16,169 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <QBrush> -#include <QFile> -#include <QFileInfo> -#include <QFont> -#include <QSaveFile> #include "model.h" -#include "modeleditor.h" -#include "documentmanager.h" -/** - * @brief Constructs a model - * @param parent QObject parent to pass forward - */ +QString modelElementToString(const ModelElement &element) +{ + return std::visit(overloaded{ + [](const Colored<SubfileReference>& ref) { + return QStringLiteral("1 %1 %2 %3") + .arg(ref.color.index) + .arg(transformToString(ref.transformation)) + .arg(ref.name); + }, + [](const Colored<LineSegment>& seg) { + return QStringLiteral("2 %1 %2 %3") + .arg(seg.color.index) + .arg(vertexToString(seg.p1)) + .arg(vertexToString(seg.p2)); + }, + [](const Colored<Triangle>& triangle) { + return QStringLiteral("3 %1 %2 %3 %4") + .arg(triangle.color.index) + .arg(vertexToString(triangle.p1)) + .arg(vertexToString(triangle.p2)) + .arg(vertexToString(triangle.p3)); + }, + [](const Colored<Quadrilateral>& quad) { + return QStringLiteral("4 %1 %2 %3 %4 %5") + .arg(quad.color.index) + .arg(vertexToString(quad.p1)) + .arg(vertexToString(quad.p2)) + .arg(vertexToString(quad.p3)) + .arg(vertexToString(quad.p4)); + }, + [](const Colored<ConditionalEdge>& cedge) { + return QStringLiteral("5 %1 %2 %3 %4 %5") + .arg(cedge.color.index) + .arg(vertexToString(cedge.p1)) + .arg(vertexToString(cedge.p2)) + .arg(vertexToString(cedge.c1)) + .arg(vertexToString(cedge.c2)); + }, + [](const Comment& comment) { + return "0 " + comment.text; + }, + [](const Empty&) { + return QStringLiteral(""); + }, + [](const ParseError& parseError) { + return parseError.code; + }, + }, element); +} + Model::Model(QObject *parent) : QAbstractListModel{parent} { } -/** - * @returns the amount of elements in the model - */ -int Model::size() const +Model::~Model() +{ +} + +ModelId Model::append(const ModelElement &value) { - return static_cast<int>(this->body.size()); + const int position = static_cast<int>(this->body.size()); + const ModelId id = this->runningId; + this->runningId.value += 1; + Q_EMIT this->beginInsertRows({}, position, position); + this->body.push_back({value, id}); + this->positions[id] = position; + Q_EMIT this->endInsertRows(); + return id; +} + +const ModelElement &Model::at(int position) const +{ + return this->body[position].data; } -/** - * @brief Looks up the object ID at the specified index. If out of bounds, returns NULL_ID. - * @param index Index of object to look up - * @return object ID - */ -ldraw::id_t Model::at(int index) const +ModelId Model::idAt(int position) const +{ + return this->body[position].id; +} + +void Model::assignAt(int position, const ModelElement &element) { - if (index >= 0 and index < this->size()) - { - return this->body[index]->id; - } - else - { - return ldraw::NULL_ID; + this->body[position].data = element; + const QModelIndex index = this->index(position); + Q_EMIT this->dataChanged(index, index); +} + +std::optional<int> Model::find(ModelId id) const +{ + return pointerToOptional(findInMap(this->positions, id)); +} + +void Model::remove(int index) +{ + if (index >= 0 and index < this->size()) { + Q_EMIT this->beginRemoveRows({}, index, index); + this->body.erase(this->body.begin() + index); + Q_EMIT this->endRemoveRows(); } } -/** - * @brief @overload QAbstractListModel::rowCount - * @return size - */ -int Model::rowCount(const QModelIndex&) const +int Model::rowCount(const QModelIndex &) const { return this->size(); } -/** - * @brief @overload QAbstractListModel::data - * @param index - * @param role - * @return QVariant - */ -QVariant Model::data(const QModelIndex& index, int role) const +QVariant Model::data(const QModelIndex &index, int role) const { - const ldraw::Object* object = (*this)[index.row()]; + const int i = index.row(); switch(role) { + /* case Qt::DecorationRole: return QPixmap{object->iconName()}.scaledToHeight(24); + */ case Qt::DisplayRole: - return object->textRepresentation(); + return modelElementToString(this->body[i].data); + /* case Qt::ForegroundRole: return object->textRepresentationForeground(); case Qt::BackgroundRole: return object->textRepresentationBackground(); case Qt::FontRole: return object->textRepresentationFont(); + */ default: return {}; } } -/** - * @brief Finds the position of the specified object in the model - * @param id Object id to look for - * @return model index - */ -QModelIndex Model::find(ldraw::id_t id) const +const ModelElement &Model::operator[](int index) const +{ + return this->body[index].data; +} + +int Model::size() const { - if (this->needObjectsByIdRebuild) - { - this->objectsById.clear(); - for (std::size_t i = 0; i < this->body.size(); ++i) - { - this->objectsById[this->body[i]->id] = i; - } - this->needObjectsByIdRebuild = false; - } - const auto it = this->objectsById.find(id); - if (it != this->objectsById.end()) - { - return this->index(it->second); - } - else - { - return {}; + return this->body.size(); +} + +void save(const Model &model, QIODevice *device) +{ + QTextStream out{device}; + for (int i = 0; i < model.size(); ++i) { + out << modelElementToString(model[i]) << "\r\n"; } } /** - * @brief Gets an object id by position in the model - * @param index Position of the object in the model - * @return id - */ -ldraw::id_t Model::idAt(const QModelIndex& index) const -{ - return (*this)[index.row()]->id; -} - -#if 0 -/** * @brief Sets the path to the model * @param path New path to use */ -void Model::setPath(const QString &path) +void updateHeaderNameField(Model& model, const QString &name) { - this->storedPath = path; - this->header.name = QFileInfo{path}.fileName(); // Update the "Name: 1234.dat" comment - if (this->body.size() >= 2) - { - const ldraw::id_t id = this->body[1]->id; - if (this->isA<ldraw::MetaCommand>(id)) - { - const QString& textBody = this->body[1]->getProperty<ldraw::Property::Text>(); - if (textBody.startsWith("Name: ")) - { - auto editor = this->edit(); - editor.setObjectProperty<ldraw::Property::Text>(id, "Name: " + this->header.name); + if (model.size() >= 2) { + if (const Comment* nameObject = std::get_if<Comment>(&model[1])) { + if (nameObject->text.startsWith("Name: ")) { + model[1] = Comment{"Name: " + name}; } } } } -#endif - -/** - * @brief Adds the given object into the model. - * @param object r-value reference to the object - */ -ldraw::id_t Model::append(ModelObjectPointer&& object) -{ - const int position = static_cast<int>(this->body.size()); - Q_EMIT this->beginInsertRows({}, position, position); - this->body.push_back(std::move(object)); - Q_EMIT this->endInsertRows(); - const ldraw::id_t id = this->body.back()->id; - this->objectsById[id] = this->body.size() - 1; - return id; -} - -/** - * @brief Removes the object at the specified position - * @param position - */ -void Model::remove(int position) -{ - if (position >= 0 and position < signed_cast(this->body.size())) - { - Q_EMIT this->beginRemoveRows({}, position, position); - this->body.erase(std::begin(this->body) + position); - this->needObjectsByIdRebuild = true; - Q_EMIT this->endRemoveRows(); - } -} - -void Model::emitDataChangedSignal(int position) -{ - Q_EMIT this->dataChanged(this->index(position), this->index(position)); -} - -/** - * @brief Gets the object pointer at the specified position - * @param index Position of the object - * @returns object pointer - */ -ldraw::Object* Model::operator[](int index) -{ - if (index >= 0 and index < this->size()) - { - return this->body[index].get(); - } - else - { - throw std::out_of_range{"index out of range"}; - } -} - -/** - * @brief Gets the object pointer at the specified position - * @param index Position of the object - * @returns object pointer - */ -const ldraw::Object* Model::operator[](int index) const -{ - if (index >= 0 and index < this->size()) - { - return this->body[index].get(); - } - else - { - throw std::out_of_range{"index out of range"}; - } -} - -/** - * @brief Gets an object pointer by id. Used by the editing context to actually modify objects. - * @param id - * @return object pointer - */ -ldraw::Object* Model::findObjectById(const ldraw::id_t id) -{ - const QModelIndex index = this->find(id); - if (index.isValid()) - { - return (*this)[index.row()]; - } - else - { - return nullptr; - } -} - -const ldraw::Object* Model::findObjectById(const ldraw::id_t id) const -{ - const QModelIndex index = this->find(id); - if (index.isValid()) - { - return (*this)[index.row()]; - } - else - { - return nullptr; - } -} - -/** - * @brief Attempts the save the model - */ -void save(const Model &model, QIODevice *device) -{ - QTextStream out{device}; - applyToModel<ldraw::Object>(model, [&](const ldraw::Object* object) { - out << object->toLDrawCode() << "\r\n"; - }); -}
--- a/src/model.h Wed May 25 20:36:34 2022 +0300 +++ b/src/model.h Mon Jun 06 22:01:22 2022 +0300 @@ -20,120 +20,96 @@ #include <QAbstractListModel> #include <memory> #include "main.h" -#include "header.h" -#include "linetypes/object.h" -#include "linetypes/metacommand.h" -#include "gl/common.h" +#include "colors.h" + +struct SubfileReference +{ + QString name; + glm::mat4 transformation; + bool inverted = false; +}; + +template<typename T> +struct Colored : T +{ + ldraw::Color color; +}; + +struct Comment +{ + QString text; +}; -enum class HeaderProperty +struct ParseError { - Name + QString code; }; +struct Empty {}; + +using ModelElement = std::variant< + Colored<SubfileReference>, + Colored<LineSegment>, + Colored<Triangle>, + Colored<Quadrilateral>, + Colored<ConditionalEdge>, + Comment, + Empty, + ParseError>; + +QString modelElementToString(const ModelElement& element); +struct ModelId +{ + std::int32_t value; + constexpr auto operator<=>(const ModelId& other) const = default; +}; + +constexpr int qHash(ModelId id) +{ + return qHash(id.value); +} + class Model : public QAbstractListModel { Q_OBJECT + struct Entry { + ModelElement data; + ModelId id; + }; + std::vector<Entry> body; + std::map<ModelId, int> positions; + ModelId runningId = {1}; public: - Model(QObject* parent = nullptr); - Model(const Model&) = delete; - - int size() const; - ldraw::id_t at(int index) const; + Model(QObject* parent); + virtual ~Model(); + ModelId append(const ModelElement& value); + const ModelElement& at(int position) const; + ModelId idAt(int position) const; + void assignAt(int position, const ModelElement& element); + std::optional<int> find(ModelId id) const; + void remove(int index); int rowCount(const QModelIndex&) const override; QVariant data(const QModelIndex& index, int role) const override; - ldraw::Object* findObjectById(const ldraw::id_t id); - const ldraw::Object* findObjectById(const ldraw::id_t id) const; - QModelIndex find(ldraw::id_t id) const; - ldraw::id_t idAt(const QModelIndex& index) const; - template<typename R> - const R* get(ldraw::Id<R> id) const; - template<typename R> - struct Get2Result - { - QModelIndex index; - const R* object; - }; - template<typename R> - Get2Result<R> get2(ldraw::Id<R> id) const; - ldraw::Object* operator[](int index); - const ldraw::Object* operator[](int index) const; - using ModelObjectPointer = std::unique_ptr<ldraw::Object>; - template<typename T, typename... Args> - ldraw::Id<T> append(Args&&... args); - ldraw::id_t append(ModelObjectPointer&& object); - template<typename T, typename... Args> - ldraw::Id<T> insert(std::size_t position, Args&&... args); - void remove(int position); - void emitDataChangedSignal(int position); -private: - bool modified = false; - std::vector<ModelObjectPointer> body; - mutable std::map<ldraw::id_t, std::size_t> objectsById; - mutable bool needObjectsByIdRebuild = false; + const ModelElement& operator[](int index) const; + int size() const; + auto operator[](int index) { + struct { + Model& model; + int index; + operator const ModelElement&() { + return model.at(index); + } + auto& operator=(const ModelElement& newData) { + model.assignAt(index, newData); + return *this; + } + const auto* operator&() { + return &(this->operator const ModelElement&()); + } + } result{*this, index}; + return result; + } }; void save(const Model& model, QIODevice *device); - -/** - * @brief Calls the specified function to all matching objects in the model - * @tparam R Type of LDraw line type object to filter by - * @param fn Function to call. - */ -template<typename R, typename Fn> -void applyToModel(const Model& model, Fn&& f) -{ - for (int i = 0; i < model.size(); i += 1) - { - const ldraw::Object* object = model[i]; - const R* subobject = dynamic_cast<const R*>(object); - if (subobject != nullptr) - { - f(subobject); - } - } -} - -template<typename T, typename... Args> -ldraw::Id<T> Model::append(Args&&... args) -{ - const int position = static_cast<int>(this->body.size()); - Q_EMIT beginInsertRows({}, position, position); - this->body.push_back(std::make_unique<T>(args...)); - ldraw::Object* pointer = this->body.back().get(); - this->objectsById[pointer->id] = this->body.size() - 1; - Q_EMIT endInsertRows(); - return ldraw::Id<T>{pointer->id.value}; -} - -template<typename T, typename... Args> -ldraw::Id<T> Model::insert(const std::size_t position, Args&&... args) -{ - Q_EMIT beginInsertRows({}, position, position); - this->body.insert(std::begin(this->body) + position, std::make_unique<T>(args...)); - ldraw::Object* pointer = this->body[position].get(); - this->objectsById[pointer->id] = position; - Q_EMIT endInsertRows(); - return ldraw::Id<T>{pointer->id.value}; -} - -template<typename R> -const R* Model::get(ldraw::Id<R> id) const -{ - return this->get2(id).object; -} - -template<typename R> -Model::Get2Result<R> Model::get2(const ldraw::Id<R> id) const -{ - Get2Result<R> result; - result.index = this->find(id); - if (result.index.isValid()) - { - result.object = static_cast<const R*>((*this)[result.index.row()]); - } - else - { - result.object = nullptr; - } - return result; -} +void updateHeaderNameField(Model& model, const QString &name);
--- a/src/modeleditor.cpp Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -#include "modeleditor.h" -#include "linetypes/triangle.h" -#include "linetypes/quadrilateral.h" - -ModelEditor::ModelEditor(Model& model) : - storedModel{model} -{ -} - -ModelEditor::~ModelEditor() -{ - for (ldraw::id_t id : this->modifiedObjects) - { - const QModelIndex index = this->model().find(id); - if (index.isValid()) - { - Q_EMIT this->objectModified(index.row()); - } - } -} - -ldraw::id_t ModelEditor::append(std::unique_ptr<ldraw::Object>&& object) -{ - this->storedModel.append(std::move(object)); - Q_EMIT this->objectAdded(this->model().size() - 1); - return object->id; -} - -void ModelEditor::remove(int position) -{ - this->storedModel.remove(position); -} - -auto ModelEditor::setObjectProperty( - const ldraw::id_t id, - const ldraw::Property property, - const QVariant& value) - -> ldraw::Object::SetPropertyResult -{ - ldraw::Object* const object = this->storedModel.findObjectById(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 ModelEditor::setObjectPoint(ldraw::id_t id, int pointId, const glm::vec3& value) -{ - ldraw::Object* object = this->storedModel.findObjectById(id); - if (object != nullptr) - { - object->setProperty(ldraw::PropertyKeyValue{ldraw::pointProperty(pointId), QVariant::fromValue(value)}); - modifiedObjects.insert(id); - } -} - -const Model &ModelEditor::model() -{ - return this->storedModel; -} -
--- a/src/modeleditor.h Wed May 25 20:36:34 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ -/* - * 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 <http://www.gnu.org/licenses/>. - */ - -#pragma once -#include "model.h" - -/** - * @brief Provides an interface for editing a model such that signals are emitted for each edit done. - * User edits to models should always be done through this class. - */ -class ModelEditor : public QObject -{ - Q_OBJECT -public: - ModelEditor(Model& model); - ~ModelEditor(); - template<typename T, typename... Args> - ldraw::Id<T> append(Args&&... args); - ldraw::id_t append(std::unique_ptr<ldraw::Object>&& object); - template<typename T, typename... Args> - ldraw::Id<T> insert(int position, Args&&... args); - void remove(int position); - template<ldraw::Property property> - void setObjectProperty(ldraw::id_t id, const ldraw::PropertyType<property>& value); - auto setObjectProperty(ldraw::id_t id, ldraw::Property property, const QVariant& value) - -> ldraw::Object::SetPropertyResult; - void setObjectPoint(ldraw::id_t id, int pointId, const glm::vec3& value); - template<typename T, typename Fn> - bool modifyObject(ldraw::Id<T> id, Fn&& function); - template<typename T, typename Fn> - bool modifyObjectAt(int position, Fn&& function); - const Model& model(); -Q_SIGNALS: - void objectAdded(int position); - void objectModified(int position); - void objectRemoved(ldraw::id_t id); -private: - QSet<ldraw::id_t> modifiedObjects; - Model& storedModel; -}; - -template<ldraw::Property Property> -void ModelEditor::setObjectProperty(const ldraw::id_t id, const ldraw::PropertyType<Property>& value) -{ - this->modifyObject(id, [&](ldraw::Object* object){ - object->setProperty<Property>(value); - }); -} - -template<typename T, typename... Args> -ldraw::Id<T> ModelEditor::append(Args&&... args) -{ - return this->storedModel.append<T>(args...); -} - -template<typename T, typename... Args> -ldraw::Id<T> ModelEditor::insert(int position, Args&&... args) -{ - return this->storedModel.insert<T>(position, args...); -} - -template<typename T, typename Fn> -bool ModelEditor::modifyObject(ldraw::Id<T> id, Fn&& function) -{ - QModelIndex index = this->model().find(id); - return this->modifyObjectAt<T>(index.row(), function); -} - -template<typename T, typename Fn> -bool ModelEditor::modifyObjectAt(int position, Fn&& function) -{ - if (position >= 0 and position < this->model().size()) - { - T* object = dynamic_cast<T*>(this->storedModel[position]); - if (object != nullptr) - { - Q_EMIT this->objectModified(position); - function(object); - return true; - } - } - return false; -} \ No newline at end of file
--- a/src/parser.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/parser.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -18,14 +18,7 @@ #include "model.h" #include "parser.h" -#include "linetypes/conditionaledge.h" -#include "linetypes/edge.h" -#include "linetypes/errorline.h" -#include "linetypes/metacommand.h" -#include "linetypes/object.h" -#include "linetypes/quadrilateral.h" -#include "linetypes/subfilereference.h" -#include "linetypes/triangle.h" +#include "ldrawalgorithm.h" struct BodyParseError { @@ -64,11 +57,12 @@ invertNext = true; continue; } - model.append(parseFromString(line)); + ModelElement element = parseLDrawLine(line); if (invertNext) { - model[model.size() - 1]->invert(nullptr); + element = inverted(element); } + model.append(element); invertNext = false; } } @@ -140,19 +134,13 @@ return result; } -static std::unique_ptr<ldraw::Object> parseType0Line( - const QString& line, - const QStringList& tokens) +static Comment parseType0Line(const QString& line) { - Q_UNUSED(tokens) - return std::make_unique<ldraw::MetaCommand>(line.mid(1).trimmed()); + return {line.mid(1).trimmed()}; } -static std::unique_ptr<ldraw::SubfileReference> parseType1Line( - const QString& line, - const QStringList& tokens) +static Colored<SubfileReference> parseType1Line(const QStringList& tokens) { - Q_UNUSED(line) constexpr int colorPosition = 1; constexpr int positionPosition = 2; // 2..4 constexpr int transformPosition = 5; // 5..13 @@ -164,15 +152,18 @@ const ldraw::Color color = colorFromString(tokens[colorPosition]); const glm::mat4 transform = matrixFromStrings(tokens, transformPosition, positionPosition); const QString& name = tokens[namePosition]; - return std::make_unique<ldraw::SubfileReference>(transform, name, color); + return Colored<SubfileReference>{ + { + .name = name, + .transformation = transform, + }, + color, + }; } -template<typename T, int NumVertices> -static std::unique_ptr<T> parsePolygon( - const QString& line, - const QStringList& tokens) +template<int NumVertices> +static auto parsePolygon(const QStringList& tokens) { - Q_UNUSED(line) constexpr int colorPosition = 1; auto vertexPosition = [](int n) { return 2 + 3*n; }; if (tokens.size() != 2 + 3 * NumVertices) @@ -185,10 +176,10 @@ { vertices[unsigned_cast(i)] = vertexFromStrings(tokens, vertexPosition(i)); } - return std::make_unique<T>(vertices, color); + return std::make_pair(vertices, color); } -std::unique_ptr<ldraw::Object> Parser::parseFromString(QString line) +ModelElement parseLDrawLine(QString line) { line = line.trimmed(); try @@ -196,7 +187,7 @@ const QStringList tokens = line.split(QRegExp{R"(\s+)"}); if (tokens.empty() or tokens == QStringList{{""}}) { - return std::make_unique<ldraw::Empty>(); + return Empty{}; } bool ok_code; const int code = tokens[0].toInt(&ok_code); @@ -207,23 +198,38 @@ switch (code) { case 0: - return parseType0Line(line, tokens); + return parseType0Line(line); case 1: - return parseType1Line(line, tokens); + return parseType1Line(tokens); case 2: - return parsePolygon<ldraw::Edge, 2>(line, tokens); + { + const auto pair = parsePolygon<2>(tokens); + return Colored<LineSegment>{{pair.first[0], pair.first[1]}, pair.second}; + } case 3: - return parsePolygon<ldraw::Triangle, 3>(line, tokens); + { + const auto pair = parsePolygon<3>(tokens); + return Colored<Triangle>{{pair.first[0], pair.first[1], pair.first[2]}, pair.second + }; + } case 4: - return parsePolygon<ldraw::Quadrilateral, 4>(line, tokens); + { + const auto pair = parsePolygon<4>(tokens); + const Quadrilateral quad{pair.first[0], pair.first[1], pair.first[2], pair.first[3]}; + return Colored<Quadrilateral>{quad, pair.second}; + } case 5: - return parsePolygon<ldraw::ConditionalEdge, 4>(line, tokens); + { + const auto pair = parsePolygon<4>(tokens); + const ConditionalEdge cedge{pair.first[0], pair.first[1], pair.first[2], pair.first[3]}; + return Colored<ConditionalEdge>{cedge, pair.second}; + } default: throw BodyParseError{utility::format("bad line type '%1'", code)}; } } catch(const BodyParseError& error) { - return std::make_unique<ldraw::ErrorLine>(line, error.message); + return ParseError{line}; } }
--- a/src/parser.h Wed May 25 20:36:34 2022 +0300 +++ b/src/parser.h Mon Jun 06 22:01:22 2022 +0300 @@ -18,20 +18,17 @@ #pragma once #include "main.h" -#include "linetypes/object.h" #include "model.h" -#include "modeleditor.h" -#include "header.h" class Parser : public QObject { Q_OBJECT public: - enum { EndOfModel = -1 }; Parser(QIODevice& device, QObject* parent = nullptr); void parseBody(Model &model); - static std::unique_ptr<ldraw::Object> parseFromString(QString line); private: QString readLine(); QIODevice& device; }; + +ModelElement parseLDrawLine(QString line);
--- a/src/polygoncache.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/polygoncache.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -1,11 +1,80 @@ #include "polygoncache.h" #include "documentmanager.h" +#include "invert.h" -static void getObjectPolygons( - Model* model, - const int index, - std::vector<gl::Polygon>& polygons_out, - ldraw::GetPolygonsContext* context); +Model* resolve(const QString& name, const ModelId callingModelId, DocumentManager* documents) +{ + return documents->findDependencyByName(callingModelId, name); +} + +/** + * @brief Gets the GL polygons of the object at the specified position in the model + * @param index Index of object in the model + * @param polygons_out Vector to add polygons into + * @param context Context to use to resolve subfile references + */ +static std::vector<gl::Polygon> getPolygonsAt(const Model* model, GetPolygonsContext* context) +{ + std::vector<gl::Polygon> result; + for (int i = 0; i < model->size(); i += 1) + { + const ModelElement& element = (*model)[i]; + const ModelId id = model->idAt(i); + std::visit<void>(overloaded{ + [&](const Colored<LineSegment>& edge) { + result.push_back(gl::edgeLine(edge, id)); + }, + [&](const Colored<Triangle>& triangle) { + result.push_back(gl::triangle(triangle, id)); + }, + [&](const Colored<Quadrilateral>& quad) { + result.push_back(gl::quadrilateral(quad, id)); + }, + [&](const Colored<ConditionalEdge>& cedge) { + result.push_back(gl::conditionalEdge(cedge, id)); + }, + [&](const Colored<SubfileReference>& ref) { + Model* dependency = context->documents->findDependencyByName(context->modelId, ref.name); + PolygonCache* cache = nullptr; + if (dependency != nullptr) + { + const auto dependencyModelId = context->documents->findIdForModel(dependency); + if (dependencyModelId.has_value()) { + cache = context->documents->getPolygonCacheForModel(dependencyModelId.value()); + } + } + if (cache != nullptr) { + const bool needInverting = glm::determinant(ref.transformation) < 0; + const std::vector<gl::Polygon>& modelPolygons = getCachedPolygons( + cache, + dependency, + context->documents); + result.reserve(result.size() + modelPolygons.size()); + for (gl::Polygon polygon : modelPolygons) + { + for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) + { + const glm::vec4 homogenousVertex {polygon.vertices[i], 1}; + polygon.vertices[i] = ref.transformation * homogenousVertex; + } + if (needInverting != ref.inverted) + { + gl::invert(polygon); + } + if (polygon.color == ldraw::MAIN_COLOR) + { + polygon.color = ref.color; + } + polygon.id = id; + result.push_back(polygon); + } + } + }, + [](const ModelElement&) {} + }, element); + } + return result; +} /** * @brief Gets a list of GL polygons that are used to represent this model. @@ -24,29 +93,10 @@ const std::optional<ModelId> modelId = documents->findIdForModel(model); if (modelId.has_value()) { - ldraw::GetPolygonsContext context{modelId.value(), documents}; - for (int i = 0; i < model->size(); i += 1) - { - getObjectPolygons(model, i, cache->cachedPolygons, &context); - } + GetPolygonsContext context{modelId.value(), documents}; + cache->cachedPolygons = getPolygonsAt(model, &context); } cache->needRecache = false; } return cache->cachedPolygons; } - -/** - * @brief Gets the GL polygons of the object at the specified position in the model - * @param index Index of object in the model - * @param polygons_out Vector to add polygons into - * @param context Context to use to resolve subfile references - */ -static void getObjectPolygons( - Model* model, - const int index, - std::vector<gl::Polygon>& polygons_out, - ldraw::GetPolygonsContext* context) -{ - const ldraw::Object* object = (*model)[unsigned_cast(index)]; - object->getPolygons(polygons_out, context); -}
--- a/src/polygoncache.h Wed May 25 20:36:34 2022 +0300 +++ b/src/polygoncache.h Mon Jun 06 22:01:22 2022 +0300 @@ -1,6 +1,13 @@ #pragma once #include "main.h" #include "model.h" +#include "gl/common.h" + +struct GetPolygonsContext +{ + ModelId modelId; + class DocumentManager* documents; +}; struct PolygonCache {
--- a/src/ui/canvas.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/ui/canvas.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -1,12 +1,11 @@ #include <QMouseEvent> #include <QPainter> -#include "modeleditor.h" #include "document.h" #include "canvas.h" Canvas::Canvas( Model* model, - Document *document, + EditorTabWidget *document, DocumentManager* documents, const ldraw::ColorTable& colorTable, QWidget* parent) : @@ -21,9 +20,9 @@ * @param selectedIds IDs of objects to select * @param deselectedIds IDs of objects to deselect. */ -void Canvas::handleSelectionChange(const QSet<ldraw::id_t>& selectedIds, const QSet<ldraw::id_t>& deselectedIds) +void Canvas::handleSelectionChange(const QSet<ModelId> &selectedIds, const QSet<ModelId> &deselectedIds) { - Q_ASSERT(not selectedIds.contains(ldraw::NULL_ID)); + Q_ASSERT(not selectedIds.contains({0})); this->selection.subtract(deselectedIds); this->selection.unite(selectedIds); gl::setModelShaderSelectedObjects(&this->shaders, this->selection); @@ -34,7 +33,7 @@ * @brief Updates vertex rendering * @param document Document to get vertices from */ -void Canvas::rebuildVertices(Document* document) +void Canvas::rebuildVertices(EditorTabWidget* document) { if (this->vertexProgram.has_value()) { @@ -45,7 +44,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) { - const ldraw::id_t id = this->pick(event->pos()); + const ModelId id = this->pick(event->pos()); this->highlighted = id; this->totalMouseMove += (event->pos() - this->lastMousePosition).manhattanLength(); this->worldPosition = this->screenToModelCoordinates(event->pos(), this->gridPlane); @@ -339,7 +338,7 @@ /** * @returns the ids of the currently selected objects */ -const QSet<ldraw::id_t> Canvas::selectedObjects() const +const QSet<ModelId> Canvas::selectedObjects() const { return this->selection; }
--- a/src/ui/canvas.h Wed May 25 20:36:34 2022 +0300 +++ b/src/ui/canvas.h Mon Jun 06 22:01:22 2022 +0300 @@ -28,7 +28,7 @@ using OverpaintCallback = std::function<void(Canvas*, QPainter*)>; Canvas( Model* model, - Document* document, + EditorTabWidget* document, DocumentManager* documents, const ldraw::ColorTable& colorTable, QWidget* parent = nullptr); @@ -38,15 +38,15 @@ void drawWorldPolygon(QPainter* painter, const std::vector<glm::vec3>& points); Winding worldPolygonWinding(const std::vector<glm::vec3>& points) const; const std::optional<glm::vec3>& getWorldPosition() const; - const QSet<ldraw::id_t> selectedObjects() const; + const QSet<ModelId> selectedObjects() const; const glm::mat4& getGridMatrix() const; void closeShape(); glm::vec3 cameraVector() const; std::optional<glm::vec3> worldPosition; DrawState* drawState = nullptr; public Q_SLOTS: - void handleSelectionChange(const QSet<ldraw::id_t>& selectedIds, const QSet<ldraw::id_t>& deselectedIds); - void rebuildVertices(Document *document); + void handleSelectionChange(const QSet<ModelId>& selectedIds, const QSet<ModelId>& deselectedIds); + void rebuildVertices(EditorTabWidget *document); void setGridMatrix(const glm::mat4 &newMatrix); protected: void mouseMoveEvent(QMouseEvent* event) override; @@ -70,9 +70,9 @@ geom::Plane gridPlane; int totalMouseMove = 0; bool isDark = true; - QSet<ldraw::id_t> selection; + QSet<ModelId> selection; OverpaintCallback overpaintCallback = nullptr; - Document* document; + EditorTabWidget* document; }; void adjustGridToView(Canvas* canvas);
--- a/src/ui/objecteditor.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/ui/objecteditor.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -3,168 +3,73 @@ #include <QFormLayout> #include "objecteditor.h" #include "document.h" -#include "modeleditor.h" #include "widgets/colorbutton.h" #include "widgets/colorindexinput.h" #include "widgets/vec3editor.h" #include "ui_objecteditor.h" -ObjectEditor::ObjectEditor(Document* document, const ldraw::id_t id) : - QWidget{document}, - ui{*new Ui_ObjectEditor}, - document{document} -{ - this->ui.setupUi(this); - this->setObjectId(id); - QFormLayout* formLayout = new QFormLayout{ui.properties}; - this->ui.properties->setLayout(formLayout); - for (const ldraw::Property property : ldraw::ALL_PROPERTIES) - { - QWidget* editorWidget = this->makeEditorWidgetForProperty(property); - if (editorWidget != nullptr) - { - editorWidget->setProperty("_property_id", static_cast<int>(property)); - QLabel* propertyLabel = new QLabel{ldraw::traits(property).name.data()}; - formLayout->addRow(propertyLabel, editorWidget); - this->propertyWidgets[property] = {propertyLabel, editorWidget}; - } - } - this->setObjectId(ldraw::NULL_ID); -} - -ObjectEditor::~ObjectEditor() -{ - delete &this->ui; -} - -QString titleCase(const QString& string) -{ - return string.left(1).toUpper() + string.mid(1); -} +using PropertyValue = std::variant< + const glm::vec3*, + const glm::mat4*, + const QString*, + ldraw::Color>; -void setValueToWidget(QWidget* widget, const QVariant& value) +enum PropertyKey { - ColorIndexInput* colorIndexInput = qobject_cast<ColorIndexInput*>(widget); - if (colorIndexInput != nullptr) - { - colorIndexInput->setSelectedColor(value.value<ldraw::Color>()); - } - else - { - Vec3Editor* vec3Editor = qobject_cast<Vec3Editor*>(widget); - if (vec3Editor != nullptr) - { - vec3Editor->setValue(value.value<glm::vec3>()); - } - else - { - QCheckBox* checkBox = qobject_cast<QCheckBox*>(widget); - if (checkBox != nullptr) - { - checkBox->setChecked(value.toBool()); - } - else - { - QLineEdit* lineEdit = qobject_cast<QLineEdit*>(widget); - if (lineEdit != nullptr) - { - lineEdit->setText(value.toString()); - } - } - } - } -} + Point1, + Point2, + Point3, + Point4, + Control1, + Control2, + Color, + Transformation, + Name, + Text, + Code, +}; -void ObjectEditor::setObjectId(const ldraw::id_t id) +std::map<PropertyKey, PropertyValue> getProperties(const ModelElement& element) { - this->objectId = id; - const ldraw::Object* object = this->document->getModel().get(id); - this->ui.properties->setEnabled(object != nullptr); - if (object != nullptr) - { - this->ui.typeNameLabel->setText("<b>" + titleCase(object->typeName()) + "</b>"); - this->ui.typeIconLabel->setPixmap(QPixmap{object->iconName()}.scaledToWidth(24)); - for (const ldraw::Property property : ldraw::ALL_PROPERTIES) - { - const QVariant value = object->getProperty(property); - const auto it = this->propertyWidgets.find(property); - if (it != this->propertyWidgets.end()) - { - it->first->setEnabled(not value.isNull()); - it->second->setEnabled(not value.isNull()); - if (not value.isNull()) - { - setValueToWidget(it->second, value); - } - } - } - } - else - { - this->ui.typeNameLabel->setText(tr("No object selected")); - this->ui.typeIconLabel->clear(); - } -} - -void ObjectEditor::handleColorChange(ldraw::Color value) -{ - this->handlePropertyChange(this->sender(), QVariant::fromValue(value)); -} - -void ObjectEditor::handleVec3Change(const glm::vec3 &value) -{ - this->handlePropertyChange(this->sender(), QVariant::fromValue(value)); -} - -void ObjectEditor::handleBoolChange(bool value) -{ - this->handlePropertyChange(this->sender(), QVariant::fromValue(value)); + std::map<PropertyKey, PropertyValue> result; + std::visit<void>(overloaded{ + [&](const Colored<LineSegment>& edge) { + result[Point1] = &edge.p1; + result[Point2] = &edge.p2; + result[Color] = edge.color; + }, + [&](const Colored<Triangle>& tri) { + result[Point1] = &tri.p1; + result[Point2] = &tri.p2; + result[Point3] = &tri.p3; + result[Color] = tri.color; + }, + [&](const Colored<Quadrilateral>& quad) { + result[Point1] = &quad.p1; + result[Point2] = &quad.p2; + result[Point3] = &quad.p3; + result[Point4] = &quad.p4; + result[Color] = quad.color; + }, + [&](const Colored<ConditionalEdge>& cedge) { + result[Point1] = &cedge.p1; + result[Point2] = &cedge.p2; + result[Control1] = &cedge.c1; + result[Control2] = &cedge.c2; + result[Color] = cedge.color; + }, + [&](const Colored<SubfileReference>& ref) { + result[Transformation] = &ref.transformation; + result[Name] = &ref.name; + result[Color] = ref.color; + }, + [&](Empty) {}, + [&](const Comment& comment) { + result[Text] = &comment.text; + }, + [&](const ParseError& parseError) { + result[Code] = &parseError.code; + }, + }, element); + return result; } - -void ObjectEditor::handleStringChange(const QString &value) -{ - this->handlePropertyChange(this->sender(), QVariant::fromValue(value)); -} - -QWidget* ObjectEditor::makeEditorWidgetForProperty(ldraw::Property property) -{ - QWidget* const parent = qobject_cast<QWidget*>(this->parent()); - if (ldraw::traits(property).type == qMetaTypeId<ldraw::Color>()) - { - ColorIndexInput* colorWidget = new ColorIndexInput{this->document, {0}, parent}; - connect(colorWidget, &ColorIndexInput::colorChanged, this, &ObjectEditor::handleColorChange); - return colorWidget; - } - else if (ldraw::traits(property).type == qMetaTypeId<glm::vec3>()) - { - Vec3Editor* editor = new Vec3Editor{{}, parent}; - connect(editor, &Vec3Editor::valueChanged, this, &ObjectEditor::handleVec3Change); - return editor; - } - else if (ldraw::traits(property).type == qMetaTypeId<bool>()) - { - QCheckBox* editor = new QCheckBox{{}, parent}; - connect(editor, &QCheckBox::clicked, this, &ObjectEditor::handleBoolChange); - return editor; - } - else if (ldraw::traits(property).type == qMetaTypeId<QString>()) - { - QLineEdit* editor = new QLineEdit{{}, parent}; - connect(editor, &QLineEdit::textChanged, this, &ObjectEditor::handleStringChange); - return editor; - } - else - { - return nullptr; - } -} - -void ObjectEditor::handlePropertyChange(QObject *caller, const QVariant &value) -{ - QVariant propertyVariant = caller->property("_property_id"); - if (not propertyVariant.isNull()) - { - ldraw::Property const property = static_cast<ldraw::Property>(propertyVariant.toInt()); - this->document->editModel()->setObjectProperty(this->objectId, property, value); - } -} \ No newline at end of file
--- a/src/ui/objecteditor.h Wed May 25 20:36:34 2022 +0300 +++ b/src/ui/objecteditor.h Mon Jun 06 22:01:22 2022 +0300 @@ -2,25 +2,3 @@ #include <QWidget> #include "../main.h" #include "../model.h" - -class Document; - -class ObjectEditor : public QWidget -{ - Q_OBJECT -public: - explicit ObjectEditor(Document* document, ldraw::id_t id = ldraw::NULL_ID); - ~ObjectEditor(); - void setObjectId(ldraw::id_t id); -private: - Q_SLOT void handleColorChange(ldraw::Color value); - Q_SLOT void handleVec3Change(const glm::vec3& value); - Q_SLOT void handleBoolChange(bool value); - Q_SLOT void handleStringChange(const QString& value); - class Ui_ObjectEditor& ui; - Document* const document; - ldraw::id_t objectId = ldraw::NULL_ID; - QMap<ldraw::Property, QPair<QWidget*, QWidget*>> propertyWidgets; - QWidget* makeEditorWidgetForProperty(ldraw::Property property); - void handlePropertyChange(QObject* caller, const QVariant& value); -};
--- a/src/utility.h Wed May 25 20:36:34 2022 +0300 +++ b/src/utility.h Mon Jun 06 22:01:22 2022 +0300 @@ -73,8 +73,28 @@ { return utility::format("(%1, %2, %3)", vertex.x, vertex.y, vertex.z); } + + inline QString transformToString(const glm::mat4& matrix) + { + return utility::format( + "%1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12", + matrix[3][0], + matrix[3][1], + matrix[3][2], + matrix[0][0], + matrix[1][0], + matrix[2][0], + matrix[0][1], + matrix[1][1], + matrix[2][1], + matrix[0][2], + matrix[1][2], + matrix[2][2]); + } } +using namespace utility; + template<typename T, glm::qualifier Q> inline unsigned int qHash(const glm::vec<3, T, Q>& key) {
--- a/src/vertexmap.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/vertexmap.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -1,5 +1,5 @@ #include "vertexmap.h" -#include "linetypes/polygonobject.h" +#include "gl/common.h" unsigned int hashVertex(const glm::vec3& vec) { @@ -40,14 +40,75 @@ const glm::vec3& b; }; -inline void edges(const ldraw::Object* object, std::function<void(Edge&&)> fn) +inline void edges(const ModelElement& element, std::function<void(Edge&&)> fn) +{ + std::visit<void>(overloaded{ + [fn](const Colored<LineSegment>& edge) { + fn(Edge{edge.p1, edge.p2}); + }, + [fn](const Colored<Triangle>& triangle) { + fn(Edge{triangle.p1, triangle.p2}); + fn(Edge{triangle.p2, triangle.p3}); + fn(Edge{triangle.p3, triangle.p1}); + }, + [fn](const Colored<Quadrilateral>& quad) { + fn(Edge{quad.p1, quad.p2}); + fn(Edge{quad.p2, quad.p3}); + fn(Edge{quad.p3, quad.p4}); + fn(Edge{quad.p4, quad.p1}); + }, + [fn](const Colored<ConditionalEdge>& cedge) { + fn(Edge{cedge.p1, cedge.p2}); + }, + [](const ModelElement&&){} + }, element); +} + +inline void points( + const ModelElement& element, + std::function<void(const glm::vec3&)> fn) { - for (int i = 0; i < object->numPoints() - 1; i += 1) - { - const glm::vec3 p1 = object->getPoint(i); - const glm::vec3 p2 = object->getPoint((i + 1) % object->numPoints()); - fn(Edge{p1, p2}); - } + std::visit<void>(overloaded{ + [fn](const Colored<LineSegment>& edge) { + fn(edge.p1); + fn(edge.p2); + }, + [fn](const Colored<Triangle>& triangle) { + fn(triangle.p1); + fn(triangle.p2); + fn(triangle.p3); + }, + [fn](const Colored<Quadrilateral>& quad) { + fn(quad.p1); + fn(quad.p2); + fn(quad.p3); + fn(quad.p4); + }, + [fn](const Colored<ConditionalEdge>& cedge) { + fn(cedge.p1); + fn(cedge.p2); + fn(cedge.c1); + fn(cedge.c2); + }, + [](const ModelElement&&){} + }, element); +} + +template<typename R> +auto ifplanar( + const ModelElement& element, + std::function<R(const glm::vec3&, const glm::vec3&, const glm::vec3&)> fn +) +{ + return std::visit(overloaded{ + [fn](const Triangle& triangle) -> std::optional<R> { + return fn(triangle.p1, triangle.p2, triangle.p3); + }, + [fn](const Quadrilateral quad) -> std::optional<R> { + return fn(quad.p1, quad.p2, quad.p3); + }, + [](const ModelElement&&) -> std::optional<R> {return {};} + }, element); } void VertexMap::build() @@ -55,30 +116,27 @@ this->map.clear(); this->vertices.clear(); this->vertexHashes.clear(); - applyToModel<ldraw::Object>(*this->model, [&](const ldraw::Object* object) + for (int i = 0; i < this->model->size(); ++i) { - glm::mat4 matrix; - if (object->numPoints() > 2) - { - const auto& p0 = object->getPoint(0); - const auto& p1 = object->getPoint(1); - const auto& p2 = object->getPoint(2); - const glm::vec3 a = glm::normalize(p1 - p0); - const glm::vec3 b = glm::normalize(p2 - p0); - const glm::vec3 c = glm::normalize(glm::cross(a, b)); - const glm::vec3 d = glm::normalize(glm::cross(a, c)); - matrix = glm::mat4{{a, 0}, {-c, 0}, {d, 0}, {}}; - } - for (int i = 0; i < object->numPoints(); i += 1) - { - const glm::vec3& point = object->getPoint(i); + const ModelElement& element = this->model->at(i); + std::optional<glm::mat4> matrix = ifplanar<glm::mat4>( + element, + [](const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3) + { + const glm::vec3 a = glm::normalize(p2 - p1); + const glm::vec3 b = glm::normalize(p3 - p1); + const glm::vec3 c = glm::normalize(glm::cross(a, b)); + const glm::vec3 d = glm::normalize(glm::cross(a, c)); + return glm::mat4{{a, 0}, {-c, 0}, {d, 0}, {}}; + }); + points(element, [&](const glm::vec3 point) { const unsigned int hash = hashVertex(point); VertexInfo& info = this->map[hash]; info.point = point; - info.objects.insert(object->id); - if (object->numPoints() > 2 and not info.transformSet) + info.objects.insert(this->model->idAt(i)); + if (matrix.has_value() and not info.transformSet) { - info.transform = matrix; + info.transform = matrix.value(); info.transform[3] = {point, 1}; info.transformSet = true; } @@ -87,8 +145,8 @@ this->vertexHashes.insert(hash); this->vertices.push_back(point); } - } - }); + }); + } for (auto& pair : this->map) { auto& info = pair.second; @@ -100,15 +158,18 @@ info.transformSet = true; } float scale = 1.0f; - for (const ldraw::id_t objectId : info.objects) + for (const ModelId objectId : info.objects) { - edges(model->get(objectId), [&](Edge&& edge) - { - if (hashVertex(edge.a) == pair.first or hashVertex(edge.b) == pair.first) + const std::optional<int> index = model->find(objectId); + if (index.has_value()) { + edges(this->model->at(index.value()), [&](Edge&& edge) { - scale = std::min(scale, glm::length(edge.b - edge.a) / 3); - } - }); + if (hashVertex(edge.a) == pair.first or hashVertex(edge.b) == pair.first) + { + scale = std::min(scale, glm::length(edge.b - edge.a) / 3); + } + }); + } } info.transform = glm::scale(info.transform, glm::vec3{scale, scale, scale}); }
--- a/src/vertexmap.h Wed May 25 20:36:34 2022 +0300 +++ b/src/vertexmap.h Mon Jun 06 22:01:22 2022 +0300 @@ -14,7 +14,7 @@ struct VertexInfo { glm::vec3 point; - std::set<ldraw::id_t> objects; + std::set<ModelId> objects; bool transformSet = false; glm::mat4 transform; };
--- a/src/widgets/colorindexinput.cpp Wed May 25 20:36:34 2022 +0300 +++ b/src/widgets/colorindexinput.cpp Mon Jun 06 22:01:22 2022 +0300 @@ -3,7 +3,7 @@ #include "colorselectdialog.h" #include "uiutilities.h" -ColorIndexInput::ColorIndexInput(Document *document, ldraw::Color color, QWidget *parent) : +ColorIndexInput::ColorIndexInput(EditorTabWidget *document, ldraw::Color color, QWidget *parent) : QWidget{parent}, document{document}, ui{*new Ui_ColorIndexInput}
--- a/src/widgets/colorindexinput.h Wed May 25 20:36:34 2022 +0300 +++ b/src/widgets/colorindexinput.h Mon Jun 06 22:01:22 2022 +0300 @@ -5,13 +5,13 @@ { Q_OBJECT public: - ColorIndexInput(Document *document, ldraw::Color color = ldraw::MAIN_COLOR, QWidget *parent = nullptr); + ColorIndexInput(EditorTabWidget *document, ldraw::Color color = ldraw::MAIN_COLOR, QWidget *parent = nullptr); ~ColorIndexInput(); ldraw::Color selectedColor() const; void setSelectedColor(ldraw::Color color); Q_SIGNALS: void colorChanged(ldraw::Color color); private: - Document* const document; + EditorTabWidget* const document; class Ui_ColorIndexInput& ui; };