File indexing completed on 2025-01-05 04:01:21

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 <variant>
0010 #include "math/bezier/bezier.hpp"
0011 #include "model/animation/keyframe_transition.hpp"
0012 #include "model/animation/frame_time.hpp"
0013 #include "model/animation/join_animatables.hpp"
0014 
0015 
0016 namespace glaxnimate::io::detail {
0017 
0018 struct ValueVariant
0019 {
0020 public:
0021     enum Type
0022     {
0023         Vector, Bezier, String, Color
0024     };
0025 
0026     ValueVariant(qreal v)
0027         : value_(std::vector<qreal>{v})
0028     {}
0029 
0030     ValueVariant(std::vector<qreal> v = {})
0031         : value_(std::move(v))
0032     {}
0033 
0034     ValueVariant(math::bezier::MultiBezier v )
0035         : value_(std::move(v))
0036     {}
0037 
0038     ValueVariant(QString v)
0039         : value_(std::move(v))
0040     {}
0041 
0042     ValueVariant(QColor v)
0043         : value_(std::move(v))
0044     {}
0045 
0046     ValueVariant(const QVariant& v)
0047     {
0048         if ( v.userType() == QMetaType::QColor )
0049             value_ = v.value<QColor>();
0050         else if ( v.userType() == QMetaType::QString )
0051             value_ = v.value<QString>();
0052         else if ( v.canConvert<qreal>() )
0053             value_ = std::vector<qreal>(1, v.toReal());
0054     }
0055 
0056     Type type() const { return Type(value_.index()); }
0057 
0058     qreal scalar() const
0059     {
0060         return vector()[0];
0061     }
0062 
0063     const std::vector<qreal>& vector() const
0064     {
0065         return std::get<int(Type::Vector)>(value_);
0066     }
0067 
0068     const math::bezier::MultiBezier& bezier() const
0069     {
0070         return std::get<int(Type::Bezier)>(value_);
0071     }
0072 
0073     const QString& string() const
0074     {
0075         return std::get<int(Type::String)>(value_);
0076     }
0077 
0078     const QColor& color() const
0079     {
0080         return std::get<int(Type::Color)>(value_);
0081     }
0082 
0083     ValueVariant lerp(const ValueVariant& other, qreal t) const
0084     {
0085         if ( type() != other.type() )
0086             return *this;
0087 
0088         switch ( type() )
0089         {
0090             case Type::Vector:
0091                 return math::lerp(vector(), other.vector(), t);
0092             case Type::Bezier:
0093                 if ( bezier().size() == 1 && other.bezier().size() == 1 )
0094                 {
0095                     math::bezier::MultiBezier mb;
0096                     mb.beziers().push_back(bezier()[0].lerp(other.bezier()[0], t));
0097                     return mb;
0098                 }
0099                 return *this;
0100             case Type::String:
0101                 return t < 1 ? string() : other.string();
0102             case Type::Color:
0103                 return math::lerp(color(), other.color(), t);
0104 
0105         }
0106 
0107         return {};
0108     }
0109 
0110     bool compatible(const ValueVariant& other) const
0111     {
0112         if ( type() != other.type() )
0113             return false;
0114 
0115         if ( type() == Vector )
0116             return vector().size() == other.vector().size();
0117 
0118         return true;
0119     }
0120 
0121 private:
0122     std::variant<std::vector<qreal>, math::bezier::MultiBezier, QString, QColor> value_;
0123 };
0124 
0125 struct PropertyKeyframe
0126 {
0127     model::FrameTime time;
0128     ValueVariant values;
0129     model::KeyframeTransition transition;
0130 
0131     bool operator< (const PropertyKeyframe& o) const
0132     {
0133         return time < o.time;
0134     }
0135 };
0136 
0137 struct JoinedPropertyKeyframe
0138 {
0139     model::FrameTime time;
0140     std::vector<ValueVariant> values;
0141     model::KeyframeTransition transition;
0142 };
0143 
0144 struct AnimatedProperty
0145 {
0146     std::vector<PropertyKeyframe> keyframes;
0147     math::bezier::Bezier motion;
0148     bool auto_orient = false;
0149 
0150     void sort()
0151     {
0152         std::sort(keyframes.begin(), keyframes.end());
0153     }
0154 };
0155 
0156 struct JoinedProperty
0157 {
0158     std::variant<const AnimatedProperty*, const QString*, ValueVariant> prop;
0159     int index = 0;
0160 
0161     bool at_end() const
0162     {
0163         if ( prop.index() == 0 )
0164             return index + 1 == int(std::get<0>(prop)->keyframes.size());
0165         return true;
0166     }
0167 
0168     const PropertyKeyframe* keyframe(int off = 0) const
0169     {
0170         return &std::get<0>(prop)->keyframes[index+off];
0171     }
0172 
0173     template<int i>
0174     decltype(auto) get() const noexcept { return std::get<i>(prop); }
0175 };
0176 
0177 struct AnimatedProperties
0178 {
0179     std::map<QString, AnimatedProperty> properties;
0180 
0181     virtual ~AnimatedProperties() {}
0182     virtual bool prepare_joined(std::vector<JoinedProperty>&) const { return true; }
0183 
0184     bool has(const QString& name) const
0185     {
0186         return properties.count(name);
0187     }
0188 
0189     std::vector<PropertyKeyframe> single(const QString& prop_name) const
0190     {
0191         auto it = properties.find(prop_name);
0192         if ( it == properties.end() || it->second.keyframes.size() < 2 )
0193             return {};
0194         return it->second.keyframes;
0195     }
0196 
0197     std::vector<JoinedPropertyKeyframe> joined(const std::vector<QString>& prop_names) const
0198     {
0199         std::vector<JoinedProperty> props;
0200         props.reserve(prop_names.size());
0201         int found = 0;
0202         for ( const auto& name : prop_names )
0203         {
0204             auto it = properties.find(name);
0205             if ( it == properties.end() || it->second.keyframes.size() < 2 )
0206             {
0207                 props.push_back({&name});
0208             }
0209             else
0210             {
0211                 props.push_back({&it->second});
0212                 found++;
0213             }
0214         }
0215 
0216         if ( !found )
0217             return {};
0218 
0219         if ( !prepare_joined(props) )
0220             return {};
0221 
0222         std::vector<JoinedPropertyKeyframe> keyframes;
0223 
0224         bool cont = true;
0225         while ( cont )
0226         {
0227             model::FrameTime time = std::numeric_limits<model::FrameTime>::max();
0228             for ( const auto& p : props )
0229             {
0230                 if ( p.prop.index() == 0 && p.keyframe()->time < time  )
0231                     time = p.keyframe()->time;
0232             }
0233 
0234             std::vector<ValueVariant> values;
0235             values.reserve(props.size());
0236 
0237             std::vector<model::KeyframeTransition> transitions;
0238             transitions.resize(found);
0239             cont = false;
0240             for ( auto& p : props )
0241             {
0242                 if ( p.prop.index() == 0 )
0243                 {
0244                     auto kf = p.keyframe();
0245                     if ( (p.at_end() && kf->time <= time) || (p.index == 0 && kf->time > time) )
0246                     {
0247                         values.push_back(kf->values);
0248                     }
0249                     else if ( kf->time == time )
0250                     {
0251                         p.index++;
0252                         values.push_back(kf->values);
0253                         transitions.push_back(kf->transition);
0254                         cont = true;
0255                     }
0256                     else
0257                     {
0258                         auto kf1 = p.keyframe(-1);
0259                         qreal x = math::unlerp(kf1->time, kf->time, time);
0260                         qreal t = kf1->transition.lerp_factor(x);
0261                         values.push_back(kf1->values.lerp(kf->values, t));
0262                         transitions.push_back(kf1->transition.split(x).first);
0263                         cont = true;
0264                     }
0265                 }
0266                 else
0267                 {
0268                     values.push_back(p.get<2>());
0269                 }
0270             }
0271 
0272             keyframes.push_back({time, std::move(values), model::JoinAnimatables::Keyframe::mix_transitions(transitions)});
0273         }
0274 
0275         return keyframes;
0276     }
0277 
0278 };
0279 
0280 } // namespace glaxnimate::io::detail