Skip to content

Commit

Permalink
Added functionality to link a clip to a tracked object (Animations)
Browse files Browse the repository at this point in the history
- Added KeyframeBase class
- Adjusted the inheritance of KeyframeBase to KeyframeBBox and Keyframe
- Added feature to attach clip to bounding box
- Added support to select tracked object with a dropdown list and removed keyframebbox downcasting
  • Loading branch information
BrennoCaldato committed Dec 23, 2020
1 parent e48405d commit b579ea6
Show file tree
Hide file tree
Showing 16 changed files with 644 additions and 310 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Expand Up @@ -86,6 +86,7 @@ set(OPENSHOT_SOURCES
FrameMapper.cpp
Json.cpp
KeyFrame.cpp
KeyFrameBase.cpp
KeyFrameBBox.cpp
OpenShotVersion.cpp
ZmqLogger.cpp
Expand Down
91 changes: 84 additions & 7 deletions src/Clip.cpp
Expand Up @@ -39,6 +39,7 @@
#include "ChunkReader.h"
#include "DummyReader.h"
#include "Timeline.h"
#include "effects/Tracker.h"

using namespace openshot;

Expand All @@ -57,6 +58,7 @@ void Clip::init_settings()
mixing = VOLUME_MIX_NONE;
waveform = false;
previous_properties = "";
attached_id = "";

// Init scale curves
scale_x = Keyframe(1.0);
Expand Down Expand Up @@ -242,6 +244,31 @@ Clip::~Clip()
}
}

// Attach clip to bounding box
void Clip::AttachToTracker(std::string tracked_id)
{
// Search for the tracked object on the timeline
Timeline *parentTimeline = (Timeline *) ParentTimeline();

// Create a smart pointer to the tracked object from the timeline
std::shared_ptr<openshot::KeyframeBase> trackedObject = parentTimeline->GetTrackedObject(tracked_id);

// Check for valid tracked object
if (trackedObject){
SetAttachedObject(trackedObject);
return;
}
else{
return;
}
}

// Set the pointer to the trackedObject this clip is attached to
void Clip::SetAttachedObject(std::shared_ptr<openshot::KeyframeBase> trackedObject){
attachedObject = trackedObject;
return;
}

/// Set the current reader
void Clip::Reader(ReaderBase* new_reader)
{
Expand Down Expand Up @@ -762,7 +789,24 @@ std::string Clip::PropertiesJSON(int64_t requested_frame) const {
root["display"] = add_property_json("Frame Number", display, "int", "", NULL, 0, 3, false, requested_frame);
root["mixing"] = add_property_json("Volume Mixing", mixing, "int", "", NULL, 0, 2, false, requested_frame);
root["waveform"] = add_property_json("Waveform", waveform, "int", "", NULL, 0, 1, false, requested_frame);

root["attached_id"] = add_property_json("Attached ID", 0.0, "string", GetAttachedId(), NULL, -1, -1, false, requested_frame);

// Add attached id choices (dropdown style)
if (timeline){
Timeline* parentTimeline = (Timeline *) timeline;
std::vector<std::string> tracked_ids = parentTimeline->GetTrackedObjectsIds();
Json::Value temp;
temp["name"] = "";
temp["value"] = "";
temp["selected"] = true;
root["attached_id"]["choices"].append(temp);
for (auto it = tracked_ids.begin(); it != tracked_ids.end(); ++it){
temp["name"] = *it;
temp["value"] = *it;
temp["selected"] = true;
root["attached_id"]["choices"].append(temp);
}
}
// Add gravity choices (dropdown style)
root["gravity"]["choices"].append(add_property_choice_json("Top Left", GRAVITY_TOP_LEFT, gravity));
root["gravity"]["choices"].append(add_property_choice_json("Top Center", GRAVITY_TOP, gravity));
Expand Down Expand Up @@ -836,6 +880,7 @@ Json::Value Clip::JsonValue() const {

// Create root json object
Json::Value root = ClipBase::JsonValue(); // get parent properties
root["attached_id"] = attached_id;
root["gravity"] = gravity;
root["scale"] = scale;
root["anchor"] = anchor;
Expand Down Expand Up @@ -913,6 +958,11 @@ void Clip::SetJsonValue(const Json::Value root) {
cache.Clear();

// Set data from Json (if key is found)
if (!root["attached_id"].isNull())
attached_id = root["attached_id"].asString();
if (attached_id.size() > 0){
AttachToTracker(attached_id);
}
if (!root["gravity"].isNull())
gravity = (GravityType) root["gravity"].asInt();
if (!root["scale"].isNull())
Expand Down Expand Up @@ -1096,6 +1146,21 @@ void Clip::AddEffect(EffectBase* effect)
// Sort effects
sort_effects();

// Add Tracker to Timeline
if (effect->info.class_name == "Tracker"){

Timeline* parentTimeline = (Timeline *) ParentTimeline();

// Downcast effect as Tracker
Tracker* tracker = (Tracker *) effect;

// Get tracked data from the Tracker effect
std::shared_ptr<openshot::KeyFrameBBox> trackedData = tracker->trackedData;

// Add tracked data to the timeline
parentTimeline->AddTrackedObject(trackedData);
}

// Clear cache
cache.Clear();
}
Expand Down Expand Up @@ -1213,6 +1278,21 @@ void Clip::apply_keyframes(std::shared_ptr<Frame> frame, int width, int height)
}
}

/* TRANSFORM CLIP TO ATTACHED OBJECT'S POSITION AND DIMENSION */
if (attachedObject){

// Access the KeyframeBBox properties
std::map<std::string, float> boxValues = attachedObject->GetBoxValues(frame->number);

// Set the bounding box keyframes to this clip keyframes
location_x.AddPoint(frame->number, (boxValues["cx"]-0.5));
location_y.AddPoint(frame->number, (boxValues["cy"]-0.5));
scale_x.AddPoint(frame->number, boxValues["w"]*boxValues["sx"]*2.0);
scale_y.AddPoint(frame->number, boxValues["h"]*boxValues["sy"]);
rotation.AddPoint(frame->number, boxValues["r"]);

}

/* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */
float x = 0.0; // left
float y = 0.0; // top
Expand Down Expand Up @@ -1261,6 +1341,8 @@ void Clip::apply_keyframes(std::shared_ptr<Frame> frame, int width, int height)
// Debug output
ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Gravity)", "frame->number", frame->number, "source_clip->gravity", gravity, "scaled_source_width", scaled_source_width, "scaled_source_height", scaled_source_height);

QTransform transform;

/* LOCATION, ROTATION, AND SCALE */
float r = rotation.GetValue(frame->number); // rotate in degrees
x += (width * location_x.GetValue(frame->number)); // move in percentage of final width
Expand All @@ -1270,16 +1352,13 @@ void Clip::apply_keyframes(std::shared_ptr<Frame> frame, int width, int height)
float origin_x_value = origin_x.GetValue(frame->number);
float origin_y_value = origin_y.GetValue(frame->number);

QTransform transform;

// Transform source image (if needed)
ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_keyframes (Build QTransform - if needed)", "frame->number", frame->number, "x", x, "y", y, "r", r, "sx", sx, "sy", sy);

if (!isEqual(x, 0) || !isEqual(y, 0)) {
// TRANSLATE/MOVE CLIP
transform.translate(x, y);
}

}
if (!isEqual(r, 0) || !isEqual(shear_x_value, 0) || !isEqual(shear_y_value, 0)) {
// ROTATE CLIP (around origin_x, origin_y)
float origin_x_offset = (scaled_source_width * origin_x_value);
Expand All @@ -1289,11 +1368,9 @@ void Clip::apply_keyframes(std::shared_ptr<Frame> frame, int width, int height)
transform.shear(shear_x_value, shear_y_value);
transform.translate(-origin_x_offset,-origin_y_offset);
}

// SCALE CLIP (if needed)
float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx;
float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy;

if (!isEqual(source_width_scale, 1.0) || !isEqual(source_height_scale, 1.0)) {
transform.scale(source_width_scale, source_height_scale);
}
Expand Down
18 changes: 16 additions & 2 deletions src/Clip.h
Expand Up @@ -54,6 +54,7 @@
#include "Fraction.h"
#include "Frame.h"
#include "KeyFrame.h"
#include "KeyFrameBase.h"
#include "ReaderBase.h"
#include "JuceHeader.h"

Expand Down Expand Up @@ -123,6 +124,8 @@ namespace openshot {
bool waveform; ///< Should a waveform be used instead of the clip's image
std::list<openshot::EffectBase*> effects; ///<List of clips on this timeline
bool is_open; ///> Is Reader opened
std::string attached_id; ///< Id of the bounding box that this clip is attached to
std::shared_ptr<openshot::KeyframeBase> attachedObject;

// Audio resampler (if time mapping)
openshot::AudioResampler *resampler;
Expand Down Expand Up @@ -161,8 +164,6 @@ namespace openshot {
/// Reverse an audio buffer
void reverse_buffer(juce::AudioSampleBuffer* buffer);



public:
openshot::GravityType gravity; ///< The gravity of a clip determines where it snaps to its parent
openshot::ScaleType scale; ///< The scale determines how a clip should be resized to fit its parent
Expand Down Expand Up @@ -196,6 +197,19 @@ namespace openshot {
/// Determine if reader is open or closed
bool IsOpen() override { return is_open; };

/// Get and set the bounding box that this clip is attached to
std::string GetAttachedId() const { return attached_id; };
/// Set id of the bounding box that this clip is attached to
void SetAttachedId(std::string value) { attached_id = value; };

/// Attach clip to bounding box
void AttachToTracker(std::string tracked_id);

/// Set the pointer to the trackedObject this clip is attached to
void SetAttachedObject(std::shared_ptr<openshot::KeyframeBase> trackedObject);
/// Return a pointer to the trackedObject this clip is attached to
std::shared_ptr<openshot::KeyframeBase> GetAttachedObject() const { return attachedObject; };

/// Return the type name of the class
std::string Name() override { return "Clip"; };

Expand Down
63 changes: 3 additions & 60 deletions src/KeyFrame.cpp
Expand Up @@ -36,66 +36,7 @@
using namespace std;
using namespace openshot;

namespace {
bool IsPointBeforeX(Point const & p, double const x) {
return p.co.X < x;
}

double InterpolateLinearCurve(Point const & left, Point const & right, double const target) {
double const diff_Y = right.co.Y - left.co.Y;
double const diff_X = right.co.X - left.co.X;
double const slope = diff_Y / diff_X;
return left.co.Y + slope * (target - left.co.X);
}

double InterpolateBezierCurve(Point const & left, Point const & right, double const target, double const allowed_error) {
double const X_diff = right.co.X - left.co.X;
double const Y_diff = right.co.Y - left.co.Y;
Coordinate const p0 = left.co;
Coordinate const p1 = Coordinate(p0.X + left.handle_right.X * X_diff, p0.Y + left.handle_right.Y * Y_diff);
Coordinate const p2 = Coordinate(p0.X + right.handle_left.X * X_diff, p0.Y + right.handle_left.Y * Y_diff);
Coordinate const p3 = right.co;

double t = 0.5;
double t_step = 0.25;
do {
// Bernstein polynoms
double B[4] = {1, 3, 3, 1};
double oneMinTExp = 1;
double tExp = 1;
for (int i = 0; i < 4; ++i, tExp *= t) {
B[i] *= tExp;
}
for (int i = 0; i < 4; ++i, oneMinTExp *= 1 - t) {
B[4 - i - 1] *= oneMinTExp;
}
double const x = p0.X * B[0] + p1.X * B[1] + p2.X * B[2] + p3.X * B[3];
double const y = p0.Y * B[0] + p1.Y * B[1] + p2.Y * B[2] + p3.Y * B[3];
if (fabs(target - x) < allowed_error) {
return y;
}
if (x > target) {
t -= t_step;
}
else {
t += t_step;
}
t_step /= 2;
} while (true);
}


double InterpolateBetween(Point const & left, Point const & right, double target, double allowed_error) {
assert(left.co.X < target);
assert(target <= right.co.X);
switch (right.interpolation) {
case CONSTANT: return left.co.Y;
case LINEAR: return InterpolateLinearCurve(left, right, target);
case BEZIER: return InterpolateBezierCurve(left, right, target, allowed_error);
}
}


namespace{
template<typename Check>
int64_t SearchBetweenPoints(Point const & left, Point const & right, int64_t const current, Check check) {
int64_t start = left.co.X;
Expand All @@ -114,6 +55,7 @@ namespace {
}



// Constructor which sets the default point & coordinate at X=1
Keyframe::Keyframe(double value) {
// Add initial point
Expand Down Expand Up @@ -544,6 +486,7 @@ void Keyframe::RemovePoint(int64_t index) {
throw OutOfBoundsPoint("Invalid point requested", index, Points.size());
}

// Replace an existing point with a new point
void Keyframe::UpdatePoint(int64_t index, Point p) {
// Remove matching point
RemovePoint(index);
Expand Down
9 changes: 6 additions & 3 deletions src/KeyFrame.h
Expand Up @@ -41,6 +41,7 @@
#include "Coordinate.h"
#include "Point.h"
#include "Json.h"
#include "KeyFrameBase.h"

namespace openshot {

Expand All @@ -61,9 +62,11 @@ namespace openshot {
* kf.PrintValues();
* \endcode
*/
class Keyframe {
class Keyframe : public KeyframeBase {


private:
std::vector<Point> Points; ///< Vector of all Points
std::vector<Point> Points; ///< Vector of all Points

public:

Expand Down Expand Up @@ -145,7 +148,7 @@ namespace openshot {

/// 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(double scale);
void ScalePoints(double scale) override;

/// Replace an existing point with a new point
void UpdatePoint(int64_t index, Point p);
Expand Down

0 comments on commit b579ea6

Please sign in to comment.