QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskFlickAnimator.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskFlickAnimator.h"
7#include <qmath.h>
8
9static inline qreal qskAligned( qreal value )
10{
11 if ( qFuzzyIsNull( value ) )
12 return 0.0;
13
14 if ( qFuzzyCompare( value, -1.0 ) )
15 return -1.0;
16
17 if ( qFuzzyCompare( value, 1.0 ) )
18 return 1.0;
19
20 return value;
21}
22
23QskFlickAnimator::QskFlickAnimator()
24 : m_velocity{ 0.0, 0.0 }
25 , m_degrees( 0.0 )
26 , m_cos( 1.0 )
27 , m_sin( 0.0 )
28 , m_elapsed( 0 )
29{
30 setDuration( 1000 );
31 setEasingCurve( QEasingCurve::OutCubic );
32}
33
34QskFlickAnimator::~QskFlickAnimator()
35{
36}
37
38void QskFlickAnimator::flick( qreal degrees, qreal velocity )
39{
40 if ( velocity < 0.0 || qFuzzyIsNull( velocity ) )
41 velocity = 0.0;
42
43 stop();
44
45 setAngle( degrees );
46
47 m_velocity[ 0 ] = velocity;
48 m_velocity[ 1 ] = 0.0;
49
50 if ( m_velocity[ 0 ] > 0.0 )
51 start();
52}
53
54void QskFlickAnimator::accelerate( qreal degrees, qreal velocity )
55{
56 if ( isRunning() && !qFuzzyIsNull( m_velocity[ 1 ] ) )
57 {
58 const qreal delta = qDegreesToRadians( degrees - m_degrees );
59
60 if ( qFuzzyIsNull( delta ) )
61 {
62 // the same as below, but faster to calculate: exp2( 2.0 * cos( 0.0 )
63 velocity += 4.0 * m_velocity[ 1 ];
64 }
65 else
66 {
67 const qreal cos = qFastCos( delta );
68 if ( cos >= 0.0 )
69 {
70 // boosting the current velocity
71 velocity += exp2( 2.0 * cos ) * m_velocity[ 1 ];
72 }
73 else
74 {
75 // slowing down
76 velocity = velocity * exp2( 2.0 * cos );
77 }
78 }
79 }
80
81 flick( degrees, velocity );
82}
83
84void QskFlickAnimator::done()
85{
86 m_velocity[ 1 ] = 0.0;
87 m_elapsed = 0;
88}
89
90void QskFlickAnimator::setAngle( qreal degrees )
91{
92 if ( degrees != m_degrees )
93 {
94 m_degrees = degrees;
95
96 const qreal radians = qDegreesToRadians( degrees );
97
98 m_cos = qskAligned( qFastCos( radians ) );
99 m_sin = qskAligned( qFastSin( radians ) );
100 }
101}
102
103void QskFlickAnimator::setVelocity( qreal velocity )
104{
105 m_velocity[ 0 ] = velocity;
106}
107
108void QskFlickAnimator::setup()
109{
110 m_elapsed = 0;
111 m_velocity[ 1 ] = m_velocity[ 0 ];
112}
113
114void QskFlickAnimator::advance( qreal value )
115{
116 const qreal oldVelocity = m_velocity[ 1 ];
117 const int oldElapsed = m_elapsed;
118
119 m_velocity[ 1 ] = m_velocity[ 0 ] * ( 1.0 - value );
120 m_elapsed = elapsed();
121
122 const qreal duration = ( m_elapsed - oldElapsed ) / 1000.0; // in seconds
123 if ( duration > 0.0 )
124 {
125 const qreal velocity = 0.5 * ( m_velocity[ 1 ] + oldVelocity ); // average
126
127 /*
128 Using the average velocity is not accurate, when having a non linear
129 curve, but the error can be ignored
130 */
131
132 const qreal distance = duration * velocity;
133 translate( m_cos * distance, m_sin * distance );
134 }
135}