Tue, 15 Mar 2022 18:52:48 +0200
- Add editors for string and bool properties
- Add invert action
--- a/CMakeLists.txt Sun Mar 13 20:26:28 2022 +0200 +++ b/CMakeLists.txt Tue Mar 15 18:52:48 2022 +0200 @@ -54,6 +54,7 @@ src/gl/gridprogram.cpp src/gl/partrenderer.cpp src/gl/vertexprogram.cpp + src/linetypes/compoundobject.cpp src/linetypes/conditionaledge.cpp src/linetypes/edge.cpp src/linetypes/errorline.cpp @@ -111,6 +112,7 @@ src/gl/gridprogram.h src/gl/partrenderer.h src/gl/vertexprogram.h + src/linetypes/compoundobject.h src/linetypes/conditionaledge.h src/linetypes/edge.h src/linetypes/errorline.h
--- a/ldforge.qrc Sun Mar 13 20:26:28 2022 +0200 +++ b/ldforge.qrc Tue Mar 15 18:52:48 2022 +0200 @@ -29,5 +29,6 @@ <file>icons/navigate-outline.png</file> <file>icons/trash-bin-outline.png</file> <file>icons/axes.png</file> + <file>icons/invert.png</file> </qresource> </RCC>
--- a/src/invert.cpp Sun Mar 13 20:26:28 2022 +0200 +++ b/src/invert.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -20,54 +20,8 @@ #include "model.h" #include "gl/common.h" #include "invert.h" - -#if 0 -/* - * Returns whether or not the document is flat. - * If it is flat, the result is stored in *axis. - */ -bool isflat(Model* model, Axis* flatDimension) -{ - // The dimensions that this model is potentially flat in. - QVector<Axis> dimensions = {X, Y, Z}; - - // Iterate through everything in the subfile. If there is any vertex with a coordinate not at - // zero, the subfile is not flat in that dimension. - for (LDObject* subfileObject : model->objects()) - { - for (int i = 0; i < subfileObject->numVertices(); ++i) - { - Vertex const& v_i = subfileObject->vertex(i); - - if (not qFuzzyCompare(v_i.x, 0.0)) - dimensions.removeOne(X); - - if (not qFuzzyCompare(v_i.y, 0.0)) - dimensions.removeOne(Y); - - if (not qFuzzyCompare(v_i.z, 0.0)) - 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. - *flatDimension = dimensions[0]; - return true; - } - else - { - // The model is not flat. - return false; - } -} -#endif +#include "documentmanager.h" +#include "modeleditor.h" /* * Returns a matrix that causes a flip on the given dimension. @@ -80,56 +34,6 @@ return result; } -#if 0 -/* - * Inverts an LDObject so that its winding is changed. - */ -void invert(LDObject* obj, DocumentManager* context) -{ - if (obj->numPolygonVertices() > 0) - { - // Object is vertex based, so change the order of the vertices. - QVector<Vertex> vertices; - vertices.resize(obj->numPolygonVertices()); - - for (int i = 0; i < vertices.size(); i += 1) - vertices[vertices.size() - 1 - i] = obj->vertex(i); - - for (int i = 0; i < vertices.size(); i += 1) - obj->setVertex(i, vertices[i]); - } - else if (obj->type() == LDObjectType::SubfileReference) - { - // Check whether subfile is flat. If it is, flip it on the axis on which it is flat. - Model model {context}; - LDSubfileReference* reference = static_cast<LDSubfileReference*>(obj); - reference->fileInfo(context)->inlineContents(model, true, false); - Axis flatDimension; - - if (::isflat(&model, &flatDimension)) - { - reference->setTransformationMatrix( - reference->transformationMatrix() * ::flipmatrix(flatDimension) - ); - } - else - { - // Subfile is not flat. Resort to invertnext. - reference->setInverted(not reference->isInverted()); - } - } - else if (obj->type() == LDObjectType::CircularPrimitive) - { - auto primitive = static_cast<LDCircularPrimitive*>(obj); - - if (primitive->isFlat()) - primitive->setTransformationMatrix(primitive->transformationMatrix() * ::flipmatrix(Y)); - else - primitive->setInverted(not primitive->isInverted()); - } -} -#endif - /* * Inverts the winding of a polygon. */
--- a/src/invert.h Sun Mar 13 20:26:28 2022 +0200 +++ b/src/invert.h Tue Mar 15 18:52:48 2022 +0200 @@ -20,8 +20,6 @@ #include "main.h" #include "gl/common.h" -bool isflat(class Model* model, Axis* axis); - namespace math { glm::mat4 flipmatrix(Axis dimension);
--- a/src/ldrawalgorithm.cpp Sun Mar 13 20:26:28 2022 +0200 +++ b/src/ldrawalgorithm.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -2,10 +2,10 @@ #include "linetypes/quadrilateral.h" #include "linetypes/triangle.h" -void ldraw::invert(ModelEditor& editor, ldraw::id_t id) +void ldraw::invert(ModelEditor& editor, ldraw::id_t id, GetPolygonsContext* context) { - editor.modifyObject(id, [](ldraw::Object* object){ - object->invert(); + editor.modifyObject(id, [context](ldraw::Object* object){ + object->invert(context); }); }
--- a/src/ldrawalgorithm.h Sun Mar 13 20:26:28 2022 +0200 +++ b/src/ldrawalgorithm.h Tue Mar 15 18:52:48 2022 +0200 @@ -17,6 +17,6 @@ Diagonal splitType = Diagonal::Diagonal_13 ) -> std::optional<std::pair<triangleid_t, triangleid_t>>; - void invert(ModelEditor& editor, ldraw::id_t id); + void invert(ModelEditor& editor, ldraw::id_t id, GetPolygonsContext *context); void makeUnofficial(ModelEditor &editor); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/linetypes/compoundobject.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -0,0 +1,115 @@ +#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 {}; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/linetypes/compoundobject.h Tue Mar 15 18:52:48 2022 +0200 @@ -0,0 +1,33 @@ +#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; + bool isInverted = false; +protected: + void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; +};
--- a/src/linetypes/object.h Sun Mar 13 20:26:28 2022 +0200 +++ b/src/linetypes/object.h Tue Mar 15 18:52:48 2022 +0200 @@ -72,7 +72,7 @@ virtual QBrush textRepresentationBackground() const; virtual QFont textRepresentationFont() const; virtual void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const; - virtual void invert() {} + virtual void invert(GetPolygonsContext*) {} virtual int numPoints() const { return 0; } virtual const glm::vec3& getPoint(int index) const; virtual QDataStream& serialize(QDataStream& stream) const;
--- a/src/linetypes/quadrilateral.cpp Sun Mar 13 20:26:28 2022 +0200 +++ b/src/linetypes/quadrilateral.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -23,7 +23,7 @@ this->id)); } -void ldraw::Quadrilateral::invert() +void ldraw::Quadrilateral::invert(GetPolygonsContext *) { // 0 1 2 3 // -> 2 1 0 3
--- a/src/linetypes/quadrilateral.h Sun Mar 13 20:26:28 2022 +0200 +++ b/src/linetypes/quadrilateral.h Tue Mar 15 18:52:48 2022 +0200 @@ -12,7 +12,7 @@ using PolygonObject<4>::PolygonObject; QString textRepresentation() const override; void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override; - void invert() override; + void invert(GetPolygonsContext *) override; Type typeIdentifier() const override; QString toLDrawCode() const override; QString iconName() const override;
--- a/src/linetypes/subfilereference.cpp Sun Mar 13 20:26:28 2022 +0200 +++ b/src/linetypes/subfilereference.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -9,8 +9,7 @@ const QString& referenceName, const Color color ) : - ColoredObject{color}, - transformation{transformation}, + CompoundObject{transformation, color}, referenceName{referenceName} { } @@ -19,20 +18,17 @@ { switch (property) { - case Property::Transformation: - return QVariant::fromValue(this->transformation); case Property::ReferenceName: return this->referenceName; default: - return ColoredObject::getProperty(property); + return CompoundObject::getProperty(property); } } void ldraw::SubfileReference::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) { - LDRAW_OBJECT_HANDLE_SET_PROPERTY(Transformation, {this->transformation = value;}); LDRAW_OBJECT_HANDLE_SET_PROPERTY(ReferenceName, {this->referenceName = value;}); - ldraw::ColoredObject::setProperty(result, pair); + ldraw::CompoundObject::setProperty(result, pair); } QString ldraw::SubfileReference::textRepresentation() const @@ -83,16 +79,6 @@ } } -glm::vec3 ldraw::SubfileReference::position() const -{ - return this->transformation[3]; -} - -void ldraw::SubfileReference::invert() -{ - this->isInverted = not this->isInverted; -} - Model* ldraw::SubfileReference::resolve(const ModelId callingModelId, DocumentManager* documents) const { return documents->findDependencyByName(callingModelId, this->referenceName); @@ -105,12 +91,12 @@ QDataStream& ldraw::SubfileReference::serialize(QDataStream &stream) const { - return ColoredObject::serialize(stream) << this->transformation << this->referenceName << this->isInverted; + return CompoundObject::serialize(stream) << this->referenceName; } QDataStream& ldraw::SubfileReference::deserialize(QDataStream &stream) { - return ColoredObject::deserialize(stream) >> this->transformation >> this->referenceName >> this->isInverted; + return CompoundObject::deserialize(stream) >> this->referenceName; } QString ldraw::SubfileReference::toLDrawCode() const
--- a/src/linetypes/subfilereference.h Sun Mar 13 20:26:28 2022 +0200 +++ b/src/linetypes/subfilereference.h Tue Mar 15 18:52:48 2022 +0200 @@ -1,5 +1,6 @@ #pragma once -#include "object.h" +#include "compoundobject.h" +#include "invert.h" class Model; @@ -8,7 +9,7 @@ class SubfileReference; } -class ldraw::SubfileReference : public ColoredObject +class ldraw::SubfileReference : public CompoundObject { public: SubfileReference() = default; @@ -19,8 +20,6 @@ QVariant getProperty(Property property) const override; QString textRepresentation() const override; void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override; - glm::vec3 position() const; - void invert() override; Model* resolve(const ModelId callingModelId, DocumentManager* documents) const; Type typeIdentifier() const override; QDataStream& serialize(QDataStream& stream) const override; @@ -28,9 +27,7 @@ QString toLDrawCode() const override; QString iconName() const override; QString typeName() const override; - glm::mat4 transformation; QString referenceName; - bool isInverted = false; protected: void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; };
--- a/src/linetypes/triangle.cpp Sun Mar 13 20:26:28 2022 +0200 +++ b/src/linetypes/triangle.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -21,7 +21,7 @@ this->id)); } -void ldraw::Triangle::invert() +void ldraw::Triangle::invert(GetPolygonsContext *) { // 0 1 2 // -> 1 0 2
--- a/src/linetypes/triangle.h Sun Mar 13 20:26:28 2022 +0200 +++ b/src/linetypes/triangle.h Tue Mar 15 18:52:48 2022 +0200 @@ -12,7 +12,7 @@ using PolygonObject<3>::PolygonObject; QString textRepresentation() const override; void getPolygons(std::vector<gl::Polygon>& polygons, GetPolygonsContext* context) const override; - void invert() override; + void invert(GetPolygonsContext*) override; Type typeIdentifier() const override; QString toLDrawCode() const override; QString iconName() const override;
--- a/src/mainwindow.cpp Sun Mar 13 20:26:28 2022 +0200 +++ b/src/mainwindow.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -79,6 +79,7 @@ this, &MainWindow::actionSaveAs); connect(this->ui->actionClose, &QAction::triggered, this, &MainWindow::actionClose); connect(this->ui->actionDelete, &QAction::triggered, this, &MainWindow::actionDelete); + connect(this->ui->actionInvert, &QAction::triggered, this, &MainWindow::actionInvert); connect(this->ui->tabs, &QTabWidget::tabCloseRequested, this, &MainWindow::handleTabCloseButton); for (auto data : ::renderStyleButtons) { @@ -379,6 +380,34 @@ } /** + * @brief Handles the "Invert" action + */ +void MainWindow::actionInvert() +{ + Document* document = this->currentDocument(); + if (document != nullptr) + { + // TODO: simplify + std::unique_ptr<ModelEditor> modelEditor = document->editModel(); + const std::optional<ModelId> modelId = this->documents.findIdForModel(&modelEditor->model()); + if (modelId.has_value()) + { + ldraw::GetPolygonsContext context = { + .modelId = modelId.value(), + .documents = &this->documents, + }; + for (const ldraw::id_t id : document->selectedObjects()) + { + modelEditor->modifyObject(id, [&context](ldraw::Object* object) + { + object->invert(&context); + }); + } + } + } +} + +/** * @brief Removes the document at the specified tab index * @param index */
--- a/src/mainwindow.h Sun Mar 13 20:26:28 2022 +0200 +++ b/src/mainwindow.h Tue Mar 15 18:52:48 2022 +0200 @@ -49,6 +49,7 @@ void actionSaveAs(); void actionClose(); void actionDelete(); + void actionInvert(); void handleTabCloseButton(int tabIndex); protected: void changeEvent(QEvent* event) override;
--- a/src/mainwindow.ui Sun Mar 13 20:26:28 2022 +0200 +++ b/src/mainwindow.ui Tue Mar 15 18:52:48 2022 +0200 @@ -30,7 +30,7 @@ <x>0</x> <y>0</y> <width>800</width> - <height>31</height> + <height>27</height> </rect> </property> <widget class="QMenu" name="menuFile"> @@ -69,6 +69,7 @@ <property name="title"> <string>Edit</string> </property> + <addaction name="actionInvert"/> <addaction name="actionDelete"/> <addaction name="separator"/> <addaction name="actionGridFine"/> @@ -120,6 +121,7 @@ <attribute name="toolBarBreak"> <bool>false</bool> </attribute> + <addaction name="actionInvert"/> <addaction name="actionDelete"/> <addaction name="actionGridFine"/> <addaction name="actionGridMedium"/> @@ -312,6 +314,15 @@ <string>Ctrl+3</string> </property> </action> + <action name="actionInvert"> + <property name="icon"> + <iconset resource="../ldforge.qrc"> + <normaloff>:/icons/invert.png</normaloff>:/icons/invert.png</iconset> + </property> + <property name="text"> + <string>Invert</string> + </property> + </action> </widget> <resources> <include location="../ldforge.qrc"/>
--- a/src/parser.cpp Sun Mar 13 20:26:28 2022 +0200 +++ b/src/parser.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -67,7 +67,7 @@ model.append(parseFromString(line)); if (invertNext) { - model[model.size() - 1]->invert(); + model[model.size() - 1]->invert(nullptr); } invertNext = false; }
--- a/src/polygoncache.cpp Sun Mar 13 20:26:28 2022 +0200 +++ b/src/polygoncache.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -16,7 +16,7 @@ * @param documents Documents to use to resolve subfile references * @return vector of GL polygons */ -std::vector<gl::Polygon> PolygonCache::getPolygons(DocumentManager* documents) +const std::vector<gl::Polygon> &PolygonCache::getPolygons(DocumentManager* documents) { if (this->needRecache) {
--- a/src/polygoncache.h Sun Mar 13 20:26:28 2022 +0200 +++ b/src/polygoncache.h Tue Mar 15 18:52:48 2022 +0200 @@ -7,7 +7,7 @@ Q_OBJECT public: PolygonCache(Model* model); - std::vector<gl::Polygon> getPolygons(class DocumentManager* documents); + const std::vector<gl::Polygon>& getPolygons(class DocumentManager* documents); private: void getObjectPolygons( const int index,
--- a/src/ui/objecteditor.cpp Sun Mar 13 20:26:28 2022 +0200 +++ b/src/ui/objecteditor.cpp Tue Mar 15 18:52:48 2022 +0200 @@ -1,9 +1,6 @@ -#include <QVBoxLayout> +#include <QCheckBox> +#include <QLineEdit> #include <QFormLayout> -#include <QPushButton> -#include <QLabel> -#include <QSpinBox> -#include <QSplitter> #include "objecteditor.h" #include "document.h" #include "modeleditor.h" @@ -59,6 +56,22 @@ { 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()); + } + } + } } } @@ -66,7 +79,7 @@ { this->objectId = id; const ldraw::Object* object = this->document->getModel().get(id); - this->ui.properties->setVisible(object != nullptr); + this->ui.properties->setEnabled(object != nullptr); if (object != nullptr) { this->ui.typeNameLabel->setText("<b>" + titleCase(object->typeName()) + "</b>"); @@ -77,8 +90,8 @@ const auto it = this->propertyWidgets.find(property); if (it != this->propertyWidgets.end()) { - it->first->setVisible(not value.isNull()); - it->second->setVisible(not value.isNull()); + it->first->setEnabled(not value.isNull()); + it->second->setEnabled(not value.isNull()); if (not value.isNull()) { setValueToWidget(it->second, value); @@ -103,6 +116,16 @@ this->handlePropertyChange(this->sender(), QVariant::fromValue(value)); } +void ObjectEditor::handleBoolChange(bool value) +{ + this->handlePropertyChange(this->sender(), QVariant::fromValue(value)); +} + +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()); @@ -118,6 +141,18 @@ 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;
--- a/src/ui/objecteditor.h Sun Mar 13 20:26:28 2022 +0200 +++ b/src/ui/objecteditor.h Tue Mar 15 18:52:48 2022 +0200 @@ -15,6 +15,8 @@ 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;