OpenShot Library | libopenshot  0.2.3
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  * @section LICENSE
7  *
8  * Copyright (c) 2008-2014 OpenShot Studios, LLC
9  * <http://www.openshotstudios.com/>. This file is part of
10  * OpenShot Library (libopenshot), an open-source project dedicated to
11  * delivering high quality video editing and animation solutions to the
12  * world. For more information visit <http://www.openshot.org/>.
13  *
14  * OpenShot Library (libopenshot) is free software: you can redistribute it
15  * and/or modify it under the terms of the GNU Lesser General Public License
16  * as published by the Free Software Foundation, either version 3 of the
17  * License, or (at your option) any later version.
18  *
19  * OpenShot Library (libopenshot) is distributed in the hope that it will be
20  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public License
25  * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
26  */
27 
28 #include "../include/KeyFrame.h"
29 
30 using namespace std;
31 using namespace openshot;
32 
33 // Because points can be added in any order, we need to reorder them
34 // in ascending order based on the point.co.X value. This simplifies
35 // processing the curve, due to all the points going from left to right.
36 void Keyframe::ReorderPoints() {
37  // Loop through all coordinates, and sort them by the X attribute
38  for (int64_t x = 0; x < Points.size(); x++) {
39  int64_t compare_index = x;
40  int64_t smallest_index = x;
41 
42  for (int64_t compare_index = x + 1; compare_index < Points.size(); compare_index++) {
43  if (Points[compare_index].co.X < Points[smallest_index].co.X) {
44  smallest_index = compare_index;
45  }
46  }
47 
48  // swap items
49  if (smallest_index != compare_index) {
50  swap(Points[compare_index], Points[smallest_index]);
51  }
52  }
53 }
54 
55 // Constructor which sets the default point & coordinate at X=0
56 Keyframe::Keyframe(double value) : needs_update(true) {
57  // Init the factorial table, needed by bezier curves
58  CreateFactorialTable();
59 
60  // Add initial point
61  AddPoint(Point(value));
62 }
63 
64 // Keyframe constructor
65 Keyframe::Keyframe() : needs_update(true) {
66  // Init the factorial table, needed by bezier curves
67  CreateFactorialTable();
68 }
69 
70 // Add a new point on the key-frame. Each point has a primary coordinate,
71 // a left handle, and a right handle.
73  // mark as dirty
74  needs_update = true;
75 
76  // Check for duplicate point (and remove it)
77  Point closest = GetClosestPoint(p);
78  if (closest.co.X == p.co.X)
79  // Remove existing point
80  RemovePoint(closest);
81 
82  // Add point at correct spot
83  Points.push_back(p);
84 
85  // Sort / Re-order points based on X coordinate
86  ReorderPoints();
87 }
88 
89 // Add a new point on the key-frame, with some defaults set (BEZIER)
90 void Keyframe::AddPoint(double x, double y)
91 {
92  // Create a point
93  Point new_point(x, y, BEZIER);
94 
95  // Add the point
96  AddPoint(new_point);
97 }
98 
99 // Add a new point on the key-frame, with a specific interpolation type
100 void Keyframe::AddPoint(double x, double y, InterpolationType interpolate)
101 {
102  // Create a point
103  Point new_point(x, y, interpolate);
104 
105  // Add the point
106  AddPoint(new_point);
107 }
108 
109 // Get the index of a point by matching a coordinate
111  // loop through points, and find a matching coordinate
112  for (int64_t x = 0; x < Points.size(); x++) {
113  // Get each point
114  Point existing_point = Points[x];
115 
116  // find a match
117  if (p.co.X == existing_point.co.X && p.co.Y == existing_point.co.Y) {
118  // Remove the matching point, and break out of loop
119  return x;
120  }
121  }
122 
123  // no matching point found
124  throw OutOfBoundsPoint("Invalid point requested", -1, Points.size());
125 }
126 
127 // Determine if point already exists
129  // loop through points, and find a matching coordinate
130  for (int64_t x = 0; x < Points.size(); x++) {
131  // Get each point
132  Point existing_point = Points[x];
133 
134  // find a match
135  if (p.co.X == existing_point.co.X) {
136  // Remove the matching point, and break out of loop
137  return true;
138  }
139  }
140 
141  // no matching point found
142  return false;
143 }
144 
145 // Get current point (or closest point) from the X coordinate (i.e. the frame number)
147  Point closest(-1, -1);
148 
149  // loop through points, and find a matching coordinate
150  for (int64_t x = 0; x < Points.size(); x++) {
151  // Get each point
152  Point existing_point = Points[x];
153 
154  // find a match
155  if (existing_point.co.X >= p.co.X && !useLeft) {
156  // New closest point found (to the Right)
157  closest = existing_point;
158  break;
159  } else if (existing_point.co.X < p.co.X && useLeft) {
160  // New closest point found (to the Left)
161  closest = existing_point;
162  } else if (existing_point.co.X >= p.co.X && useLeft) {
163  // We've gone past the left point... so break
164  break;
165  }
166  }
167 
168  // Handle edge cases (if no point was found)
169  if (closest.co.X == -1) {
170  if (p.co.X <= 1 && Points.size() > 0)
171  // Assign 1st point
172  closest = Points[0];
173  else if (Points.size() > 0)
174  // Assign last point
175  closest = Points[Points.size() - 1];
176  }
177 
178  // no matching point found
179  return closest;
180 }
181 
182 // Get current point (or closest point to the right) from the X coordinate (i.e. the frame number)
184  return GetClosestPoint(p, false);
185 }
186 
187 // Get previous point (if any)
189 
190  // Lookup the index of this point
191  try {
192  int64_t index = FindIndex(p);
193 
194  // If not the 1st point
195  if (index > 0)
196  return Points[index - 1];
197  else
198  return Points[0];
199 
200  } catch (OutOfBoundsPoint) {
201  // No previous point
202  return Point(-1, -1);
203  }
204 }
205 
206 // Get max point (by Y coordinate)
208  Point maxPoint(-1, -1);
209 
210  // loop through points, and find the largest Y value
211  for (int64_t x = 0; x < Points.size(); x++) {
212  // Get each point
213  Point existing_point = Points[x];
214 
215  // Is point larger than max point
216  if (existing_point.co.Y >= maxPoint.co.Y) {
217  // New max point found
218  maxPoint = existing_point;
219  }
220  }
221 
222  return maxPoint;
223 }
224 
225 // Get the value at a specific index
226 double Keyframe::GetValue(int64_t index)
227 {
228  // Check if it needs to be processed
229  if (needs_update)
230  Process();
231 
232  // Is index a valid point?
233  if (index >= 0 && index < Values.size())
234  // Return value
235  return Values[index].Y;
236  else if (index < 0 && Values.size() > 0)
237  // Return the minimum value
238  return Values[0].Y;
239  else if (index >= Values.size() && Values.size() > 0)
240  // return the maximum value
241  return Values[Values.size() - 1].Y;
242  else
243  // return a blank coordinate (0,0)
244  return 0.0;
245 }
246 
247 // Get the rounded INT value at a specific index
248 int Keyframe::GetInt(int64_t index)
249 {
250  // Check if it needs to be processed
251  if (needs_update)
252  Process();
253 
254  // Is index a valid point?
255  if (index >= 0 && index < Values.size())
256  // Return value
257  return int(round(Values[index].Y));
258  else if (index < 0 && Values.size() > 0)
259  // Return the minimum value
260  return int(round(Values[0].Y));
261  else if (index >= Values.size() && Values.size() > 0)
262  // return the maximum value
263  return int(round(Values[Values.size() - 1].Y));
264  else
265  // return a blank coordinate (0,0)
266  return 0;
267 }
268 
269 // Get the rounded INT value at a specific index
270 int64_t Keyframe::GetLong(int64_t index)
271 {
272  // Check if it needs to be processed
273  if (needs_update)
274  Process();
275 
276  // Is index a valid point?
277  if (index >= 0 && index < Values.size())
278  // Return value
279  return long(round(Values[index].Y));
280  else if (index < 0 && Values.size() > 0)
281  // Return the minimum value
282  return long(round(Values[0].Y));
283  else if (index >= Values.size() && Values.size() > 0)
284  // return the maximum value
285  return long(round(Values[Values.size() - 1].Y));
286  else
287  // return a blank coordinate (0,0)
288  return 0;
289 }
290 
291 // Get the direction of the curve at a specific index (increasing or decreasing)
292 bool Keyframe::IsIncreasing(int index)
293 {
294  // Check if it needs to be processed
295  if (needs_update)
296  Process();
297 
298  // Is index a valid point?
299  if (index >= 1 && (index + 1) < Values.size()) {
300  int64_t current_value = GetLong(index);
301  int64_t previous_value = 0;
302  int64_t next_value = 0;
303  int64_t previous_repeats = 0;
304  int64_t next_repeats = 0;
305 
306  // Loop backwards and look for the next unique value
307  for (vector<Coordinate>::iterator backwards_it = Values.begin() + index; backwards_it != Values.begin(); backwards_it--) {
308  previous_value = long(round((*backwards_it).Y));
309  if (previous_value == current_value) {
310  // Found same value
311  previous_repeats++;
312  } else {
313  // Found non repeating value, no more repeats found
314  break;
315  }
316  }
317 
318  // Loop forwards and look for the next unique value
319  for (vector<Coordinate>::iterator forwards_it = Values.begin() + (index + 1); forwards_it != Values.end(); forwards_it++) {
320  next_value = long(round((*forwards_it).Y));
321  if (next_value == current_value) {
322  // Found same value
323  next_repeats++;
324  } else {
325  // Found non repeating value, no more repeats found
326  break;
327  }
328  }
329 
330  if (current_value < next_value) {
331  // Increasing
332  return true;
333  }
334  else if (current_value >= next_value) {
335  // Decreasing
336  return false;
337  }
338  }
339  else
340  // return default true (since most curves increase)
341  return true;
342 }
343 
344 // Generate JSON string of this object
345 string Keyframe::Json() {
346 
347  // Return formatted string
348  return JsonValue().toStyledString();
349 }
350 
351 // Generate Json::JsonValue for this object
352 Json::Value Keyframe::JsonValue() {
353 
354  // Create root json object
355  Json::Value root;
356  root["Points"] = Json::Value(Json::arrayValue);
357 
358  // loop through points, and find a matching coordinate
359  for (int x = 0; x < Points.size(); x++) {
360  // Get each point
361  Point existing_point = Points[x];
362  root["Points"].append(existing_point.JsonValue());
363  }
364 
365  // return JsonValue
366  return root;
367 }
368 
369 // Load JSON string into this object
370 void Keyframe::SetJson(string value) {
371 
372  // Parse JSON string into JSON objects
373  Json::Value root;
374  Json::Reader reader;
375  bool success = reader.parse( value, root );
376  if (!success)
377  // Raise exception
378  throw InvalidJSON("JSON could not be parsed (or is invalid)", "");
379 
380  try
381  {
382  // Set all values that match
383  SetJsonValue(root);
384  }
385  catch (exception e)
386  {
387  // Error parsing JSON (or missing keys)
388  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)", "");
389  }
390 }
391 
392 // Load Json::JsonValue into this object
393 void Keyframe::SetJsonValue(Json::Value root) {
394 
395  // mark as dirty
396  needs_update = true;
397 
398  // Clear existing points
399  Points.clear();
400 
401  if (!root["Points"].isNull())
402  // loop through points
403  for (int64_t x = 0; x < root["Points"].size(); x++) {
404  // Get each point
405  Json::Value existing_point = root["Points"][(Json::UInt) x];
406 
407  // Create Point
408  Point p;
409 
410  // Load Json into Point
411  p.SetJsonValue(existing_point);
412 
413  // Add Point to Keyframe
414  AddPoint(p);
415  }
416 }
417 
418 // Get the fraction that represents how many times this value is repeated in the curve
419 // This is depreciated and will be removed soon.
421 {
422  // Check if it needs to be processed
423  if (needs_update)
424  Process();
425 
426  // Is index a valid point?
427  if (index >= 1 && (index + 1) < Values.size()) {
428  int64_t current_value = GetLong(index);
429  int64_t previous_value = 0;
430  int64_t next_value = 0;
431  int64_t previous_repeats = 0;
432  int64_t next_repeats = 0;
433 
434  // Loop backwards and look for the next unique value
435  for (vector<Coordinate>::iterator backwards_it = Values.begin() + index; backwards_it != Values.begin(); backwards_it--) {
436  previous_value = long(round((*backwards_it).Y));
437  if (previous_value == current_value) {
438  // Found same value
439  previous_repeats++;
440  } else {
441  // Found non repeating value, no more repeats found
442  break;
443  }
444  }
445 
446  // Loop forwards and look for the next unique value
447  for (vector<Coordinate>::iterator forwards_it = Values.begin() + (index + 1); forwards_it != Values.end(); forwards_it++) {
448  next_value = long(round((*forwards_it).Y));
449  if (next_value == current_value) {
450  // Found same value
451  next_repeats++;
452  } else {
453  // Found non repeating value, no more repeats found
454  break;
455  }
456  }
457 
458  int64_t total_repeats = previous_repeats + next_repeats;
459  return Fraction(previous_repeats, total_repeats);
460  }
461  else
462  // return a blank coordinate
463  return Fraction(1,1);
464 }
465 
466 // Get the change in Y value (from the previous Y value)
467 double Keyframe::GetDelta(int64_t index)
468 {
469  // Check if it needs to be processed
470  if (needs_update)
471  Process();
472 
473  // Is index a valid point?
474  if (index >= 1 && (index + 1) < Values.size()) {
475  int64_t current_value = GetLong(index);
476  int64_t previous_value = 0;
477  int64_t next_value = 0;
478  int64_t previous_repeats = 0;
479  int64_t next_repeats = 0;
480 
481  // Loop backwards and look for the next unique value
482  for (vector<Coordinate>::iterator backwards_it = Values.begin() + index; backwards_it != Values.begin(); backwards_it--) {
483  previous_value = long(round((*backwards_it).Y));
484  if (previous_value == current_value) {
485  // Found same value
486  previous_repeats++;
487  } else {
488  // Found non repeating value, no more repeats found
489  break;
490  }
491  }
492 
493  // Loop forwards and look for the next unique value
494  for (vector<Coordinate>::iterator forwards_it = Values.begin() + (index + 1); forwards_it != Values.end(); forwards_it++) {
495  next_value = long(round((*forwards_it).Y));
496  if (next_value == current_value) {
497  // Found same value
498  next_repeats++;
499  } else {
500  // Found non repeating value, no more repeats found
501  break;
502  }
503  }
504 
505  // Check for matching previous value (special case for 1st element)
506  if (current_value == previous_value)
507  previous_value = 0;
508 
509  if (previous_repeats == 1)
510  return current_value - previous_value;
511  else
512  return 0.0;
513  }
514  else
515  // return a blank coordinate
516  return 0.0;
517 }
518 
519 // Get a point at a specific index
520 Point& Keyframe::GetPoint(int64_t index) {
521  // Is index a valid point?
522  if (index >= 0 && index < Points.size())
523  return Points[index];
524  else
525  // Invalid index
526  throw OutOfBoundsPoint("Invalid point requested", index, Points.size());
527 }
528 
529 // Get the number of values (i.e. coordinates on the X axis)
531  // Check if it needs to be processed
532  if (needs_update)
533  Process();
534 
535  // return the size of the Values vector
536  return Values.size();
537 }
538 
539 // Get the number of points (i.e. # of points)
541 
542  // return the size of the Values vector
543  return Points.size();
544 }
545 
546 // Remove a point by matching a coordinate
548  // mark as dirty
549  needs_update = true;
550 
551  // loop through points, and find a matching coordinate
552  for (int64_t x = 0; x < Points.size(); x++) {
553  // Get each point
554  Point existing_point = Points[x];
555 
556  // find a match
557  if (p.co.X == existing_point.co.X && p.co.Y == existing_point.co.Y) {
558  // Remove the matching point, and break out of loop
559  Points.erase(Points.begin() + x);
560  return;
561  }
562  }
563 
564  // no matching point found
565  throw OutOfBoundsPoint("Invalid point requested", -1, Points.size());
566 }
567 
568 // Remove a point by index
569 void Keyframe::RemovePoint(int64_t index) {
570  // mark as dirty
571  needs_update = true;
572 
573  // Is index a valid point?
574  if (index >= 0 && index < Points.size())
575  {
576  // Remove a specific point by index
577  Points.erase(Points.begin() + index);
578  }
579  else
580  // Invalid index
581  throw OutOfBoundsPoint("Invalid point requested", index, Points.size());
582 }
583 
584 void Keyframe::UpdatePoint(int64_t index, Point p) {
585  // mark as dirty
586  needs_update = true;
587 
588  // Remove matching point
589  RemovePoint(index);
590 
591  // Add new point
592  AddPoint(p);
593 
594  // Reorder points
595  ReorderPoints();
596 }
597 
599  // Check if it needs to be processed
600  if (needs_update)
601  Process();
602 
603  cout << fixed << setprecision(4);
604  for (vector<Point>::iterator it = Points.begin(); it != Points.end(); it++) {
605  Point p = *it;
606  cout << p.co.X << "\t" << p.co.Y << endl;
607  }
608 }
609 
611  // Check if it needs to be processed
612  if (needs_update)
613  Process();
614 
615  cout << fixed << setprecision(4);
616  cout << "Frame Number (X)\tValue (Y)\tIs Increasing\tRepeat Numerator\tRepeat Denominator\tDelta (Y Difference)" << endl;
617 
618  for (vector<Coordinate>::iterator it = Values.begin() + 1; it != Values.end(); it++) {
619  Coordinate c = *it;
620  cout << long(round(c.X)) << "\t" << c.Y << "\t" << IsIncreasing(c.X) << "\t" << GetRepeatFraction(c.X).num << "\t" << GetRepeatFraction(c.X).den << "\t" << GetDelta(c.X) << endl;
621  }
622 }
623 
625 
626  #pragma omp critical (keyframe_process)
627  {
628  // only process if needed
629  if (needs_update && Points.size() == 0) {
630  // Clear all values
631  Values.clear();
632  }
633  else if (needs_update && Points.size() > 0)
634  {
635  // Clear all values
636  Values.clear();
637 
638  // fill in all values between 1 and 1st point's co.X
639  Point p1 = Points[0];
640  if (Points.size() > 1)
641  // Fill in previous X values (before 1st point)
642  for (int64_t x = 0; x < p1.co.X; x++)
643  Values.push_back(Coordinate(Values.size(), p1.co.Y));
644  else
645  // Add a single value (since we only have 1 point)
646  Values.push_back(Coordinate(Values.size(), p1.co.Y));
647 
648  // Loop through each pair of points (1 less than the max points). Each
649  // pair of points is used to process a segment of the keyframe.
650  Point p2(0, 0);
651  for (int64_t x = 0; x < Points.size() - 1; x++) {
652  p1 = Points[x];
653  p2 = Points[x + 1];
654 
655  // process segment p1,p2
656  ProcessSegment(x, p1, p2);
657  }
658  }
659 
660  // reset flag
661  needs_update = false;
662  }
663 }
664 
665 void Keyframe::ProcessSegment(int Segment, Point p1, Point p2) {
666  // Determine the number of values for this segment
667  int64_t number_of_values = round(p2.co.X) - round(p1.co.X);
668 
669  // Exit function if no values
670  if (number_of_values == 0)
671  return;
672 
673  // Based on the interpolation mode, fill the "values" vector with the coordinates
674  // for this segment
675  switch (p2.interpolation) {
676 
677  // Calculate the "values" for this segment in with a LINEAR equation, effectively
678  // creating a straight line with coordinates.
679  case LINEAR: {
680  // Get the difference in value
681  double current_value = p1.co.Y;
682  double value_difference = p2.co.Y - p1.co.Y;
683  double value_increment = 0.0f;
684 
685  // Get the increment value, but take into account the
686  // first segment has 1 extra value
687  value_increment = value_difference / (double) (number_of_values);
688 
689  if (Segment == 0)
690  // Add an extra value to the first segment
691  number_of_values++;
692  else
693  // If not 1st segment, skip the first value
694  current_value += value_increment;
695 
696  // Add each increment to the values vector
697  for (int64_t x = 0; x < number_of_values; x++) {
698  // add value as a coordinate to the "values" vector
699  Values.push_back(Coordinate(Values.size(), current_value));
700 
701  // increment value
702  current_value += value_increment;
703  }
704 
705  break;
706  }
707 
708  // Calculate the "values" for this segment using a quadratic Bezier curve. This creates a
709  // smooth curve.
710  case BEZIER: {
711 
712  // Always increase the number of points by 1 (need all possible points
713  // to correctly calculate the curve).
714  number_of_values++;
715  number_of_values *= 4; // We need a higher resolution curve (4X)
716 
717  // Diff between points
718  double X_diff = p2.co.X - p1.co.X;
719  double Y_diff = p2.co.Y - p1.co.Y;
720 
721  vector<Coordinate> segment_coordinates;
722  segment_coordinates.push_back(p1.co);
723  segment_coordinates.push_back(Coordinate(p1.co.X + (p1.handle_right.X * X_diff), p1.co.Y + (p1.handle_right.Y * Y_diff)));
724  segment_coordinates.push_back(Coordinate(p1.co.X + (p2.handle_left.X * X_diff), p1.co.Y + (p2.handle_left.Y * Y_diff)));
725  segment_coordinates.push_back(p2.co);
726 
727  vector<Coordinate> raw_coordinates;
728  int64_t npts = segment_coordinates.size();
729  int64_t icount, jcount;
730  double step, t;
731  double last_x = -1; // small number init, to track the last used x
732 
733  // Calculate points on curve
734  icount = 0;
735  t = 0;
736 
737  step = (double) 1.0 / (number_of_values - 1);
738 
739  for (int64_t i1 = 0; i1 < number_of_values; i1++) {
740  if ((1.0 - t) < 5e-6)
741  t = 1.0;
742 
743  jcount = 0;
744 
745  double new_x = 0.0f;
746  double new_y = 0.0f;
747 
748  for (int64_t i = 0; i < npts; i++) {
749  Coordinate co = segment_coordinates[i];
750  double basis = Bernstein(npts - 1, i, t);
751  new_x += basis * co.X;
752  new_y += basis * co.Y;
753  }
754 
755  // Add new value to the vector
756  Coordinate current_value(new_x, new_y);
757 
758  // Add all values for 1st segment
759  raw_coordinates.push_back(current_value);
760 
761  // increment counters
762  icount += 2;
763  t += step;
764  }
765 
766  // Loop through the raw coordinates, and map them correctly to frame numbers. For example,
767  // we can't have duplicate X values, since X represents our frame numbers.
768  int64_t current_frame = p1.co.X;
769  double current_value = p1.co.Y;
770  for (int64_t i = 0; i < raw_coordinates.size(); i++)
771  {
772  // Get the raw coordinate
773  Coordinate raw = raw_coordinates[i];
774 
775  if (current_frame == round(raw.X))
776  // get value of raw coordinate
777  current_value = raw.Y;
778  else
779  {
780  // Missing X values (use last known Y values)
781  int64_t number_of_missing = round(raw.X) - current_frame;
782  for (int64_t missing = 0; missing < number_of_missing; missing++)
783  {
784  // Add new value to the vector
785  Coordinate new_coord(current_frame, current_value);
786 
787  if (Segment == 0 || Segment > 0 && current_frame > p1.co.X)
788  // Add to "values" vector
789  Values.push_back(new_coord);
790 
791  // Increment frame
792  current_frame++;
793  }
794 
795  // increment the current value
796  current_value = raw.Y;
797  }
798  }
799 
800  // Add final coordinate
801  Coordinate new_coord(current_frame, current_value);
802  Values.push_back(new_coord);
803 
804  break;
805  }
806 
807  // Calculate the "values" of this segment by maintaining the value of p1 until the
808  // last point, and then make the value jump to p2. This effectively just jumps
809  // the value, instead of ramping up or down the value.
810  case CONSTANT: {
811 
812  if (Segment == 0)
813  // first segment has 1 extra value
814  number_of_values++;
815 
816  // Add each increment to the values vector
817  for (int64_t x = 0; x < number_of_values; x++) {
818  if (x < (number_of_values - 1)) {
819  // Not the last value of this segment
820  // add coordinate to "values"
821  Values.push_back(Coordinate(Values.size(), p1.co.Y));
822  } else {
823  // This is the last value of this segment
824  // add coordinate to "values"
825  Values.push_back(Coordinate(Values.size(), p2.co.Y));
826  }
827  }
828  break;
829  }
830 
831  }
832 }
833 
834 // Create lookup table for fast factorial calculation
835 void Keyframe::CreateFactorialTable() {
836  // Only 4 lookups are needed, because we only support 4 coordinates per curve
837  FactorialLookup[0] = 1.0;
838  FactorialLookup[1] = 1.0;
839  FactorialLookup[2] = 2.0;
840  FactorialLookup[3] = 6.0;
841 }
842 
843 // Get a factorial for a coordinate
844 double Keyframe::Factorial(int64_t n) {
845  assert(n >= 0 && n <= 3);
846  return FactorialLookup[n]; /* returns the value n! as a SUMORealing point number */
847 }
848 
849 // Calculate the factorial function for Bernstein basis
850 double Keyframe::Ni(int64_t n, int64_t i) {
851  double ni;
852  double a1 = Factorial(n);
853  double a2 = Factorial(i);
854  double a3 = Factorial(n - i);
855  ni = a1 / (a2 * a3);
856  return ni;
857 }
858 
859 // Calculate Bernstein basis
860 double Keyframe::Bernstein(int64_t n, int64_t i, double t) {
861  double basis;
862  double ti; /* t^i */
863  double tni; /* (1 - t)^i */
864 
865  /* Prevent problems with pow */
866  if (t == 0.0 && i == 0)
867  ti = 1.0;
868  else
869  ti = pow(t, i);
870 
871  if (n == i && t == 1.0)
872  tni = 1.0;
873  else
874  tni = pow((1 - t), (n - i));
875 
876  // Bernstein basis
877  basis = Ni(n, i) * ti * tni;
878  return basis;
879 }
880 
881 // Scale all points by a percentage (good for evenly lengthening or shortening an openshot::Keyframe)
882 // 1.0 = same size, 1.05 = 5% increase, etc...
883 void Keyframe::ScalePoints(double scale)
884 {
885  // Loop through each point (skipping the 1st point)
886  for (int64_t point_index = 0; point_index < Points.size(); point_index++) {
887  // Skip the 1st point
888  if (point_index == 0)
889  continue;
890 
891  // Scale X value
892  Points[point_index].co.X = round(Points[point_index].co.X * scale);
893 
894  // Mark for re-processing
895  needs_update = true;
896  }
897 }
898 
899 // Flip all the points in this openshot::Keyframe (useful for reversing an effect or transition, etc...)
901 {
902  // Loop through each point
903  vector<Point> FlippedPoints;
904  for (int64_t point_index = 0, reverse_index = Points.size() - 1; point_index < Points.size(); point_index++, reverse_index--) {
905  // Flip the points
906  Point p = Points[point_index];
907  p.co.Y = Points[reverse_index].co.Y;
908  FlippedPoints.push_back(p);
909  }
910 
911  // Swap vectors
912  Points.swap(FlippedPoints);
913 
914  // Mark for re-processing
915  needs_update = true;
916 }
vector< Coordinate > Values
Vector of all Values (i.e. the processed coordinates from the curve)
Definition: KeyFrame.h:93
Point GetMaxPoint()
Get max point (by Y coordinate)
Definition: KeyFrame.cpp:207
Json::Value JsonValue()
Generate Json::JsonValue for this object.
Definition: Point.cpp:115
This class represents a Cartesian coordinate (X, Y) used in the Keyframe animation system...
Definition: Coordinate.h:54
int64_t GetCount()
Get the number of points (i.e. # of points)
Definition: KeyFrame.cpp:540
int num
Numerator for the fraction.
Definition: Fraction.h:44
Keyframe()
Default constructor for the Keyframe class.
Definition: KeyFrame.cpp:65
void FlipPoints()
Flip all the points in this openshot::Keyframe (useful for reversing an effect or transition...
Definition: KeyFrame.cpp:900
Json::Value JsonValue()
Generate Json::JsonValue for this object.
Definition: KeyFrame.cpp:352
Bezier curves are quadratic curves, which create a smooth curve.
Definition: Point.h:46
Point & GetPoint(int64_t index)
Get a point at a specific index.
Definition: KeyFrame.cpp:520
InterpolationType interpolation
This is the interpolation mode.
Definition: Point.h:86
Coordinate handle_right
This is the right handle coordinate (in percentages from 0 to 1)
Definition: Point.h:85
bool Contains(Point p)
Does this keyframe contain a specific point.
Definition: KeyFrame.cpp:128
Coordinate handle_left
This is the left handle coordinate (in percentages from 0 to 1)
Definition: Point.h:84
void ScalePoints(double scale)
Definition: KeyFrame.cpp:883
A Point is the basic building block of a key-frame curve.
Definition: Point.h:81
Point GetPreviousPoint(Point p)
Get previous point (.
Definition: KeyFrame.cpp:188
void UpdatePoint(int64_t index, Point p)
Replace an existing point with a new point.
Definition: KeyFrame.cpp:584
void SetJsonValue(Json::Value root)
Load Json::JsonValue into this object.
Definition: KeyFrame.cpp:393
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:72
double Y
The Y value of the coordinate (usually representing the value of the property being animated) ...
Definition: Coordinate.h:57
void PrintValues()
Print just the Y value of the point's primary coordinate.
Definition: KeyFrame.cpp:610
void RemovePoint(Point p)
Remove a point by matching a coordinate.
Definition: KeyFrame.cpp:547
int64_t GetLength()
Definition: KeyFrame.cpp:530
bool IsIncreasing(int index)
Get the direction of the curve at a specific index (increasing or decreasing)
Definition: KeyFrame.cpp:292
This class represents a fraction.
Definition: Fraction.h:42
int GetInt(int64_t index)
Get the rounded INT value at a specific index.
Definition: KeyFrame.cpp:248
int64_t FindIndex(Point p)
Get the index of a point by matching a coordinate.
Definition: KeyFrame.cpp:110
void Process()
Calculate all of the values for this keyframe.
Definition: KeyFrame.cpp:624
Fraction GetRepeatFraction(int64_t index)
Get the fraction that represents how many times this value is repeated in the curve.
Definition: KeyFrame.cpp:420
vector< Point > Points
Vector of all Points.
Definition: KeyFrame.h:92
double X
The X value of the coordinate (usually representing the frame #)
Definition: Coordinate.h:56
double GetDelta(int64_t index)
Get the change in Y value (from the previous Y value)
Definition: KeyFrame.cpp:467
Point GetClosestPoint(Point p)
Get current point (or closest point to the right) from the X coordinate (i.e. the frame number) ...
Definition: KeyFrame.cpp:183
InterpolationType
This controls how a Keyframe uses this point to interpolate between two points.
Definition: Point.h:45
double GetValue(int64_t index)
Get the value at a specific index.
Definition: KeyFrame.cpp:226
string Json()
Get and Set JSON methods.
Definition: KeyFrame.cpp:345
Linear curves are angular, straight lines between two points.
Definition: Point.h:47
Coordinate co
This is the primary coordinate.
Definition: Point.h:83
Exception for invalid JSON.
Definition: Exceptions.h:152
int64_t GetLong(int64_t index)
Get the rounded LONG value at a specific index.
Definition: KeyFrame.cpp:270
Exception for an out of bounds key-frame point.
Definition: Exceptions.h:213
void PrintPoints()
Print a list of points.
Definition: KeyFrame.cpp:598
void SetJsonValue(Json::Value root)
Load Json::JsonValue into this object.
Definition: Point.cpp:155
void SetJson(string value)
Load JSON string into this object.
Definition: KeyFrame.cpp:370
int den
Denominator for the fraction.
Definition: Fraction.h:45
Constant curves jump from their previous position to a new one (with no interpolation).
Definition: Point.h:48