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