andrewm@0
|
1 /*
|
andrewm@0
|
2 TouchKeys: multi-touch musical keyboard control software
|
andrewm@0
|
3 Copyright (c) 2013 Andrew McPherson
|
andrewm@0
|
4
|
andrewm@0
|
5 This program is free software: you can redistribute it and/or modify
|
andrewm@0
|
6 it under the terms of the GNU General Public License as published by
|
andrewm@0
|
7 the Free Software Foundation, either version 3 of the License, or
|
andrewm@0
|
8 (at your option) any later version.
|
andrewm@0
|
9
|
andrewm@0
|
10 This program is distributed in the hope that it will be useful,
|
andrewm@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
andrewm@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
andrewm@0
|
13 GNU General Public License for more details.
|
andrewm@0
|
14
|
andrewm@0
|
15 You should have received a copy of the GNU General Public License
|
andrewm@0
|
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
andrewm@0
|
17
|
andrewm@0
|
18 =====================================================================
|
andrewm@0
|
19
|
andrewm@0
|
20 LineSegment.h: template class implementing a line segment over time.
|
andrewm@0
|
21 */
|
andrewm@0
|
22
|
andrewm@0
|
23 #ifndef touchkeys_LineSegment_h
|
andrewm@0
|
24 #define touchkeys_LineSegment_h
|
andrewm@0
|
25
|
andrewm@0
|
26 #include <iostream>
|
andrewm@0
|
27 #include <list>
|
andrewm@0
|
28 #include <boost/function.hpp>
|
andrewm@0
|
29 #include "Types.h"
|
andrewm@0
|
30
|
andrewm@0
|
31 /*
|
andrewm@0
|
32 * LineSegment
|
andrewm@0
|
33 *
|
andrewm@0
|
34 * This template class implements a line (or curve) segment. Given start and end times and
|
andrewm@0
|
35 * start and end values, this class calculates the current value based on the given timestamp.
|
andrewm@0
|
36 * Options are available to freeze at the final value or to extrapolate from it.
|
andrewm@0
|
37 *
|
andrewm@0
|
38 * One assumption that is made with this class is that calls are always made in monotonically
|
andrewm@0
|
39 * non-decreasing order of timestamp. Evaluating the value of the segment may implicitly update
|
andrewm@0
|
40 * its state so out-of-order evaluations could return unexpected results.
|
andrewm@0
|
41 */
|
andrewm@0
|
42
|
andrewm@0
|
43
|
andrewm@0
|
44 template<typename DataType>
|
andrewm@0
|
45 class LineSegment {
|
andrewm@0
|
46 public:
|
andrewm@0
|
47 typedef boost::function<DataType const& (DataType const&)> warp_function;
|
andrewm@0
|
48
|
andrewm@0
|
49 struct {
|
andrewm@0
|
50 timestamp_type time; // Time to achieve a given value
|
andrewm@0
|
51 DataType value; // Value that should be achieved
|
andrewm@0
|
52 warp_function warp; // How to warp the data getting there. FIXME: null?
|
andrewm@0
|
53 bool jump; // Whether to do an immediate jump
|
andrewm@0
|
54 } Endpoint;
|
andrewm@0
|
55
|
andrewm@0
|
56 public:
|
andrewm@0
|
57 // ***** Constructors *****
|
andrewm@0
|
58
|
andrewm@0
|
59 // Default constructor with default starting value
|
andrewm@0
|
60 LineSegment() : lastEvaluatedTimestamp_(0), lastEvaluatedValue_() {
|
andrewm@0
|
61
|
andrewm@0
|
62 }
|
andrewm@0
|
63
|
andrewm@0
|
64 // Constructor with arbitrary starting value
|
andrewm@0
|
65 LineSegment(DataType const& startingValue) : lastEvaluatedTimestamp_(0),
|
andrewm@0
|
66 lastEvaluatedValue_(startingValue) {
|
andrewm@0
|
67
|
andrewm@0
|
68 }
|
andrewm@0
|
69
|
andrewm@0
|
70 // Copy constructor
|
andrewm@0
|
71 LineSegment(LineSegment<DataType> const& obj) :
|
andrewm@0
|
72 lastEvaluatedTimestamp_(obj.lastEvaluatedTimestamp_),
|
andrewm@0
|
73 lastEvaluatedValue_(obj.lastEvaluatedValue_) {
|
andrewm@0
|
74
|
andrewm@0
|
75 }
|
andrewm@0
|
76
|
andrewm@0
|
77 // ***** Destructor *****
|
andrewm@0
|
78 ~LineSegment() {
|
andrewm@0
|
79
|
andrewm@0
|
80 }
|
andrewm@0
|
81
|
andrewm@0
|
82 // ***** Modifiers *****
|
andrewm@0
|
83 // Add a new segment from a given time to a given time. The "from" value is implicitly
|
andrewm@0
|
84 // calculated based on the current state of the segment.
|
andrewm@0
|
85 void addSegment(timestamp_type const fromTimestamp, timestamp_type const toTimestamp, DataType const& toValue) {
|
andrewm@0
|
86
|
andrewm@0
|
87 }
|
andrewm@0
|
88
|
andrewm@0
|
89 // As above, but use a non-linear (warped) transfer function to get from one end to the other.
|
andrewm@0
|
90 // The function should take in one value between 0 and 1 (or their DataType equivalents) and return
|
andrewm@0
|
91 // a value between 0 and 1 (or their DataType equivalents). Scaling will be done internally.
|
andrewm@0
|
92 void addSegment(timestamp_type const fromTimestamp, timestamp_type const toTimestamp, DataType const& toValue,
|
andrewm@0
|
93 warp_function& warp) {
|
andrewm@0
|
94
|
andrewm@0
|
95 }
|
andrewm@0
|
96
|
andrewm@0
|
97 // Freeze the value starting at the indicated timestamp (immediately if no argument is given)
|
andrewm@0
|
98 void hold(timestamp_type const timestamp = 0) {
|
andrewm@0
|
99 if(timestamp == 0) {
|
andrewm@0
|
100 segments_.clear(); // Hold at whatever last evaluation returned
|
andrewm@0
|
101 }
|
andrewm@0
|
102 else {
|
andrewm@0
|
103 evaluate(timestamp); // Recalculate value for this time, then hold
|
andrewm@0
|
104 segments_.clear();
|
andrewm@0
|
105 }
|
andrewm@0
|
106 }
|
andrewm@0
|
107
|
andrewm@0
|
108 // Jump to a given value at the indicated timestamp (immediately if no argument is given)
|
andrewm@0
|
109 void jump(timestamp_type const timestamp = 0, DataType const& toValue) {
|
andrewm@0
|
110 if(timestamp == 0) {
|
andrewm@0
|
111 lastEvaluatedValue_ = toValue;
|
andrewm@0
|
112 segments_.clear();
|
andrewm@0
|
113 }
|
andrewm@0
|
114 else {
|
andrewm@0
|
115 // Jump to a value at a given time
|
andrewm@0
|
116 Endpoint newEndpoint;
|
andrewm@0
|
117
|
andrewm@0
|
118 newEndpoint.time = timestamp;
|
andrewm@0
|
119 newEndpoint.value = toValue;
|
andrewm@0
|
120 newEndpoint.jump = true;
|
andrewm@0
|
121
|
andrewm@0
|
122 if(segments_.empty())
|
andrewm@0
|
123 segments_.push_back(newEndpoint);
|
andrewm@0
|
124 else {
|
andrewm@0
|
125 std::list<Endpoint>::iterator it = segments_.begin();
|
andrewm@0
|
126
|
andrewm@0
|
127 // Look for any elements in the list that have a later timestamp.
|
andrewm@0
|
128 // Remove them and make this jump the last element.
|
andrewm@0
|
129 while(it != segments_.end()) {
|
andrewm@0
|
130 if(it->time >= timestamp) {
|
andrewm@0
|
131 segments_.erase(it, segments_.end());
|
andrewm@0
|
132 break;
|
andrewm@0
|
133 }
|
andrewm@0
|
134 it++;
|
andrewm@0
|
135 }
|
andrewm@0
|
136
|
andrewm@0
|
137 segments_.push_back(newEndpoint);
|
andrewm@0
|
138 }
|
andrewm@0
|
139 }
|
andrewm@0
|
140 }
|
andrewm@0
|
141
|
andrewm@0
|
142 // Reset state to defaults, including times
|
andrewm@0
|
143 void reset() {
|
andrewm@0
|
144 lastEvaluatedValue_ = DataType();
|
andrewm@0
|
145 lastEvaluatedTimestamp_ = 0;
|
andrewm@0
|
146 segments_.clear();
|
andrewm@0
|
147 }
|
andrewm@0
|
148
|
andrewm@0
|
149 // ***** Evaluator *****
|
andrewm@0
|
150
|
andrewm@0
|
151 // Return the current value of the segment based on current timestamp
|
andrewm@0
|
152 DataType const& evaluate(timestamp_type const timestamp) {
|
andrewm@0
|
153 // Check that the current timestamp is later than the previous one. If
|
andrewm@0
|
154 // earlier, print a warning. If identical, return the last value identically.
|
andrewm@0
|
155 // If there are no further changes planned, likewise return the last value.
|
andrewm@0
|
156 if(timestamp < lastEvaluatedTimestamp_) {
|
andrewm@0
|
157 std::cout << "Warning: LineSegment::evaluate() called with timestamp " << timestamp << " earlier than previous " << lastEvaluatedTimestamp_ << std::endl;
|
andrewm@0
|
158 return lastEvaluatedValue_;
|
andrewm@0
|
159 }
|
andrewm@0
|
160 if(timestamp == lastEvaluatedValue_ || !changesRemaining_)
|
andrewm@0
|
161 return lastEvaluatedValue_;
|
andrewm@0
|
162
|
andrewm@0
|
163 // TODO: evaluate
|
andrewm@0
|
164 }
|
andrewm@0
|
165
|
andrewm@0
|
166 // Return whether all in-progress segments have finished. Call this after evaluate().
|
andrewm@0
|
167 bool finished() {
|
andrewm@0
|
168 return segments_.empty();
|
andrewm@0
|
169 }
|
andrewm@0
|
170
|
andrewm@0
|
171 private:
|
andrewm@0
|
172 // ***** Internal Methods *****
|
andrewm@0
|
173
|
andrewm@0
|
174
|
andrewm@0
|
175 // ***** Member Variables *****
|
andrewm@0
|
176 timestamp_type lastEvaluatedTimestamp_; // When evaluate() was last called
|
andrewm@0
|
177 DataType lastEvaluatedValue_; // What evaluate() last returned
|
andrewm@0
|
178 std::list<Endpoint> segments_; // List of segment times and values
|
andrewm@0
|
179 };
|
andrewm@0
|
180
|
andrewm@0
|
181
|
andrewm@0
|
182
|
andrewm@0
|
183
|
andrewm@0
|
184
|
andrewm@0
|
185 #endif
|