File: animvalue.hh

package info (click to toggle)
performous 1.1%2Bgit20181118-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 11,712 kB
  • sloc: cpp: 30,008; ansic: 2,751; sh: 801; xml: 464; python: 374; makefile: 22
file content (117 lines) | stat: -rw-r--r-- 4,091 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#pragma once

#include "chrono.hh"
#include "util.hh"
#include <cmath>
#include <stdexcept>

/// class for simple/linear animation/transition of values
class AnimValue {
  public:
	/// constructor
	AnimValue(double value = 0.0, double rate = 1.0): m_value(value), m_target(value), m_rate(rate), m_time(Clock::now()) {}
	/// move animation forward by diff
	void move(double diff) { m_value += diff; }
	/// gets animition target
	double getTarget() const { return m_target; }
	/// sets animation target (end point)
	void setTarget(double target, bool step = false) { m_target = target; if (step) m_value = target; }
	/// sets animation range
	void setRange(double tmin, double tmax) {
		if (tmin > tmax) throw std::logic_error("AnimValue range is reversed");
		m_target = clamp(m_target, tmin, tmax);
	}
	/// hard-sets anim value
	void setValue(double value) { m_value = value; }
	/// set the adjustment rate
	void setRate(double value) { m_rate = value; }
	/// get current anim value
	double get() const {
		double maxadj = m_rate * duration();
		double diff = m_value - m_target;
		double adj = std::min(maxadj, std::fabs(diff));
		if (diff > 0.0) m_value -= adj; else m_value += adj;
		return m_value;
	}

  private:
	double duration() const {
		auto newtime = Clock::now();
		Seconds t = newtime - m_time;
		m_time = newtime;
		return clamp(t.count());
	}
	mutable double m_value;
	double m_target;
	double m_rate;
	mutable Time m_time;
};

/// easing animations
class AnimAcceleration {
  public:
	/// constructor
	AnimAcceleration(): m_target(), m_songs(), m_position(), m_velocity(), m_marginLeft(), m_marginRight(), m_time() {}
	/// set margins
	void setMargins(double left, double right) { m_marginLeft = left; m_marginRight = right; }
	/// get current value
	double getValue() {
		const double acceleration = 50.0; // the coefficient of velocity changes (animation speed)
		const double overshoot = 0.95; // Over 1.0 decelerates too late, less than 1.0 decelerates too early
		if (m_songs == 0) return m_target;
		double num = m_marginLeft + m_songs + m_marginRight;
		auto curtime = Clock::now();
		double duration = Seconds(curtime - m_time).count();
		m_time = curtime;
		if (!(duration > 0.0)) return m_position; // Negative value or NaN, or no songs - skip processing
		if (duration > 1.0) {
			// More than one second has elapsed, assume that we have stopped on target already
			m_velocity = 0.0;
			return m_position = m_target;
		}
		std::size_t rounds = 1.0 + 1000.0 * duration; // 1 ms or shorter timesteps
		double t = duration / rounds;
		for (std::size_t i = 0; i < rounds; ++i) {
			double d = remainder(m_target - m_position, num); // Distance (via shortest way)
			// Allow it to stop nicely, without jitter
			if (std::abs(m_velocity) < 0.1 && std::abs(d) < 0.001) {
				m_velocity = 0.0;
				m_position = m_target;
				break;
			}
			double a = d > 0.0 ? acceleration : -acceleration; // Acceleration vector
			// Are we going to right direction && can we stop in time if we start decelerating now?
			if (d * m_velocity > 0.0 && std::abs(m_velocity) > 2.0 * overshoot * acceleration * d / m_velocity) a *= -1.0;
			// Apply Newtonian mechanics
			m_velocity += t * a;
			m_position += t * m_velocity;
		}
		if ((m_position = fmod(m_position, num)) < 0.0) m_position += num; // Normalize to [0, num]
		if (m_position > m_marginLeft + m_songs) m_position -= num; // Normalize to [-m_marginLeft, songs + m_marginRight]
		return m_position;
	}
	/// get target
	unsigned int getTarget() const { return m_target; };
	/// set target
	void setTarget(unsigned int target, unsigned int songs) {
		m_target = target;
		if (m_songs == songs) return;
		// Number of songs has changed => reset animation
		m_songs = songs;
		m_position = target;
		m_velocity = 0.0;
	};
	/// resets animation target to 0
	void reset() { setTarget(0, 0); }
	/// get current velocity
	double getVelocity() const { return m_velocity; }

  private:
	unsigned int m_target;
	unsigned int m_songs;
	double m_position;
	double m_velocity;
	double m_marginLeft;
	double m_marginRight;
	Time m_time;
};