File indexing completed on 2025-02-02 04:11:06

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "layer.hpp"
0008 
0009 #include <QPainter>
0010 
0011 #include "model/assets/composition.hpp"
0012 #include "model/document.hpp"
0013 
0014 GLAXNIMATE_OBJECT_IMPL(glaxnimate::model::Layer)
0015 
0016 void glaxnimate::model::Layer::ChildLayerIterator::find_first()
0017 {
0018     while ( index < comp->size() && (*comp)[index]->docnode_group_parent() != parent )
0019         ++index;
0020 }
0021 
0022 glaxnimate::model::VisualNode* glaxnimate::model::Layer::ChildLayerIterator::operator*() const
0023 {
0024     return (*comp)[index];
0025 }
0026 
0027 glaxnimate::model::VisualNode* glaxnimate::model::Layer::ChildLayerIterator::operator->() const
0028 {
0029     return (*comp)[index];
0030 }
0031 
0032 glaxnimate::model::VisualNode * glaxnimate::model::Layer::docnode_group_parent() const
0033 {
0034     return parent.get();
0035 }
0036 
0037 glaxnimate::model::VisualNode * glaxnimate::model::Layer::docnode_group_child(int index) const
0038 {
0039     ChildLayerIterator iter(owner(), this, 0);
0040     std::advance(iter, index);
0041     return *iter;
0042 }
0043 
0044 int glaxnimate::model::Layer::docnode_group_child_count() const
0045 {
0046     if ( !owner() )
0047         return 0;
0048     int sz = 0;
0049     for ( const auto& sib : *owner() )
0050         if ( sib->docnode_group_parent() == this )
0051             sz++;
0052     return sz;
0053 }
0054 
0055 std::vector<glaxnimate::model::DocumentNode*> glaxnimate::model::Layer::valid_parents() const
0056 {
0057     std::vector<glaxnimate::model::DocumentNode*> refs;
0058     refs.push_back(nullptr);
0059 
0060     if ( is_top_level() )
0061     {
0062         for ( const auto& sh : *owner() )
0063         {
0064             if ( auto lay = qobject_cast<glaxnimate::model::Layer*>(sh.get()) )
0065                 if ( !is_ancestor_of(lay) )
0066                     refs.push_back(lay);
0067         }
0068     }
0069 
0070     return refs;
0071 }
0072 
0073 bool glaxnimate::model::Layer::is_valid_parent(glaxnimate::model::DocumentNode* node) const
0074 {
0075     if ( node == nullptr )
0076         return true;
0077 
0078     if ( is_top_level() )
0079     {
0080         if ( Layer* layer = qobject_cast<Layer*>(node) )
0081             return !is_ancestor_of(layer);
0082     }
0083 
0084     return false;
0085 }
0086 
0087 bool glaxnimate::model::Layer::is_ancestor_of ( const glaxnimate::model::Layer* other ) const
0088 {
0089     while ( other )
0090     {
0091         if ( other == this )
0092             return true;
0093 
0094         other = other->parent.get();
0095     }
0096 
0097     return false;
0098 }
0099 
0100 void glaxnimate::model::Layer::set_time(glaxnimate::model::FrameTime t)
0101 {
0102     Object::set_time(relative_time(t));
0103 }
0104 
0105 
0106 bool glaxnimate::model::Layer::is_top_level() const
0107 {
0108     return qobject_cast<Composition*>(docnode_parent());
0109 }
0110 
0111 void glaxnimate::model::Layer::paint(QPainter* painter, FrameTime time, PaintMode mode, glaxnimate::model::Modifier* modifier) const
0112 {
0113     if ( !visible.get() || (mode == Render && !render.get()) )
0114         return;
0115 
0116     time = relative_time(time);
0117     if ( !animation->time_visible(time) )
0118         return;
0119 
0120     if ( mask->has_mask() )
0121     {
0122         auto n_shapes = shapes.size();
0123         if ( n_shapes <= 1 )
0124             return;
0125 
0126         painter->save();
0127         auto transform = group_transform_matrix(time);
0128         painter->setTransform(transform, true);
0129 
0130         if ( shapes[0]->visible.get() )
0131         {
0132             QPainterPath clip = shapes[0]->to_clip(time);
0133             clip.setFillRule(Qt::WindingFill);
0134             if ( mask->inverted.get() )
0135             {
0136                 QPainterPath outer_clip;
0137                 outer_clip.addPolygon(
0138                     transform.inverted().map(owner_composition()->rect())
0139                 );
0140                 clip = outer_clip.subtracted(clip);
0141             }
0142             painter->setClipPath(clip, Qt::IntersectClip);
0143         }
0144 
0145 
0146         on_paint(painter, time, mode, modifier);
0147 
0148         for ( int i = 1; i < n_shapes; i++ )
0149             docnode_visual_child(i)->paint(painter, time, mode);
0150 
0151         painter->restore();
0152     }
0153     else
0154     {
0155         VisualNode::paint(painter, time, mode);
0156     }
0157 }
0158 
0159 QPainterPath glaxnimate::model::Layer::to_clip(glaxnimate::model::FrameTime time) const
0160 {
0161     time = relative_time(time);
0162     if ( !animation->time_visible(time) || !render.get() )
0163         return {};
0164 
0165     return Group::to_clip(time);
0166 }
0167 
0168 QPainterPath glaxnimate::model::Layer::to_painter_path_impl(glaxnimate::model::FrameTime time) const
0169 {
0170     time = relative_time(time);
0171     if ( !animation->time_visible(time) || !render.get() )
0172         return {};
0173 
0174     return Group::to_painter_path_impl(time);
0175 }
0176 
0177 QIcon glaxnimate::model::Layer::tree_icon() const
0178 {
0179     return mask->has_mask() ? QIcon::fromTheme("path-clip-edit") : QIcon::fromTheme("folder");
0180 }
0181 
0182 QIcon glaxnimate::model::Layer::static_tree_icon()
0183 {
0184     return QIcon::fromTheme("folder");
0185 }
0186 
0187 std::unique_ptr<glaxnimate::model::ShapeElement> glaxnimate::model::Layer::to_path() const
0188 {
0189     auto clone = std::make_unique<glaxnimate::model::Layer>(document());
0190 
0191     for ( BaseProperty* prop : properties() )
0192     {
0193         if ( prop != &shapes )
0194             clone->get_property(prop->name())->assign_from(prop);
0195     }
0196 
0197     for ( const auto& shape : shapes )
0198     {
0199         clone->shapes.insert(shape->to_path());
0200         if ( shape->is_instance<glaxnimate::model::Modifier>() )
0201             break;
0202     }
0203 
0204     return clone;
0205 }