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