diff --git a/include/KeyFrame.h b/include/KeyFrame.h index 8bb1d31b..ed106cdb 100644 --- a/include/KeyFrame.h +++ b/include/KeyFrame.h @@ -113,6 +113,9 @@ namespace openshot { /// Set the handles, used for smooth curves. The handles are based on the surrounding points. void SetHandles(Point current); + /// Flip all the points in this openshot::Keyframe (useful for reversing an effect or transition, etc...) + void FlipPoints(); + /// Get the index of a point by matching a coordinate int FindIndex(Point p) throw(OutOfBoundsPoint); @@ -163,6 +166,10 @@ namespace openshot { /// Remove a point by index void RemovePoint(int index) throw(OutOfBoundsPoint); + /// Scale all points by a percentage (good for evenly lengthening or shortening an openshot::Keyframe) + /// 1.0 = same size, 1.05 = 5% increase, etc... + void ScalePoints(float scale); + /// Replace an existing point with a new point void UpdatePoint(int index, Point p); diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp index 952cf818..ff7c4d56 100644 --- a/src/KeyFrame.cpp +++ b/src/KeyFrame.cpp @@ -818,3 +818,39 @@ double Keyframe::Bernstein(int n, int i, double t) { return basis; } +// Scale all points by a percentage (good for evenly lengthening or shortening an openshot::Keyframe) +// 1.0 = same size, 1.05 = 5% increase, etc... +void Keyframe::ScalePoints(float scale) +{ + // Loop through each point (skipping the 1st point) + for (int point_index = 0; point_index < Points.size(); point_index++) { + // Skip the 1st point + if (point_index == 0) + continue; + + // Scale X value + Points[point_index].co.X = round(Points[point_index].co.X * scale); + + // Mark for re-processing + needs_update = true; + } +} + +// Flip all the points in this openshot::Keyframe (useful for reversing an effect or transition, etc...) +void Keyframe::FlipPoints() +{ + // Loop through each point + vector FlippedPoints; + for (int point_index = 0, reverse_index = Points.size() - 1; point_index < Points.size(); point_index++, reverse_index--) { + // Flip the points + Point p = Points[point_index]; + p.co.Y = Points[reverse_index].co.Y; + FlippedPoints.push_back(p); + } + + // Swap vectors + Points.swap(FlippedPoints); + + // Mark for re-processing + needs_update = true; +} diff --git a/src/examples/Example.cpp b/src/examples/Example.cpp index dac6c851..ff6e317c 100644 --- a/src/examples/Example.cpp +++ b/src/examples/Example.cpp @@ -51,7 +51,7 @@ int main(int argc, char* argv[]) openshot::Keyframe k; //cout << "creating " << outer << endl; for (int z = 0; z<10; z++) { - openshot::Point p(z * 10, 1 * z * outer); + openshot::Point p(z * 10, 1 * z * outer, BEZIER); k.AddPoint(p); } root.append(k.JsonValue()); diff --git a/tests/KeyFrame_Tests.cpp b/tests/KeyFrame_Tests.cpp index fb358697..b2d1022b 100644 --- a/tests/KeyFrame_Tests.cpp +++ b/tests/KeyFrame_Tests.cpp @@ -243,3 +243,81 @@ TEST(Keyframe_Get_Closest_Point) CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(3000, 3000)).co.X, 2500); } + +TEST(Keyframe_Scale_Keyframe) +{ + // Create a keyframe curve with 2 points + Keyframe kf; + kf.Auto_Handle_Percentage = 0.4f; + kf.AddPoint(openshot::Point(Coordinate(1, 1), BEZIER)); + kf.AddPoint(openshot::Point(Coordinate(25, 8), BEZIER)); + kf.AddPoint(openshot::Point(Coordinate(50, 2), BEZIER)); + + // Spot check values from the curve + CHECK_CLOSE(1.0f, kf.GetValue(1), 0.01); + CHECK_CLOSE(7.99f, kf.GetValue(24), 0.01); + CHECK_CLOSE(8.0f, kf.GetValue(25), 0.01); + CHECK_CLOSE(3.84f, kf.GetValue(40), 0.01); + CHECK_CLOSE(2.0f, kf.GetValue(49), 0.01); + CHECK_CLOSE(2.0f, kf.GetValue(50), 0.01); + + // Resize / Scale the keyframe + kf.ScalePoints(2.0); // 100% larger + + // Spot check values from the curve + CHECK_CLOSE(1.0f, kf.GetValue(1), 0.01); + CHECK_CLOSE(6.25f, kf.GetValue(24), 0.01); + CHECK_CLOSE(6.38f, kf.GetValue(25), 0.01); + CHECK_CLOSE(7.80f, kf.GetValue(40), 0.01); + CHECK_CLOSE(7.99f, kf.GetValue(49), 0.01); + CHECK_CLOSE(8.0f, kf.GetValue(50), 0.01); + CHECK_CLOSE(2.06f, kf.GetValue(90), 0.01); + CHECK_CLOSE(2.0f, kf.GetValue(100), 0.01); + + // Resize / Scale the keyframe + kf.ScalePoints(0.5); // 50% smaller, which should match the original size + + // Spot check values from the curve + CHECK_CLOSE(1.0f, kf.GetValue(1), 0.01); + CHECK_CLOSE(7.99f, kf.GetValue(24), 0.01); + CHECK_CLOSE(8.0f, kf.GetValue(25), 0.01); + CHECK_CLOSE(3.84f, kf.GetValue(40), 0.01); + CHECK_CLOSE(2.0f, kf.GetValue(49), 0.01); + CHECK_CLOSE(2.0f, kf.GetValue(50), 0.01); + +} + +TEST(Keyframe_Flip_Keyframe) +{ + // Create a keyframe curve with 2 points + Keyframe kf; + kf.Auto_Handle_Percentage = 0.4f; + kf.AddPoint(openshot::Point(Coordinate(1, 1), LINEAR)); + kf.AddPoint(openshot::Point(Coordinate(25, 8), LINEAR)); + kf.AddPoint(openshot::Point(Coordinate(50, 2), LINEAR)); + kf.AddPoint(openshot::Point(Coordinate(100, 10), LINEAR)); + + // Spot check values from the curve + CHECK_CLOSE(1.0f, kf.GetValue(1), 0.01); + CHECK_CLOSE(8.0f, kf.GetValue(25), 0.01); + CHECK_CLOSE(2.0f, kf.GetValue(50), 0.01); + CHECK_CLOSE(10.0f, kf.GetValue(100), 0.01); + + // Flip the points + kf.FlipPoints(); + + // Spot check values from the curve + CHECK_CLOSE(10.0f, kf.GetValue(1), 0.01); + CHECK_CLOSE(2.0f, kf.GetValue(25), 0.01); + CHECK_CLOSE(8.0f, kf.GetValue(50), 0.01); + CHECK_CLOSE(1.0f, kf.GetValue(100), 0.01); + + // Flip the points again (back to the original) + kf.FlipPoints(); + + // Spot check values from the curve + CHECK_CLOSE(1.0f, kf.GetValue(1), 0.01); + CHECK_CLOSE(8.0f, kf.GetValue(25), 0.01); + CHECK_CLOSE(2.0f, kf.GetValue(50), 0.01); + CHECK_CLOSE(10.0f, kf.GetValue(100), 0.01); +}