31 #include "../include/KeyFrame.h"
40 bool IsPointBeforeX(
Point const & p,
double const x) {
44 double InterpolateLinearCurve(
Point const & left,
Point const & right,
double const target) {
45 double const diff_Y = right.
co.
Y - left.
co.
Y;
46 double const diff_X = right.
co.
X - left.
co.
X;
47 double const slope = diff_Y / diff_X;
48 return left.
co.
Y + slope * (target - left.
co.
X);
51 double InterpolateBezierCurve(
Point const & left,
Point const & right,
double const target,
double const allowed_error) {
52 double const X_diff = right.
co.
X - left.
co.
X;
53 double const Y_diff = right.
co.
Y - left.
co.
Y;
63 double B[4] = {1, 3, 3, 1};
64 double oneMinTExp = 1;
66 for (
int i = 0; i < 4; ++i, tExp *= t) {
69 for (
int i = 0; i < 4; ++i, oneMinTExp *= 1 - t) {
70 B[4 - i - 1] *= oneMinTExp;
72 double const x = p0.
X * B[0] + p1.
X * B[1] + p2.
X * B[2] + p3.
X * B[3];
73 double const y = p0.
Y * B[0] + p1.
Y * B[1] + p2.
Y * B[2] + p3.
Y * B[3];
74 if (fabs(target - x) < allowed_error) {
88 double InterpolateBetween(
Point const & left,
Point const & right,
double target,
double allowed_error) {
89 assert(left.
co.
X < target);
90 assert(target <= right.
co.
X);
93 case LINEAR:
return InterpolateLinearCurve(left, right, target);
94 case BEZIER:
return InterpolateBezierCurve(left, right, target, allowed_error);
99 template<
typename Check>
100 int64_t SearchBetweenPoints(
Point const & left,
Point const & right, int64_t
const current, Check check) {
101 int64_t start = left.
co.
X;
102 int64_t stop = right.
co.
X;
103 while (start < stop) {
104 int64_t
const mid = (start + stop + 1) / 2;
105 double const value = InterpolateBetween(left, right, mid, 0.01);
106 if (check(round(value), current)) {
118 Keyframe::Keyframe(
double value) {
120 AddPoint(
Point(value));
128 std::vector<Point>::iterator candidate =
129 std::lower_bound(begin(Points), end(Points), p.
co.
X, IsPointBeforeX);
130 if (candidate == end(Points)) {
134 }
else if ((*candidate).co.X == p.
co.
X) {
142 size_t const candidate_index = candidate - begin(Points);
144 std::move_backward(begin(Points) + candidate_index, end(Points) - 1, end(Points));
145 Points[candidate_index] = p;
150 void Keyframe::AddPoint(
double x,
double y)
163 Point new_point(x, y, interpolate);
170 int64_t Keyframe::FindIndex(
Point p)
const {
172 for (std::vector<Point>::size_type x = 0; x < Points.size(); x++) {
174 Point existing_point = Points[x];
177 if (p.
co.
X == existing_point.
co.
X && p.
co.
Y == existing_point.
co.
Y) {
188 bool Keyframe::Contains(
Point p)
const {
189 std::vector<Point>::const_iterator i =
190 std::lower_bound(begin(Points), end(Points), p.
co.
X, IsPointBeforeX);
191 return i != end(Points) && i->co.X == p.
co.
X;
195 Point Keyframe::GetClosestPoint(
Point p,
bool useLeft)
const {
196 if (Points.size() == 0) {
197 return Point(-1, -1);
202 std::vector<Point>::const_iterator candidate =
203 std::lower_bound(begin(Points), end(Points), p.
co.
X, IsPointBeforeX);
205 if (candidate == end(Points)) {
209 return Points.back();
211 if (candidate == begin(Points)) {
215 return Points.front();
218 return *(candidate - 1);
226 return GetClosestPoint(p,
false);
234 int64_t index = FindIndex(p);
238 return Points[index - 1];
244 return Point(-1, -1);
249 Point Keyframe::GetMaxPoint()
const {
250 Point maxPoint(-1, -1);
252 for (
Point const & existing_point: Points) {
253 if (existing_point.co.Y >= maxPoint.
co.
Y) {
254 maxPoint = existing_point;
262 double Keyframe::GetValue(int64_t index)
const {
263 if (Points.empty()) {
266 std::vector<Point>::const_iterator candidate =
267 std::lower_bound(begin(Points), end(Points),
static_cast<double>(index), IsPointBeforeX);
269 if (candidate == end(Points)) {
271 return Points.back().co.Y;
273 if (candidate == begin(Points)) {
275 return Points.front().co.Y;
277 if (candidate->co.X == index) {
279 return candidate->co.Y;
281 std::vector<Point>::const_iterator predecessor = candidate - 1;
282 return InterpolateBetween(*predecessor, *candidate, index, 0.01);
286 int Keyframe::GetInt(int64_t index)
const {
287 return int(round(GetValue(index)));
291 int64_t Keyframe::GetLong(int64_t index)
const {
292 return long(round(GetValue(index)));
296 bool Keyframe::IsIncreasing(
int index)
const
298 if (index < 1 || (index + 1) >= GetLength()) {
301 std::vector<Point>::const_iterator candidate =
302 std::lower_bound(begin(Points), end(Points),
static_cast<double>(index), IsPointBeforeX);
303 if (candidate == end(Points)) {
306 if ((candidate->co.X == index) || (candidate == begin(Points))) {
309 int64_t
const value = GetLong(index);
311 if (value < round(candidate->co.Y)) {
313 }
else if (value > round(candidate->co.Y)) {
317 }
while (candidate != end(Points));
322 std::string Keyframe::Json()
const {
325 return JsonValue().toStyledString();
329 Json::Value Keyframe::JsonValue()
const {
333 root[
"Points"] = Json::Value(Json::arrayValue);
336 for (
const auto existing_point : Points) {
337 root[
"Points"].append(existing_point.JsonValue());
345 void Keyframe::SetJson(
const std::string value) {
354 catch (
const std::exception& e)
357 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
362 void Keyframe::SetJsonValue(
const Json::Value root) {
366 if (!root[
"Points"].isNull())
368 for (
const auto existing_point : root[
"Points"]) {
382 Fraction Keyframe::GetRepeatFraction(int64_t index)
const {
385 if (index < 1 || (index + 1) >= GetLength()) {
388 assert(Points.size() > 1);
392 int64_t
const current_value = GetLong(index);
393 std::vector<Point>::const_iterator
const candidate =
394 std::lower_bound(begin(Points), end(Points),
static_cast<double>(index), IsPointBeforeX);
395 assert(candidate != end(Points));
398 int64_t next_repeats = 0;
399 std::vector<Point>::const_iterator i = candidate;
404 if (i->co.X == index) {
408 bool all_constant =
true;
409 for (; i != end(Points); ++i) {
410 if (current_value != round(i->co.Y)) {
411 all_constant =
false;
415 if (! all_constant) {
420 assert(i != begin(Points));
421 Point const left = *(i - 1);
422 Point const right = *i;
424 if (current_value < round(i->co.Y)) {
425 change_at = SearchBetweenPoints(left, right, current_value, std::less_equal<double>{});
427 assert(current_value > round(i->co.Y));
428 change_at = SearchBetweenPoints(left, right, current_value, std::greater_equal<double>{});
430 next_repeats = change_at - index;
433 next_repeats = Points.back().co.X - index;
439 if (i != begin(Points)) {
447 int64_t previous_repeats = 0;
449 for (; i != begin(Points); --i) {
450 if (current_value != round(i->co.Y)) {
451 all_constant =
false;
458 if (current_value != round(i->co.Y)) {
459 assert(i != candidate);
460 all_constant =
false;
462 if (! all_constant) {
465 Point const left = *i;
466 Point const right = *(i + 1);
468 if (current_value > round(left.
co.
Y)) {
469 change_at = SearchBetweenPoints(left, right, current_value, std::less<double>{});
471 assert(current_value < round(left.
co.
Y));
472 change_at = SearchBetweenPoints(left, right, current_value, std::greater<double>{});
474 previous_repeats = index - change_at;
478 previous_repeats = index;
480 int64_t total_repeats = previous_repeats + next_repeats;
481 return Fraction(previous_repeats, total_repeats);
485 double Keyframe::GetDelta(int64_t index)
const {
486 if (index < 1)
return 0;
487 if (index == 1 && ! Points.empty())
return Points[0].co.Y;
488 if (index >= GetLength())
return 0;
489 return GetLong(index) - GetLong(index - 1);
493 Point const & Keyframe::GetPoint(int64_t index)
const {
495 if (index >= 0 && index < (int64_t)Points.size())
496 return Points[index];
503 int64_t Keyframe::GetLength()
const {
504 if (Points.empty())
return 0;
505 if (Points.size() == 1)
return 1;
506 return round(Points.back().co.X) + 1;
510 int64_t Keyframe::GetCount()
const {
512 return Points.size();
516 void Keyframe::RemovePoint(
Point p) {
518 for (std::vector<Point>::size_type x = 0; x < Points.size(); x++) {
520 Point existing_point = Points[x];
523 if (p.
co.
X == existing_point.
co.
X && p.
co.
Y == existing_point.
co.
Y) {
525 Points.erase(Points.begin() + x);
535 void Keyframe::RemovePoint(int64_t index) {
537 if (index >= 0 && index < (int64_t)Points.size())
540 Points.erase(Points.begin() + index);
547 void Keyframe::UpdatePoint(int64_t index,
Point p) {
555 void Keyframe::PrintPoints()
const {
556 cout << fixed << setprecision(4);
557 for (std::vector<Point>::const_iterator it = Points.begin(); it != Points.end(); it++) {
559 cout << p.
co.
X <<
"\t" << p.
co.
Y << endl;
563 void Keyframe::PrintValues()
const {
564 cout << fixed << setprecision(4);
565 cout <<
"Frame Number (X)\tValue (Y)\tIs Increasing\tRepeat Numerator\tRepeat Denominator\tDelta (Y Difference)\n";
567 for (int64_t i = 1; i < GetLength(); ++i) {
568 cout << i <<
"\t" << GetValue(i) <<
"\t" << IsIncreasing(i) <<
"\t" ;
569 cout << GetRepeatFraction(i).num <<
"\t" << GetRepeatFraction(i).den <<
"\t" << GetDelta(i) <<
"\n";
576 void Keyframe::ScalePoints(
double scale)
583 for (std::vector<Point>::size_type point_index = 1; point_index < Points.size(); point_index++) {
585 Points[point_index].co.X = round(Points[point_index].co.X * scale);
590 void Keyframe::FlipPoints() {
591 for (std::vector<Point>::size_type point_index = 0, reverse_index = Points.size() - 1; point_index < reverse_index; point_index++, reverse_index--) {
594 swap(Points[point_index].co.Y, Points[reverse_index].co.Y);
This class represents a Cartesian coordinate (X, Y) used in the Keyframe animation system.
double X
The X value of the coordinate (usually representing the frame #)
double Y
The Y value of the coordinate (usually representing the value of the property being animated)
This class represents a fraction.
Exception for invalid JSON.
Exception for an out of bounds key-frame point.
A Point is the basic building block of a key-frame curve.
Coordinate handle_left
This is the left handle coordinate (in percentages from 0 to 1)
Coordinate co
This is the primary coordinate.
InterpolationType interpolation
This is the interpolation mode.
Coordinate handle_right
This is the right handle coordinate (in percentages from 0 to 1)
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
This namespace is the default namespace for all code in the openshot library.
const Json::Value stringToJson(const std::string value)
InterpolationType
This controls how a Keyframe uses this point to interpolate between two points.
@ CONSTANT
Constant curves jump from their previous position to a new one (with no interpolation).
@ BEZIER
Bezier curves are quadratic curves, which create a smooth curve.
@ LINEAR
Linear curves are angular, straight lines between two points.