From 559d6a0545e28bc29afb9011e90f45d04272b03f Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 19 Oct 2016 02:19:07 -0500 Subject: [PATCH] Huge refactor of keyframe point handles (bezier quadratic curves) to use CSS-style syntax. Basically, the left and right handles of a point now use percentages, such as 0.0, 1.0, 0.5, 1.0 (between 0 and 1), and become real coordinates based on the value differences between points (in real time). Also simplified the JSON property creation. Also, many unit tests have been updated, because they were actually generating the incorrect values from before... although only slightly in most cases. Now they have correct values from the default bezier handles. --- include/ClipBase.h | 3 +- include/KeyFrame.h | 15 +++--- include/Point.h | 25 +++++---- src/Clip.cpp | 61 ++++++++++----------- src/ClipBase.cpp | 25 +++++++-- src/KeyFrame.cpp | 102 +++++++++++++++--------------------- src/Point.cpp | 19 +++++-- src/effects/Blur.cpp | 23 ++++---- src/effects/Brightness.cpp | 19 +++---- src/effects/ChromaKey.cpp | 25 ++++----- src/effects/Deinterlace.cpp | 17 +++--- src/effects/Mask.cpp | 21 ++++---- src/effects/Negate.cpp | 15 +++--- src/effects/Saturation.cpp | 17 +++--- tests/KeyFrame_Tests.cpp | 96 +++++++++++++++++++-------------- tests/Point_Tests.cpp | 24 --------- 16 files changed, 247 insertions(+), 260 deletions(-) diff --git a/include/ClipBase.h b/include/ClipBase.h index f3e38c85..8a4e2763 100644 --- a/include/ClipBase.h +++ b/include/ClipBase.h @@ -37,6 +37,7 @@ #include #include "Exceptions.h" #include "Point.h" +#include "KeyFrame.h" #include "Json.h" using namespace std; @@ -61,7 +62,7 @@ namespace openshot { int max_height; ///< The maximium image height needed by this clip (used for optimizations) /// Generate JSON for a property - Json::Value add_property_json(string name, float value, string type, string memo, bool contains_point, int number_of_points, float min_value, float max_value, InterpolationType intepolation, int closest_point_x, bool readonly); + Json::Value add_property_json(string name, float value, string type, string memo, Keyframe* keyframe, float min_value, float max_value, bool readonly, long int requested_frame); /// Generate JSON choice for a property (dropdown properties) Json::Value add_property_choice_json(string name, int value, int selected_value); diff --git a/include/KeyFrame.h b/include/KeyFrame.h index 8a3fcfd5..6ee68c31 100644 --- a/include/KeyFrame.h +++ b/include/KeyFrame.h @@ -91,7 +91,6 @@ namespace openshot { public: vector Points; ///< Vector of all Points vector Values; ///< Vector of all Values (i.e. the processed coordinates from the curve) - float Auto_Handle_Percentage; ///< Percentage the left and right handles should be adjusted to, to create a smooth curve /// Default constructor for the Keyframe class Keyframe(); @@ -102,7 +101,7 @@ namespace openshot { /// Add a new point on the key-frame. Each point has a primary coordinate, a left handle, and a right handle. void AddPoint(Point p); - /// Add a new point on the key-frame, with some defaults set (BEZIER, AUTO Handles, etc...) + /// Add a new point on the key-frame, with some defaults set (BEZIER) void AddPoint(float x, float y); /// Add a new point on the key-frame, with a specific interpolation type @@ -111,9 +110,6 @@ namespace openshot { /// Does this keyframe contain a specific point bool Contains(Point p); - /// 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(); @@ -138,9 +134,16 @@ namespace openshot { /// Get a point at a specific index Point& GetPoint(long int index) throw(OutOfBoundsPoint); - /// Get current point (or closest point) from the X coordinate (i.e. the frame number) + /// Get current point (or closest point to the right) from the X coordinate (i.e. the frame number) Point GetClosestPoint(Point p); + /// Get current point (or closest point) from the X coordinate (i.e. the frame number) + /// Either use the closest left point, or right point + Point GetClosestPoint(Point p, bool useLeft); + + /// Get previous point ( + Point GetPreviousPoint(Point p); + /// Get max point (by Y coordinate) Point GetMaxPoint(); diff --git a/include/Point.h b/include/Point.h index f5fb7296..ffa0d6d4 100644 --- a/include/Point.h +++ b/include/Point.h @@ -81,8 +81,8 @@ namespace openshot class Point { public: Coordinate co; ///< This is the primary coordinate - Coordinate handle_left; ///< This is the left handle coordinate - Coordinate handle_right; ///< This is the right handle coordinate + Coordinate handle_left; ///< This is the left handle coordinate (in percentages from 0 to 1) + Coordinate handle_right; ///< This is the right handle coordinate (in percentages from 0 to 1) InterpolationType interpolation; ///< This is the interpolation mode HandleType handle_type; ///< This is the handle mode @@ -98,21 +98,24 @@ namespace openshot /// Constructor which also creates a Point and sets the X,Y, and interpolation of the Point. Point(float x, float y, InterpolationType interpolation); - // Constructor which takes a coordinate + /// Constructor which takes a coordinate Point(Coordinate co); - // Constructor which takes a coordinate and interpolation mode + /// Constructor which takes a coordinate and interpolation mode Point(Coordinate co, InterpolationType interpolation); - // Constructor which takes a coordinate, interpolation mode, and handle type + /// Constructor which takes a coordinate, interpolation mode, and handle type Point(Coordinate co, InterpolationType interpolation, HandleType handle_type); - /** - * Set the left and right handles to the same Y coordinate as the primary - * coordinate, but offset the X value by a given amount. This is typically used - * to smooth the curve (if BEZIER interpolation mode is used) - */ - void Initialize_Handles(float Offset = 0.0f); + /// Set the left and right handles to a percent of the primary coordinate (0 to 1) + /// Defaults to a smooth curve (Ease in and out) + void Initialize_Handles(); + + /// Set the left handle to a percent of the primary coordinate (0 to 1) + void Initialize_LeftHandle(float x, float y); + + /// Set the right handle to a percent of the primary coordinate (0 to 1) + void Initialize_RightHandle(float x, float y); /// Get and Set JSON methods string Json(); ///< Generate JSON string of this object diff --git a/src/Clip.cpp b/src/Clip.cpp index 8d2ac24d..a254199e 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -659,22 +659,19 @@ string Clip::Json() { // Get all properties for a specific frame string Clip::PropertiesJSON(long int requested_frame) { - // Requested Point - Point requested_point(requested_frame, requested_frame); - // Generate JSON properties list Json::Value root; - root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); - root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["layer"] = add_property_json("Track", Layer(), "int", "", false, 0, 0, 20, CONSTANT, -1, false); - root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, true); - root["gravity"] = add_property_json("Gravity", gravity, "int", "", false, 0, 0, 8, CONSTANT, -1, false); - root["scale"] = add_property_json("Scale", scale, "int", "", false, 0, 0, 3, CONSTANT, -1, false); - root["anchor"] = add_property_json("Anchor", anchor, "int", "", false, 0, 0, 1, CONSTANT, -1, false); - root["handles"] = add_property_json("Handles", handles, "int", "", false, 0, 0, 1, CONSTANT, -1, false); - root["waveform"] = add_property_json("Waveform", waveform, "int", "", false, 0, 0, 1, CONSTANT, -1, false); + root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); + root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); + root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame); + root["gravity"] = add_property_json("Gravity", gravity, "int", "", NULL, 0, 8, false, requested_frame); + root["scale"] = add_property_json("Scale", scale, "int", "", NULL, 0, 3, false, requested_frame); + root["anchor"] = add_property_json("Anchor", anchor, "int", "", NULL, 0, 1, false, requested_frame); + root["handles"] = add_property_json("Handles", handles, "int", "", NULL, 0, 1, false, requested_frame); + root["waveform"] = add_property_json("Waveform", waveform, "int", "", NULL, 0, 1, false, requested_frame); // Add gravity choices (dropdown style) root["gravity"]["choices"].append(add_property_choice_json("Top Left", GRAVITY_TOP_LEFT, gravity)); @@ -706,25 +703,25 @@ string Clip::PropertiesJSON(long int requested_frame) { root["waveform"]["choices"].append(add_property_choice_json("No", false, waveform)); // Keyframes - root["location_x"] = add_property_json("Location X", location_x.GetValue(requested_frame), "float", "", location_x.Contains(requested_point), location_x.GetCount(), -1.0, 1.0, location_x.GetClosestPoint(requested_point).interpolation, location_x.GetClosestPoint(requested_point).co.X, false); - root["location_y"] = add_property_json("Location Y", location_y.GetValue(requested_frame), "float", "", location_y.Contains(requested_point), location_y.GetCount(), -1.0, 1.0, location_y.GetClosestPoint(requested_point).interpolation, location_y.GetClosestPoint(requested_point).co.X, false); - root["scale_x"] = add_property_json("Scale X", scale_x.GetValue(requested_frame), "float", "", scale_x.Contains(requested_point), scale_x.GetCount(), 0.0, 1.0, scale_x.GetClosestPoint(requested_point).interpolation, scale_x.GetClosestPoint(requested_point).co.X, false); - root["scale_y"] = add_property_json("Scale Y", scale_y.GetValue(requested_frame), "float", "", scale_y.Contains(requested_point), scale_y.GetCount(), 0.0, 1.0, scale_y.GetClosestPoint(requested_point).interpolation, scale_y.GetClosestPoint(requested_point).co.X, false); - root["alpha"] = add_property_json("Alpha", alpha.GetValue(requested_frame), "float", "", alpha.Contains(requested_point), alpha.GetCount(), 0.0, 1.0, alpha.GetClosestPoint(requested_point).interpolation, alpha.GetClosestPoint(requested_point).co.X, false); - root["shear_x"] = add_property_json("Shear X", shear_x.GetValue(requested_frame), "float", "", shear_x.Contains(requested_point), shear_x.GetCount(), -1.0, 1.0, shear_x.GetClosestPoint(requested_point).interpolation, shear_x.GetClosestPoint(requested_point).co.X, false); - root["shear_y"] = add_property_json("Shear Y", shear_y.GetValue(requested_frame), "float", "", shear_y.Contains(requested_point), shear_y.GetCount(), -1.0, 1.0, shear_y.GetClosestPoint(requested_point).interpolation, shear_y.GetClosestPoint(requested_point).co.X, false); - root["rotation"] = add_property_json("Rotation", rotation.GetValue(requested_frame), "float", "", rotation.Contains(requested_point), rotation.GetCount(), -360, 360, rotation.GetClosestPoint(requested_point).interpolation, rotation.GetClosestPoint(requested_point).co.X, false); - root["volume"] = add_property_json("Volume", volume.GetValue(requested_frame), "float", "", volume.Contains(requested_point), volume.GetCount(), 0.0, 1.0, volume.GetClosestPoint(requested_point).interpolation, volume.GetClosestPoint(requested_point).co.X, false); - root["time"] = add_property_json("Time", time.GetValue(requested_frame), "float", "", time.Contains(requested_point), time.GetCount(), 0.0, 30 * 60 * 60 * 48, time.GetClosestPoint(requested_point).interpolation, time.GetClosestPoint(requested_point).co.X, false); - root["channel_filter"] = add_property_json("Channel Filter", channel_filter.GetValue(requested_frame), "int", "", channel_filter.Contains(requested_point), channel_filter.GetCount(), -1, 10, channel_filter.GetClosestPoint(requested_point).interpolation, channel_filter.GetClosestPoint(requested_point).co.X, false); - root["channel_mapping"] = add_property_json("Channel Mapping", channel_mapping.GetValue(requested_frame), "int", "", channel_mapping.Contains(requested_point), channel_mapping.GetCount(), -1, 10, channel_mapping.GetClosestPoint(requested_point).interpolation, channel_mapping.GetClosestPoint(requested_point).co.X, false); - root["has_audio"] = add_property_json("Enable Audio", has_audio.GetValue(requested_frame), "int", "", has_audio.Contains(requested_point), has_audio.GetCount(), -1, 1.0, has_audio.GetClosestPoint(requested_point).interpolation, has_audio.GetClosestPoint(requested_point).co.X, false); - root["has_video"] = add_property_json("Enable Video", has_video.GetValue(requested_frame), "int", "", has_video.Contains(requested_point), has_video.GetCount(), -1, 1.0, has_video.GetClosestPoint(requested_point).interpolation, has_video.GetClosestPoint(requested_point).co.X, false); + root["location_x"] = add_property_json("Location X", location_x.GetValue(requested_frame), "float", "", &location_x, -1.0, 1.0, false, requested_frame); + root["location_y"] = add_property_json("Location Y", location_y.GetValue(requested_frame), "float", "", &location_y, -1.0, 1.0, false, requested_frame); + root["scale_x"] = add_property_json("Scale X", scale_x.GetValue(requested_frame), "float", "", &scale_x, 0.0, 1.0, false, requested_frame); + root["scale_y"] = add_property_json("Scale Y", scale_y.GetValue(requested_frame), "float", "", &scale_y, 0.0, 1.0, false, requested_frame); + root["alpha"] = add_property_json("Alpha", alpha.GetValue(requested_frame), "float", "", &alpha, 0.0, 1.0, false, requested_frame); + root["shear_x"] = add_property_json("Shear X", shear_x.GetValue(requested_frame), "float", "", &shear_x, -1.0, 1.0, false, requested_frame); + root["shear_y"] = add_property_json("Shear Y", shear_y.GetValue(requested_frame), "float", "", &shear_y, -1.0, 1.0, false, requested_frame); + root["rotation"] = add_property_json("Rotation", rotation.GetValue(requested_frame), "float", "", &rotation, -360, 360, false, requested_frame); + root["volume"] = add_property_json("Volume", volume.GetValue(requested_frame), "float", "", &volume, 0.0, 1.0, false, requested_frame); + root["time"] = add_property_json("Time", time.GetValue(requested_frame), "float", "", &time, 0.0, 30 * 60 * 60 * 48, false, requested_frame); + root["channel_filter"] = add_property_json("Channel Filter", channel_filter.GetValue(requested_frame), "int", "", &channel_filter, -1, 10, false, requested_frame); + root["channel_mapping"] = add_property_json("Channel Mapping", channel_mapping.GetValue(requested_frame), "int", "", &channel_mapping, -1, 10, false, requested_frame); + root["has_audio"] = add_property_json("Enable Audio", has_audio.GetValue(requested_frame), "int", "", &has_audio, -1, 1.0, false, requested_frame); + root["has_video"] = add_property_json("Enable Video", has_video.GetValue(requested_frame), "int", "", &has_video, -1, 1.0, false, requested_frame); - root["wave_color"] = add_property_json("Wave Color", 0.0, "color", "", wave_color.red.Contains(requested_point), wave_color.red.GetCount(), 0, 255, wave_color.red.GetClosestPoint(requested_point).interpolation, wave_color.red.GetClosestPoint(requested_point).co.X, false); - root["wave_color"]["red"] = add_property_json("Red", wave_color.red.GetValue(requested_frame), "float", "", wave_color.red.Contains(requested_point), wave_color.red.GetCount(), 0, 255, wave_color.red.GetClosestPoint(requested_point).interpolation, wave_color.red.GetClosestPoint(requested_point).co.X, false); - root["wave_color"]["blue"] = add_property_json("Blue", wave_color.blue.GetValue(requested_frame), "float", "", wave_color.blue.Contains(requested_point), wave_color.blue.GetCount(), 0, 255, wave_color.blue.GetClosestPoint(requested_point).interpolation, wave_color.blue.GetClosestPoint(requested_point).co.X, false); - root["wave_color"]["green"] = add_property_json("Green", wave_color.green.GetValue(requested_frame), "float", "", wave_color.green.Contains(requested_point), wave_color.green.GetCount(), 0, 255, wave_color.green.GetClosestPoint(requested_point).interpolation, wave_color.green.GetClosestPoint(requested_point).co.X, false); + root["wave_color"] = add_property_json("Wave Color", 0.0, "color", "", &wave_color.red, 0, 255, false, requested_frame); + root["wave_color"]["red"] = add_property_json("Red", wave_color.red.GetValue(requested_frame), "float", "", &wave_color.red, 0, 255, false, requested_frame); + root["wave_color"]["blue"] = add_property_json("Blue", wave_color.blue.GetValue(requested_frame), "float", "", &wave_color.blue, 0, 255, false, requested_frame); + root["wave_color"]["green"] = add_property_json("Green", wave_color.green.GetValue(requested_frame), "float", "", &wave_color.green, 0, 255, false, requested_frame); // Return formatted string diff --git a/src/ClipBase.cpp b/src/ClipBase.cpp index 66363284..a5c24342 100644 --- a/src/ClipBase.cpp +++ b/src/ClipBase.cpp @@ -62,7 +62,10 @@ void ClipBase::SetJsonValue(Json::Value root) { } // Generate JSON for a property -Json::Value ClipBase::add_property_json(string name, float value, string type, string memo, bool contains_point, int number_of_points, float min_value, float max_value, InterpolationType intepolation, int closest_point_x, bool readonly) { +Json::Value ClipBase::add_property_json(string name, float value, string type, string memo, Keyframe* keyframe, float min_value, float max_value, bool readonly, long int requested_frame) { + + // Requested Point + Point requested_point(requested_frame, requested_frame); // Create JSON Object Json::Value prop = Json::Value(Json::objectValue); @@ -72,11 +75,23 @@ Json::Value ClipBase::add_property_json(string name, float value, string type, s prop["type"] = type; prop["min"] = min_value; prop["max"] = max_value; - prop["keyframe"] = contains_point; - prop["points"] = number_of_points; + if (keyframe) { + prop["keyframe"] = keyframe->Contains(requested_point); + prop["points"] = int(keyframe->GetCount()); + Point closest_point = keyframe->GetClosestPoint(requested_point); + prop["interpolation"] = closest_point.interpolation; + prop["closest_point_x"] = closest_point.co.X; + prop["previous_point_x"] = keyframe->GetPreviousPoint(closest_point).co.X; + } + else { + prop["keyframe"] = false; + prop["points"] = 0; + prop["interpolation"] = CONSTANT; + prop["closest_point_x"] = -1; + prop["previous_point_x"] = -1; + } + prop["readonly"] = readonly; - prop["interpolation"] = intepolation; - prop["closest_point_x"] = closest_point_x; prop["choices"] = Json::Value(Json::arrayValue); // return JsonValue diff --git a/src/KeyFrame.cpp b/src/KeyFrame.cpp index 4bb4b062..6c0c8178 100644 --- a/src/KeyFrame.cpp +++ b/src/KeyFrame.cpp @@ -53,7 +53,7 @@ void Keyframe::ReorderPoints() { } // Constructor which sets the default point & coordinate at X=0 -Keyframe::Keyframe(float value) : Auto_Handle_Percentage(0.4f), needs_update(true) { +Keyframe::Keyframe(float value) : needs_update(true) { // Init the factorial table, needed by bezier curves CreateFactorialTable(); @@ -62,7 +62,7 @@ Keyframe::Keyframe(float value) : Auto_Handle_Percentage(0.4f), needs_update(tru } // Keyframe constructor -Keyframe::Keyframe() : Auto_Handle_Percentage(0.4f), needs_update(true) { +Keyframe::Keyframe() : needs_update(true) { // Init the factorial table, needed by bezier curves CreateFactorialTable(); } @@ -84,12 +84,9 @@ void Keyframe::AddPoint(Point p) { // Sort / Re-order points based on X coordinate ReorderPoints(); - - // Set Handles (used for smooth curves). - SetHandles(p); } -// Add a new point on the key-frame, with some defaults set (BEZIER, AUTO Handles, etc...) +// Add a new point on the key-frame, with some defaults set (BEZIER) void Keyframe::AddPoint(float x, float y) { // Create a point @@ -109,50 +106,6 @@ void Keyframe::AddPoint(float x, float y, InterpolationType interpolate) AddPoint(new_point); } -// Set the handles, used for smooth curves. The handles are based -// on the surrounding points. -void Keyframe::SetHandles(Point current) -{ - // mark as dirty - needs_update = true; - - // Lookup the index of this point - long int index = FindIndex(current); - Point *Current_Point = &Points[index]; - - // Find the previous point and next points (if any) - Point *Previous_Point = NULL; - Point *Next_Point = NULL; - float Previous_X_diff = 0.0f; - float Next_X_diff = 0.0f; - - // If not the 1st point - if (index > 0) - Previous_Point = &Points[index - 1]; - - // If not the last point - if (index < (Points.size() - 1)) - Next_Point = &Points[index + 1]; - - // Update the previous point's right handle - if (Previous_Point) - Previous_X_diff = (Current_Point->co.X - Previous_Point->co.X) * Auto_Handle_Percentage; // Use the keyframe handle percentage to size the handle - if (Previous_Point && Previous_Point->handle_type == AUTO) - Previous_Point->handle_right.X = Previous_Point->co.X + Previous_X_diff; - // Update the current point's left handle - if (Current_Point->handle_type == AUTO) - Current_Point->handle_left.X = Current_Point->co.X - Previous_X_diff; - - // Update the next point's left handle - if (Next_Point) - Next_X_diff = (Next_Point->co.X - Current_Point->co.X) * Auto_Handle_Percentage; // Use the keyframe handle percentage to size the handle - if (Next_Point && Next_Point->handle_type == AUTO) - Next_Point->handle_left.X = Next_Point->co.X - Next_X_diff; - // Update the next point's right handle - if (Current_Point->handle_type == AUTO) - Current_Point->handle_right.X = Current_Point->co.X + Next_X_diff; -} - // Get the index of a point by matching a coordinate long int Keyframe::FindIndex(Point p) throw(OutOfBoundsPoint) { // loop through points, and find a matching coordinate @@ -190,7 +143,7 @@ bool Keyframe::Contains(Point p) { } // Get current point (or closest point) from the X coordinate (i.e. the frame number) -Point Keyframe::GetClosestPoint(Point p) { +Point Keyframe::GetClosestPoint(Point p, bool useLeft) { Point closest(-1, -1); // loop through points, and find a matching coordinate @@ -199,16 +152,22 @@ Point Keyframe::GetClosestPoint(Point p) { Point existing_point = Points[x]; // find a match - if (existing_point.co.X >= p.co.X) { - // New closest point found + if (existing_point.co.X >= p.co.X && !useLeft) { + // New closest point found (to the Right) closest = existing_point; break; + } else if (existing_point.co.X < p.co.X && useLeft) { + // New closest point found (to the Left) + closest = existing_point; + } else if (existing_point.co.X >= p.co.X && useLeft) { + // We've gone past the left point... so break + break; } } // Handle edge cases (if no point was found) if (closest.co.X == -1) { - if (p.co.X < 1 && Points.size() > 0) + if (p.co.X <= 1 && Points.size() > 0) // Assign 1st point closest = Points[0]; else if (Points.size() > 0) @@ -220,6 +179,30 @@ Point Keyframe::GetClosestPoint(Point p) { return closest; } +// Get current point (or closest point to the right) from the X coordinate (i.e. the frame number) +Point Keyframe::GetClosestPoint(Point p) { + return GetClosestPoint(p, false); +} + +// Get previous point (if any) +Point Keyframe::GetPreviousPoint(Point p) { + + // Lookup the index of this point + try { + long int index = FindIndex(p); + + // If not the 1st point + if (index > 0) + return Points[index - 1]; + else + return Points[0]; + + } catch (OutOfBoundsPoint) { + // No previous point + return Point(-1, -1); + } +} + // Get max point (by Y coordinate) Point Keyframe::GetMaxPoint() { Point maxPoint(-1, -1); @@ -399,9 +382,6 @@ void Keyframe::SetJsonValue(Json::Value root) { // Add Point to Keyframe AddPoint(p); } - - if (!root["Auto_Handle_Percentage"].isNull()) - Auto_Handle_Percentage = root["Auto_Handle_Percentage"].asBool(); } // Get the fraction that represents how many times this value is repeated in the curve @@ -709,10 +689,14 @@ void Keyframe::ProcessSegment(int Segment, Point p1, Point p2) { number_of_values++; number_of_values *= 4; // We need a higher resolution curve (4X) + // Diff between points + float X_diff = p2.co.X - p1.co.X; + float Y_diff = p2.co.Y - p1.co.Y; + vector segment_coordinates; segment_coordinates.push_back(p1.co); - segment_coordinates.push_back(p1.handle_right); - segment_coordinates.push_back(p2.handle_left); + segment_coordinates.push_back(Coordinate(p1.co.X + (p1.handle_right.X * X_diff), p1.co.Y + (p1.handle_right.Y * Y_diff))); + segment_coordinates.push_back(Coordinate(p1.co.X + (p2.handle_left.X * X_diff), p1.co.Y + (p2.handle_left.Y * Y_diff))); segment_coordinates.push_back(p2.co); vector raw_coordinates; diff --git a/src/Point.cpp b/src/Point.cpp index 0f0917b8..b4646df8 100644 --- a/src/Point.cpp +++ b/src/Point.cpp @@ -87,10 +87,21 @@ Point::Point(Coordinate co, InterpolationType interpolation, HandleType handle_t Initialize_Handles(); } -void Point::Initialize_Handles(float Offset) { - // initialize left and right handles - handle_left = Coordinate(co.X - Offset, co.Y); - handle_right = Coordinate(co.X + Offset, co.Y); +void Point::Initialize_Handles() { + // initialize left and right handles (in percentages from 0 to 1) + // default to a smooth curve + Initialize_LeftHandle(0.5, 1.0); + Initialize_RightHandle(0.5, 0.0); +} + +void Point::Initialize_LeftHandle(float x, float y) { + // initialize left handle (in percentages from 0 to 1) + handle_left = Coordinate(x, y); +} + +void Point::Initialize_RightHandle(float x, float y) { + // initialize right handle (in percentages from 0 to 1) + handle_right = Coordinate(x, y); } // Generate JSON string of this object diff --git a/src/effects/Blur.cpp b/src/effects/Blur.cpp index 60a5592e..f47cf303 100644 --- a/src/effects/Blur.cpp +++ b/src/effects/Blur.cpp @@ -301,23 +301,20 @@ void Blur::SetJsonValue(Json::Value root) { // Get all properties for a specific frame string Blur::PropertiesJSON(long int requested_frame) { - // Requested Point - Point requested_point(requested_frame, requested_frame); - // Generate JSON properties list Json::Value root; - root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); - root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, false); - root["layer"] = add_property_json("Track", Layer(), "int", "", false, 0, 0, 20, CONSTANT, -1, false); - root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, false); - root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, false); - root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 1000 * 60 * 30, CONSTANT, -1, true); + root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); + root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame); + root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); + root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame); + root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 1000 * 60 * 30, false, requested_frame); + root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 1000 * 60 * 30, true, requested_frame); // Keyframes - root["horizontal_radius"] = add_property_json("Horizontal Radius", horizontal_radius.GetValue(requested_frame), "float", "", horizontal_radius.Contains(requested_point), horizontal_radius.GetCount(), 0, 100, horizontal_radius.GetClosestPoint(requested_point).interpolation, horizontal_radius.GetClosestPoint(requested_point).co.X, false); - root["vertical_radius"] = add_property_json("Vertical Radius", vertical_radius.GetValue(requested_frame), "float", "", vertical_radius.Contains(requested_point), vertical_radius.GetCount(), 0, 100, vertical_radius.GetClosestPoint(requested_point).interpolation, vertical_radius.GetClosestPoint(requested_point).co.X, false); - root["sigma"] = add_property_json("Sigma", sigma.GetValue(requested_frame), "float", "", sigma.Contains(requested_point), sigma.GetCount(), 0, 100, sigma.GetClosestPoint(requested_point).interpolation, sigma.GetClosestPoint(requested_point).co.X, false); - root["iterations"] = add_property_json("Iterations", iterations.GetValue(requested_frame), "float", "", iterations.Contains(requested_point), iterations.GetCount(), 0, 100, iterations.GetClosestPoint(requested_point).interpolation, iterations.GetClosestPoint(requested_point).co.X, false); + root["horizontal_radius"] = add_property_json("Horizontal Radius", horizontal_radius.GetValue(requested_frame), "float", "", &horizontal_radius, 0, 100, false, requested_frame); + root["vertical_radius"] = add_property_json("Vertical Radius", vertical_radius.GetValue(requested_frame), "float", "", &vertical_radius, 0, 100, false, requested_frame); + root["sigma"] = add_property_json("Sigma", sigma.GetValue(requested_frame), "float", "", &sigma, 0, 100, false, requested_frame); + root["iterations"] = add_property_json("Iterations", iterations.GetValue(requested_frame), "float", "", &iterations, 0, 100, false, requested_frame); // Return formatted string return root.toStyledString(); diff --git a/src/effects/Brightness.cpp b/src/effects/Brightness.cpp index 28520a5e..daf9a09a 100644 --- a/src/effects/Brightness.cpp +++ b/src/effects/Brightness.cpp @@ -176,21 +176,18 @@ void Brightness::SetJsonValue(Json::Value root) { // Get all properties for a specific frame string Brightness::PropertiesJSON(long int requested_frame) { - // Requested Point - Point requested_point(requested_frame, requested_frame); - // Generate JSON properties list Json::Value root; - root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); - root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["layer"] = add_property_json("Track", Layer(), "int", "", false, 0, 0, 20, CONSTANT, -1, false); - root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, true); + root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); + root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); + root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame); // Keyframes - root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", brightness.Contains(requested_point), brightness.GetCount(), -1.0, 1.0, brightness.GetClosestPoint(requested_point).interpolation, brightness.GetClosestPoint(requested_point).co.X, false); - root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", contrast.Contains(requested_point), contrast.GetCount(), 0.0, 100.0, contrast.GetClosestPoint(requested_point).interpolation, contrast.GetClosestPoint(requested_point).co.X, false); + root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", &brightness, -1.0, 1.0, false, requested_frame); + root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", &contrast, 0.0, 100.0, false, requested_frame); // Return formatted string return root.toStyledString(); diff --git a/src/effects/ChromaKey.cpp b/src/effects/ChromaKey.cpp index 8f354b4c..ae59dfc4 100644 --- a/src/effects/ChromaKey.cpp +++ b/src/effects/ChromaKey.cpp @@ -156,24 +156,21 @@ void ChromaKey::SetJsonValue(Json::Value root) { // Get all properties for a specific frame string ChromaKey::PropertiesJSON(long int requested_frame) { - // Requested Point - Point requested_point(requested_frame, requested_frame); - // Generate JSON properties list Json::Value root; - root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); - root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["layer"] = add_property_json("Track", Layer(), "int", "", false, 0, 0, 20, CONSTANT, -1, false); - root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, true); + root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); + root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); + root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame); // Keyframes - root["color"] = add_property_json("Key Color", 0.0, "color", "", color.red.Contains(requested_point), color.red.GetCount(), 0, 255, color.red.GetClosestPoint(requested_point).interpolation, color.red.GetClosestPoint(requested_point).co.X, false); - root["color"]["red"] = add_property_json("Red", color.red.GetValue(requested_frame), "float", "", color.red.Contains(requested_point), color.red.GetCount(), 0, 255, color.red.GetClosestPoint(requested_point).interpolation, color.red.GetClosestPoint(requested_point).co.X, false); - root["color"]["blue"] = add_property_json("Blue", color.blue.GetValue(requested_frame), "float", "", color.blue.Contains(requested_point), color.blue.GetCount(), 0, 255, color.blue.GetClosestPoint(requested_point).interpolation, color.blue.GetClosestPoint(requested_point).co.X, false); - root["color"]["green"] = add_property_json("Green", color.green.GetValue(requested_frame), "float", "", color.green.Contains(requested_point), color.green.GetCount(), 0, 255, color.green.GetClosestPoint(requested_point).interpolation, color.green.GetClosestPoint(requested_point).co.X, false); - root["fuzz"] = add_property_json("Fuzz", fuzz.GetValue(requested_frame), "float", "", fuzz.Contains(requested_point), fuzz.GetCount(), 0, 25, fuzz.GetClosestPoint(requested_point).interpolation, fuzz.GetClosestPoint(requested_point).co.X, false); + root["color"] = add_property_json("Key Color", 0.0, "color", "", NULL, 0, 255, false, requested_frame); + root["color"]["red"] = add_property_json("Red", color.red.GetValue(requested_frame), "float", "", &color.red, 0, 255, false, requested_frame); + root["color"]["blue"] = add_property_json("Blue", color.blue.GetValue(requested_frame), "float", "", &color.blue, 0, 255, false, requested_frame); + root["color"]["green"] = add_property_json("Green", color.green.GetValue(requested_frame), "float", "", &color.green, 0, 255, false, requested_frame); + root["fuzz"] = add_property_json("Fuzz", fuzz.GetValue(requested_frame), "float", "", &fuzz, 0, 25, false, requested_frame); // Return formatted string return root.toStyledString(); diff --git a/src/effects/Deinterlace.cpp b/src/effects/Deinterlace.cpp index 2ec25a5b..c78b5b3f 100644 --- a/src/effects/Deinterlace.cpp +++ b/src/effects/Deinterlace.cpp @@ -148,18 +148,15 @@ void Deinterlace::SetJsonValue(Json::Value root) { // Get all properties for a specific frame string Deinterlace::PropertiesJSON(long int requested_frame) { - // Requested Point - Point requested_point(requested_frame, requested_frame); - // Generate JSON properties list Json::Value root; - root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); - root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["layer"] = add_property_json("Track", Layer(), "int", "", false, 0, 0, 20, CONSTANT, -1, false); - root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, true); - root["isOdd"] = add_property_json("Is Odd Frame", isOdd, "bool", "", false, 0, 0, 1, CONSTANT, -1, true); + root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); + root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); + root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame); + root["isOdd"] = add_property_json("Is Odd Frame", isOdd, "bool", "", NULL, 0, 1, true, requested_frame); // Add Is Odd Frame choices (dropdown style) root["isOdd"]["choices"].append(add_property_choice_json("Yes", true, isOdd)); diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp index fda3342e..7e316f23 100644 --- a/src/effects/Mask.cpp +++ b/src/effects/Mask.cpp @@ -277,26 +277,23 @@ void Mask::SetJsonValue(Json::Value root) { // Get all properties for a specific frame string Mask::PropertiesJSON(long int requested_frame) { - // Requested Point - Point requested_point(requested_frame, requested_frame); - // Generate JSON properties list Json::Value root; - root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); - root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["layer"] = add_property_json("Track", Layer(), "int", "", false, 0, 0, 20, CONSTANT, -1, false); - root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, true); - root["replace_image"] = add_property_json("Replace Image", replace_image, "int", "", false, 0, 0, 1, CONSTANT, -1, false); + root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); + root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); + root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame); + root["replace_image"] = add_property_json("Replace Image", replace_image, "int", "", NULL, 0, 1, false, requested_frame); // Add replace_image choices (dropdown style) root["replace_image"]["choices"].append(add_property_choice_json("Yes", true, replace_image)); root["replace_image"]["choices"].append(add_property_choice_json("No", false, replace_image)); // Keyframes - root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", brightness.Contains(requested_point), brightness.GetCount(), -1.0, 1.0, brightness.GetClosestPoint(requested_point).interpolation, brightness.GetClosestPoint(requested_point).co.X, false); - root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", contrast.Contains(requested_point), contrast.GetCount(), 0, 20, contrast.GetClosestPoint(requested_point).interpolation, contrast.GetClosestPoint(requested_point).co.X, false); + root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", &brightness, -1.0, 1.0, false, requested_frame); + root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", &contrast, 0, 20, false, requested_frame); // Return formatted string return root.toStyledString(); diff --git a/src/effects/Negate.cpp b/src/effects/Negate.cpp index 08b6e50f..fa3eae35 100644 --- a/src/effects/Negate.cpp +++ b/src/effects/Negate.cpp @@ -106,17 +106,14 @@ void Negate::SetJsonValue(Json::Value root) { // Get all properties for a specific frame string Negate::PropertiesJSON(long int requested_frame) { - // Requested Point - Point requested_point(requested_frame, requested_frame); - // Generate JSON properties list Json::Value root; - root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); - root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["layer"] = add_property_json("Track", Layer(), "int", "", false, 0, 0, 20, CONSTANT, -1, false); - root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, true); + root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); + root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); + root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame); // Return formatted string return root.toStyledString(); diff --git a/src/effects/Saturation.cpp b/src/effects/Saturation.cpp index b8bd4996..03c487fa 100644 --- a/src/effects/Saturation.cpp +++ b/src/effects/Saturation.cpp @@ -178,20 +178,17 @@ void Saturation::SetJsonValue(Json::Value root) { // Get all properties for a specific frame string Saturation::PropertiesJSON(long int requested_frame) { - // Requested Point - Point requested_point(requested_frame, requested_frame); - // Generate JSON properties list Json::Value root; - root["id"] = add_property_json("ID", 0.0, "string", Id(), false, 0, -1, -1, CONSTANT, -1, true); - root["position"] = add_property_json("Position", Position(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["layer"] = add_property_json("Track", Layer(), "int", "", false, 0, 0, 20, CONSTANT, -1, false); - root["start"] = add_property_json("Start", Start(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["end"] = add_property_json("End", End(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, false); - root["duration"] = add_property_json("Duration", Duration(), "float", "", false, 0, 0, 30 * 60 * 60 * 48, CONSTANT, -1, true); + root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame); + root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame); + root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame); + root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame); // Keyframes - root["saturation"] = add_property_json("Saturation", saturation.GetValue(requested_frame), "float", "", saturation.Contains(requested_point), saturation.GetCount(), 0.0, 4.0, saturation.GetClosestPoint(requested_point).interpolation, saturation.GetClosestPoint(requested_point).co.X, false); + root["saturation"] = add_property_json("Saturation", saturation.GetValue(requested_frame), "float", "", &saturation, 0.0, 4.0, false, requested_frame); // Return formatted string return root.toStyledString(); diff --git a/tests/KeyFrame_Tests.cpp b/tests/KeyFrame_Tests.cpp index 0a8739c2..fe96ea5a 100644 --- a/tests/KeyFrame_Tests.cpp +++ b/tests/KeyFrame_Tests.cpp @@ -57,7 +57,6 @@ TEST(Keyframe_AddPoint_With_1_Point) { // Create an empty keyframe Keyframe k1; - k1.Auto_Handle_Percentage = 0.4f; k1.AddPoint(openshot::Point(2,9)); CHECK_CLOSE(2.0f, k1.GetPoint(0).co.X, 0.00001); @@ -69,7 +68,6 @@ TEST(Keyframe_AddPoint_With_2_Points) { // Create an empty keyframe Keyframe k1; - k1.Auto_Handle_Percentage = 0.4f; k1.AddPoint(openshot::Point(2,9)); k1.AddPoint(openshot::Point(5,20)); @@ -83,7 +81,6 @@ TEST(Keyframe_GetValue_For_Bezier_Curve_2_Points) { // 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(50, 4), BEZIER)); @@ -91,9 +88,9 @@ TEST(Keyframe_GetValue_For_Bezier_Curve_2_Points) CHECK_CLOSE(1.0f, kf.GetValue(-1), 0.0001); CHECK_CLOSE(1.0f, kf.GetValue(0), 0.0001); CHECK_CLOSE(1.00023f, kf.GetValue(1), 0.0001); - CHECK_CLOSE(1.18398f, kf.GetValue(9), 0.0001); - CHECK_CLOSE(1.99988f, kf.GetValue(20), 0.0001); - CHECK_CLOSE(3.75424f, kf.GetValue(40), 0.0001); + CHECK_CLOSE(1.14025f, kf.GetValue(9), 0.0001); + CHECK_CLOSE(1.91492f, kf.GetValue(20), 0.0001); + CHECK_CLOSE(3.81602f, kf.GetValue(40), 0.0001); CHECK_CLOSE(4.0f, kf.GetValue(50), 0.0001); // Check the expected number of values CHECK_EQUAL(kf.Values.size(), 51); @@ -103,7 +100,6 @@ TEST(Keyframe_GetValue_For_Bezier_Curve_5_Points_40_Percent_Handle) { // 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(50, 4), BEZIER)); kf.AddPoint(openshot::Point(Coordinate(100, 10), BEZIER)); @@ -114,10 +110,10 @@ TEST(Keyframe_GetValue_For_Bezier_Curve_5_Points_40_Percent_Handle) CHECK_CLOSE(kf.GetValue(-1), 1.0f, 0.0001); CHECK_CLOSE(1.0f, kf.GetValue(0), 0.0001); CHECK_CLOSE(1.00023f, kf.GetValue(1), 0.0001); - CHECK_CLOSE(2.69174f, kf.GetValue(27), 0.0001); - CHECK_CLOSE(7.46386f, kf.GetValue(77), 0.0001); - CHECK_CLOSE(4.22691f, kf.GetValue(127), 0.0001); - CHECK_CLOSE(1.73193f, kf.GetValue(177), 0.0001); + CHECK_CLOSE(2.73656f, kf.GetValue(27), 0.0001); + CHECK_CLOSE(7.55139f, kf.GetValue(77), 0.0001); + CHECK_CLOSE(4.08102f, kf.GetValue(127), 0.0001); + CHECK_CLOSE(1.77569f, kf.GetValue(177), 0.0001); CHECK_CLOSE(3.0f, kf.GetValue(200), 0.0001); // Check the expected number of values CHECK_EQUAL(kf.Values.size(), 201); @@ -127,7 +123,6 @@ TEST(Keyframe_GetValue_For_Bezier_Curve_5_Points_25_Percent_Handle) { // Create a keyframe curve with 2 points Keyframe kf; - kf.Auto_Handle_Percentage = 0.25f; kf.AddPoint(openshot::Point(Coordinate(1, 1), BEZIER)); kf.AddPoint(openshot::Point(Coordinate(50, 4), BEZIER)); kf.AddPoint(openshot::Point(Coordinate(100, 10), BEZIER)); @@ -137,11 +132,11 @@ TEST(Keyframe_GetValue_For_Bezier_Curve_5_Points_25_Percent_Handle) // Spot check values from the curve CHECK_CLOSE(1.0f, kf.GetValue(-1), 0.0001); CHECK_CLOSE(1.0f, kf.GetValue(0), 0.0001); - CHECK_CLOSE(1.0009f, kf.GetValue(1), 0.0001); - CHECK_CLOSE(2.64678f, kf.GetValue(27), 0.0001); - CHECK_CLOSE(7.37597f, kf.GetValue(77), 0.0001); - CHECK_CLOSE(4.37339f, kf.GetValue(127), 0.0001); - CHECK_CLOSE(1.68798f, kf.GetValue(177), 0.0001); + CHECK_CLOSE(1.00023f, kf.GetValue(1), 0.0001); + CHECK_CLOSE(2.73656f, kf.GetValue(27), 0.0001); + CHECK_CLOSE(7.55139f, kf.GetValue(77), 0.0001); + CHECK_CLOSE(4.08102f, kf.GetValue(127), 0.0001); + CHECK_CLOSE(1.77569f, kf.GetValue(177), 0.0001); CHECK_CLOSE(3.0f, kf.GetValue(200), 0.0001); // Check the expected number of values CHECK_EQUAL(kf.Values.size(), 201); @@ -151,7 +146,6 @@ TEST(Keyframe_GetValue_For_Linear_Curve_3_Points) { // 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)); @@ -172,7 +166,6 @@ TEST(Keyframe_GetValue_For_Constant_Curve_3_Points) { // Create a keyframe curve with 2 points Keyframe kf; - kf.Auto_Handle_Percentage = 0.4f; kf.AddPoint(openshot::Point(Coordinate(1, 1), CONSTANT)); kf.AddPoint(openshot::Point(Coordinate(25, 8), CONSTANT)); kf.AddPoint(openshot::Point(Coordinate(50, 2), CONSTANT)); @@ -202,26 +195,26 @@ TEST(Keyframe_Check_Direction_and_Repeat_Fractions) CHECK_EQUAL(kf.GetInt(1), 500); CHECK_EQUAL(kf.IsIncreasing(1), false); CHECK_EQUAL(kf.GetRepeatFraction(1).num, 1); - CHECK_EQUAL(kf.GetRepeatFraction(1).den, 10); + CHECK_EQUAL(kf.GetRepeatFraction(1).den, 12); CHECK_EQUAL(kf.GetDelta(1), 500); - CHECK_EQUAL(kf.GetInt(24), 497); + CHECK_EQUAL(kf.GetInt(24), 498); CHECK_EQUAL(kf.IsIncreasing(24), false); - CHECK_EQUAL(kf.GetRepeatFraction(24).num, 2); - CHECK_EQUAL(kf.GetRepeatFraction(24).den, 4); + CHECK_EQUAL(kf.GetRepeatFraction(24).num, 3); + CHECK_EQUAL(kf.GetRepeatFraction(24).den, 6); CHECK_EQUAL(kf.GetDelta(24), 0); - CHECK_EQUAL(kf.GetLong(390), 101); - CHECK_EQUAL(kf.IsIncreasing(390), false); - CHECK_EQUAL(kf.GetRepeatFraction(390).num, 8); - CHECK_EQUAL(kf.GetRepeatFraction(390).den, 8); + CHECK_EQUAL(kf.GetLong(390), 100); + CHECK_EQUAL(kf.IsIncreasing(390), true); + CHECK_EQUAL(kf.GetRepeatFraction(390).num, 3); + CHECK_EQUAL(kf.GetRepeatFraction(390).den, 15); CHECK_EQUAL(kf.GetDelta(390), 0); CHECK_EQUAL(kf.GetLong(391), 100); CHECK_EQUAL(kf.IsIncreasing(391), true); - CHECK_EQUAL(kf.GetRepeatFraction(391).num, 1); - CHECK_EQUAL(kf.GetRepeatFraction(391).den, 12); - CHECK_EQUAL(kf.GetDelta(391), -1); + CHECK_EQUAL(kf.GetRepeatFraction(391).num, 4); + CHECK_EQUAL(kf.GetRepeatFraction(391).den, 15); + CHECK_EQUAL(kf.GetDelta(388), -1); } @@ -233,7 +226,7 @@ TEST(Keyframe_Get_Closest_Point) kf.AddPoint(1000, 1.0); kf.AddPoint(2500, 0.0); - // Spot check values from the curve + // Spot check values from the curve (to the right) CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(900, 900)).co.X, 1000); CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(1, 1)).co.X, 1); CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(5, 5)).co.X, 1000); @@ -242,9 +235,36 @@ TEST(Keyframe_Get_Closest_Point) CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(2500, 2500)).co.X, 2500); CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(3000, 3000)).co.X, 2500); + // Spot check values from the curve (to the left) + CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(900, 900), true).co.X, 1); + CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(1, 1), true).co.X, 1); + CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(5, 5), true).co.X, 1); + CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(1000, 1000), true).co.X, 1); + CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(1001, 1001), true).co.X, 1000); + CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(2500, 2500), true).co.X, 1000); + CHECK_EQUAL(kf.GetClosestPoint(openshot::Point(3000, 3000), true).co.X, 2500); } +TEST(Keyframe_Get_Previous_Point) +{ + // Create a keyframe curve with 2 points + Keyframe kf; + kf.AddPoint(1, 0.0); + kf.AddPoint(1000, 1.0); + kf.AddPoint(2500, 0.0); + + // Spot check values from the curve + CHECK_EQUAL(kf.GetPreviousPoint(kf.GetClosestPoint(openshot::Point(900, 900))).co.X, 1); + CHECK_EQUAL(kf.GetPreviousPoint(kf.GetClosestPoint(openshot::Point(1, 1))).co.X, 1); + CHECK_EQUAL(kf.GetPreviousPoint(kf.GetClosestPoint(openshot::Point(5, 5))).co.X, 1); + CHECK_EQUAL(kf.GetPreviousPoint(kf.GetClosestPoint(openshot::Point(1000, 1000))).co.X, 1); + CHECK_EQUAL(kf.GetPreviousPoint(kf.GetClosestPoint(openshot::Point(1001, 1001))).co.X, 1000); + CHECK_EQUAL(kf.GetPreviousPoint(kf.GetClosestPoint(openshot::Point(2500, 2500))).co.X, 1000); + CHECK_EQUAL(kf.GetPreviousPoint(kf.GetClosestPoint(openshot::Point(3000, 3000))).co.X, 1000); + +} + TEST(Keyframe_Get_Max_Point) { // Create a keyframe curve @@ -274,7 +294,6 @@ 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)); @@ -283,7 +302,7 @@ TEST(Keyframe_Scale_Keyframe) 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(3.68f, kf.GetValue(40), 0.01); CHECK_CLOSE(2.0f, kf.GetValue(49), 0.01); CHECK_CLOSE(2.0f, kf.GetValue(50), 0.01); @@ -292,12 +311,12 @@ TEST(Keyframe_Scale_Keyframe) // 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(4.21f, kf.GetValue(24), 0.01); + CHECK_CLOSE(4.47f, kf.GetValue(25), 0.01); + CHECK_CLOSE(7.57f, 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.35f, kf.GetValue(90), 0.01); CHECK_CLOSE(2.0f, kf.GetValue(100), 0.01); // Resize / Scale the keyframe @@ -307,7 +326,7 @@ TEST(Keyframe_Scale_Keyframe) 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(3.68f, kf.GetValue(40), 0.01); CHECK_CLOSE(2.0f, kf.GetValue(49), 0.01); CHECK_CLOSE(2.0f, kf.GetValue(50), 0.01); @@ -317,7 +336,6 @@ 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)); diff --git a/tests/Point_Tests.cpp b/tests/Point_Tests.cpp index 27d1a599..376a69d1 100644 --- a/tests/Point_Tests.cpp +++ b/tests/Point_Tests.cpp @@ -111,27 +111,3 @@ TEST(Point_Constructor_With_Coordinate_And_BEZIER_And_MANUAL_Handle) CHECK_EQUAL(BEZIER, p1.interpolation); CHECK_EQUAL(MANUAL, p1.handle_type); } - -TEST(Point_Set_Handles_Auto_No_Value) -{ - // Create a point with X and Y values - openshot::Point p1(2,8); - p1.Initialize_Handles(); - - CHECK_EQUAL(p1.co.Y, p1.handle_left.Y); - CHECK_EQUAL(p1.co.Y, p1.handle_right.Y); - CHECK_CLOSE(p1.co.X, p1.handle_left.X, 0.000001); - CHECK_CLOSE(p1.co.X, p1.handle_right.X, 0.000001); -} - -TEST(Point_Set_Handles_Auto_With_Values) -{ - // Create a point with X and Y values - openshot::Point p1(2,8); - p1.Initialize_Handles(4.2); - - CHECK_EQUAL(p1.co.Y, p1.handle_left.Y); - CHECK_EQUAL(p1.co.Y, p1.handle_right.Y); - CHECK_CLOSE(p1.co.X - 4.2, p1.handle_left.X, 0.000001); - CHECK_CLOSE(p1.co.X + 4.2, p1.handle_right.X, 0.000001); -}