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 (abs(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 (
auto existing_point : Points) {
337 root[
"Points"].append(existing_point.JsonValue());
345 void Keyframe::SetJson(std::string value) {
349 Json::CharReaderBuilder rbuilder;
350 Json::CharReader* reader(rbuilder.newCharReader());
353 bool success = reader->parse( value.c_str(),
354 value.c_str() + value.size(), &root, &errors );
359 throw InvalidJSON(
"JSON could not be parsed (or is invalid)");
366 catch (
const std::exception& e)
369 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
374 void Keyframe::SetJsonValue(Json::Value root) {
378 if (!root[
"Points"].isNull())
380 for (
const auto existing_point : root[
"Points"]) {
394 Fraction Keyframe::GetRepeatFraction(int64_t index)
const {
397 if (index < 1 || (index + 1) >= GetLength()) {
400 assert(Points.size() > 1);
404 int64_t
const current_value = GetLong(index);
405 std::vector<Point>::const_iterator
const candidate =
406 std::lower_bound(begin(Points), end(Points), static_cast<double>(index), IsPointBeforeX);
407 assert(candidate != end(Points));
410 int64_t next_repeats = 0;
411 std::vector<Point>::const_iterator i = candidate;
416 if (i->co.X == index) {
420 bool all_constant =
true;
421 for (; i != end(Points); ++i) {
422 if (current_value != round(i->co.Y)) {
423 all_constant =
false;
427 if (! all_constant) {
432 assert(i != begin(Points));
433 Point const left = *(i - 1);
434 Point const right = *i;
436 if (current_value < round(i->co.Y)) {
437 change_at = SearchBetweenPoints(left, right, current_value, std::less_equal<double>{});
439 assert(current_value > round(i->co.Y));
440 change_at = SearchBetweenPoints(left, right, current_value, std::greater_equal<double>{});
442 next_repeats = change_at - index;
445 next_repeats = Points.back().co.X - index;
451 if (i != begin(Points)) {
459 int64_t previous_repeats = 0;
461 for (; i != begin(Points); --i) {
462 if (current_value != round(i->co.Y)) {
463 all_constant =
false;
470 if (current_value != round(i->co.Y)) {
471 assert(i != candidate);
472 all_constant =
false;
474 if (! all_constant) {
477 Point const left = *i;
478 Point const right = *(i + 1);
480 if (current_value > round(left.
co.
Y)) {
481 change_at = SearchBetweenPoints(left, right, current_value, std::less<double>{});
483 assert(current_value < round(left.
co.
Y));
484 change_at = SearchBetweenPoints(left, right, current_value, std::greater<double>{});
486 previous_repeats = index - change_at;
490 previous_repeats = index;
492 int64_t total_repeats = previous_repeats + next_repeats;
493 return Fraction(previous_repeats, total_repeats);
497 double Keyframe::GetDelta(int64_t index)
const {
498 if (index < 1)
return 0;
499 if (index == 1 && ! Points.empty())
return Points[0].co.Y;
500 if (index >= GetLength())
return 0;
501 return GetLong(index) - GetLong(index - 1);
505 Point const & Keyframe::GetPoint(int64_t index)
const {
507 if (index >= 0 && index < (int64_t)Points.size())
508 return Points[index];
515 int64_t Keyframe::GetLength()
const {
516 if (Points.empty())
return 0;
517 if (Points.size() == 1)
return 1;
518 return round(Points.back().co.X) + 1;
522 int64_t Keyframe::GetCount()
const {
524 return Points.size();
528 void Keyframe::RemovePoint(
Point p) {
530 for (std::vector<Point>::size_type x = 0; x < Points.size(); x++) {
532 Point existing_point = Points[x];
535 if (p.
co.
X == existing_point.
co.
X && p.
co.
Y == existing_point.
co.
Y) {
537 Points.erase(Points.begin() + x);
547 void Keyframe::RemovePoint(int64_t index) {
549 if (index >= 0 && index < (int64_t)Points.size())
552 Points.erase(Points.begin() + index);
559 void Keyframe::UpdatePoint(int64_t index,
Point p) {
567 void Keyframe::PrintPoints()
const {
568 cout << fixed << setprecision(4);
569 for (std::vector<Point>::const_iterator it = Points.begin(); it != Points.end(); it++) {
571 cout << p.
co.
X <<
"\t" << p.
co.
Y << endl;
575 void Keyframe::PrintValues()
const {
576 cout << fixed << setprecision(4);
577 cout <<
"Frame Number (X)\tValue (Y)\tIs Increasing\tRepeat Numerator\tRepeat Denominator\tDelta (Y Difference)\n";
579 for (int64_t i = 1; i < GetLength(); ++i) {
580 cout << i <<
"\t" << GetValue(i) <<
"\t" << IsIncreasing(i) <<
"\t" ;
581 cout << GetRepeatFraction(i).num <<
"\t" << GetRepeatFraction(i).den <<
"\t" << GetDelta(i) <<
"\n";
588 void Keyframe::ScalePoints(
double scale)
595 for (std::vector<Point>::size_type point_index = 1; point_index < Points.size(); point_index++) {
597 Points[point_index].co.X = round(Points[point_index].co.X * scale);
602 void Keyframe::FlipPoints() {
603 for (std::vector<Point>::size_type point_index = 0, reverse_index = Points.size() - 1; point_index < reverse_index; point_index++, reverse_index--) {
606 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...
Bezier curves are quadratic curves, which create a smooth curve.
InterpolationType interpolation
This is the interpolation mode.
Coordinate handle_right
This is the right handle coordinate (in percentages from 0 to 1)
Coordinate handle_left
This is the left handle coordinate (in percentages from 0 to 1)
A Point is the basic building block of a key-frame curve.
double Y
The Y value of the coordinate (usually representing the value of the property being animated) ...
This class represents a fraction.
double X
The X value of the coordinate (usually representing the frame #)
InterpolationType
This controls how a Keyframe uses this point to interpolate between two points.
This namespace is the default namespace for all code in the openshot library.
Linear curves are angular, straight lines between two points.
Coordinate co
This is the primary coordinate.
Exception for invalid JSON.
Exception for an out of bounds key-frame point.
void SetJsonValue(Json::Value root)
Load Json::JsonValue into this object.
Constant curves jump from their previous position to a new one (with no interpolation).