Mon, 09 Mar 2020 14:21:54 +0200
ldraw::Id is now templated for extra type safety
--- a/src/colors.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/colors.h Mon Mar 09 14:21:54 2020 +0200 @@ -49,34 +49,37 @@ QMap<qint32, ColorDefinition> definitions; }; -inline bool operator==(const ldraw::Color& one, const ldraw::Color& other) -{ - return one.index == other.index; -} - -inline bool operator!=(const ldraw::Color& one, const ldraw::Color& other) -{ - return one.index != other.index; -} - -inline bool operator<(const ldraw::Color& one, const ldraw::Color& other) +namespace ldraw { - return one.index < other.index; -} + inline bool operator==(const ldraw::Color& one, const ldraw::Color& other) + { + return one.index == other.index; + } -inline bool operator<=(const ldraw::Color& one, const ldraw::Color& other) -{ - return one.index <= other.index; -} + inline bool operator!=(const ldraw::Color& one, const ldraw::Color& other) + { + return one.index != other.index; + } + + inline bool operator<(const ldraw::Color& one, const ldraw::Color& other) + { + return one.index < other.index; + } -inline bool operator>(const ldraw::Color& one, const ldraw::Color& other) -{ - return one.index > other.index; -} + inline bool operator<=(const ldraw::Color& one, const ldraw::Color& other) + { + return one.index <= other.index; + } -inline bool operator>=(const ldraw::Color& one, const ldraw::Color& other) -{ - return one.index >= other.index; + inline bool operator>(const ldraw::Color& one, const ldraw::Color& other) + { + return one.index > other.index; + } + + inline bool operator>=(const ldraw::Color& one, const ldraw::Color& other) + { + return one.index >= other.index; + } } namespace ldraw
--- a/src/document.cpp Fri Mar 06 23:45:44 2020 +0200 +++ b/src/document.cpp Mon Mar 09 14:21:54 2020 +0200 @@ -41,11 +41,11 @@ this->setMouseTracking(true); connect(this->ui.splitter, &QSplitter::splitterMoved, this, &Document::splitterChanged); connect(this->renderer, &Canvas::newStatusText, this, &Document::newStatusText); - connect(this->renderer, &Canvas::selectionChanged, [&](const QSet<ldraw::Id>& newSelection) + connect(this->renderer, &Canvas::selectionChanged, [&](const QSet<ldraw::id_t>& newSelection) { QItemSelectionModel* selectionModel = this->ui.listView->selectionModel(); QItemSelection selection; - for (ldraw::Id id : newSelection) + for (ldraw::id_t id : newSelection) { QModelIndex index = this->model->lookup(id); if (index != QModelIndex{}) @@ -61,7 +61,7 @@ auto resolveIndex = [this](const QModelIndex& index){ return this->model->resolve(index); }; auto resolve = [resolveIndex](const QItemSelection& selection) { - return fn::map<QSet<ldraw::Id>>(selection.indexes(), resolveIndex); + return fn::map<QSet<ldraw::id_t>>(selection.indexes(), resolveIndex); }; this->renderer->handleSelectionChange(resolve(selected), resolve(deselected)); });
--- a/src/gl/common.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/gl/common.h Mon Mar 09 14:21:54 2020 +0200 @@ -77,7 +77,7 @@ Type type; glm::vec3 vertices[4]; ldraw::Color color; - ldraw::Id id; + ldraw::id_t id; /** * @return amount of vertices used for geometry @@ -113,7 +113,7 @@ namespace gl { - inline Polygon edgeLine(const glm::vec3& v_1, const glm::vec3& v_2, ldraw::Color color, ldraw::Id id) + inline Polygon edgeLine(const glm::vec3& v_1, const glm::vec3& v_2, ldraw::Color color, ldraw::id_t id) { return {Polygon::EdgeLine, {v_1, v_2}, color, id}; } @@ -123,7 +123,7 @@ const glm::vec3& v_2, const glm::vec3& v_3, ldraw::Color color, - ldraw::Id id) + ldraw::id_t id) { return {Polygon::Triangle, {v_1, v_2, v_3}, color, id}; } @@ -134,7 +134,7 @@ const glm::vec3& v_3, const glm::vec3& v_4, ldraw::Color color, - ldraw::Id id) + ldraw::id_t id) { return {Polygon::Quadrilateral, {v_1, v_2, v_3, v_4}, color, id}; } @@ -145,7 +145,7 @@ const glm::vec3& control_1, const glm::vec3& control_2, ldraw::Color color, - ldraw::Id id) + ldraw::id_t id) { return {Polygon::ConditionalEdge, {v_1, v_2, control_1, control_2}, color, id}; }
--- a/src/gl/compiler.cpp Fri Mar 06 23:45:44 2020 +0200 +++ b/src/gl/compiler.cpp Mon Mar 09 14:21:54 2020 +0200 @@ -237,7 +237,7 @@ return gl::ArrayClass::Lines; } -ldraw::Id gl::Compiler::idFromColor(const std::array<GLbyte, 3>& data) +ldraw::id_t gl::Compiler::idFromColor(const std::array<GLbyte, 3>& data) { return {data[0] * std::int32_t{0x10000} + data[1] * std::int32_t{0x100} + data[2]}; } @@ -311,7 +311,7 @@ object.vertexArray.release(); } -void gl::Compiler::setSelectedObjects(const QSet<ldraw::Id> ids) +void gl::Compiler::setSelectedObjects(const QSet<ldraw::id_t> ids) { for (int i = 0; i < gl::NUM_ARRAY_CLASSES; i += 1) {
--- a/src/gl/compiler.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/gl/compiler.h Mon Mar 09 14:21:54 2020 +0200 @@ -61,9 +61,9 @@ void bindVertexArray(gl::ArrayClass arrayClass); void releaseVertexArray(gl::ArrayClass arrayClass); void buildShaders(int arrayId); - void setSelectedObjects(const QSet<ldraw::Id> ids); + void setSelectedObjects(const QSet<ldraw::id_t> ids); - static ldraw::Id idFromColor(const std::array<GLbyte, 3>& data); + static ldraw::id_t idFromColor(const std::array<GLbyte, 3>& data); template<typename T> void setUniform(const char* uniformName, T&& value) @@ -90,7 +90,7 @@ bool initialized = false; BoundingBox boundingBox; const ldraw::ColorTable& colorTable; - ldraw::Id hovered = ldraw::NULL_ID; + ldraw::id_t hovered = ldraw::NULL_ID; struct { QOpenGLShaderProgram* program = nullptr;
--- a/src/gl/partrenderer.cpp Fri Mar 06 23:45:44 2020 +0200 +++ b/src/gl/partrenderer.cpp Mon Mar 09 14:21:54 2020 +0200 @@ -330,7 +330,7 @@ viewportVector); } -ldraw::Id PartRenderer::pick(const QPoint& where) +ldraw::id_t PartRenderer::pick(const QPoint& where) { const gl::RenderStyle oldRenderStyle = this->renderPreferences.style; this->renderPreferences.style = gl::RenderStyle::PickScene;
--- a/src/gl/partrenderer.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/gl/partrenderer.h Mon Mar 09 14:21:54 2020 +0200 @@ -23,7 +23,7 @@ ~PartRenderer() override; void setRenderPreferences(const gl::RenderPreferences& newPreferences); protected: - ldraw::Id pick(const QPoint& where); + ldraw::id_t pick(const QPoint& where); void initializeGL() override; void resizeGL(int width, int height) override; void paintGL() override; @@ -33,7 +33,7 @@ DocumentManager* const documents; const ldraw::ColorTable& colorTable; gl::Compiler* const compiler; - ldraw::Id highlighted = ldraw::NULL_ID; + ldraw::id_t highlighted = ldraw::NULL_ID; 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/linetypes/object.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/linetypes/object.h Mon Mar 09 14:21:54 2020 +0200 @@ -53,7 +53,7 @@ Object(); Object(const Object&) = delete; virtual ~Object(); - const Id id; + const id_t id; //virtual void toString(QTextStream &out) = 0; virtual bool hasColor() const; virtual QVariant getProperty(Property id) const;
--- a/src/linetypes/triangle.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/linetypes/triangle.h Mon Mar 09 14:21:54 2020 +0200 @@ -1,3 +1,4 @@ +#pragma once #include "object.h" namespace ldraw
--- a/src/main.cpp Fri Mar 06 23:45:44 2020 +0200 +++ b/src/main.cpp Mon Mar 09 14:21:54 2020 +0200 @@ -21,6 +21,8 @@ #include "mainwindow.h" #include "version.h" +#include "linetypes/quadrilateral.h" + int main(int argc, char *argv[]) { ::glutInit(&argc, argv);
--- a/src/main.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/main.h Mon Mar 09 14:21:54 2020 +0200 @@ -35,25 +35,65 @@ namespace ldraw { + class Object; + // Uniquely identifies a model body object + template<typename T> struct Id { std::int32_t value; - constexpr bool operator<(ldraw::Id other) const + 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 id) + friend constexpr unsigned int qHash(ldraw::Id<T> id) { return qHash(id.value); } - friend bool operator==(ldraw::Id one, ldraw::Id other) + // 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; - constexpr id_t NULL_ID = id_t{0}; + + 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>; + + constexpr struct + { + template<typename T> + constexpr operator Id<T>() const + { + return Id<T>{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; + } } constexpr std::size_t operator""_z(const unsigned long long int x)
--- a/src/model.cpp Fri Mar 06 23:45:44 2020 +0200 +++ b/src/model.cpp Mon Mar 09 14:21:54 2020 +0200 @@ -88,7 +88,7 @@ return this->cachedPolygons; } -QModelIndex Model::lookup(ldraw::Id id) const +QModelIndex Model::lookup(ldraw::id_t id) const { // FIXME: This linear search will probably cause performance issues for (std::size_t i = 0; i < this->body.size(); i += 1) @@ -101,7 +101,7 @@ return {}; } -ldraw::Id Model::resolve(const QModelIndex& index) const +ldraw::id_t Model::resolve(const QModelIndex& index) const { return this->objectAt(index)->id; }
--- a/src/model.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/model.h Mon Mar 09 14:21:54 2020 +0200 @@ -44,19 +44,33 @@ const QString& getName() const; QVariant getObjectProperty(const int index, const ldraw::Property property) const; std::vector<gl::Polygon> getPolygons(class DocumentManager* documents) const; - QModelIndex lookup(ldraw::Id id) const; - ldraw::Id resolve(const QModelIndex& index) const; + QModelIndex lookup(ldraw::id_t id) const; + ldraw::id_t resolve(const QModelIndex& index) const; + template<typename R> + ldraw::Id<R> checkType(ldraw::id_t id) const; + template<typename R> + const R* get(ldraw::Id<R> id) const; signals: - void objectAdded(ldraw::Id id, int position); + void objectAdded(ldraw::id_t id, int position); private: using ModelObjectPointer = std::unique_ptr<ldraw::Object>; template<typename T, typename... Args> - ldraw::Id append(Args&&... args); + ldraw::Id<T> append(Args&&... args); void append(ModelObjectPointer&& object); template<typename T, typename... Args> - ldraw::Id insert(int position, Args&&... args); + ldraw::Id<T> insert(int position, Args&&... args); ldraw::Object* objectAt(const QModelIndex& index); const ldraw::Object* objectAt(const QModelIndex& index) const; + template<typename T> + T* objectAt(ldraw::Id<T> id) + { + return static_cast<T*>(this->objectAt(this->lookup(id))); + } + template<typename T> + const T* objectAt(ldraw::Id<T> id) const + { + return static_cast<const T*>(this->objectAt(this->lookup(id))); + } void getObjectPolygons( const int index, std::vector<gl::Polygon>& polygons_out, @@ -65,13 +79,31 @@ QString path; LDHeader header; std::vector<ModelObjectPointer> body; - std::map<ldraw::Id, ldraw::Object*> objectsById; + std::map<ldraw::id_t, ldraw::Object*> objectsById; mutable std::vector<gl::Polygon> cachedPolygons; mutable bool needRecache = true; }; +/** + * \brief Checks type of object behind id + * Checks whether the specified id refers to an object of the specified type. + * \returns id casted to subclass if appropriate, null id otherwise + */ +template<typename R> +ldraw::Id<R> Model::checkType(ldraw::id_t id) const +{ + if (dynamic_cast<const R*>(this->objectAt(this->lookup(id))) != nullptr) + { + return ldraw::Id<R>{id.value}; + } + else + { + return ldraw::NULL_ID; + } +} + template<typename T, typename... Args> -ldraw::Id Model::append(Args&&... args) +ldraw::Id<T> Model::append(Args&&... args) { emit layoutAboutToBeChanged(); this->body.push_back(std::make_unique<T>(args...)); @@ -79,11 +111,11 @@ this->objectsById[pointer->id] = pointer; emit objectAdded(pointer->id, this->body.size() - 1); emit layoutChanged(); - return pointer->id; + return ldraw::Id<T>{pointer->id}; } template<typename T, typename... Args> -ldraw::Id Model::insert(int position, Args&&... args) +ldraw::Id<T> Model::insert(int position, Args&&... args) { emit layoutAboutToBeChanged(); this->body.insert(position, std::make_unique<T>(args...)); @@ -91,5 +123,11 @@ this->objectsById[pointer->id] = pointer; emit objectAdded(pointer->id, position); emit layoutChanged(); - return pointer->id; + return ldraw::Id<T>{pointer->id}; } + +template<typename R> +const R* Model::get(ldraw::Id<R> id) const +{ + return this->objectAt(id); +}
--- a/src/modeleditcontext.cpp Fri Mar 06 23:45:44 2020 +0200 +++ b/src/modeleditcontext.cpp Mon Mar 09 14:21:54 2020 +0200 @@ -19,15 +19,15 @@ #include "modeleditcontext.h" Model::EditContext::EditContext(Model& model) : - model{model} + storedModel{model} { } -ldraw::Id Model::EditContext::append(std::unique_ptr<ldraw::Object>&& object) +ldraw::id_t Model::EditContext::append(std::unique_ptr<ldraw::Object>&& object) { - const ldraw::Id id = object->id; - this->model.objectsById[id] = object.get(); - this->model.append(std::move(object)); + const ldraw::id_t id = object->id; + this->model().objectsById[id] = object.get(); + this->model().append(std::move(object)); return id; } @@ -39,12 +39,32 @@ object->setProperty(property, value); } -void Model::EditContext::invertObject(ldraw::Id id) +void Model::EditContext::invertObject(ldraw::id_t id) { - auto it = this->model.objectsById.find(id); - if (it != this->model.objectsById.end()) + auto it = this->model().objectsById.find(id); + if (it != this->model().objectsById.end()) { ldraw::Object* object = it->second; object->invert(); } } + +Model& Model::EditContext::model() +{ + return this->storedModel; +} + +auto ldraw::splitQuadrilateral( + Model::EditContext& editor, + ldraw::quadrilateralid_t quadrilateral_id, + ldraw::QuadrilateralSplit splitType +) -> std::optional<std::pair<ldraw::triangleid_t, ldraw::triangleid_t>> +{ + std::optional<std::pair<ldraw::triangleid_t, ldraw::triangleid_t>> result; + const ldraw::Quadrilateral* quadrilateral = editor.model().get(quadrilateral_id); + if (quadrilateral != nullptr) + { + + } + return result; +}
--- a/src/modeleditcontext.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/modeleditcontext.h Mon Mar 09 14:21:54 2020 +0200 @@ -19,34 +19,54 @@ #pragma once #include "model.h" #include "linetypes/object.h" +#include "linetypes/quadrilateral.h" +#include "linetypes/triangle.h" class Model::EditContext { public: template<typename T, typename... Args> - ldraw::Id append(Args&&... args); - ldraw::Id append(std::unique_ptr<ldraw::Object>&& object); + ldraw::id_t append(Args&&... args); + ldraw::id_t append(std::unique_ptr<ldraw::Object>&& object); template<typename T, typename... Args> - ldraw::Id insert(int position, Args&&... args); + ldraw::id_t insert(int position, Args&&... args); void setObjectProperty( ldraw::Object* object, ldraw::Property property, const QVariant &value); - void invertObject(ldraw::Id id); + void invertObject(ldraw::id_t id); + Model& model(); private: EditContext(Model& model); friend class Model; - Model& model; + Model& storedModel; }; template<typename T, typename... Args> -ldraw::Id Model::EditContext::append(Args&&... args) +ldraw::id_t Model::EditContext::append(Args&&... args) { - return this->model.append<T>(args...); + return this->storedModel.append<T>(args...); } template<typename T, typename... Args> -ldraw::Id Model::EditContext::insert(int position, Args&&... args) +ldraw::id_t Model::EditContext::insert(int position, Args&&... args) +{ + return this->storedModel.insert<T>(position, args...); +} + +namespace ldraw { - return this->model.insert<T>(position, args...); + /// Determines how quadrilaterals are split into triangles + enum class QuadrilateralSplit + { + Split123_134, + Split124_234 + }; + + // Splits the specified quadrilateral into triangles. + // If it is not a quadrilateral then no action is performed + auto splitQuadrilateral(Model::EditContext& editor, + quadrilateralid_t quadrilateral_id, + QuadrilateralSplit splitType = QuadrilateralSplit::Split123_134 + ) -> std::optional<std::pair<triangleid_t, triangleid_t>>; }
--- a/src/ui/canvas.cpp Fri Mar 06 23:45:44 2020 +0200 +++ b/src/ui/canvas.cpp Mon Mar 09 14:21:54 2020 +0200 @@ -12,7 +12,7 @@ this->setMouseTracking(true); } -void Canvas::handleSelectionChange(const QSet<ldraw::Id>& selectedIds, const QSet<ldraw::Id>& deselectedIds) +void Canvas::handleSelectionChange(const QSet<ldraw::id_t>& selectedIds, const QSet<ldraw::id_t>& deselectedIds) { Q_ASSERT(not selectedIds.contains(ldraw::NULL_ID)); this->selection.subtract(deselectedIds); @@ -23,7 +23,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) { - const ldraw::Id id = this->pick(event->pos()); + const ldraw::id_t id = this->pick(event->pos()); this->highlighted = id; this->totalMouseMove += (event->pos() - this->lastMousePosition).manhattanLength(); this->worldPosition = this->screenToModelCoordinates(event->pos(), this->gridPlane);
--- a/src/ui/canvas.h Fri Mar 06 23:45:44 2020 +0200 +++ b/src/ui/canvas.h Mon Mar 09 14:21:54 2020 +0200 @@ -13,7 +13,7 @@ const ldraw::ColorTable& colorTable, QWidget* parent = nullptr); public slots: - void handleSelectionChange(const QSet<ldraw::Id>& selectedIds, const QSet<ldraw::Id>& deselectedIds); + void handleSelectionChange(const QSet<ldraw::id_t>& selectedIds, const QSet<ldraw::id_t>& deselectedIds); protected: void mouseMoveEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override; @@ -22,7 +22,7 @@ void paintGL() override; signals: void newStatusText(const QString& newStatusText); - void selectionChanged(const QSet<ldraw::Id>& newSelection); + void selectionChanged(const QSet<ldraw::id_t>& newSelection); private: void updateGridMatrix(); glm::vec3 cameraVector() const; @@ -33,5 +33,5 @@ glm::mat4 gridMatrix; geom::Plane gridPlane; int totalMouseMove = 0; - QSet<ldraw::Id> selection; + QSet<ldraw::id_t> selection; };