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

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 "animatable_path.hpp"
0008 
0009 #include "command/undo_macro_guard.hpp"
0010 #include "command/animation_commands.hpp"
0011 
0012 using namespace glaxnimate;
0013 
0014 void glaxnimate::model::detail::AnimatedPropertyBezier::set_closed(bool closed)
0015 {
0016     value_.set_closed(closed);
0017     for ( auto& keyframe : keyframes_ )
0018     {
0019         auto v = keyframe->get();
0020         v.set_closed(closed);
0021         keyframe->set(v);
0022     }
0023     value_changed();
0024     emitter(object(), value_);
0025 }
0026 
0027 
0028 void glaxnimate::model::detail::AnimatedPropertyBezier::split_segment(int index, qreal factor)
0029 {
0030     command::UndoMacroGuard guard(i18n("Split Segment"), object()->document());
0031 
0032     QVariant before = QVariant::fromValue(value_);
0033     auto bez = value_;
0034 
0035     bool set = true;
0036     for ( const auto& kf : keyframes_ )
0037     {
0038         auto bez = kf->get();
0039         bez.split_segment(index, factor);
0040         if ( !mismatched_ && kf->time() == time() )
0041             set = false;
0042         object()->push_command(new command::SetKeyframe(this, kf->time(), QVariant::fromValue(bez), true));
0043     }
0044 
0045     if ( set )
0046     {
0047         bez.split_segment(index, factor);
0048         QVariant after = QVariant::fromValue(bez);
0049         object()->push_command(new command::SetMultipleAnimated("", {this}, {before}, {after}, true));
0050     }
0051 }
0052 
0053 void glaxnimate::model::detail::AnimatedPropertyBezier::remove_point(int index)
0054 {
0055     remove_points({index});
0056 }
0057 
0058 void glaxnimate::model::detail::AnimatedPropertyBezier::remove_points(const std::set<int>& indices)
0059 {
0060     command::UndoMacroGuard guard(i18n("Remove Nodes"), object()->document());
0061 
0062     QVariant before = QVariant::fromValue(value_);
0063     auto bez = value_;
0064 
0065     bool set = true;
0066     for ( const auto& kf : keyframes_ )
0067     {
0068         auto bez = kf->get().removed_points(indices);
0069         if ( !mismatched_ && kf->time() == time() )
0070             set = false;
0071         object()->push_command(new command::SetKeyframe(this, kf->time(), QVariant::fromValue(bez), true));
0072     }
0073 
0074     if ( set )
0075     {
0076         bez = bez.removed_points(indices);
0077         object()->push_command(new command::SetMultipleAnimated(this, QVariant::fromValue(bez), true));
0078     }
0079 }
0080 
0081 static QVariant extend_impl(math::bezier::Bezier subject, const math::bezier::Bezier& target, bool at_end)
0082 {
0083     if ( target.closed() )
0084     {
0085         subject.set_closed(true);
0086 
0087         if ( !subject.empty() )
0088         {
0089             if ( at_end )
0090                 subject[0].type = math::bezier::Corner;
0091             else
0092                 subject.back().type = math::bezier::Corner;
0093 
0094             if ( !target.empty() )
0095             {
0096                 subject[0].tan_in = target[0].tan_in;
0097                 subject.back().tan_out = target.back().tan_out;
0098             }
0099         }
0100     }
0101 
0102     if ( subject.size() < target.size() )
0103     {
0104         if ( at_end )
0105         {
0106             if ( !subject.empty() )
0107             {
0108                 subject.back().type = math::bezier::Corner;
0109                 subject.back().tan_out = target.back().tan_out;
0110             }
0111 
0112             subject.points().insert(
0113                 subject.points().end(),
0114                 target.points().begin() + subject.size(),
0115                 target.points().end()
0116             );
0117         }
0118         else
0119         {
0120             if ( !subject.empty() )
0121             {
0122                 subject[0].type = math::bezier::Corner;
0123                 subject[0].tan_in = target[0].tan_in;
0124             }
0125 
0126             subject.points().insert(
0127                 subject.points().begin(),
0128                 target.points().begin(),
0129                 target.points().begin() + target.size() - subject.size()
0130             );
0131         }
0132     }
0133 
0134     return QVariant::fromValue(subject);
0135 }
0136 
0137 void glaxnimate::model::detail::AnimatedPropertyBezier::extend(const math::bezier::Bezier& target, bool at_end)
0138 {
0139     command::UndoMacroGuard guard(i18n("Extend Shape"), object()->document());
0140 
0141     auto bez = value_;
0142 
0143     bool set = true;
0144 
0145     for ( auto& kf : keyframes_ )
0146     {
0147         if ( !mismatched_ && kf->time() == time() )
0148             set = false;
0149         object()->push_command(
0150             new command::SetKeyframe(this, kf->time(), extend_impl(kf->get(), target, at_end), true)
0151         );
0152     }
0153 
0154     if ( set )
0155     {
0156         QVariant before = QVariant::fromValue(bez);
0157         QVariant after = extend_impl(bez, target, at_end);
0158         object()->push_command(new command::SetMultipleAnimated("", {this}, {before}, {after}, true));
0159     }
0160 }