NDEVR
API Documentation
TimePath.h
1/*--------------------------------------------------------------------------------------------
2Copyright (c) 2019, NDEVR LLC
3tyler.parke@ndevr.org
4 __ __ ____ _____ __ __ _______
5 | \ | | | __ \ | ___|\ \ / / | __ \
6 | \ | | | | \ \ | |___ \ \ / / | |__) |
7 | . \| | | |__/ / | |___ \ V / | _ /
8 | |\ |_|_____/__|_____|___\_/____| | \ \
9 |__| \__________________________________| \__\
10
11Subject to the terms of the Enterprise+ Agreement, NDEVR hereby grants
12Licensee a limited, non-exclusive, non-transferable, royalty-free license
13(without the right to sublicense) to use the API solely for the purpose of
14Licensee's internal development efforts to develop applications for which
15the API was provided.
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
21INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
22PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
23FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25DEALINGS IN THE SOFTWARE.
26
27Library: Design
28File: TimePath
29Included in API: True
30Author(s): Tyler Parke
31 *-----------------------------------------------------------------------------------------**/
32#pragma once
33#include <NDEVR/Buffer.h>
34#include <NDEVR/Time.h>
35#include <NDEVR/TimeSpan.h>
36namespace NDEVR
37{
46 class Path
47 {
48 public:
53 {}
54
61 Path(const Buffer<Time>& time, const Buffer<fltp04>& y, const Buffer<fltp04>& m)
62 : mX(time)
63 , mY(y)
64 , mM(m)
65 {}
66
71 Path(const Path& path)
72 : mX(path.mX)
73 , mY(path.mY)
74 , mM(path.mM)
75 {}
76
81 Path(const Path&& path)
82 : mX(std::move(path.mX))
83 , mY(std::move(path.mY))
84 , mM(std::move(path.mM))
85 {}
86
98 fltp08 interpolateX(const Time& current_time)
99 {
100 // Handle the boundary cases.
101 uint04 n = mX.size();
102 if (IsInvalid(current_time) || n == 0) {
103 return Constant<fltp08>::Invalid;
104 }
105 if (current_time <= mX[0]) {
106 return mY[0];
107 }
108 if (current_time >= mX.last()) {
109 return mY.last();
110 }
111 // Find the index 'i' of the last point with smaller X.
112 // We know this will be within the spline due to the boundary tests.
113 int i = 0;
114 while (current_time >= mX[i + 1])
115 {
116 i += 1;
117 if (current_time == mX[i])
118 return mY[i];
119 }
120 // Perform cubic Hermite spline interpolation.
121 fltp08 h = (mX[i + 1] - mX[i]).elapsedSeconds();
122 fltp08 t = (current_time - mX[i]).elapsedSeconds() / h;
123 return (mY[i] * (1 + 2 * t) + h * mM[i] * t) * (1 - t) * (1 - t)
124 + (mY[i + 1] * (3 - 2 * t) + h * mM[i + 1] * (t - 1)) * t * t;
125 }
126
141 {
142 if (time.size() != y.size() || time.size() < 2)
143 throw Exception(_t("There must be at least two control points and the arrays must be of equal length."));
144 uint04 n = time.size();
145 fltp04 object = 0.0f;
146 Buffer<fltp04> dx(n - 1, object); // could optimize this out
147 Buffer<fltp04> mx(n, object);
148 // Compute slopes of secant lines between successive points.
149 for (uint04 i = 0; i < n - 1; i++)
150 {
151 fltp08 h = (time[i + 1] - time[i]).elapsedSeconds();
152 if (h < 0.0)
153 throw Exception("The control points must all have strictly increasing X values:" + String(time[i]) + " " + String(time[i + 1]));
154 dx[i] = (y[i + 1] - y[i]) / cast<fltp04>(h);
155
156 }
157 // Initialize the tangents as the average of the secants.
158 mx[0] = dx[0];
159 for (uint04 i = 1; i < n - 1; i++)
160 {
161 mx[i] = (dx[i - 1] + dx[i]) * 0.5f;
162 }
163 mx[n - 1] = dx[n - 2];
164
165 // Update the tangents to preserve monotonicity.
166 for (uint04 i = 0; i < n - 1; i++)
167 {
168 if (dx[i] == 0.0f)
169 { // successive Y values are equal
170 mx[i] = 0.0f;
171 mx[i + 1] = 0.0f;
172 }
173 else
174 {
175 float a = mx[i] / dx[i];
176 float b = mx[i + 1] / dx[i];
177 if (a < 0.0f || b < 0.0f) {
178 //throw new IllegalArgumentException("The control points must have "
179 //+ "monotonic Y values.");
180 }
181 float h = (float)hypot(a, b);
182 if (h > 9.0f) {
183 float t = 3.0f / h;
184 mx[i] = t * a * dx[i];
185 mx[i + 1] = t * b * dx[i];
186 }
187 }
188 }
189 return Path(time, y, mx);
190 }
191
209 {
210 if (time.size() != y.size() || time.size() < 2)
211 {
212 throw new Exception(_t("There must be at least two control points and the arrays must be of equal length."));
213 }
214 Buffer<Time> temp_time(y.size() * 2);
215 Buffer<fltp04> temp_y(y.size() * 2);
216 for (uint04 i = 0; i < time.size() - 1; i++)
217 {
218 temp_y.add(cast<fltp04>(y[i].as<DEGREES>()));
219 temp_time.add(time[i]);
220 if((y[i].as<DEGREES>() - y[i + 1].as<DEGREES>()) > 180.0)
221 {
222 temp_y.add( 179.99f);
223 temp_y.add(-179.99f);
224 temp_time.add(time[i] + (time[i + 1] - time[i]) / 2.0);
225 temp_time.add(time[i] + (time[i + 1] - time[i]) / 2.0);
226 }
227 if ((y[i].as<DEGREES>() - y[i + 1].as<DEGREES>()) < -180.0)
228 {
229 temp_y.add(-179.99f);
230 temp_y.add( 179.99f);
231 temp_time.add(time[i] + (time[i + 1] - time[i]) / 2.0);
232 temp_time.add(time[i] + (time[i + 1] - time[i]) / 2.0);
233 }
234 }
235 return createMonotoneCubicPath(temp_time, temp_y);
236 }
237
253 {
254 Buffer<std::pair<Time, fltp04>> sorted_path(time.size());
255 for (uint04 i = 0; i < time.size(); i++)
256 {
257 sorted_path.add(std::pair<Time, fltp04>(time[i], y[i]));
258 }
259 std::sort(sorted_path.begin(), sorted_path.end(), [](const std::pair<Time, fltp04>& p1, const std::pair<Time, fltp04>& p2)
260 {
261 return p1.first > p2.first;
262 });
263 Buffer<Time> corrected_time(time.size());
264 Buffer<fltp04> corrected_value(time.size());
265 for (uint04 i = 0; i < time.size(); i++)
266 {
267 corrected_value.add(sorted_path[i].second);
268 corrected_time.add(sorted_path[i].first);
269 }
270 return createMonotoneCubicPath(corrected_time, corrected_value);
271 }
272
286 {
287 Buffer<std::pair<Time, Angle<fltp08>>> sorted_path(time.size());
288 for (uint04 i = 0; i < time.size(); i++)
289 {
290 sorted_path.add(std::pair<Time, Angle<fltp08>>(time[i], y[i]));
291 }
292 std::sort(sorted_path.begin(), sorted_path.end(), [](const std::pair<Time, Angle<fltp08>>& p1, const std::pair<Time, Angle<fltp08>>& p2)
293 {
294 return p1.first > p2.first;
295 });
296 Buffer<Time> corrected_time(time.size());
297 Buffer<Angle<fltp08>> corrected_value(time.size());
298 for (uint04 i = 0; i < time.size(); i++)
299 {
300 corrected_value.add(sorted_path[i].second);
301 corrected_time.add(sorted_path[i].first);
302 }
303 return createMonotoneCubicPathAzimuth(corrected_time, corrected_value);
304 }
305
311 Path& operator=(const Path& path)
312 {
313 mX = path.mX;
314 mY = path.mY;
315 mM = path.mM;
316 return *this;
317 }
318 protected:
322 };
323}
324
325
326
327
Stores an angle in an optimized internal format with support for efficient trigonometric operations.
Definition Angle.h:83
The equivelent of std::vector but with a bit more control.
Definition Buffer.hpp:58
void add(t_type &&object)
Adds object to the end of the buffer.
Definition Buffer.hpp:190
Provides consistent interface to handle errors through the throw expression.
Definition Exception.hpp:47
Path(const Buffer< Time > &time, const Buffer< fltp04 > &y, const Buffer< fltp04 > &m)
Constructs a Path from pre-computed time, value, and tangent buffers.
Definition TimePath.h:61
fltp08 interpolateX(const Time &current_time)
Interpolates the path value at a given time using cubic Hermite spline interpolation.
Definition TimePath.h:98
Buffer< fltp04 > mM
The tangent (slope) values at each control point, used for Hermite interpolation.
Definition TimePath.h:321
Buffer< fltp04 > mY
The interpolated values (Y-axis) at each control point.
Definition TimePath.h:320
Path(const Path &&path)
Move constructor.
Definition TimePath.h:81
static Path createMonotoneCubicPathAzimuth(const Buffer< Time > &time, const Buffer< Angle< fltp08 > > &y)
Creates a monotone cubic Hermite spline path for azimuth (angular) values over time.
Definition TimePath.h:208
Path & operator=(const Path &path)
Copy assignment operator.
Definition TimePath.h:311
static Path createMonotoneAngleCubicPathSorted(const Buffer< Time > &time, const Buffer< Angle< fltp08 > > &y)
Creates a monotone cubic angle path after sorting the control points by time in descending order.
Definition TimePath.h:285
Path(const Path &path)
Copy constructor.
Definition TimePath.h:71
static Path createMonotoneCubicPath(const Buffer< Time > &time, const Buffer< fltp04 > &y)
Creates a monotone cubic Hermite spline path from time-value control points.
Definition TimePath.h:140
Buffer< Time > mX
The time values (X-axis) for each control point.
Definition TimePath.h:319
Path()
Default constructor.
Definition TimePath.h:52
static Path createMonotoneCubicPathSorted(const Buffer< Time > &time, const Buffer< fltp04 > &y)
Creates a monotone cubic path after sorting the control points by time in descending order.
Definition TimePath.h:252
The core String class for the NDEVR API.
Definition String.h:95
Represents a timestamp with utilities for manipulation and conversion.
Definition Time.h:62
The primary namespace for the NDEVR SDK.
float fltp04
Defines an alias representing a 4 byte floating-point number Bit layout is as follows: -Sign: 1 bit a...
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
double fltp08
Defines an alias representing an 8 byte floating-point number.
static constexpr bool IsInvalid(const Angle< t_type > &value)
Checks whether the given Angle holds an invalid value.
Definition Angle.h:388
T hypot(T x, T y)
return the hypot of x and y
Definition misc.h:30
constexpr t_to cast(const Angle< t_from > &value)
Casts an Angle from one backing type to another.
Definition Angle.h:408
STL namespace.