annotate Source/Utility/LineSegment.h @ 56:b4a2d2ae43cf tip

merge
author Andrew McPherson <andrewm@eecs.qmul.ac.uk>
date Fri, 23 Nov 2018 15:48:14 +0000
parents 3580ffe87dc8
children
rev   line source
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