implemented bezier interpolation into parameters inputs
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
common/bezier.h
|
||||
common/bezier.cpp
|
||||
common/channellayout.h
|
||||
common/clamp.h
|
||||
common/constructors.h
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "bezier.h"
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
double Bezier::QuadraticXtoT(double x, double a, double b, double c)
|
||||
{
|
||||
return (a - b + qSqrt(a*x + c*x - 2*b*x + qPow(b, 2) - a*c))/(a - 2*b + c);
|
||||
}
|
||||
|
||||
double Bezier::QuadraticTtoY(double a, double b, double c, double t)
|
||||
{
|
||||
return qPow(1.0 - t, 2)*a + 2*(1.0 - t)*t*b + qPow(t, 2)*c;
|
||||
}
|
||||
|
||||
double Bezier::CubicXtoT(double x_target, double a, double b, double c, double d)
|
||||
{
|
||||
double tolerance = 0.0001;
|
||||
|
||||
double lower = 0.0;
|
||||
double upper = 1.0;
|
||||
|
||||
double percent = 0.5;
|
||||
double x = CubicTtoY(a, b, c, d, percent);
|
||||
|
||||
while (qAbs(x_target - x) > tolerance) {
|
||||
if (x_target > x) {
|
||||
lower = percent;
|
||||
} else {
|
||||
upper = percent;
|
||||
}
|
||||
|
||||
percent = (upper + lower) / 2.0;
|
||||
x = CubicTtoY(a, b, c, d, percent);
|
||||
}
|
||||
|
||||
return percent;
|
||||
}
|
||||
|
||||
double Bezier::CubicTtoY(double a, double b, double c, double d, double t)
|
||||
{
|
||||
return qPow(1.0 - t, 3)*a + 3*qPow(1.0 - t, 2)*t*b + 3*(1.0 - t)*qPow(t, 2)*c + qPow(t, 3)*d;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef BEZIER_H
|
||||
#define BEZIER_H
|
||||
|
||||
|
||||
class Bezier
|
||||
{
|
||||
public:
|
||||
static double QuadraticXtoT(double x, double a, double b, double c);
|
||||
|
||||
static double QuadraticTtoY(double a, double b, double c, double t);
|
||||
|
||||
static double CubicXtoT(double x_target, double a, double b, double c, double d);
|
||||
|
||||
static double CubicTtoY(double a, double b, double c, double d, double t);
|
||||
};
|
||||
|
||||
#endif // BEZIER_H
|
||||
+76
-5
@@ -24,6 +24,7 @@
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
|
||||
#include "common/bezier.h"
|
||||
#include "common/lerp.h"
|
||||
#include "node.h"
|
||||
#include "output.h"
|
||||
@@ -126,11 +127,47 @@ QVariant NodeInput::get_value_at_time(const rational &time) const
|
||||
// We must interpolate between these keyframes
|
||||
|
||||
if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) {
|
||||
// FIXME: Perform a cubic bezier interpolation
|
||||
} else if (before->type() == NodeKeyframe::kLinear && after->type() == NodeKeyframe::kBezier) {
|
||||
// FIXME: Perform a quadratic bezier interpolation with anchors from the AFTER keyframe
|
||||
} else if (before->type() == NodeKeyframe::kLinear && after->type() == NodeKeyframe::kBezier) {
|
||||
// FIXME: Perform a quadratic bezier interpolation with anchors from the BEFORE keyframe
|
||||
// Perform a cubic bezier with two control points
|
||||
|
||||
double t = Bezier::CubicXtoT(time.toDouble(),
|
||||
before->time().toDouble(),
|
||||
before->time().toDouble() + before->bezier_control_out().x(),
|
||||
after->time().toDouble() + after->bezier_control_in().x(),
|
||||
after->time().toDouble());
|
||||
|
||||
double y = Bezier::CubicTtoY(before->value().toDouble(),
|
||||
before->value().toDouble() + before->bezier_control_out().y(),
|
||||
after->value().toDouble() + after->bezier_control_in().y(),
|
||||
after->value().toDouble(),
|
||||
t);
|
||||
|
||||
return y;
|
||||
|
||||
} else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) {
|
||||
// Perform a quadratic bezier with only one control point
|
||||
|
||||
QPointF control_point;
|
||||
double control_point_time;
|
||||
double control_point_value;
|
||||
|
||||
if (before->type() == NodeKeyframe::kBezier) {
|
||||
control_point = before->bezier_control_out();
|
||||
control_point_time = before->time().toDouble() + control_point.x();
|
||||
control_point_value = before->value().toDouble() + control_point.y();
|
||||
} else {
|
||||
control_point = after->bezier_control_in();
|
||||
control_point_time = after->time().toDouble() + control_point.x();
|
||||
control_point_value = after->value().toDouble() + control_point.y();
|
||||
}
|
||||
|
||||
// Generate T from time values - used to determine bezier progress
|
||||
double t = Bezier::QuadraticXtoT(time.toDouble(), before->time().toDouble(), control_point_time, after->time().toDouble());
|
||||
|
||||
// Generate value using T
|
||||
double y = Bezier::QuadraticTtoY(before->value().toDouble(), control_point_value, after->value().toDouble(), t);
|
||||
|
||||
return y;
|
||||
|
||||
} else {
|
||||
// To have arrived here, the keyframes must both be linear
|
||||
qreal period_progress = (time.toDouble() - before->time().toDouble()) / (after->time().toDouble() - before->time().toDouble());
|
||||
@@ -231,6 +268,8 @@ void NodeInput::insert_keyframe(NodeKeyframePtr key)
|
||||
connect(key.get(), &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged);
|
||||
connect(key.get(), &NodeKeyframe::ValueChanged, this, &NodeInput::KeyframeValueChanged);
|
||||
connect(key.get(), &NodeKeyframe::TypeChanged, this, &NodeInput::KeyframeTypeChanged);
|
||||
connect(key.get(), &NodeKeyframe::BezierControlInChanged, this, &NodeInput::KeyframeBezierInChanged);
|
||||
connect(key.get(), &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::KeyframeBezierOutChanged);
|
||||
|
||||
emit KeyframeAdded(key);
|
||||
|
||||
@@ -246,6 +285,8 @@ void NodeInput::remove_keyframe(NodeKeyframePtr key)
|
||||
disconnect(key.get(), &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged);
|
||||
disconnect(key.get(), &NodeKeyframe::ValueChanged, this, &NodeInput::KeyframeValueChanged);
|
||||
disconnect(key.get(), &NodeKeyframe::TypeChanged, this, &NodeInput::KeyframeTypeChanged);
|
||||
disconnect(key.get(), &NodeKeyframe::BezierControlInChanged, this, &NodeInput::KeyframeBezierInChanged);
|
||||
disconnect(key.get(), &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::KeyframeBezierOutChanged);
|
||||
|
||||
keyframes_.removeOne(key);
|
||||
|
||||
@@ -299,6 +340,36 @@ void NodeInput::KeyframeTypeChanged()
|
||||
emit_time_range(get_range_around_index(keyframe_index));
|
||||
}
|
||||
|
||||
void NodeInput::KeyframeBezierInChanged()
|
||||
{
|
||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
||||
int keyframe_index = FindIndexOfKeyframeFromRawPtr(key);
|
||||
|
||||
rational start = RATIONAL_MIN;
|
||||
rational end = key->time();
|
||||
|
||||
if (keyframe_index > 0) {
|
||||
start = keyframes_.at(keyframe_index - 1)->time();
|
||||
}
|
||||
|
||||
emit ValueChanged(start, end);
|
||||
}
|
||||
|
||||
void NodeInput::KeyframeBezierOutChanged()
|
||||
{
|
||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
||||
int keyframe_index = FindIndexOfKeyframeFromRawPtr(key);
|
||||
|
||||
rational start = key->time();
|
||||
rational end = RATIONAL_MAX;
|
||||
|
||||
if (keyframe_index < keyframes_.size() - 1) {
|
||||
end = keyframes_.at(keyframe_index + 1)->time();
|
||||
}
|
||||
|
||||
emit ValueChanged(start, end);
|
||||
}
|
||||
|
||||
int NodeInput::FindIndexOfKeyframeFromRawPtr(NodeKeyframe *raw_ptr) const
|
||||
{
|
||||
for (int i=0;i<keyframes_.size();i++) {
|
||||
|
||||
@@ -290,6 +290,16 @@ private slots:
|
||||
*/
|
||||
void KeyframeTypeChanged();
|
||||
|
||||
/**
|
||||
* @brief Slot when a keyframe's bezier in value changes to signal that the cache needs updating
|
||||
*/
|
||||
void KeyframeBezierInChanged();
|
||||
|
||||
/**
|
||||
* @brief Slot when a keyframe's bezier out value changes to signal that the cache needs updating
|
||||
*/
|
||||
void KeyframeBezierOutChanged();
|
||||
|
||||
};
|
||||
|
||||
#endif // NODEINPUT_H
|
||||
|
||||
@@ -74,7 +74,9 @@ void BezierControlPointItem::UpdatePos()
|
||||
|
||||
// Scale handle offset
|
||||
handle_offset.setX(handle_offset.x() * x_scale_);
|
||||
handle_offset.setY(handle_offset.y() * y_scale_);
|
||||
|
||||
// Flip the Y coordinate because bezier curves are drawn bottom to top
|
||||
handle_offset.setY(-handle_offset.y() * y_scale_);
|
||||
|
||||
setPos(handle_offset - rect().center());
|
||||
}
|
||||
|
||||
@@ -236,7 +236,8 @@ void CurveView::SetItemYFromKeyframeValue(NodeKeyframe *key, KeyframeViewItem *i
|
||||
|
||||
QPointF CurveView::ScalePoint(const QPointF &point)
|
||||
{
|
||||
return QPointF(point.x() * scale_, point.y() * y_scale_);
|
||||
// Flips Y coordinate because curves are drawn bottom to top
|
||||
return QPointF(point.x() * scale_, - point.y() * y_scale_);
|
||||
}
|
||||
|
||||
void CurveView::KeyframeValueChanged()
|
||||
|
||||
@@ -54,8 +54,6 @@ void KeyframeViewBase::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
active_tool_ = Core::instance()->tool();
|
||||
|
||||
rubberBandSelectionMode();
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
QGraphicsView::mousePressEvent(event);
|
||||
|
||||
@@ -203,8 +201,11 @@ QPointF KeyframeViewBase::GenerateBezierControlPosition(const NodeKeyframe::Bezi
|
||||
return new_bezier_pos;
|
||||
}
|
||||
|
||||
void KeyframeViewBase::ProcessBezierDrag(const QPointF& mouse_diff_scaled, bool include_opposing, bool undoable)
|
||||
void KeyframeViewBase::ProcessBezierDrag(QPointF mouse_diff_scaled, bool include_opposing, bool undoable)
|
||||
{
|
||||
// Flip the mouse Y because bezier control points are drawn bottom to top, not top to bottom
|
||||
mouse_diff_scaled.setY(-mouse_diff_scaled.y());
|
||||
|
||||
QPointF new_bezier_pos = GenerateBezierControlPosition(dragging_bezier_point_->mode(),
|
||||
dragging_bezier_point_start_,
|
||||
mouse_diff_scaled);
|
||||
|
||||
@@ -38,7 +38,7 @@ private:
|
||||
const QPointF& start_point,
|
||||
const QPointF& scaled_cursor_diff);
|
||||
|
||||
void ProcessBezierDrag(const QPointF& mouse_diff_scaled, bool include_opposing, bool undoable);
|
||||
void ProcessBezierDrag(QPointF mouse_diff_scaled, bool include_opposing, bool undoable);
|
||||
|
||||
QPointF GetScaledCursorPos(const QPoint& cursor_pos);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user