OpenShot Library | libopenshot  0.2.6
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 
41 using namespace std;
42 using namespace openshot;
43 
44 namespace 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 
114 template<typename Check>
115 int64_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
131 Keyframe::Keyframe(double value) {
132  // Add initial point
133  AddPoint(Point(value));
134 }
135 
136 // Constructor which takes a vector of Points
137 Keyframe::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)
166 void 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
176 int64_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
194 bool Keyframe::Contains(Point p) const {
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)
201 Point 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
268 double 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
292 int Keyframe::GetInt(int64_t index) const {
293  return int(round(GetValue(index)));
294 }
295 
296 // Get the rounded INT value at a specific index
297 int64_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)
302 bool 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
328 std::string Keyframe::Json() const {
329 
330  // Return formatted string
331  return JsonValue().toStyledString();
332 }
333 
334 // Generate Json::Value for this object
335 Json::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
351 void 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
368 void 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.
388 Fraction Keyframe::GetRepeatFraction(int64_t index) const {
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)
491 double 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
499 Point 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)
509 int64_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)
516 int64_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
541 void 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
554 void Keyframe::UpdatePoint(int64_t index, Point p) {
555  // Remove matching point
556  RemovePoint(index);
557 
558  // Add new point
559  AddPoint(p);
560 }
561 
562 void Keyframe::PrintPoints() const {
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 
570 void Keyframe::PrintValues() const {
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...
583 void 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 }
double GetDelta(int64_t index) const
Get the change in Y value (from the previous Y value)
Definition: KeyFrame.cpp:491
This class represents a Cartesian coordinate (X, Y) used in the Keyframe animation system...
Definition: Coordinate.h:54
int num
Numerator for the fraction.
Definition: Fraction.h:50
Point GetMaxPoint() const
Get max point (by Y coordinate)
Definition: KeyFrame.cpp:255
void FlipPoints()
Flip all the points in this openshot::Keyframe (useful for reversing an effect or transition...
Definition: KeyFrame.cpp:597
Point const & GetPoint(int64_t index) const
Get a point at a specific index.
Definition: KeyFrame.cpp:499
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: Point.cpp:122
int64_t GetLong(int64_t index) const
Get the rounded LONG value at a specific index.
Definition: KeyFrame.cpp:297
Bezier curves are quadratic curves, which create a smooth curve.
Definition: Point.h:47
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
STL namespace.
Coordinate handle_left
This is the left handle coordinate (in percentages from 0 to 1)
Definition: Point.h:85
void ScalePoints(double scale)
Definition: KeyFrame.cpp:583
A Point is the basic building block of a key-frame curve.
Definition: Point.h:82
Point GetPreviousPoint(Point p) const
Get previous point (.
Definition: KeyFrame.cpp:236
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:34
bool IsIncreasing(int index) const
Get the direction of the curve at a specific index (increasing or decreasing)
Definition: KeyFrame.cpp:302
Header file for the Keyframe class.
bool Contains(Point p) const
Does this keyframe contain a specific point.
Definition: KeyFrame.cpp:194
double InterpolateBezierCurve(Point const &left, Point const &right, double const target, double const allowed_error)
Bezier interpolation between two points.
Definition: KeyFrame.cpp:60
void UpdatePoint(int64_t index, Point p)
Replace an existing point with a new point.
Definition: KeyFrame.cpp:554
int GetInt(int64_t index) const
Get the rounded INT value at a specific index.
Definition: KeyFrame.cpp:292
double InterpolateLinearCurve(Point const &left, Point const &right, double const target)
Linear interpolation between two points.
Definition: KeyFrame.cpp:52
void AddPoint(Point p)
Add a new point on the key-frame. Each point has a primary coordinate, a left handle, and a right handle.
Definition: KeyFrame.cpp:141
Header file for all Exception classes.
double Y
The Y value of the coordinate (usually representing the value of the property being animated) ...
Definition: Coordinate.h:57
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
void PrintPoints() const
Print a list of points.
Definition: KeyFrame.cpp:562
void RemovePoint(Point p)
Remove a point by matching a coordinate.
Definition: KeyFrame.cpp:522
This class represents a fraction.
Definition: Fraction.h:48
double InterpolateBetween(Point const &left, Point const &right, double target, double allowed_error)
Interpolate two points using the right Point&#39;s interpolation method.
Definition: KeyFrame.cpp:96
double X
The X value of the coordinate (usually representing the frame #)
Definition: Coordinate.h:56
void PrintValues() const
Print just the Y value of the point&#39;s primary coordinate.
Definition: KeyFrame.cpp:570
int64_t SearchBetweenPoints(Point const &left, Point const &right, int64_t const current, Check check)
Definition: KeyFrame.cpp:115
InterpolationType
This controls how a Keyframe uses this point to interpolate between two points.
Definition: Point.h:46
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
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: KeyFrame.cpp:368
if(!codec) codec
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:46
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: KeyFrame.cpp:335
Linear curves are angular, straight lines between two points.
Definition: Point.h:48
Coordinate co
This is the primary coordinate.
Definition: Point.h:84
int64_t FindIndex(Point p) const
Get the index of a point by matching a coordinate.
Definition: KeyFrame.cpp:176
Exception for invalid JSON.
Definition: Exceptions.h:205
int64_t GetCount() const
Get the number of points (i.e. # of points)
Definition: KeyFrame.cpp:516
double GetValue(int64_t index) const
Get the value at a specific index.
Definition: KeyFrame.cpp:268
Exception for an out of bounds key-frame point.
Definition: Exceptions.h:303
void SetJson(const std::string value)
Load JSON string into this object.
Definition: KeyFrame.cpp:351
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
int den
Denominator for the fraction.
Definition: Fraction.h:51
int64_t GetLength() const
Definition: KeyFrame.cpp:509
Constant curves jump from their previous position to a new one (with no interpolation).
Definition: Point.h:49
std::string Json() const
Generate JSON string of this object.
Definition: KeyFrame.cpp:328