implemented bezier interpolation into parameters inputs

This commit is contained in:
itsmattkc
2019-12-28 23:21:07 +11:00
parent 1d3d52db9b
commit 994d8be961
9 changed files with 157 additions and 11 deletions
+2
View File
@@ -16,6 +16,8 @@
set(OLIVE_SOURCES
${OLIVE_SOURCES}
common/bezier.h
common/bezier.cpp
common/channellayout.h
common/clamp.h
common/constructors.h
+42
View File
@@ -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;
}
+17
View File
@@ -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
View File
@@ -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++) {
+10
View File
@@ -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());
}
+2 -1
View File
@@ -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()
+4 -3
View File
@@ -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);
+1 -1
View File
@@ -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);