OpenShot Library | libopenshot  0.1.9
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 >= 0 && index < Values.size())
300  // Return value
301  return long(round(Values[index].IsIncreasing()));
302  else if (index < 0 && Values.size() > 0)
303  // Return the minimum value
304  return long(round(Values[0].IsIncreasing()));
305  else if (index >= Values.size() && Values.size() > 0)
306  // return the maximum value
307  return long(round(Values[Values.size() - 1].IsIncreasing()));
308  else
309  // return the default direction of most curves (i.e. increasing is true)
310  return true;
311 }
312 
313 // Generate JSON string of this object
314 string Keyframe::Json() {
315 
316  // Return formatted string
317  return JsonValue().toStyledString();
318 }
319 
320 // Generate Json::JsonValue for this object
321 Json::Value Keyframe::JsonValue() {
322 
323  // Create root json object
324  Json::Value root;
325  root["Points"] = Json::Value(Json::arrayValue);
326 
327  // loop through points, and find a matching coordinate
328  for (int x = 0; x < Points.size(); x++) {
329  // Get each point
330  Point existing_point = Points[x];
331  root["Points"].append(existing_point.JsonValue());
332  }
333 
334  // return JsonValue
335  return root;
336 }
337 
338 // Load JSON string into this object
339 void Keyframe::SetJson(string value) {
340 
341  // Parse JSON string into JSON objects
342  Json::Value root;
343  Json::Reader reader;
344  bool success = reader.parse( value, root );
345  if (!success)
346  // Raise exception
347  throw InvalidJSON("JSON could not be parsed (or is invalid)", "");
348 
349  try
350  {
351  // Set all values that match
352  SetJsonValue(root);
353  }
354  catch (exception e)
355  {
356  // Error parsing JSON (or missing keys)
357  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)", "");
358  }
359 }
360 
361 // Load Json::JsonValue into this object
362 void Keyframe::SetJsonValue(Json::Value root) {
363 
364  // mark as dirty
365  needs_update = true;
366 
367  // Clear existing points
368  Points.clear();
369 
370  if (!root["Points"].isNull())
371  // loop through points
372  for (int64_t x = 0; x < root["Points"].size(); x++) {
373  // Get each point
374  Json::Value existing_point = root["Points"][(Json::UInt) x];
375 
376  // Create Point
377  Point p;
378 
379  // Load Json into Point
380  p.SetJsonValue(existing_point);
381 
382  // Add Point to Keyframe
383  AddPoint(p);
384  }
385 }
386 
387 // Get the fraction that represents how many times this value is repeated in the curve
389 {
390  // Check if it needs to be processed
391  if (needs_update)
392  Process();
393 
394  // Is index a valid point?
395  if (index >= 0 && index < Values.size())
396  // Return value
397  return Values[index].Repeat();
398  else if (index < 0 && Values.size() > 0)
399  // Return the minimum value
400  return Values[0].Repeat();
401  else if (index >= Values.size() && Values.size() > 0)
402  // return the maximum value
403  return Values[Values.size() - 1].Repeat();
404  else
405  // return a blank coordinate (0,0)
406  return Fraction(1,1);
407 }
408 
409 // Get the change in Y value (from the previous Y value)
410 double Keyframe::GetDelta(int64_t index)
411 {
412  // Check if it needs to be processed
413  if (needs_update)
414  Process();
415 
416  // Is index a valid point?
417  if (index >= 0 && index < Values.size())
418  // Return value
419  return Values[index].Delta();
420  else if (index < 0 && Values.size() > 0)
421  // Return the minimum value
422  return Values[0].Delta();
423  else if (index >= Values.size() && Values.size() > 0)
424  // return the maximum value
425  return Values[Values.size() - 1].Delta();
426  else
427  // return a blank coordinate (0,0)
428  return 0.0;
429 }
430 
431 // Get a point at a specific index
432 Point& Keyframe::GetPoint(int64_t index) {
433  // Is index a valid point?
434  if (index >= 0 && index < Points.size())
435  return Points[index];
436  else
437  // Invalid index
438  throw OutOfBoundsPoint("Invalid point requested", index, Points.size());
439 }
440 
441 // Get the number of values (i.e. coordinates on the X axis)
443  // Check if it needs to be processed
444  if (needs_update)
445  Process();
446 
447  // return the size of the Values vector
448  return Values.size();
449 }
450 
451 // Get the number of points (i.e. # of points)
453 
454  // return the size of the Values vector
455  return Points.size();
456 }
457 
458 // Remove a point by matching a coordinate
460  // mark as dirty
461  needs_update = true;
462 
463  // loop through points, and find a matching coordinate
464  for (int64_t x = 0; x < Points.size(); x++) {
465  // Get each point
466  Point existing_point = Points[x];
467 
468  // find a match
469  if (p.co.X == existing_point.co.X && p.co.Y == existing_point.co.Y) {
470  // Remove the matching point, and break out of loop
471  Points.erase(Points.begin() + x);
472  return;
473  }
474  }
475 
476  // no matching point found
477  throw OutOfBoundsPoint("Invalid point requested", -1, Points.size());
478 }
479 
480 // Remove a point by index
481 void Keyframe::RemovePoint(int64_t index) {
482  // mark as dirty
483  needs_update = true;
484 
485  // Is index a valid point?
486  if (index >= 0 && index < Points.size())
487  {
488  // Remove a specific point by index
489  Points.erase(Points.begin() + index);
490  }
491  else
492  // Invalid index
493  throw OutOfBoundsPoint("Invalid point requested", index, Points.size());
494 }
495 
496 void Keyframe::UpdatePoint(int64_t index, Point p) {
497  // mark as dirty
498  needs_update = true;
499 
500  // Remove matching point
501  RemovePoint(index);
502 
503  // Add new point
504  AddPoint(p);
505 
506  // Reorder points
507  ReorderPoints();
508 }
509 
511  // Check if it needs to be processed
512  if (needs_update)
513  Process();
514 
515  cout << fixed << setprecision(4);
516  for (vector<Point>::iterator it = Points.begin(); it != Points.end(); it++) {
517  Point p = *it;
518  cout << p.co.X << "\t" << p.co.Y << endl;
519  }
520 }
521 
523  // Check if it needs to be processed
524  if (needs_update)
525  Process();
526 
527  cout << fixed << setprecision(4);
528  cout << "Frame Number (X)\tValue (Y)\tIs Increasing\tRepeat Numerator\tRepeat Denominator\tDelta (Y Difference)" << endl;
529 
530  for (vector<Coordinate>::iterator it = Values.begin() + 1; it != Values.end(); it++) {
531  Coordinate c = *it;
532  cout << long(round(c.X)) << "\t" << c.Y << "\t" << c.IsIncreasing() << "\t" << c.Repeat().num << "\t" << c.Repeat().den << "\t" << c.Delta() << endl;
533  }
534 }
535 
537 
538  #pragma omp critical (keyframe_process)
539  {
540  // only process if needed
541  if (needs_update && Points.size() == 0) {
542  // Clear all values
543  Values.clear();
544  }
545  else if (needs_update && Points.size() > 0)
546  {
547  // Clear all values
548  Values.clear();
549 
550  // fill in all values between 1 and 1st point's co.X
551  Point p1 = Points[0];
552  if (Points.size() > 1)
553  // Fill in previous X values (before 1st point)
554  for (int64_t x = 0; x < p1.co.X; x++)
555  Values.push_back(Coordinate(Values.size(), p1.co.Y));
556  else
557  // Add a single value (since we only have 1 point)
558  Values.push_back(Coordinate(Values.size(), p1.co.Y));
559 
560  // Loop through each pair of points (1 less than the max points). Each
561  // pair of points is used to process a segment of the keyframe.
562  Point p2(0, 0);
563  for (int64_t x = 0; x < Points.size() - 1; x++) {
564  p1 = Points[x];
565  p2 = Points[x + 1];
566 
567  // process segment p1,p2
568  ProcessSegment(x, p1, p2);
569  }
570 
571  // Loop through each Value, and set the direction of the coordinate. This is used
572  // when time mapping, to determine what direction the audio waveforms play.
573  bool increasing = true;
574  int repeat_count = 1;
575  int64_t last_value = 0;
576  for (vector<Coordinate>::iterator it = Values.begin() + 1; it != Values.end(); it++) {
577  int current_value = long(round((*it).Y));
578  int64_t next_value = long(round((*it).Y));
579  int64_t prev_value = long(round((*it).Y));
580  if (it + 1 != Values.end())
581  next_value = long(round((*(it + 1)).Y));
582  if (it - 1 >= Values.begin())
583  prev_value = long(round((*(it - 1)).Y));
584 
585  // Loop forward and look for the next unique value (to determine direction)
586  for (vector<Coordinate>::iterator direction_it = it + 1; direction_it != Values.end(); direction_it++) {
587  int64_t next = long(round((*direction_it).Y));
588 
589  // Detect direction
590  if (current_value < next)
591  {
592  increasing = true;
593  break;
594  }
595  else if (current_value > next)
596  {
597  increasing = false;
598  break;
599  }
600  }
601 
602  // Set direction
603  (*it).IsIncreasing(increasing);
604 
605  // Detect repeated Y value
606  if (current_value == last_value)
607  // repeated, so increment count
608  repeat_count++;
609  else
610  // reset repeat counter
611  repeat_count = 1;
612 
613  // Detect how many 'more' times it's repeated
614  int additional_repeats = 0;
615  for (vector<Coordinate>::iterator repeat_it = it + 1; repeat_it != Values.end(); repeat_it++) {
616  int64_t next = long(round((*repeat_it).Y));
617  if (next == current_value)
618  // repeated, so increment count
619  additional_repeats++;
620  else
621  break; // stop looping
622  }
623 
624  // Set repeat fraction
625  (*it).Repeat(Fraction(repeat_count, repeat_count + additional_repeats));
626 
627  // Set delta (i.e. different from previous unique Y value)
628  (*it).Delta(current_value - last_value);
629 
630  // track the last value
631  last_value = current_value;
632  }
633  }
634 
635  // reset flag
636  needs_update = false;
637  }
638 }
639 
640 void Keyframe::ProcessSegment(int Segment, Point p1, Point p2) {
641  // Determine the number of values for this segment
642  int64_t number_of_values = round(p2.co.X) - round(p1.co.X);
643 
644  // Exit function if no values
645  if (number_of_values == 0)
646  return;
647 
648  // Based on the interpolation mode, fill the "values" vector with the coordinates
649  // for this segment
650  switch (p2.interpolation) {
651 
652  // Calculate the "values" for this segment in with a LINEAR equation, effectively
653  // creating a straight line with coordinates.
654  case LINEAR: {
655  // Get the difference in value
656  double current_value = p1.co.Y;
657  double value_difference = p2.co.Y - p1.co.Y;
658  double value_increment = 0.0f;
659 
660  // Get the increment value, but take into account the
661  // first segment has 1 extra value
662  value_increment = value_difference / (double) (number_of_values);
663 
664  if (Segment == 0)
665  // Add an extra value to the first segment
666  number_of_values++;
667  else
668  // If not 1st segment, skip the first value
669  current_value += value_increment;
670 
671  // Add each increment to the values vector
672  for (int64_t x = 0; x < number_of_values; x++) {
673  // add value as a coordinate to the "values" vector
674  Values.push_back(Coordinate(Values.size(), current_value));
675 
676  // increment value
677  current_value += value_increment;
678  }
679 
680  break;
681  }
682 
683  // Calculate the "values" for this segment using a quadratic Bezier curve. This creates a
684  // smooth curve.
685  case BEZIER: {
686 
687  // Always increase the number of points by 1 (need all possible points
688  // to correctly calculate the curve).
689  number_of_values++;
690  number_of_values *= 4; // We need a higher resolution curve (4X)
691 
692  // Diff between points
693  double X_diff = p2.co.X - p1.co.X;
694  double Y_diff = p2.co.Y - p1.co.Y;
695 
696  vector<Coordinate> segment_coordinates;
697  segment_coordinates.push_back(p1.co);
698  segment_coordinates.push_back(Coordinate(p1.co.X + (p1.handle_right.X * X_diff), p1.co.Y + (p1.handle_right.Y * Y_diff)));
699  segment_coordinates.push_back(Coordinate(p1.co.X + (p2.handle_left.X * X_diff), p1.co.Y + (p2.handle_left.Y * Y_diff)));
700  segment_coordinates.push_back(p2.co);
701 
702  vector<Coordinate> raw_coordinates;
703  int64_t npts = segment_coordinates.size();
704  int64_t icount, jcount;
705  double step, t;
706  double last_x = -1; // small number init, to track the last used x
707 
708  // Calculate points on curve
709  icount = 0;
710  t = 0;
711 
712  step = (double) 1.0 / (number_of_values - 1);
713 
714  for (int64_t i1 = 0; i1 < number_of_values; i1++) {
715  if ((1.0 - t) < 5e-6)
716  t = 1.0;
717 
718  jcount = 0;
719 
720  double new_x = 0.0f;
721  double new_y = 0.0f;
722 
723  for (int64_t i = 0; i < npts; i++) {
724  Coordinate co = segment_coordinates[i];
725  double basis = Bernstein(npts - 1, i, t);
726  new_x += basis * co.X;
727  new_y += basis * co.Y;
728  }
729 
730  // Add new value to the vector
731  Coordinate current_value(new_x, new_y);
732 
733  // Add all values for 1st segment
734  raw_coordinates.push_back(current_value);
735 
736  // increment counters
737  icount += 2;
738  t += step;
739  }
740 
741  // Loop through the raw coordinates, and map them correctly to frame numbers. For example,
742  // we can't have duplicate X values, since X represents our frame numbers.
743  int64_t current_frame = p1.co.X;
744  double current_value = p1.co.Y;
745  for (int64_t i = 0; i < raw_coordinates.size(); i++)
746  {
747  // Get the raw coordinate
748  Coordinate raw = raw_coordinates[i];
749 
750  if (current_frame == round(raw.X))
751  // get value of raw coordinate
752  current_value = raw.Y;
753  else
754  {
755  // Missing X values (use last known Y values)
756  int64_t number_of_missing = round(raw.X) - current_frame;
757  for (int64_t missing = 0; missing < number_of_missing; missing++)
758  {
759  // Add new value to the vector
760  Coordinate new_coord(current_frame, current_value);
761 
762  if (Segment == 0 || Segment > 0 && current_frame > p1.co.X)
763  // Add to "values" vector
764  Values.push_back(new_coord);
765 
766  // Increment frame
767  current_frame++;
768  }
769 
770  // increment the current value
771  current_value = raw.Y;
772  }
773  }
774 
775  // Add final coordinate
776  Coordinate new_coord(current_frame, current_value);
777  Values.push_back(new_coord);
778 
779  break;
780  }
781 
782  // Calculate the "values" of this segment by maintaining the value of p1 until the
783  // last point, and then make the value jump to p2. This effectively just jumps
784  // the value, instead of ramping up or down the value.
785  case CONSTANT: {
786 
787  if (Segment == 0)
788  // first segment has 1 extra value
789  number_of_values++;
790 
791  // Add each increment to the values vector
792  for (int64_t x = 0; x < number_of_values; x++) {
793  if (x < (number_of_values - 1)) {
794  // Not the last value of this segment
795  // add coordinate to "values"
796  Values.push_back(Coordinate(Values.size(), p1.co.Y));
797  } else {
798  // This is the last value of this segment
799  // add coordinate to "values"
800  Values.push_back(Coordinate(Values.size(), p2.co.Y));
801  }
802  }
803  break;
804  }
805 
806  }
807 }
808 
809 // Create lookup table for fast factorial calculation
810 void Keyframe::CreateFactorialTable() {
811  // Only 4 lookups are needed, because we only support 4 coordinates per curve
812  FactorialLookup[0] = 1.0;
813  FactorialLookup[1] = 1.0;
814  FactorialLookup[2] = 2.0;
815  FactorialLookup[3] = 6.0;
816 }
817 
818 // Get a factorial for a coordinate
819 double Keyframe::Factorial(int64_t n) {
820  assert(n >= 0 && n <= 3);
821  return FactorialLookup[n]; /* returns the value n! as a SUMORealing point number */
822 }
823 
824 // Calculate the factorial function for Bernstein basis
825 double Keyframe::Ni(int64_t n, int64_t i) {
826  double ni;
827  double a1 = Factorial(n);
828  double a2 = Factorial(i);
829  double a3 = Factorial(n - i);
830  ni = a1 / (a2 * a3);
831  return ni;
832 }
833 
834 // Calculate Bernstein basis
835 double Keyframe::Bernstein(int64_t n, int64_t i, double t) {
836  double basis;
837  double ti; /* t^i */
838  double tni; /* (1 - t)^i */
839 
840  /* Prevent problems with pow */
841  if (t == 0.0 && i == 0)
842  ti = 1.0;
843  else
844  ti = pow(t, i);
845 
846  if (n == i && t == 1.0)
847  tni = 1.0;
848  else
849  tni = pow((1 - t), (n - i));
850 
851  // Bernstein basis
852  basis = Ni(n, i) * ti * tni;
853  return basis;
854 }
855 
856 // Scale all points by a percentage (good for evenly lengthening or shortening an openshot::Keyframe)
857 // 1.0 = same size, 1.05 = 5% increase, etc...
858 void Keyframe::ScalePoints(double scale)
859 {
860  // Loop through each point (skipping the 1st point)
861  for (int64_t point_index = 0; point_index < Points.size(); point_index++) {
862  // Skip the 1st point
863  if (point_index == 0)
864  continue;
865 
866  // Scale X value
867  Points[point_index].co.X = round(Points[point_index].co.X * scale);
868 
869  // Mark for re-processing
870  needs_update = true;
871  }
872 }
873 
874 // Flip all the points in this openshot::Keyframe (useful for reversing an effect or transition, etc...)
876 {
877  // Loop through each point
878  vector<Point> FlippedPoints;
879  for (int64_t point_index = 0, reverse_index = Points.size() - 1; point_index < Points.size(); point_index++, reverse_index--) {
880  // Flip the points
881  Point p = Points[point_index];
882  p.co.Y = Points[reverse_index].co.Y;
883  FlippedPoints.push_back(p);
884  }
885 
886  // Swap vectors
887  Points.swap(FlippedPoints);
888 
889  // Mark for re-processing
890  needs_update = true;
891 }
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:452
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:875
Json::Value JsonValue()
Generate Json::JsonValue for this object.
Definition: KeyFrame.cpp:321
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:432
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:858
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:496
void SetJsonValue(Json::Value root)
Load Json::JsonValue into this object.
Definition: KeyFrame.cpp:362
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
void IsIncreasing(bool is_increasing)
Set the increasing flag (used internally on the timeline, to track changes to coordinates) ...
Definition: Coordinate.h:81
double Y
The Y value of the coordinate (usually representing the value of the property being animated) ...
Definition: Coordinate.h:62
void PrintValues()
Print just the Y value of the point&#39;s primary coordinate.
Definition: KeyFrame.cpp:522
void Repeat(Fraction is_repeated)
Set the repeating Fraction (used internally on the timeline, to track changes to coordinates) ...
Definition: Coordinate.h:74
void RemovePoint(Point p)
Remove a point by matching a coordinate.
Definition: KeyFrame.cpp:459
int64_t GetLength()
Definition: KeyFrame.cpp:442
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:536
Fraction GetRepeatFraction(int64_t index)
Get the fraction that represents how many times this value is repeated in the curve.
Definition: KeyFrame.cpp:388
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:61
double GetDelta(int64_t index)
Get the change in Y value (from the previous Y value)
Definition: KeyFrame.cpp:410
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
This namespace is the default namespace for all code in the openshot library.
void Delta(double new_delta)
Set the delta / difference between previous coordinate value (used internally on the timeline...
Definition: Coordinate.h:88
string Json()
Get and Set JSON methods.
Definition: KeyFrame.cpp:314
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:510
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:339
Constant curves jump from their previous position to a new one (with no interpolation).
Definition: Point.h:48