File indexing completed on 2025-02-02 04:11:08
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include "model/document_node.hpp" 0010 #include "math/bezier/bezier.hpp" 0011 #include "model/property/object_list_property.hpp" 0012 0013 namespace glaxnimate::model { 0014 0015 using ShapeListProperty = ObjectListProperty<class ShapeElement>; 0016 class Composition; 0017 0018 template<class T> 0019 class PathCache 0020 { 0021 public: 0022 bool is_dirty(FrameTime time) const 0023 { 0024 return time != cached_time || dirty; 0025 } 0026 0027 void mark_dirty() { dirty = true; } 0028 0029 const T& path() const { return cached_path; } 0030 0031 void set_path(FrameTime time, const T& path) 0032 { 0033 cached_time = time; 0034 dirty = false; 0035 cached_path = path; 0036 } 0037 0038 private: 0039 bool dirty = true; 0040 T cached_path = {}; 0041 FrameTime cached_time = 0; 0042 }; 0043 0044 /** 0045 * \brief Base class for all shape elements 0046 */ 0047 class ShapeElement : public VisualNode 0048 { 0049 Q_OBJECT 0050 0051 public: 0052 explicit ShapeElement(model::Document* document); 0053 ~ShapeElement(); 0054 0055 int docnode_child_count() const override { return 0; } 0056 DocumentNode* docnode_child(int) const override { return nullptr; } 0057 int docnode_child_index(DocumentNode*) const override { return -1; } 0058 0059 /** 0060 * \brief Index within its parent 0061 */ 0062 int position() const; 0063 0064 virtual void add_shapes(FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const = 0; 0065 math::bezier::MultiBezier shapes(FrameTime t) const; 0066 0067 ShapeListProperty* owner() const; 0068 Composition* owner_composition() const; 0069 void clear_owner(); 0070 0071 virtual QPainterPath to_clip(FrameTime t) const; 0072 QPainterPath to_painter_path(FrameTime t) const; 0073 virtual std::unique_ptr<ShapeElement> to_path() const; 0074 0075 Q_SIGNALS: 0076 void position_updated(); 0077 void siblings_changed(); 0078 0079 protected: 0080 const ShapeListProperty& siblings() const; 0081 void on_property_changed(const BaseProperty* prop, const QVariant& value) override; 0082 void on_parent_changed(model::DocumentNode* old_parent, model::DocumentNode* new_parent) override; 0083 void refresh_owner_composition(glaxnimate::model::Composition* comp); 0084 virtual void on_composition_changed(model::Composition* old_comp, model::Composition* new_comp) 0085 { 0086 Q_UNUSED(old_comp); 0087 Q_UNUSED(new_comp); 0088 } 0089 0090 virtual QPainterPath to_painter_path_impl(FrameTime t) const = 0; 0091 void on_graphics_changed() override; 0092 0093 private: 0094 void set_position(ShapeListProperty* property, int pos); 0095 0096 class Private; 0097 std::unique_ptr<Private> d; 0098 friend ShapeListProperty; 0099 friend class Group; 0100 }; 0101 0102 template<> 0103 class ObjectListProperty<ShapeElement> : public detail::ObjectListProperty<ShapeElement> 0104 { 0105 public: 0106 using detail::ObjectListProperty<ShapeElement>::ObjectListProperty; 0107 0108 /** 0109 * \brief End iterator for a range that includes a modifier then stops 0110 */ 0111 iterator past_first_modifier() const; 0112 0113 QRectF bounding_rect(FrameTime t) const; 0114 0115 protected: 0116 void update_pos(int index) 0117 { 0118 int i; 0119 for ( i = size() - 1; i >= index; i-- ) 0120 objects[i]->set_position(this, i); 0121 for ( ; i >= 0; i-- ) 0122 objects[i]->siblings_changed(); 0123 } 0124 0125 void on_insert(int index) override 0126 { 0127 update_pos(index); 0128 } 0129 0130 void on_remove(int index) override 0131 { 0132 update_pos(index); 0133 } 0134 0135 void on_move(int index_a, int index_b) override 0136 { 0137 if ( index_b < index_a ) 0138 std::swap(index_a, index_b); 0139 0140 for ( int i = index_a; i <= index_b; i++ ) 0141 objects[i]->set_position(this, i); 0142 0143 for ( int i = 0; i <= index_b; i++ ) 0144 objects[i]->siblings_changed(); 0145 } 0146 }; 0147 0148 class Path; 0149 0150 /** 0151 * \brief Classes that define shapes on their own (but not necessarily style) 0152 */ 0153 class Shape : public ShapeElement 0154 { 0155 Q_OBJECT 0156 0157 GLAXNIMATE_PROPERTY(bool, reversed, false, {}, {}, PropertyTraits::Visual|PropertyTraits::Hidden) 0158 0159 public: 0160 using ShapeElement::ShapeElement; 0161 0162 virtual math::bezier::Bezier to_bezier(FrameTime t) const = 0; 0163 0164 void add_shapes(FrameTime t, math::bezier::MultiBezier & bez, const QTransform& transform) const override; 0165 0166 std::unique_ptr<ShapeElement> to_path() const override; 0167 0168 protected: 0169 QPainterPath to_painter_path_impl(FrameTime t) const override; 0170 }; 0171 0172 /** 0173 * \brief Base class for types that perform operations on their sibling shapes 0174 */ 0175 class ShapeOperator : public ShapeElement 0176 { 0177 Q_OBJECT 0178 0179 public: 0180 ShapeOperator(model::Document* doc); 0181 0182 math::bezier::MultiBezier collect_shapes(FrameTime t, const QTransform& transform) const; 0183 math::bezier::MultiBezier collect_shapes_from(const std::vector<ShapeElement*>& shapes, FrameTime t, const QTransform& transform) const; 0184 0185 const std::vector<ShapeElement*>& affected() const { return affected_elements; } 0186 0187 protected: 0188 virtual void do_collect_shapes(const std::vector<ShapeElement*>& shapes, FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const; 0189 virtual bool skip_stylers() const { return true; } 0190 void on_graphics_changed() override; 0191 0192 private Q_SLOTS: 0193 void update_affected(); 0194 0195 Q_SIGNALS: 0196 void shape_changed(); 0197 0198 private: 0199 std::vector<ShapeElement*> affected_elements; 0200 mutable PathCache<math::bezier::MultiBezier> bezier_cache; 0201 }; 0202 0203 /** 0204 * \brief Base class for elements that modify other shapes 0205 */ 0206 class Modifier : public ShapeOperator 0207 { 0208 Q_OBJECT 0209 0210 public: 0211 using ShapeOperator::ShapeOperator; 0212 0213 void add_shapes(FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const override; 0214 0215 0216 QRectF local_bounding_rect(FrameTime t) const override; 0217 0218 virtual math::bezier::MultiBezier process(FrameTime t, const math::bezier::MultiBezier& mbez) const = 0; 0219 0220 protected: 0221 QPainterPath to_painter_path_impl(FrameTime t) const override; 0222 0223 /** 0224 * \brief Whether to process on the whole thing (or individual objects) 0225 */ 0226 virtual bool process_collected() const = 0; 0227 0228 void do_collect_shapes(const std::vector<ShapeElement*>& shapes, FrameTime t, math::bezier::MultiBezier& bez, const QTransform& transform) const override; 0229 0230 virtual bool skip_stylers() const override { return false; } 0231 0232 }; 0233 0234 /** 0235 * \brief CRTP to override some methods using static functions 0236 * (so said methods can be accessed with no object) 0237 */ 0238 template<class Derived, class Base> 0239 class StaticOverrides : public Base 0240 { 0241 public: 0242 using Ctor = StaticOverrides; 0243 using Base::Base; 0244 0245 QIcon tree_icon() const override 0246 { 0247 return Derived::static_tree_icon(); 0248 } 0249 0250 QString type_name_human() const override 0251 { 0252 return Derived::static_type_name_human(); 0253 } 0254 0255 static QString static_class_name() 0256 { 0257 return detail::naked_type_name<Derived>(); 0258 } 0259 }; 0260 0261 0262 } // namespace glaxnimate::model