OpenShot Library | libopenshot 0.2.7
KeyFrame.cpp
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Source file for the Keyframe class
4 * @author Jonathan Thomas <jonathan@openshot.org>
5 *
6 * @ref License
7 */
8
9/* LICENSE
10 *
11 * Copyright (c) 2008-2019 OpenShot Studios, LLC
12 * <http://www.openshotstudios.com/>. This file is part of
13 * OpenShot Library (libopenshot), an open-source project dedicated to
14 * delivering high quality video editing and animation solutions to the
15 * world. For more information visit <http://www.openshot.org/>.
16 *
17 * OpenShot Library (libopenshot) is free software: you can redistribute it
18 * and/or modify it under the terms of the GNU Lesser General Public License
19 * as published by the Free Software Foundation, either version 3 of the
20 * License, or (at your option) any later version.
21 *
22 * OpenShot Library (libopenshot) is distributed in the hope that it will be
23 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU Lesser General Public License for more details.
26 *
27 * You should have received a copy of the GNU Lesser General Public License
28 * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
29 */
30
31#include "KeyFrame.h"
32#include "Exceptions.h"
33
34#include <algorithm>
35#include <functional>
36#include <utility>
37#include <cassert> // For assert()
38#include <iostream> // For std::cout
39#include <iomanip> // For std::setprecision
40
41using namespace std;
42using namespace openshot;
43
44namespace openshot{
45
46 // Check if the X coordinate of a given Point is lower than a given value
47 bool IsPointBeforeX(Point const & p, double const x) {
48 return p.co.X < x;
49 }
50
51 // Linear interpolation between two points
52 double InterpolateLinearCurve(Point const & left, Point const & right, double const target) {
53 double const diff_Y = right.co.Y - left.co.Y;
54 double const diff_X = right.co.X - left.co.X;
55 double const slope = diff_Y / diff_X;
56 return left.co.Y + slope * (target - left.co.X);
57 }
58
59 // Bezier interpolation between two points
60 double InterpolateBezierCurve(Point const & left, Point const & right, double const target, double const allowed_error) {
61 double const X_diff = right.co.X - left.co.X;
62 double const Y_diff = right.co.Y - left.co.Y;
63 Coordinate const p0 = left.co;
64 Coordinate const p1 = Coordinate(p0.X + left.handle_right.X * X_diff, p0.Y + left.handle_right.Y * Y_diff);
65 Coordinate const p2 = Coordinate(p0.X + right.handle_left.X * X_diff, p0.Y + right.handle_left.Y * Y_diff);
66 Coordinate const p3 = right.co;
67
68 double t = 0.5;
69 double t_step = 0.25;
70 do {
71 // Bernstein polynoms
72 double B[4] = {1, 3, 3, 1};
73 double oneMinTExp = 1;
74 double tExp = 1;
75 for (int i = 0; i < 4; ++i, tExp *= t) {
76 B[i] *= tExp;
77 }
78 for (int i = 0; i < 4; ++i, oneMinTExp *= 1 - t) {
79 B[4 - i - 1] *= oneMinTExp;
80 }
81 double const x = p0.X * B[0] + p1.X * B[1] + p2.X * B[2] + p3.X * B[3];
82 double const y = p0.Y * B[0] + p1.Y * B[1] + p2.Y * B[2] + p3.Y * B[3];
83 if (fabs(target - x) < allowed_error) {
84 return y;
85 }
86 if (x > target) {
87 t -= t_step;
88 }
89 else {
90 t += t_step;
91 }
92 t_step /= 2;
93 } while (true);
94 }
95 // Interpolate two points using the right Point's interpolation method
96 double InterpolateBetween(Point const & left, Point const & right, double target, double allowed_error) {
97 // check if target is outside of the extremities poits
98 // This can occur when moving fast the play head
99 if(left.co.X > target){
100 return left.co.Y;
101 }
102 if(target > right.co.X){
103 return right.co.Y;
104 }
105 switch (right.interpolation) {
106 case CONSTANT: return left.co.Y;
107 case LINEAR: return InterpolateLinearCurve(left, right, target);
108 case BEZIER: return InterpolateBezierCurve(left, right, target, allowed_error);
109 default: return InterpolateLinearCurve(left, right, target);
110 }
111 }
112}
113
114template<typename Check>
115int64_t SearchBetweenPoints(Point const & left, Point const & right, int64_t const current, Check check) {
116 int64_t start = left.co.X;
117 int64_t stop = right.co.X;
118 while (start < stop) {
119 int64_t const mid = (start + stop + 1) / 2;
120 double const value = InterpolateBetween(left, right, mid, 0.01);
121 if (check(round(value), current)) {
122 start = mid;
123 } else {
124 stop = mid - 1;
125 }
126 }
127 return start;
128}
129
130// Constructor which sets the default point & coordinate at X=1
131Keyframe::Keyframe(double value) {
132 // Add initial point
133 AddPoint(Point(value));
134}
135
136// Constructor which takes a vector of Points
137Keyframe::Keyframe(const std::vector<openshot::Point>& points) : Points(points) {};
138
139// Add a new point on the key-frame. Each point has a primary coordinate,
140// a left handle, and a right handle.
142 // candidate is not less (greater or equal) than the new point in
143 // the X coordinate.
144 std::vector<Point>::iterator candidate =
145 std::lower_bound(begin(Points), end(Points), p.co.X, IsPointBeforeX);
146 if (candidate == end(Points)) {
147 // New point X is greater than all other points' X, add to
148 // back.
149 Points.push_back(p);
150 } else if ((*candidate).co.X == p.co.X) {
151 // New point is at same X coordinate as some point, overwrite
152 // point.
153 *candidate = p;
154 } else {
155 // New point needs to be inserted before candidate; thus move
156 // candidate and all following one to the right and insert new
157 // point then where candidate was.
158 size_t const candidate_index = candidate - begin(Points);
159 Points.push_back(p); // Make space; could also be a dummy point. INVALIDATES candidate!
160 std::move_backward(begin(Points) + candidate_index, end(Points) - 1, end(Points));
161 Points[candidate_index] = p;
162 }
163}
164
165// Add a new point on the key-frame, interpolate is optional (default: BEZIER)
166void Keyframe::AddPoint(double x, double y, InterpolationType interpolate)
167{
168 // Create a point
169 Point new_point(x, y, interpolate);
170
171 // Add the point
172 AddPoint(new_point);
173}
174
175// Get the index of a point by matching a coordinate
176int64_t Keyframe::FindIndex(Point p) const {
177 // loop through points, and find a matching coordinate
178 for (std::vector<Point>::size_type x = 0; x < Points.size(); x++) {
179 // Get each point
180 Point existing_point = Points[x];
181
182 // find a match
183 if (p.co.X == existing_point.co.X && p.co.Y == existing_point.co.Y) {
184 // Remove the matching point, and break out of loop
185 return x;
186 }
187 }
188
189 // no matching point found
190 throw OutOfBoundsPoint("Invalid point requested", -1, Points.size());
191}
192
193// Determine if point already exists
195 std::vector<Point>::const_iterator i =
196 std::lower_bound(begin(Points), end(Points), p.co.X, IsPointBeforeX);
197 return i != end(Points) && i->co.X == p.co.X;
198}
199
200// Get current point (or closest point) from the X coordinate (i.e. the frame number)
201Point Keyframe::GetClosestPoint(Point p, bool useLeft) const {
202 if (Points.size() == 0) {
203 return Point(-1, -1);
204 }
205
206 // Finds a point with an X coordinate which is "not less" (greater
207 // or equal) than the queried X coordinate.
208 std::vector<Point>::const_iterator candidate =
209 std::lower_bound(begin(Points), end(Points), p.co.X, IsPointBeforeX);
210
211 if (candidate == end(Points)) {
212 // All points are before the queried point.
213 //
214 // Note: Behavior the same regardless of useLeft!
215 return Points.back();
216 }
217 if (candidate == begin(Points)) {
218 // First point is greater or equal to the queried point.
219 //
220 // Note: Behavior the same regardless of useLeft!
221 return Points.front();
222 }
223 if (useLeft) {
224 return *(candidate - 1);
225 } else {
226 return *candidate;
227 }
228}
229
230// Get current point (or closest point to the right) from the X coordinate (i.e. the frame number)
232 return GetClosestPoint(p, false);
233}
234
235// Get previous point (if any)
237
238 // Lookup the index of this point
239 try {
240 int64_t index = FindIndex(p);
241
242 // If not the 1st point
243 if (index > 0)
244 return Points[index - 1];
245 else
246 return Points[0];
247
248 } catch (const OutOfBoundsPoint& e) {
249 // No previous point
250 return Point(-1, -1);
251 }
252}
253
254// Get max point (by Y coordinate)
256 Point maxPoint(-1, -1);
257
258 for (Point const & existing_point: Points) {
259 if (existing_point.co.Y >= maxPoint.co.Y) {
260 maxPoint = existing_point;
261 }
262 }
263
264 return maxPoint;
265}
266
267// Get the value at a specific index
268double Keyframe::GetValue(int64_t index) const {
269 if (Points.empty()) {
270 return 0;
271 }
272 std::vector<Point>::const_iterator candidate =
273 std::lower_bound(begin(Points), end(Points), static_cast<double>(index), IsPointBeforeX);
274
275 if (candidate == end(Points)) {
276 // index is behind last point
277 return Points.back().co.Y;
278 }
279 if (candidate == begin(Points)) {
280 // index is at or before first point
281 return Points.front().co.Y;
282 }
283 if (candidate->co.X == index) {
284 // index is directly on a point
285 return candidate->co.Y;
286 }
287 std::vector<Point>::const_iterator predecessor = candidate - 1;
288 return InterpolateBetween(*predecessor, *candidate, index, 0.01);
289}
290
291// Get the rounded INT value at a specific index
292int Keyframe::GetInt(int64_t index) const {
293 return int(round(GetValue(index)));
294}
295
296// Get the rounded INT value at a specific index
297int64_t Keyframe::GetLong(int64_t index) const {
298 return long(round(GetValue(index)));
299}
300
301// Get the direction of the curve at a specific index (increasing or decreasing)
302bool Keyframe::IsIncreasing(int index) const
303{
304 if (index < 1 || (index + 1) >= GetLength()) {
305 return true;
306 }
307 std::vector<Point>::const_iterator candidate =
308 std::lower_bound(begin(Points), end(Points), static_cast<double>(index), IsPointBeforeX);
309 if (candidate == end(Points)) {
310 return false; // After the last point, thus constant.
311 }
312 if ((candidate->co.X == index) || (candidate == begin(Points))) {
313 ++candidate;
314 }
315 int64_t const value = GetLong(index);
316 do {
317 if (value < round(candidate->co.Y)) {
318 return true;
319 } else if (value > round(candidate->co.Y)) {
320 return false;
321 }
322 ++candidate;
323 } while (candidate != end(Points));
324 return false;
325}
326
327// Generate JSON string of this object
328std::string Keyframe::Json() const {
329
330 // Return formatted string
331 return JsonValue().toStyledString();
332}
333
334// Generate Json::Value for this object
335Json::Value Keyframe::JsonValue() const {
336
337 // Create root json object
338 Json::Value root;
339 root["Points"] = Json::Value(Json::arrayValue);
340
341 // loop through points
342 for (const auto existing_point : Points) {
343 root["Points"].append(existing_point.JsonValue());
344 }
345
346 // return JsonValue
347 return root;
348}
349
350// Load JSON string into this object
351void Keyframe::SetJson(const std::string value) {
352
353 // Parse JSON string into JSON objects
354 try
355 {
356 const Json::Value root = openshot::stringToJson(value);
357 // Set all values that match
358 SetJsonValue(root);
359 }
360 catch (const std::exception& e)
361 {
362 // Error parsing JSON (or missing keys)
363 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
364 }
365}
366
367// Load Json::Value into this object
368void Keyframe::SetJsonValue(const Json::Value root) {
369 // Clear existing points
370 Points.clear();
371
372 if (!root["Points"].isNull())
373 // loop through points
374 for (const auto existing_point : root["Points"]) {
375 // Create Point
376 Point p;
377
378 // Load Json into Point
379 p.SetJsonValue(existing_point);
380
381 // Add Point to Keyframe
382 AddPoint(p);
383 }
384}
385
386// Get the fraction that represents how many times this value is repeated in the curve
387// This is depreciated and will be removed soon.
389 // Frame numbers (index) outside of the "defined" range of this
390 // keyframe result in a 1/1 default value.
391 if (index < 1 || (index + 1) >= GetLength()) {
392 return Fraction(1,1);
393 }
394 assert(Points.size() > 1); // Due to ! ((index + 1) >= GetLength) there are at least two points!
395
396 // First, get the value at the given frame and the closest point
397 // to the right.
398 int64_t const current_value = GetLong(index);
399 std::vector<Point>::const_iterator const candidate =
400 std::lower_bound(begin(Points), end(Points), static_cast<double>(index), IsPointBeforeX);
401 assert(candidate != end(Points)); // Due to the (index + 1) >= GetLength check above!
402
403 // Calculate how many of the next values are going to be the same:
404 int64_t next_repeats = 0;
405 std::vector<Point>::const_iterator i = candidate;
406 // If the index (frame number) is the X coordinate of the closest
407 // point, then look at the segment to the right; the "current"
408 // segement is not interesting because we're already at the last
409 // value of it.
410 if (i->co.X == index) {
411 ++i;
412 }
413 // Skip over "constant" (when rounded) segments.
414 bool all_constant = true;
415 for (; i != end(Points); ++i) {
416 if (current_value != round(i->co.Y)) {
417 all_constant = false;
418 break;
419 }
420 }
421 if (! all_constant) {
422 // Found a point which defines a segment which will give a
423 // different value than the current value. This means we
424 // moved at least one segment to the right, thus we cannot be
425 // at the first point.
426 assert(i != begin(Points));
427 Point const left = *(i - 1);
428 Point const right = *i;
429 int64_t change_at;
430 if (current_value < round(i->co.Y)) {
431 change_at = SearchBetweenPoints(left, right, current_value, std::less_equal<double>{});
432 } else {
433 assert(current_value > round(i->co.Y));
434 change_at = SearchBetweenPoints(left, right, current_value, std::greater_equal<double>{});
435 }
436 next_repeats = change_at - index;
437 } else {
438 // All values to the right are the same!
439 next_repeats = Points.back().co.X - index;
440 }
441
442 // Now look to the left, to the previous values.
443 all_constant = true;
444 i = candidate;
445 if (i != begin(Points)) {
446 // The binary search below assumes i to be the left point;
447 // candidate is the right point of the current segment
448 // though. So change this if possible. If this branch is NOT
449 // taken, then we're at/before the first point and all is
450 // constant!
451 --i;
452 }
453 int64_t previous_repeats = 0;
454 // Skip over constant (when rounded) segments!
455 for (; i != begin(Points); --i) {
456 if (current_value != round(i->co.Y)) {
457 all_constant = false;
458 break;
459 }
460 }
461 // Special case when skipped until the first point, but the first
462 // point is actually different. Will not happen if index is
463 // before the first point!
464 if (current_value != round(i->co.Y)) {
465 assert(i != candidate);
466 all_constant = false;
467 }
468 if (! all_constant) {
469 // There are at least two points, and we're not at the end,
470 // thus the following is safe!
471 Point const left = *i;
472 Point const right = *(i + 1);
473 int64_t change_at;
474 if (current_value > round(left.co.Y)) {
475 change_at = SearchBetweenPoints(left, right, current_value, std::less<double>{});
476 } else {
477 assert(current_value < round(left.co.Y));
478 change_at = SearchBetweenPoints(left, right, current_value, std::greater<double>{});
479 }
480 previous_repeats = index - change_at;
481 } else {
482 // Every previous value is the same (rounded) as the current
483 // value.
484 previous_repeats = index;
485 }
486 int64_t total_repeats = previous_repeats + next_repeats;
487 return Fraction(previous_repeats, total_repeats);
488}
489
490// Get the change in Y value (from the previous Y value)
491double Keyframe::GetDelta(int64_t index) const {
492 if (index < 1) return 0;
493 if (index == 1 && ! Points.empty()) return Points[0].co.Y;
494 if (index >= GetLength()) return 0;
495 return GetLong(index) - GetLong(index - 1);
496}
497
498// Get a point at a specific index
499Point const & Keyframe::GetPoint(int64_t index) const {
500 // Is index a valid point?
501 if (index >= 0 && index < (int64_t)Points.size())
502 return Points[index];
503 else
504 // Invalid index
505 throw OutOfBoundsPoint("Invalid point requested", index, Points.size());
506}
507
508// Get the number of values (i.e. coordinates on the X axis)
509int64_t Keyframe::GetLength() const {
510 if (Points.empty()) return 0;
511 if (Points.size() == 1) return 1;
512 return round(Points.back().co.X) + 1;
513}
514
515// Get the number of points (i.e. # of points)
516int64_t Keyframe::GetCount() const {
517
518 return Points.size();
519}
520
521// Remove a point by matching a coordinate
523 // loop through points, and find a matching coordinate
524 for (std::vector<Point>::size_type x = 0; x < Points.size(); x++) {
525 // Get each point
526 Point existing_point = Points[x];
527
528 // find a match
529 if (p.co.X == existing_point.co.X && p.co.Y == existing_point.co.Y) {
530 // Remove the matching point, and break out of loop
531 Points.erase(Points.begin() + x);
532 return;
533 }
534 }
535
536 // no matching point found
537 throw OutOfBoundsPoint("Invalid point requested", -1, Points.size());
538}
539
540// Remove a point by index
541void Keyframe::RemovePoint(int64_t index) {
542 // Is index a valid point?
543 if (index >= 0 && index < (int64_t)Points.size())
544 {
545 // Remove a specific point by index
546 Points.erase(Points.begin() + index);
547 }
548 else
549 // Invalid index
550 throw OutOfBoundsPoint("Invalid point requested", index, Points.size());
551}
552
553// Replace an existing point with a new point
554void Keyframe::UpdatePoint(int64_t index, Point p) {
555 // Remove matching point
556 RemovePoint(index);
557
558 // Add new point
559 AddPoint(p);
560}
561
563 cout << fixed << setprecision(4);
564 for (std::vector<Point>::const_iterator it = Points.begin(); it != Points.end(); it++) {
565 Point p = *it;
566 cout << p.co.X << "\t" << p.co.Y << endl;
567 }
568}
569
571 cout << fixed << setprecision(4);
572 cout << "Frame Number (X)\tValue (Y)\tIs Increasing\tRepeat Numerator\tRepeat Denominator\tDelta (Y Difference)\n";
573
574 for (int64_t i = 1; i < GetLength(); ++i) {
575 cout << i << "\t" << GetValue(i) << "\t" << IsIncreasing(i) << "\t" ;
576 cout << GetRepeatFraction(i).num << "\t" << GetRepeatFraction(i).den << "\t" << GetDelta(i) << "\n";
577 }
578}
579
580
581// Scale all points by a percentage (good for evenly lengthening or shortening an openshot::Keyframe)
582// 1.0 = same size, 1.05 = 5% increase, etc...
583void Keyframe::ScalePoints(double scale)
584{
585 // TODO: What if scale is small so that two points land on the
586 // same X coordinate?
587 // TODO: What if scale < 0?
588
589 // Loop through each point (skipping the 1st point)
590 for (std::vector<Point>::size_type point_index = 1; point_index < Points.size(); point_index++) {
591 // Scale X value
592 Points[point_index].co.X = round(Points[point_index].co.X * scale);
593 }
594}
595
596// Flip all the points in this openshot::Keyframe (useful for reversing an effect or transition, etc...)
598 for (std::vector<Point>::size_type point_index = 0, reverse_index = Points.size() - 1; point_index < reverse_index; point_index++, reverse_index--) {
599 // Flip the points
600 using std::swap;
601 swap(Points[point_index].co.Y, Points[reverse_index].co.Y);
602 // TODO: check that this has the desired effect even with
603 // regards to handles!
604 }
605}
Header file for all Exception classes.
int64_t SearchBetweenPoints(Point const &left, Point const &right, int64_t const current, Check check)
Definition: KeyFrame.cpp:115
Header file for the Keyframe class.
This class represents a Cartesian coordinate (X, Y) used in the Keyframe animation system.
Definition: Coordinate.h:54
double X
The X value of the coordinate (usually representing the frame #)
Definition: Coordinate.h:56
double Y
The Y value of the coordinate (usually representing the value of the property being animated)
Definition: Coordinate.h:57
This class represents a fraction.
Definition: Fraction.h:48
int num
Numerator for the fraction.
Definition: Fraction.h:50
int den
Denominator for the fraction.
Definition: Fraction.h:51
Exception for invalid JSON.
Definition: Exceptions.h:206
int64_t FindIndex(Point p) const
Get the index of a point by matching a coordinate.
Definition: KeyFrame.cpp:176
void RemovePoint(Point p)
Remove a point by matching a coordinate.
Definition: KeyFrame.cpp:522
void SetJson(const std::string value)
Load JSON string into this object.
Definition: KeyFrame.cpp:351
bool Contains(Point p) const
Does this keyframe contain a specific point.
Definition: KeyFrame.cpp:194
Point GetMaxPoint() const
Get max point (by Y coordinate)
Definition: KeyFrame.cpp:255
int GetInt(int64_t index) const
Get the rounded INT value at a specific index.
Definition: KeyFrame.cpp:292
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: KeyFrame.cpp:368
void AddPoint(Point p)
Add a new point on the key-frame. Each point has a primary coordinate, a left handle,...
Definition: KeyFrame.cpp:141
double GetDelta(int64_t index) const
Get the change in Y value (from the previous Y value)
Definition: KeyFrame.cpp:491
Point const & GetPoint(int64_t index) const
Get a point at a specific index.
Definition: KeyFrame.cpp:499
int64_t GetLength() const
Definition: KeyFrame.cpp:509
Fraction GetRepeatFraction(int64_t index) const
Get the fraction that represents how many times this value is repeated in the curve.
Definition: KeyFrame.cpp:388
void PrintValues() const
Print just the Y value of the point's primary coordinate.
Definition: KeyFrame.cpp:570
Point GetPreviousPoint(Point p) const
Get previous point (.
Definition: KeyFrame.cpp:236
int64_t GetLong(int64_t index) const
Get the rounded LONG value at a specific index.
Definition: KeyFrame.cpp:297
void UpdatePoint(int64_t index, Point p)
Replace an existing point with a new point.
Definition: KeyFrame.cpp:554
void ScalePoints(double scale)
Definition: KeyFrame.cpp:583
std::string Json() const
Generate JSON string of this object.
Definition: KeyFrame.cpp:328
double GetValue(int64_t index) const
Get the value at a specific index.
Definition: KeyFrame.cpp:268
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: KeyFrame.cpp:335
void FlipPoints()
Flip all the points in this openshot::Keyframe (useful for reversing an effect or transition,...
Definition: KeyFrame.cpp:597
void PrintPoints() const
Print a list of points.
Definition: KeyFrame.cpp:562
Keyframe()=default
Default constructor for the Keyframe class.
bool IsIncreasing(int index) const
Get the direction of the curve at a specific index (increasing or decreasing)
Definition: KeyFrame.cpp:302
int64_t GetCount() const
Get the number of points (i.e. # of points)
Definition: KeyFrame.cpp:516
Point GetClosestPoint(Point p) const
Get current point (or closest point to the right) from the X coordinate (i.e. the frame number)
Definition: KeyFrame.cpp:231
Exception for an out of bounds key-frame point.
Definition: Exceptions.h:304
A Point is the basic building block of a key-frame curve.
Definition: Point.h:82
Coordinate handle_left
This is the left handle coordinate (in percentages from 0 to 1)
Definition: Point.h:85
Coordinate co
This is the primary coordinate.
Definition: Point.h:84
InterpolationType interpolation
This is the interpolation mode.
Definition: Point.h:87
Coordinate handle_right
This is the right handle coordinate (in percentages from 0 to 1)
Definition: Point.h:86
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: Point.cpp:122
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:47
double InterpolateLinearCurve(Point const &left, Point const &right, double const target)
Linear interpolation between two points.
Definition: KeyFrame.cpp:52
double InterpolateBezierCurve(Point const &left, Point const &right, double const target, double const allowed_error)
Bezier interpolation between two points.
Definition: KeyFrame.cpp:60
bool IsPointBeforeX(Point const &p, double const x)
Check if the X coordinate of a given Point is lower than a given value.
Definition: KeyFrame.cpp:47
double InterpolateBetween(Point const &left, Point const &right, double target, double allowed_error)
Interpolate two points using the right Point's interpolation method.
Definition: KeyFrame.cpp:96
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:34
InterpolationType
This controls how a Keyframe uses this point to interpolate between two points.
Definition: Point.h:46
@ CONSTANT
Constant curves jump from their previous position to a new one (with no interpolation).
Definition: Point.h:49
@ BEZIER
Bezier curves are quadratic curves, which create a smooth curve.
Definition: Point.h:47
@ LINEAR
Linear curves are angular, straight lines between two points.
Definition: Point.h:48