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 }