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 "keyframe_transition.hpp" 0008 #include "math/bezier/segment.hpp" 0009 #include "math/polynomial.hpp" 0010 0011 namespace { 0012 0013 constexpr QPointF bound_vec(const QPointF& v) 0014 { 0015 return { 0016 qBound(glaxnimate::math::scalar_type<QPointF>(0), v.x(), glaxnimate::math::scalar_type<QPointF>(1)), 0017 v.y() 0018 }; 0019 } 0020 0021 } // namespace 0022 0023 glaxnimate::model::KeyframeTransition::Descriptive glaxnimate::model::KeyframeTransition::before_descriptive() const 0024 { 0025 if ( hold_ ) 0026 return Hold; 0027 0028 if ( qFuzzyIsNull(bezier_.points()[1].x() - bezier_.points()[1].y()) ) 0029 return Linear; 0030 0031 if ( bezier_.points()[1].y() == 0 ) 0032 return Ease; 0033 0034 if ( bezier_.points()[1].y() < 0 ) 0035 return Overshoot; 0036 0037 if ( bezier_.points()[1].x() < bezier_.points()[1].y() ) 0038 return Fast; 0039 0040 return Custom; 0041 } 0042 0043 glaxnimate::model::KeyframeTransition::Descriptive glaxnimate::model::KeyframeTransition::after_descriptive() const 0044 { 0045 if ( hold_ ) 0046 return Hold; 0047 0048 if ( qFuzzyIsNull(bezier_.points()[2].x() - bezier_.points()[2].y()) ) 0049 return Linear; 0050 0051 if ( bezier_.points()[2].y() == 1 ) 0052 return Ease; 0053 0054 if ( bezier_.points()[2].y() > 1 ) 0055 return Overshoot; 0056 0057 if ( bezier_.points()[2].x() > bezier_.points()[2].y() ) 0058 return Fast; 0059 0060 return Custom; 0061 } 0062 0063 void glaxnimate::model::KeyframeTransition::set_before_descriptive(model::KeyframeTransition::Descriptive d) 0064 { 0065 switch ( d ) 0066 { 0067 case Hold: 0068 set_hold(true); 0069 return; 0070 case Linear: 0071 bezier_.set<1>(QPointF{1./3., 1./3.}); 0072 hold_ = false; 0073 break; 0074 case Ease: 0075 bezier_.set<1>(QPointF{1./3., 0}); 0076 hold_ = false; 0077 break; 0078 case Fast: 0079 bezier_.set<1>(QPointF{1./6., 1./3.}); 0080 hold_ = false; 0081 break; 0082 case Overshoot: 0083 bezier_.set<1>(QPointF{2./3., -1./3.}); 0084 hold_ = false; 0085 break; 0086 case Custom: 0087 hold_ = false; 0088 break; 0089 } 0090 } 0091 0092 void glaxnimate::model::KeyframeTransition::set_after_descriptive(model::KeyframeTransition::Descriptive d) 0093 { 0094 switch ( d ) 0095 { 0096 case Hold: 0097 set_hold(true); 0098 return; 0099 case Linear: 0100 bezier_.set<2>(QPointF{2./3., 2./3.}); 0101 hold_ = false; 0102 break; 0103 case Ease: 0104 bezier_.set<2>(QPointF{2./3., 1}); 0105 hold_ = false; 0106 break; 0107 case Fast: 0108 bezier_.set<2>(QPointF{5./6., 2./3.}); 0109 hold_ = false; 0110 break; 0111 case Overshoot: 0112 bezier_.set<2>(QPointF{1./3., 4./3.}); 0113 hold_ = false; 0114 break; 0115 case Custom: 0116 hold_ = false; 0117 break; 0118 } 0119 } 0120 0121 void glaxnimate::model::KeyframeTransition::set_after(const QPointF& after) 0122 { 0123 bezier_.set<2>(bound_vec(after)); 0124 } 0125 0126 void glaxnimate::model::KeyframeTransition::set_before(const QPointF& before) 0127 { 0128 bezier_.set<1>(bound_vec(before)); 0129 } 0130 0131 void glaxnimate::model::KeyframeTransition::set_handles(const QPointF& before, const QPointF& after) 0132 { 0133 set_before(before); 0134 set_after(after); 0135 } 0136 0137 void glaxnimate::model::KeyframeTransition::set_hold(bool hold) 0138 { 0139 hold_ = hold; 0140 } 0141 0142 double glaxnimate::model::KeyframeTransition::lerp_factor(double ratio) const 0143 { 0144 if ( hold_ ) 0145 { 0146 if ( ratio >= 1 || qFuzzyCompare(float(ratio), 1.f) ) 0147 return 1; 0148 return 0; 0149 } 0150 0151 if ( ratio <= 0 ) 0152 return 0; 0153 if ( ratio >= 1 ) 0154 return 1; 0155 double t = bezier_.t_at_value(ratio); 0156 return bezier_.solve_component(t, 1); 0157 } 0158 0159 double glaxnimate::model::KeyframeTransition::bezier_parameter(double ratio) const 0160 { 0161 if ( ratio <= 0 || hold_ ) 0162 return 0; 0163 if ( ratio >= 1 ) 0164 return 1; 0165 return bezier_.t_at_value(ratio); 0166 } 0167 0168 glaxnimate::model::KeyframeTransition::KeyframeTransition(const QPointF& before_handle, const QPointF& after_handle, bool hold) 0169 : bezier_({0, 0}, before_handle, after_handle, {1,1}), 0170 hold_(hold) 0171 {} 0172 0173 glaxnimate::model::KeyframeTransition::KeyframeTransition( 0174 glaxnimate::model::KeyframeTransition::Descriptive before, 0175 glaxnimate::model::KeyframeTransition::Descriptive after) 0176 : KeyframeTransition() 0177 { 0178 set_before_descriptive(before); 0179 set_after_descriptive(after); 0180 } 0181 0182 glaxnimate::model::KeyframeTransition::KeyframeTransition(glaxnimate::model::KeyframeTransition::Descriptive descriptive) 0183 : KeyframeTransition(descriptive, descriptive) 0184 { 0185 } 0186 0187 std::pair<glaxnimate::model::KeyframeTransition, glaxnimate::model::KeyframeTransition> glaxnimate::model::KeyframeTransition::split(double x) const 0188 { 0189 return split_t(bezier_.t_at_value(x)); 0190 } 0191 0192 std::pair<glaxnimate::model::KeyframeTransition, glaxnimate::model::KeyframeTransition> glaxnimate::model::KeyframeTransition::split_t(double t) const 0193 { 0194 if ( hold_ ) 0195 return { {{0, 0}, {1, 1}, true}, {{0, 0}, {1, 1}, true} }; 0196 0197 if ( qFuzzyIsNull(t) ) 0198 { 0199 return { {{0, 0}, {1, 1}, false}, *this }; 0200 } 0201 else if ( qFuzzyCompare(t, 1) ) 0202 { 0203 return { *this, {{0, 0}, {1, 1}, false} }; 0204 } 0205 0206 qreal x = bezier_.solve_component(t, 0); 0207 qreal y = bezier_.solve_component(t, 1); 0208 math::bezier::BezierSegment left, right; 0209 std::tie(left, right) = bezier_.split(t); 0210 0211 qreal left_factor_x = 1 / x; 0212 qreal left_factor_y = 1 / y; 0213 qreal right_factor_x = 1 / (1-x); 0214 qreal right_factor_y = 1 / (1-y); 0215 qreal right_offset_y = 0; 0216 0217 QPointF left_p1{left[1].x() * left_factor_x, left[1].y() * left_factor_y}; 0218 QPointF left_p2{left[2].x() * left_factor_x, left[2].y() * left_factor_y}; 0219 QPointF right_p1{(right[1].x() - x) / (1-x), (right[1].y() - y) / (1-y)}; 0220 QPointF right_p2{(right[2].x() - x) / (1-x), (right[2].y() - y) / (1-y)}; 0221 0222 if ( y < 0 ) 0223 { 0224 left_p1.setY(-left[1].y() / (y - 1)); 0225 left_p2.setY(1 - left[2].y() / (y - 1)); 0226 } 0227 else if ( y > 1 ) 0228 { 0229 right_p1.setY(-(right[1].y() - 1) / (y - 1)); 0230 right_p2.setY(1 - (right[2].y() - 1) / (y - 1)); 0231 } 0232 0233 return { 0234 { 0235 {left[1].x() * left_factor_x, left[1].y() * left_factor_y}, 0236 {left[2].x() * left_factor_x, left[2].y() * left_factor_y} 0237 }, 0238 { 0239 {(right[1].x() - x) * right_factor_x, right_offset_y + (right[1].y() - y) * right_factor_y}, 0240 {(right[2].x() - x) * right_factor_x, right_offset_y + (right[2].y() - y) * right_factor_y} 0241 } 0242 }; 0243 }