Tue, 28 Jun 2022 22:18:11 +0300
minor updates to main window ui
/* * 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 <QAbstractListModel> #include <memory> #include "src/basics.h" #include "src/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; }; struct ParseError { QString code; }; struct Empty {}; struct CircularFraction { unsigned int segments; unsigned int divisions; }; constexpr bool operator<(const CircularFraction& p, const CircularFraction& q) { // a/b < c/d // a < c * b / d // a * d < c * b return p.segments * q.divisions < q.segments / p.divisions; } struct CircularPrimitive { enum Type { Circle, Disc, Cylinder, CylinderOpen, CylinderClosed, DiscNegative, Chord, } type; static constexpr int NUM_TYPES = Chord + 1; CircularFraction fraction; glm::mat4 transformation; bool inverted = false; }; constexpr char circularPrimitiveStems[CircularPrimitive::NUM_TYPES][5] = { "edge", "disc", "cyli", "cylo", "cylc", "ndis", "chrd", }; Q_DECLARE_METATYPE(CircularPrimitive::Type) struct CircleToolOptions { CircularFraction fraction; CircularPrimitive::Type type; }; using ModelElement = std::variant< Colored<SubfileReference>, Colored<LineSegment>, Colored<Triangle>, Colored<Quadrilateral>, Colored<ConditionalEdge>, Colored<CircularPrimitive>, Comment, Empty, ParseError>; using PolygonElement = Colored<std::variant< LineSegment, Triangle, Quadrilateral, ConditionalEdge>>; template<typename T> struct remove_color {}; template<typename T> struct remove_color<Colored<T>> { using type = T; }; template<typename T> struct remove_color<Colored<T>&> { using type = T&; }; template<typename T> struct remove_color<const Colored<T>&> { using type = const T&; }; template<typename T> struct remove_color<Colored<T>&&> { using type = T&&; }; template<typename T> using remove_color_t = typename remove_color<T>::type; static_assert(std::is_same_v<remove_color_t<Colored<Triangle>>, Triangle>); static_assert(std::is_same_v<remove_color_t<Colored<Triangle>&>, Triangle&>); static_assert(std::is_same_v<remove_color_t<const Colored<Triangle>&>, const Triangle&>); template<typename T> constexpr remove_color_t<T&&> extract_colored(T&& x) { return static_cast<remove_color_t<T&&>>(x); } template<typename Ret, typename Fn1, typename Fn2, typename Fn3, typename Fn4, typename T> constexpr auto visitPolygon(Fn1&& f1, Fn2&& f2, Fn3&& f3, Fn4&& f4, T&& element) { if (std::holds_alternative<LineSegment>(element)) { return f1(std::get<LineSegment>(element)); } else if (std::holds_alternative<Triangle>(element)) { return f2(std::get<Triangle>(element)); } else if (std::holds_alternative<Quadrilateral>(element)) { return f3(std::get<Quadrilateral>(element)); } else { return f4(std::get<ConditionalEdge>(element)); } } template<typename T, typename Fn> constexpr void visitPoints(Fn&& func, T&& element) { visitPolygon<void>( [&func](transfer_cvref_t<T&&, LineSegment> edge) { func(edge.p1); func(edge.p2); }, [&func](transfer_cvref_t<T&&, Triangle>& tri) { func(tri.p1); func(tri.p2); func(tri.p3); }, [&func](transfer_cvref_t<T&&, Quadrilateral>& quad) { func(quad.p1); func(quad.p2); func(quad.p3); func(quad.p4); }, [&func](transfer_cvref_t<T&&, ConditionalEdge>& cedge) { func(cedge.p1); func(cedge.p2); func(cedge.c1); func(cedge.c2); }, element); } QString modelElementToString(const ModelElement& element); struct ModelId { std::int32_t value; constexpr auto operator<=>(const ModelId& other) const = default; }; constexpr auto 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, std::size_t> positions; ModelId runningId = {1}; public: explicit Model(QObject* parent); virtual ~Model(); ModelId append(const ModelElement& value); const ModelElement& at(std::size_t position) const; ModelId idAt(std::size_t position) const; void assignAt(std::size_t position, const ModelElement& element); std::optional<std::size_t> find(ModelId id) const; void remove(std::size_t index); int rowCount(const QModelIndex&) const override; QVariant data(const QModelIndex& index, int role) const override; const ModelElement& operator[](std::size_t index) const; std::size_t size() const; auto operator[](const std::size_t index) { struct { Model& model; const std::size_t 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); void updateHeaderNameField(Model& model, const QString &name); template<typename T> void iterate(const Model& model, std::function<void(const T&)> fn) { for (std::size_t i = 0; i < model.size(); ++i) { if (std::holds_alternative<T>(model[i])) { fn(std::get<T>(model[i])); } } } constexpr Colored<LineSegment> edge(const glm::vec3& p1, const glm::vec3& p2) { return Colored<LineSegment>{{.p1 = p1, .p2 = p2}, EDGE_COLOR}; } constexpr Colored<Triangle> triangle(const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3) { return Colored<Triangle>{{.p1 = p1, .p2 = p2, .p3 = p3}, MAIN_COLOR}; } constexpr Colored<Quadrilateral> quadrilateral( const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3, const glm::vec3& p4) { return Colored<Quadrilateral>{{.p1 = p1, .p2 = p2, .p3 = p3, .p4 = p4}, MAIN_COLOR}; } struct AppendToModel { ModelElement newElement; }; struct DeleteFromModel { std::size_t position; }; struct ModifyModel { std::size_t position; ModelElement newElement; }; using ModelAction = std::variant< AppendToModel, DeleteFromModel, ModifyModel>;