Mon, 11 May 2020 12:18:59 +0300
object editing
--- a/CMakeLists.txt Mon May 11 12:18:04 2020 +0300 +++ b/CMakeLists.txt Mon May 11 12:18:59 2020 +0300 @@ -94,6 +94,8 @@ src/linetypes/errorline.h src/linetypes/metacommand.h src/linetypes/object.h + src/linetypes/polygonobject.h + src/linetypes/propertygenerics.h src/linetypes/quadrilateral.h src/linetypes/subfilereference.h src/linetypes/triangle.h
--- a/src/colors.h Mon May 11 12:18:04 2020 +0300 +++ b/src/colors.h Mon May 11 12:18:59 2020 +0300 @@ -28,9 +28,11 @@ struct ldraw::Color { - qint32 index; + qint32 index = 0; }; +Q_DECLARE_METATYPE(ldraw::Color) + class ldraw::ColorTable { public:
--- a/src/linetypes/conditionaledge.h Mon May 11 12:18:04 2020 +0300 +++ b/src/linetypes/conditionaledge.h Mon May 11 12:18:59 2020 +0300 @@ -1,5 +1,5 @@ #pragma once -#include "edge.h" +#include "polygonobject.h" namespace ldraw {
--- a/src/linetypes/edge.h Mon May 11 12:18:04 2020 +0300 +++ b/src/linetypes/edge.h Mon May 11 12:18:59 2020 +0300 @@ -1,5 +1,5 @@ #pragma once -#include "object.h" +#include "polygonobject.h" namespace ldraw {
--- a/src/linetypes/object.cpp Mon May 11 12:18:04 2020 +0300 +++ b/src/linetypes/object.cpp Mon May 11 12:18:59 2020 +0300 @@ -1,6 +1,8 @@ #include <QBrush> #include <QFont> #include "object.h" +#include "widgets/vec3editor.h" +#include "modeleditcontext.h" static std::int32_t getIdForNewObject() { @@ -87,7 +89,7 @@ switch (id) { case Property::Color: - return colorIndex.index; + return QVariant::fromValue<Color>(colorIndex); default: return Object::getProperty(id); } @@ -95,7 +97,7 @@ void ldraw::ColoredObject::setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) { - LDRAW_OBJECT_HANDLE_SET_PROPERTY(Color, {colorIndex.index = value;}); + LDRAW_OBJECT_HANDLE_SET_PROPERTY(Color, {colorIndex = value;}); Object::setProperty(result, pair); }
--- a/src/linetypes/object.h Mon May 11 12:18:04 2020 +0300 +++ b/src/linetypes/object.h Mon May 11 12:18:59 2020 +0300 @@ -5,17 +5,17 @@ #include "main.h" #include "colors.h" #include "gl/common.h" +#include "linetypes/propertygenerics.h" + +class Model; namespace ldraw { struct GetPolygonsContext; - enum class Property; class Object; class ColoredObject; class Empty; class UnhandledProperty; - template<int N> - class PolygonObject; } class DocumentManager; @@ -25,67 +25,6 @@ ::DocumentManager* documents; }; -/** - * @brief Different properties that can be queried with getProperty - */ -enum class ldraw::Property -{ - Color, // Color of the object - Text, // Text contained in a comment - 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 -}; - -// Mapping of properties to types -#define LDFORGE_DEFINE_PROPERTY_TYPE(PROPERTY, TYPE) \ - namespace ldraw { \ - template<> struct PropertyType<ldraw::Property::PROPERTY> { using type = TYPE; }; \ - } - -namespace ldraw -{ - template<ldraw::Property property> - struct PropertyType - { - }; - - template<ldraw::Property property> - using PropertyType_t = typename PropertyType<property>::type; - - struct PropertyKeyValue - { - Property key; - QVariant value; - }; - - constexpr Property pointProperty(int n) - { - Q_ASSERT(n >= 0 and n < 4); - return static_cast<Property>(static_cast<int>(Property::Point0) + n); - } -} - -LDFORGE_DEFINE_PROPERTY_TYPE(Color, int) -LDFORGE_DEFINE_PROPERTY_TYPE(Text, QString) -LDFORGE_DEFINE_PROPERTY_TYPE(Point0, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(Point1, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(Point2, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(Point3, glm::vec3) -LDFORGE_DEFINE_PROPERTY_TYPE(Transformation, glm::mat4) -LDFORGE_DEFINE_PROPERTY_TYPE(ReferenceName, QString) -LDFORGE_DEFINE_PROPERTY_TYPE(IsInverted, bool) -LDFORGE_DEFINE_PROPERTY_TYPE(ErrorMessage, QString) - -#define LDRAW_OBJECT_HANDLE_SET_PROPERTY(PROPERTY, HANDLER) \ - {this->handle<ldraw::Property::PROPERTY>(result, pair, \ - [&](const ldraw::PropertyType_t<ldraw::Property::PROPERTY>& value) HANDLER);} - class ldraw::Object { public: @@ -108,7 +47,7 @@ virtual bool hasColor() const; virtual QVariant getProperty(Property id) const; template<ldraw::Property property> - SetPropertyResult setProperty(const PropertyType_t<property>& value); + SetPropertyResult setProperty(const PropertyType<property>& value); SetPropertyResult setProperty(const PropertyKeyValue& pair); virtual QString textRepresentation() const = 0; virtual QBrush textRepresentationForeground() const; @@ -125,7 +64,7 @@ }; template<ldraw::Property property> -ldraw::Object::SetPropertyResult ldraw::Object::setProperty(const PropertyType_t<property>& value) +ldraw::Object::SetPropertyResult ldraw::Object::setProperty(const ldraw::PropertyType<property>& value) { SetPropertyResult result = SetPropertyResult::PropertyNotHandled; this->setProperty(&result, PropertyKeyValue{property, QVariant::fromValue(value)}); @@ -137,7 +76,7 @@ { if (pair.key == property) { - function(pair.value.value<ldraw::PropertyType_t<property>>()); + function(pair.value.value<ldraw::PropertyType<property>>()); *result = SetPropertyResult::Success; } } @@ -153,64 +92,6 @@ void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) override; }; -template<int N> -class ldraw::PolygonObject : public ColoredObject -{ -public: - 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 ColoredObject::getProperty(id); - } - void setProperty(SetPropertyResult* result, const PropertyKeyValue& pair) - { - LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point0, {points[0] = value;}) - LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point1, {points[1] = value;}) - if constexpr (N >= 3) - { - LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point2, {points[2] = value;}) - } - if constexpr (N >= 4) - { - LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point3, {points[2] = value;}) - } - ColoredObject::setProperty(result, pair); - } - std::array<std::enable_if_t<(N > 0 and N <= 4), glm::vec3>, N> points; -}; - /** * @brief Represents an empty line. */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/linetypes/polygonobject.h Mon May 11 12:18:59 2020 +0300 @@ -0,0 +1,70 @@ +#pragma once +#include "object.h" +#include "widgets/vec3editor.h" +#include "model.h" +#include "modeleditcontext.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) + { + LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point0, {points[0] = value;}) + LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point1, {points[1] = value;}) + if constexpr (N >= 3) + { + LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point2, {points[2] = value;}) + } + if constexpr (N >= 4) + { + LDRAW_OBJECT_HANDLE_SET_PROPERTY(Point3, {points[3] = value;}) + } + ColoredObject::setProperty(result, pair); + } + std::array<glm::vec3, N> points; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/linetypes/propertygenerics.h Mon May 11 12:18:59 2020 +0300 @@ -0,0 +1,153 @@ +#pragma once +#include "main.h" +#include "colors.h" + +class Vec3Editor; +class MatrixEditor; + +namespace ldraw +{ + enum class Property; + struct PropertyKeyValue; + template<Property property> + struct PropertyTraits + { + static constexpr bool defined = false; + }; +} + +/** + * Different properties + */ +enum class ldraw::Property +{ + Color, // Color of the object + Text, // Text contained in a comment + Point0, // First vertex in a polygon or edge line + Point1, // Second vertex in a polygon or edge line + Point2, // Third vertex in a polygon + Point3, // Fourth vertex in a quadrilateral + Transformation, // 4x4 transformation matrix of a subfile reference + ReferenceName, // Subfile reference name + IsInverted, // Whether or not the object has been inverted with BFC INVERTNEXT + ErrorMessage // For error lines, why parsing failed +}; + +Q_DECLARE_METATYPE(ldraw::Property) + +// Mapping of properties to types +#define LDFORGE_DEFINE_PROPERTY_TYPE(PROPERTY, TYPE) \ + namespace ldraw \ + { \ + template<> struct PropertyTraits<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) + +#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; + + struct PropertyKeyValue + { + Property key; + QVariant value; + }; + + constexpr Property pointProperty(int n) + { + Q_ASSERT(n >= 0 and n < 4); + 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.h Mon May 11 12:18:04 2020 +0300 +++ b/src/linetypes/quadrilateral.h Mon May 11 12:18:59 2020 +0300 @@ -1,5 +1,5 @@ #pragma once -#include "object.h" +#include "polygonobject.h" namespace ldraw {
--- a/src/linetypes/triangle.h Mon May 11 12:18:04 2020 +0300 +++ b/src/linetypes/triangle.h Mon May 11 12:18:59 2020 +0300 @@ -1,5 +1,5 @@ #pragma once -#include "object.h" +#include "polygonobject.h" namespace ldraw {
--- a/src/main.cpp Mon May 11 12:18:04 2020 +0300 +++ b/src/main.cpp Mon May 11 12:18:59 2020 +0300 @@ -20,6 +20,8 @@ #include <GL/glut.h> #include "mainwindow.h" #include "version.h" +#include <iostream> +#include <QMessageBox> int main(int argc, char *argv[]) { @@ -30,6 +32,9 @@ ::qRegisterMetaTypeStreamOperators<Library>("Library"); ::qRegisterMetaTypeStreamOperators<Libraries>("Libraries"); QApplication app{argc, argv}; + /* + QMessageBox::information(nullptr, "", QMetaType::typeName( qMetaTypeId<ldraw::Color>() )); + */ MainWindow mainwindow; mainwindow.show(); return app.exec();
--- a/src/modeleditcontext.cpp Mon May 11 12:18:04 2020 +0300 +++ b/src/modeleditcontext.cpp Mon May 11 12:18:59 2020 +0300 @@ -17,6 +17,8 @@ */ #include "modeleditcontext.h" +#include "linetypes/triangle.h" +#include "linetypes/quadrilateral.h" Model::EditContext::EditContext(Model& model) : storedModel{model} @@ -45,6 +47,25 @@ this->model().remove(position); } +auto Model::EditContext::setObjectProperty( + const ldraw::id_t id, + const ldraw::Property property, + const QVariant& value) + -> ldraw::Object::SetPropertyResult +{ + ldraw::Object* const object = this->model().objectAt(id); + if (object != nullptr) + { + const ldraw::Object::SetPropertyResult result = object->setProperty(ldraw::PropertyKeyValue{property, value}); + modifiedObjects.insert(id); + return result; + } + else + { + return ldraw::Object::SetPropertyResult::PropertyNotHandled; + } +} + void Model::EditContext::setObjectPoint(ldraw::id_t id, int pointId, const glm::vec3& value) { ldraw::Object* object = this->model().objectAt(id);
--- a/src/modeleditcontext.h Mon May 11 12:18:04 2020 +0300 +++ b/src/modeleditcontext.h Mon May 11 12:18:59 2020 +0300 @@ -18,9 +18,6 @@ #pragma once #include "model.h" -#include "linetypes/object.h" -#include "linetypes/quadrilateral.h" -#include "linetypes/triangle.h" class Model::EditContext { @@ -33,15 +30,9 @@ 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_t<property>& value) - { - ldraw::Object* object = this->model().objectAt(id); - if (object != nullptr) - { - object->setProperty<property>(value); - modifiedObjects.insert(id); - } - } + 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); void invertObject(ldraw::id_t id); Model& model(); @@ -52,6 +43,17 @@ Model& storedModel; }; +template<ldraw::Property Property> +void Model::EditContext::setObjectProperty(const ldraw::id_t id, const ldraw::PropertyType<Property>& value) +{ + ldraw::Object* object = this->model().objectAt(id); + if (object != nullptr) + { + object->setProperty<Property>(value); + modifiedObjects.insert(id); + } +} + template<typename T, typename... Args> ldraw::Id<T> Model::EditContext::append(Args&&... args) {
--- a/src/ui/polygonobjecteditor.cpp Mon May 11 12:18:04 2020 +0300 +++ b/src/ui/polygonobjecteditor.cpp Mon May 11 12:18:59 2020 +0300 @@ -1,27 +1,21 @@ -#include <QVBoxLayout> +#include <QFormLayout> #include <QSplitter> #include "model.h" #include "modeleditcontext.h" #include "widgets/vec3editor.h" #include "ui/polygonobjecteditor.h" -constexpr char COLUMN_PROPERTY[] = "_ldforge_column"; +static constexpr char INDEX_NAME[] = "_ldforge_index"; +static constexpr char PROPERTY_NAME[] = "_ldforge_property"; +static constexpr char OBJECT_ID_NAME[] = "_ldforge_id"; +static constexpr char LABEL_NAME[] = "_ldforge_label"; PolygonObjectEditor::PolygonObjectEditor(Model* model, ldraw::id_t id, QWidget* parent) : QWidget{parent}, model{model}, storedObjectId{ldraw::NULL_ID.value} { - this->setLayout(new QVBoxLayout{this}); - for (int i = 0; i < countof(this->vec3Editors); i += 1) - { - std::optional<Vec3Editor>& editorPointer = this->vec3Editors[i]; - editorPointer.emplace(glm::vec3{}, this); - editorPointer->setProperty(COLUMN_PROPERTY, QVariant::fromValue(i)); - this->layout()->addWidget(&*editorPointer); - connect(&*editorPointer, &Vec3Editor::valueChanged, this, &PolygonObjectEditor::vectorChanged); - } - this->layout()->addWidget(new QSplitter{Qt::Vertical, this}); + this->splitter.emplace(Qt::Vertical, this); this->setObjectId(id); } @@ -38,37 +32,46 @@ void PolygonObjectEditor::setObjectId(ldraw::id_t id) { this->storedObjectId = id; - this->updateNumRows(); + this->buildWidgets(); } -void PolygonObjectEditor::vectorChanged(const glm::vec3& value) +void PolygonObjectEditor::buildWidgets() { - bool ok; - const int num = sender()->property(COLUMN_PROPERTY).toInt(&ok); - if (ok and num >= 0 and num < countof(this->vec3Editors)) + this->widgets.clear(); + delete this->layout(); + QFormLayout* layout = new QFormLayout{this}; + this->setLayout(layout); + for (int n : {0, 1, 2, 3}) + { + this->setupPointWidget(n); + } + for (std::unique_ptr<QWidget>& widget : this->widgets) { - Model::EditContext editor = this->model->edit(); - editor.setObjectPoint(this->objectId(), num, value); + const QString label = widget->property(LABEL_NAME).toString(); + layout->addRow(label, widget.get()); + } + layout->addRow("", &*this->splitter); +} + +void PolygonObjectEditor::setupPointWidget(int n) +{ + const ldraw::Object* const object = this->model->get(this->objectId()); + const ldraw::Property property = ldraw::pointProperty(n); + const QVariant value = object->getProperty(property); + if (value.isValid()) + { + std::unique_ptr<Vec3Editor> editor = std::make_unique<Vec3Editor>(value.value<glm::vec3>(), this); + QObject::connect(editor.get(), &Vec3Editor::valueChanged, this, &PolygonObjectEditor::pointChanged); + editor->setProperty(INDEX_NAME, QVariant::fromValue(n)); + editor->setProperty(LABEL_NAME, &ldraw::traits(property).name[0]); + this->widgets.push_back(std::move(editor)); } } -void PolygonObjectEditor::updateNumRows() +void PolygonObjectEditor::pointChanged(const glm::vec3& value) { - const ldraw::Object* object = model->get(this->storedObjectId); - const int numPoints = object != nullptr ? object->numPoints() : 0; - for (int i = 0; i < countof(this->vec3Editors); i += 1) - { - Vec3Editor& editor = *this->vec3Editors[i]; - QSignalBlocker blocker{&editor}; - if (i < numPoints) - { - editor.setVisible(true); - editor.setValue(object->getPoint(i)); - } - else - { - editor.setVisible(false); - editor.setValue(glm::vec3{}); - } - } + Model::EditContext editcontext = this->model->edit(); + const int n = this->sender()->property(INDEX_NAME).toInt(); + const ldraw::Property property = ldraw::pointProperty(n); + editcontext.setObjectProperty(this->objectId(), property, QVariant::fromValue(value)); }
--- a/src/ui/polygonobjecteditor.h Mon May 11 12:18:04 2020 +0300 +++ b/src/ui/polygonobjecteditor.h Mon May 11 12:18:59 2020 +0300 @@ -13,9 +13,11 @@ ldraw::id_t objectId() const; void setObjectId(ldraw::id_t id); private: - Q_SLOT void vectorChanged(const glm::vec3& value); - void updateNumRows(); + void buildWidgets(); + void setupPointWidget(int n); + Q_SLOT void pointChanged(const glm::vec3& value); Model* model; ldraw::id_t storedObjectId; - std::optional<Vec3Editor> vec3Editors[4]; + std::vector<std::unique_ptr<QWidget>> widgets; + std::optional<class QSplitter> splitter; };