OpenShot Library | libopenshot  0.7.0
AnimatedCurve.cpp
Go to the documentation of this file.
1 
9 // Copyright (c) 2008-2026 OpenShot Studios, LLC
10 //
11 // SPDX-License-Identifier: LGPL-3.0-or-later
12 
13 #include "AnimatedCurve.h"
14 #include "Exceptions.h"
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <sstream>
19 
20 using namespace openshot;
21 
22 namespace {
23 constexpr double kCurveDomainMax = 255.0;
24 
25 static double clamp01(const double value) {
26  return std::max(0.0, std::min(1.0, value));
27 }
28 }
29 
31  : AnimatedCurveNode(0, 0.0, 0.0, LINEAR) {}
32 
33 AnimatedCurveNode::AnimatedCurveNode(int node_id, double node_x, double node_y, InterpolationType node_interpolation)
34  : id(node_id),
35  x(clamp01(node_x)),
36  y(clamp01(node_y)),
37  left_handle_x(0.5),
38  left_handle_y(1.0),
39  right_handle_x(0.5),
40  right_handle_y(0.0),
41  interpolation(node_interpolation),
42  handle_type(AUTO) {}
43 
44 Point AnimatedCurveNode::Evaluate(int64_t frame_number, double x_scale) const {
45  Point point(static_cast<float>(clamp01(x.GetValue(frame_number)) * x_scale),
46  static_cast<float>(clamp01(y.GetValue(frame_number))),
48  point.handle_left = Coordinate(clamp01(left_handle_x.GetValue(frame_number)),
49  clamp01(left_handle_y.GetValue(frame_number)));
50  point.handle_right = Coordinate(clamp01(right_handle_x.GetValue(frame_number)),
51  clamp01(right_handle_y.GetValue(frame_number)));
52  point.handle_type = handle_type;
53  return point;
54 }
55 
56 std::string AnimatedCurveNode::Json() const {
57  return JsonValue().toStyledString();
58 }
59 
60 Json::Value AnimatedCurveNode::JsonValue() const {
61  Json::Value root(Json::objectValue);
62  root["id"] = id;
63  root["x"] = x.JsonValue();
64  root["y"] = y.JsonValue();
65  root["left_handle_x"] = left_handle_x.JsonValue();
66  root["left_handle_y"] = left_handle_y.JsonValue();
67  root["right_handle_x"] = right_handle_x.JsonValue();
68  root["right_handle_y"] = right_handle_y.JsonValue();
69  root["interpolation"] = interpolation;
70  root["handle_type"] = handle_type;
71  return root;
72 }
73 
74 void AnimatedCurveNode::SetJson(const std::string value) {
75  try {
77  } catch (const std::exception&) {
78  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
79  }
80 }
81 
82 void AnimatedCurveNode::SetJsonValue(const Json::Value& root) {
83  if (!root["id"].isNull())
84  id = root["id"].asInt();
85  if (!root["x"].isNull())
86  x.SetJsonValue(root["x"]);
87  if (!root["y"].isNull())
88  y.SetJsonValue(root["y"]);
89  if (!root["left_handle_x"].isNull())
90  left_handle_x.SetJsonValue(root["left_handle_x"]);
91  if (!root["left_handle_y"].isNull())
92  left_handle_y.SetJsonValue(root["left_handle_y"]);
93  if (!root["right_handle_x"].isNull())
94  right_handle_x.SetJsonValue(root["right_handle_x"]);
95  if (!root["right_handle_y"].isNull())
96  right_handle_y.SetJsonValue(root["right_handle_y"]);
97  if (!root["interpolation"].isNull())
98  interpolation = static_cast<InterpolationType>(root["interpolation"].asInt());
99  if (!root["handle_type"].isNull())
100  handle_type = static_cast<HandleType>(root["handle_type"].asInt());
101 }
102 
104  : enabled(1.0) {
105  nodes.emplace_back(0, 0.0, 0.0, LINEAR);
106  nodes.emplace_back(1, 1.0, 1.0, LINEAR);
107 }
108 
109 Keyframe AnimatedCurve::BuildCurve(int64_t frame_number, double x_scale) const {
110  Keyframe curve;
111  std::vector<AnimatedCurveNode> ordered_nodes = nodes;
112 
113  std::sort(ordered_nodes.begin(), ordered_nodes.end(),
114  [frame_number](const AnimatedCurveNode& lhs, const AnimatedCurveNode& rhs) {
115  const double lhs_x = lhs.x.GetValue(frame_number);
116  const double rhs_x = rhs.x.GetValue(frame_number);
117  if (lhs_x == rhs_x)
118  return lhs.id < rhs.id;
119  return lhs_x < rhs_x;
120  });
121 
122  for (const auto& node : ordered_nodes) {
123  curve.AddPoint(node.Evaluate(frame_number, x_scale));
124  }
125  return curve;
126 }
127 
128 float AnimatedCurve::Sample(float input, int64_t frame_number) const {
129  if (enabled.GetValue(frame_number) < 0.5)
130  return static_cast<float>(clamp01(input));
131 
132  const Keyframe curve = BuildCurve(frame_number, kCurveDomainMax);
133  return static_cast<float>(clamp01(curve.GetValue(std::lround(clamp01(input) * kCurveDomainMax))));
134 }
135 
136 std::string AnimatedCurve::Summary(int64_t frame_number) const {
137  std::ostringstream ss;
138  if (enabled.GetValue(frame_number) < 0.5)
139  ss << "Disabled, ";
140  ss << nodes.size() << " nodes";
141  return ss.str();
142 }
143 
144 std::string AnimatedCurve::Json() const {
145  return JsonValue().toStyledString();
146 }
147 
148 Json::Value AnimatedCurve::JsonValue() const {
149  Json::Value root(Json::objectValue);
150  root["enabled"] = enabled.JsonValue();
151  root["nodes"] = Json::Value(Json::arrayValue);
152  for (const auto& node : nodes)
153  root["nodes"].append(node.JsonValue());
154  return root;
155 }
156 
157 void AnimatedCurve::SetJson(const std::string value) {
158  try {
160  } catch (const std::exception&) {
161  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
162  }
163 }
164 
165 void AnimatedCurve::SetJsonValue(const Json::Value& root) {
166  if (!root["enabled"].isNull())
167  enabled.SetJsonValue(root["enabled"]);
168 
169  if (root["nodes"].isArray()) {
170  std::vector<AnimatedCurveNode> parsed_nodes;
171  for (const auto& item : root["nodes"]) {
172  AnimatedCurveNode node;
173  node.SetJsonValue(item);
174  parsed_nodes.push_back(node);
175  }
176  if (!parsed_nodes.empty())
177  nodes.swap(parsed_nodes);
178  }
179 }
openshot::stringToJson
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:16
openshot::AnimatedCurveNode::SetJson
void SetJson(const std::string value)
Definition: AnimatedCurve.cpp:74
openshot::AnimatedCurveNode::right_handle_x
Keyframe right_handle_x
Definition: AnimatedCurve.h:33
openshot::AnimatedCurveNode::x
Keyframe x
Definition: AnimatedCurve.h:29
openshot::AnimatedCurveNode::left_handle_y
Keyframe left_handle_y
Definition: AnimatedCurve.h:32
openshot::AnimatedCurve::Sample
float Sample(float input, int64_t frame_number) const
Definition: AnimatedCurve.cpp:128
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: AnimatedCurve.h:24
openshot::AnimatedCurve::BuildCurve
Keyframe BuildCurve(int64_t frame_number, double x_scale=1.0) const
Definition: AnimatedCurve.cpp:109
openshot::AnimatedCurveNode::right_handle_y
Keyframe right_handle_y
Definition: AnimatedCurve.h:34
openshot::AnimatedCurveNode::Evaluate
Point Evaluate(int64_t frame_number, double x_scale=1.0) const
Definition: AnimatedCurve.cpp:44
openshot::AnimatedCurveNode::JsonValue
Json::Value JsonValue() const
Definition: AnimatedCurve.cpp:60
openshot::AnimatedCurveNode::Json
std::string Json() const
Definition: AnimatedCurve.cpp:56
openshot::AnimatedCurve::enabled
Keyframe enabled
Definition: AnimatedCurve.h:54
openshot::AnimatedCurve::SetJsonValue
void SetJsonValue(const Json::Value &root)
Definition: AnimatedCurve.cpp:165
openshot::Keyframe::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: KeyFrame.cpp:372
openshot::Point::handle_type
HandleType handle_type
This is the handle mode.
Definition: Point.h:70
openshot::Keyframe::JsonValue
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: KeyFrame.cpp:339
openshot::HandleType
HandleType
When BEZIER interpolation is used, the point's left and right handles are used to influence the direc...
Definition: Point.h:41
openshot::AnimatedCurve::Json
std::string Json() const
Definition: AnimatedCurve.cpp:144
openshot::Keyframe::AddPoint
void AddPoint(Point p)
Add a new point on the key-frame. Each point has a primary coordinate, a left handle,...
Definition: KeyFrame.cpp:131
openshot::AnimatedCurveNode::id
int id
Definition: AnimatedCurve.h:28
openshot::AnimatedCurve::AnimatedCurve
AnimatedCurve()
Definition: AnimatedCurve.cpp:103
openshot::AnimatedCurveNode::left_handle_x
Keyframe left_handle_x
Definition: AnimatedCurve.h:31
openshot::Keyframe
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition: KeyFrame.h:53
openshot::AUTO
@ AUTO
Automatically adjust the handles to achieve the smoothest curve.
Definition: Point.h:42
openshot::InvalidJSON
Exception for invalid JSON.
Definition: Exceptions.h:223
AnimatedCurve.h
Header file for AnimatedCurve classes.
openshot::AnimatedCurve::JsonValue
Json::Value JsonValue() const
Definition: AnimatedCurve.cpp:148
openshot::LINEAR
@ LINEAR
Linear curves are angular, straight lines between two points.
Definition: Point.h:30
openshot::AnimatedCurveNode::y
Keyframe y
Definition: AnimatedCurve.h:30
openshot::Point::handle_left
Coordinate handle_left
This is the left handle coordinate (in percentages from 0 to 1)
Definition: Point.h:67
openshot::AnimatedCurveNode::interpolation
InterpolationType interpolation
Definition: AnimatedCurve.h:35
openshot::AnimatedCurveNode::handle_type
HandleType handle_type
Definition: AnimatedCurve.h:36
openshot::InterpolationType
InterpolationType
This controls how a Keyframe uses this point to interpolate between two points.
Definition: Point.h:28
openshot::Point::handle_right
Coordinate handle_right
This is the right handle coordinate (in percentages from 0 to 1)
Definition: Point.h:68
openshot::AnimatedCurve::Summary
std::string Summary(int64_t frame_number) const
Definition: AnimatedCurve.cpp:136
openshot::AnimatedCurve::SetJson
void SetJson(const std::string value)
Definition: AnimatedCurve.cpp:157
openshot::AnimatedCurveNode
Definition: AnimatedCurve.h:26
openshot::Coordinate
A Cartesian coordinate (X, Y) used in the Keyframe animation system.
Definition: Coordinate.h:38
openshot::AnimatedCurveNode::AnimatedCurveNode
AnimatedCurveNode()
Definition: AnimatedCurve.cpp:30
openshot::Point
A Point is the basic building block of a key-frame curve.
Definition: Point.h:64
Exceptions.h
Header file for all Exception classes.
openshot::Keyframe::GetValue
double GetValue(int64_t index) const
Get the value at a specific index.
Definition: KeyFrame.cpp:258
openshot::AnimatedCurveNode::SetJsonValue
void SetJsonValue(const Json::Value &root)
Definition: AnimatedCurve.cpp:82