PolygonGenerator: Finished main implementation

It's been 84 years, but the polygon generator not only works, but now has bezier support. The UI could still use some work, but this was the hardest part. I guess complex masking should be possible now, if perhaps slightly cumbersome until the UI is smoothed out.
This commit is contained in:
itsmattkc
2022-01-24 22:17:19 -08:00
parent 1e7d5a1751
commit 3f7381f7cb
14 changed files with 523 additions and 93 deletions
+30
View File
@@ -26,6 +26,36 @@
namespace olive {
Bezier::Bezier() :
x_(0),
y_(0),
cp1_x_(0),
cp1_y_(0),
cp2_x_(0),
cp2_y_(0)
{
}
Bezier::Bezier(double x, double y) :
x_(x),
y_(y),
cp1_x_(0),
cp1_y_(0),
cp2_x_(0),
cp2_y_(0)
{
}
Bezier::Bezier(double x, double y, double cp1_x, double cp1_y, double cp2_x, double cp2_y) :
x_(x),
y_(y),
cp1_x_(cp1_x),
cp1_y_(cp1_y),
cp2_x_(cp2_x),
cp2_y_(cp2_y)
{
}
double Bezier::QuadraticXtoT(double x, double a, double b, double c)
{
// Clamp to prevent infinite loop
+45
View File
@@ -22,6 +22,7 @@
#define BEZIER_H
#include <QPointF>
#include <QObject>
#include "common/define.h"
@@ -30,6 +31,39 @@ namespace olive {
class Bezier
{
public:
Bezier();
Bezier(double x, double y);
Bezier(double x, double y, double cp1_x, double cp1_y, double cp2_x, double cp2_y);
const double &x() const {return x_; }
const double &y() const {return y_; }
const double &cp1_x() const { return cp1_x_; }
const double &cp1_y() const { return cp1_y_; }
const double &cp2_x() const { return cp2_x_; }
const double &cp2_y() const { return cp2_y_; }
QPointF ToPointF() const
{
return QPointF(x_, y_);
}
QPointF ControlPoint1ToPointF() const
{
return QPointF(cp1_x_, cp1_y_);
}
QPointF ControlPoint2ToPointF() const
{
return QPointF(cp2_x_, cp2_y_);
}
void set_x(const double &x) { x_ = x; }
void set_y(const double &y) { y_ = y; }
void set_cp1_x(const double &cp1_x) { cp1_x_ = cp1_x; }
void set_cp1_y(const double &cp1_y) { cp1_y_ = cp1_y; }
void set_cp2_x(const double &cp2_x) { cp2_x_ = cp2_x; }
void set_cp2_y(const double &cp2_y) { cp2_y_ = cp2_y; }
static double QuadraticXtoT(double x, double a, double b, double c);
static double QuadraticTtoY(double a, double b, double c, double t);
@@ -51,8 +85,19 @@ public:
private:
static double CalculateTFromX(bool cubic, double x, double a, double b, double c, double d);
double x_;
double y_;
double cp1_x_;
double cp1_y_;
double cp2_x_;
double cp2_y_;
};
}
Q_DECLARE_METATYPE(olive::Bezier);
#endif // BEZIER_H
+128 -79
View File
@@ -21,7 +21,6 @@
#include "polygon.h"
#include <QGuiApplication>
#include <QPainterPath>
#include <QVector2D>
#include "common/cpuoptimize.h"
@@ -33,7 +32,7 @@ const QString PolygonGenerator::kColorInput = QStringLiteral("color_in");
PolygonGenerator::PolygonGenerator()
{
AddInput(kPointsInput, NodeValue::kVec2, QVector2D(0, 0), InputFlags(kInputFlagArray));
AddInput(kPointsInput, NodeValue::kBezier, QVector2D(0, 0), InputFlags(kInputFlagArray));
AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0)));
@@ -47,13 +46,13 @@ PolygonGenerator::PolygonGenerator()
InputArrayResize(kPointsInput, 5);
SetSplitStandardValueOnTrack(kPointsInput, 0, 0, 0);
SetSplitStandardValueOnTrack(kPointsInput, 1, -kTopY, 0);
SetSplitStandardValueOnTrack(kPointsInput, 0, -kMiddleX, 1);
SetSplitStandardValueOnTrack(kPointsInput, 0, kMiddleX, 1);
SetSplitStandardValueOnTrack(kPointsInput, 1, -kMiddleY, 1);
SetSplitStandardValueOnTrack(kPointsInput, 0, -kBottomX, 2);
SetSplitStandardValueOnTrack(kPointsInput, 0, kBottomX, 2);
SetSplitStandardValueOnTrack(kPointsInput, 1, kBottomY, 2);
SetSplitStandardValueOnTrack(kPointsInput, 0, kBottomX, 3);
SetSplitStandardValueOnTrack(kPointsInput, 0, -kBottomX, 3);
SetSplitStandardValueOnTrack(kPointsInput, 1, kBottomY, 3);
SetSplitStandardValueOnTrack(kPointsInput, 0, kMiddleX, 4);
SetSplitStandardValueOnTrack(kPointsInput, 0, -kMiddleX, 4);
SetSplitStandardValueOnTrack(kPointsInput, 1, -kMiddleY, 4);
}
@@ -108,18 +107,9 @@ void PolygonGenerator::GenerateFrame(FramePtr frame, const GenerateJob &job) con
QImage img(frame->width(), frame->height(), QImage::Format_Grayscale8);
img.fill(Qt::transparent);
QPainterPath path;
QVector<NodeValue> points = job.GetValue(kPointsInput).data().value< QVector<NodeValue> >();
if (!points.isEmpty()) {
path.moveTo(points.first().data().value<QVector2D>().toPointF());
// TODO: Implement bezier
for (int i=1; i<points.size(); i++) {
path.lineTo(points.at(i).data().value<QVector2D>().toPointF());
}
}
QPainterPath path = GeneratePath(points);
QPainter p(&img);
double par = frame->video_params().pixel_aspect_ratio().toDouble();
@@ -164,100 +154,159 @@ bool PolygonGenerator::HasGizmos() const
return true;
}
/*void PolygonGenerator::DrawGizmos(NodeValueDatabase &db, QPainter *p) const
void PolygonGenerator::DrawGizmos(const NodeValueRow &row, const NodeGlobals &globals, QPainter *p)
{
Q_UNUSED(viewport)
if (!points_input_->GetSize()) {
return;
}
const double handle_radius = GetGizmoHandleRadius(p->transform());
const double bezier_radius = handle_radius/2;
p->setPen(Qt::white);
p->setBrush(Qt::white);
p->translate(globals.resolution_by_par().x()/2, globals.resolution_by_par().y()/2);
QVector<QPointF> points = GetGizmoCoordinates(db, scale);
QVector<QRectF> rects = GetGizmoRects(points);
QVector<NodeValue> points = row[kPointsInput].data().value< QVector<NodeValue> >();
points.append(points.first());
gizmo_position_handles_.resize(points.size());
gizmo_bezier_handles_.resize(points.size() * 2);
p->drawPolyline(points.constData(), points.size());
p->drawRects(rects);
p->setPen(QPen(Qt::white, 0));
p->setBrush(Qt::NoBrush);
if (!points.isEmpty()) {
QVector<QLineF> lines(points.size() * 2);
for (int i=0; i<points.size(); i++) {
const Bezier &pt = points.at(i).data().value<Bezier>();
QPointF main = pt.ToPointF();
QPointF cp1 = main + pt.ControlPoint1ToPointF();
QPointF cp2 = main + pt.ControlPoint2ToPointF();
gizmo_position_handles_[i] = CreateGizmoHandleRect(main, handle_radius);
gizmo_bezier_handles_[i*2] = CreateGizmoHandleRect(cp1, bezier_radius);
lines[i*2] = QLineF(main, cp1);
gizmo_bezier_handles_[i*2+1] = CreateGizmoHandleRect(cp2, bezier_radius);
lines[i*2+1] = QLineF(main, cp2);
}
p->drawLines(lines);
}
gizmo_polygon_path_ = GeneratePath(points);
p->drawPath(gizmo_polygon_path_);
DrawAndExpandGizmoHandles(p, handle_radius, gizmo_position_handles_.data(), gizmo_position_handles_.size());
DrawAndExpandGizmoHandles(p, handle_radius, gizmo_bezier_handles_.data(), gizmo_bezier_handles_.size());
}
bool PolygonGenerator::GizmoPress(NodeValueDatabase &db, const QPointF &p, const QVector2D &scale, const QSize& viewport)
bool PolygonGenerator::GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p)
{
Q_UNUSED(viewport)
QPointF adjusted = p - (globals.resolution_by_par() / 2).toPointF();
QVector<QPointF> points = GetGizmoCoordinates(db, scale);
QVector<QRectF> rects = GetGizmoRects(points);
// First, look for main points
for (int i=0;i<rects.size();i++) {
const QRectF& r = rects.at(i);
if (r.contains(p)) {
gizmo_drag_ = points_input_->At(i);
gizmo_drag_start_ = points.at(i);
return true;
for (int i=0; i<gizmo_position_handles_.size(); i++) {
if (gizmo_position_handles_.at(i).contains(adjusted)) {
gizmo_x_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 0));
gizmo_y_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 1));
break;
}
}
return false;
}
// Next, if no main points were found, look for beziers
if (gizmo_x_active_.isEmpty() && gizmo_y_active_.isEmpty()) {
for (int i=0; i<gizmo_bezier_handles_.size(); i++) {
if (gizmo_bezier_handles_.at(i).contains(adjusted)) {
int start = (i%2 == 0) ? 2 : 4;
int element = i/2;
gizmo_x_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, element), start + 0));
gizmo_y_active_.append(NodeKeyframeTrackReference(NodeInput(this, kPointsInput, element), start + 1));
break;
}
}
void PolygonGenerator::GizmoMove(const QPointF &p, const QVector2D &scale, const rational& time)
{
QVector2D new_pos = QVector2D(p) / scale;
if (!gizmo_x_dragger_.IsStarted()) {
gizmo_x_dragger_.Start(gizmo_drag_, time, 0);
// Finally, see if the cursor is inside the polygon
if (gizmo_x_active_.isEmpty() && gizmo_y_active_.isEmpty()) {
if (gizmo_polygon_path_.contains(adjusted)) {
gizmo_x_active_.resize(gizmo_position_handles_.size());
gizmo_y_active_.resize(gizmo_position_handles_.size());
for (int i=0; i<gizmo_position_handles_.size(); i++) {
gizmo_x_active_[i] = NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 0);
gizmo_y_active_[i] = NodeKeyframeTrackReference(NodeInput(this, kPointsInput, i), 1);
}
}
}
}
if (!gizmo_y_dragger_.IsStarted()) {
gizmo_y_dragger_.Start(gizmo_drag_, time, 1);
gizmo_drag_start_ = p;
return !gizmo_x_active_.isEmpty() || !gizmo_y_active_.isEmpty();
}
void PolygonGenerator::GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers)
{
if (gizmo_x_draggers_.isEmpty() && gizmo_y_draggers_.isEmpty()) {
gizmo_x_draggers_.resize(gizmo_x_active_.size());
gizmo_y_draggers_.resize(gizmo_y_active_.size());
for (int i=0; i<gizmo_x_active_.size(); i++) {
gizmo_x_draggers_[i].Start(gizmo_x_active_.at(i), time);
}
for (int i=0; i<gizmo_y_active_.size(); i++) {
gizmo_y_draggers_[i].Start(gizmo_y_active_.at(i), time);
}
}
gizmo_x_dragger_.Drag(new_pos.x());
gizmo_y_dragger_.Drag(new_pos.y());
}
QPointF diff = p - gizmo_drag_start_;
void PolygonGenerator::GizmoRelease()
{
gizmo_x_dragger_.End();
gizmo_y_dragger_.End();
}*/
QVector<QPointF> PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const
{
// FIXME: Should Get() use a `kArray` type instead of a `kVec2` type?
QVector<NodeValueTable> array_tbl = db[kPointsInput].Get(NodeValue::kVec2).value< QVector<NodeValueTable> >();
QVector<QPointF> points(array_tbl.size());
for (int i=0;i<array_tbl.size();i++) {
QVector2D v = array_tbl.at(i).Get(NodeValue::kVec2).value<QVector2D>();
v *= scale;
QPointF pt = v.toPointF();
points[i] = pt;
for (NodeInputDragger &dragger : gizmo_x_draggers_) {
dragger.Drag(dragger.GetStartValue().toDouble() + diff.x());
}
return points;
for (NodeInputDragger &dragger : gizmo_y_draggers_) {
dragger.Drag(dragger.GetStartValue().toDouble() + diff.y());
}
}
QVector<QRectF> PolygonGenerator::GetGizmoRects(const QVector<QPointF> &points) const
void PolygonGenerator::GizmoRelease(MultiUndoCommand *command)
{
QVector<QRectF> rects(points.size());
for (NodeInputDragger &dragger : gizmo_x_draggers_) {
dragger.End(command);
}
gizmo_x_draggers_.clear();
int rect_sz = QFontMetrics(qApp->font()).height() / 8;
for (NodeInputDragger &dragger : gizmo_y_draggers_) {
dragger.End(command);
}
gizmo_y_draggers_.clear();
for (int i=0;i<points.size();i++) {
const QPointF& p = points.at(i);
gizmo_x_active_.clear();
gizmo_y_active_.clear();
}
rects[i] = QRectF(p - QPointF(rect_sz, rect_sz),
p + QPointF(rect_sz, rect_sz));
void PolygonGenerator::AddPointToPath(QPainterPath *path, const Bezier &before, const Bezier &after)
{
path->cubicTo(before.ToPointF() + before.ControlPoint2ToPointF(),
after.ToPointF() + after.ControlPoint1ToPointF(),
after.ToPointF());
}
QPainterPath PolygonGenerator::GeneratePath(const QVector<NodeValue> &points)
{
QPainterPath path;
if (!points.isEmpty()) {
const Bezier &first_pt = points.first().data().value<Bezier>();
path.moveTo(first_pt.ToPointF());
for (int i=1; i<points.size(); i++) {
AddPointToPath(&path, points.at(i-1).data().value<Bezier>(), points.at(i).data().value<Bezier>());
}
AddPointToPath(&path, points.last().data().value<Bezier>(), first_pt);
}
return rects;
return path;
}
}
+17 -10
View File
@@ -21,6 +21,9 @@
#ifndef POLYGONGENERATOR_H
#define POLYGONGENERATOR_H
#include <QPainterPath>
#include "common/bezier.h"
#include "node/node.h"
#include "node/inputdragger.h"
@@ -48,26 +51,30 @@ public:
virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override;
virtual bool HasGizmos() const override;
//virtual void DrawGizmos(NodeValueDatabase& db, QPainter *p) const override;
virtual void DrawGizmos(const NodeValueRow& row, const NodeGlobals &globals, QPainter *p) override;
//virtual bool GizmoPress(NodeValueDatabase &db, const QPointF &p) override;
//virtual void GizmoMove(const QPointF &p, const QVector2D &scale, const rational &time) override;
//virtual void GizmoRelease() override;
virtual bool GizmoPress(const NodeValueRow& row, const NodeGlobals &globals, const QPointF &p) override;
virtual void GizmoMove(const QPointF &p, const rational &time, const Qt::KeyboardModifiers &modifiers) override;
virtual void GizmoRelease(MultiUndoCommand *command) override;
static const QString kPointsInput;
static const QString kColorInput;
private:
QVector<QPointF> GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D &scale) const;
static void AddPointToPath(QPainterPath *path, const Bezier &before, const Bezier &after);
QVector<QRectF> GetGizmoRects(const QVector<QPointF>& points) const;
static QPainterPath GeneratePath(const QVector<NodeValue> &points);
NodeInput* gizmo_drag_;
QPainterPath gizmo_polygon_path_;
QVector<QRectF> gizmo_position_handles_;
QVector<QRectF> gizmo_bezier_handles_;
QVector<NodeKeyframeTrackReference> gizmo_x_active_;
QVector<NodeKeyframeTrackReference> gizmo_y_active_;
QVector<NodeInputDragger> gizmo_x_draggers_;
QVector<NodeInputDragger> gizmo_y_draggers_;
QPointF gizmo_drag_start_;
NodeInputDragger gizmo_x_dragger_;
NodeInputDragger gizmo_y_dragger_;
};
}
+5
View File
@@ -46,6 +46,11 @@ public:
return input_being_dragged;
}
const QVariant &GetStartValue() const
{
return start_value_;
}
private:
NodeKeyframeTrackReference input_;
+39
View File
@@ -26,6 +26,7 @@
#include <QVector3D>
#include <QVector4D>
#include "common/bezier.h"
#include "common/tohex.h"
#include "render/audioparams.h"
#include "render/videoparams.h"
@@ -64,6 +65,15 @@ QString NodeValue::ValueToString(Type data_type, const QVariant &value, bool val
QString::number(c.green()),
QString::number(c.blue()),
QString::number(c.alpha()));
} else if (!value_is_a_key_track && data_type == kBezier) {
Bezier b = value.value<Bezier>();
return QStringLiteral("%1:%2:%3:%4:%5:%6").arg(QString::number(b.x()),
QString::number(b.y()),
QString::number(b.cp1_x()),
QString::number(b.cp1_y()),
QString::number(b.cp2_x()),
QString::number(b.cp2_y()));
} else if (data_type == kRational) {
return value.value<rational>().toString();
} else if (data_type == kTexture
@@ -116,6 +126,7 @@ QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value)
case kVec3: return ValueToBytesInternal<QVector3D>(value);
case kVec4: return ValueToBytesInternal<QVector4D>(value);
case kCombo: return ValueToBytesInternal<int>(value);
case kBezier: return ValueToBytesInternal<Bezier>(value);
case kVideoParams:
return value.value<VideoParams>().toBytes();
@@ -174,6 +185,17 @@ QVector<QVariant> NodeValue::split_normal_value_into_track_values(Type type, con
vals.replace(3, c.alpha());
break;
}
case kBezier:
{
Bezier b = value.value<Bezier>();
vals.replace(0, b.x());
vals.replace(1, b.y());
vals.replace(2, b.cp1_x());
vals.replace(3, b.cp1_y());
vals.replace(4, b.cp2_x());
vals.replace(5, b.cp2_y());
break;
}
default:
vals.replace(0, value);
}
@@ -213,6 +235,13 @@ QVariant NodeValue::combine_track_values_into_normal_value(Type type, const QVec
split.at(2).toFloat(),
split.at(3).toFloat()));
}
case kBezier:
return QVariant::fromValue(Bezier(split.at(0).toDouble(),
split.at(1).toDouble(),
split.at(2).toDouble(),
split.at(3).toDouble(),
split.at(4).toDouble(),
split.at(5).toDouble()));
default:
return split.first();
}
@@ -228,6 +257,8 @@ int NodeValue::get_number_of_keyframe_tracks(Type type)
case NodeValue::kVec4:
case NodeValue::kColor:
return 4;
case NodeValue::kBezier:
return 6;
default:
return 1;
}
@@ -259,6 +290,12 @@ QVariant NodeValue::StringToValue(Type data_type, const QString &string, bool va
ValidateVectorString(&vals, 4);
return QVariant::fromValue(Color(vals.at(0).toDouble(), vals.at(1).toDouble(), vals.at(2).toDouble(), vals.at(3).toDouble()));
} else if (!value_is_a_key_track && data_type == kBezier) {
QStringList vals = string.split(':');
ValidateVectorString(&vals, 6);
return QVariant::fromValue(Bezier(vals.at(0).toDouble(), vals.at(1).toDouble(), vals.at(2).toDouble(), vals.at(3).toDouble(), vals.at(4).toDouble(), vals.at(5).toDouble()));
} else if (data_type == kInt) {
return QVariant::fromValue(string.toLongLong());
} else if (data_type == kRational) {
@@ -309,6 +346,8 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
return QCoreApplication::translate("NodeValue", "Vector 3D");
case kVec4:
return QCoreApplication::translate("NodeValue", "Vector 4D");
case kBezier:
return QCoreApplication::translate("NodeValue", "Bezier");
case kVideoParams:
return QCoreApplication::translate("NodeValue", "Video Parameters");
case kAudioParams:
+7
View File
@@ -142,6 +142,13 @@ public:
*/
kVec4,
/**
* Cubic bezier type that contains three X/Y coordinates, the main point, and two control points
*
* Resolves to `Bezier`
*/
kBezier,
/**
* ComboBox type
*
+1
View File
@@ -521,6 +521,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video
case NodeValue::kSampleJob:
case NodeValue::kGenerateJob:
case NodeValue::kFootageJob:
case NodeValue::kBezier:
case NodeValue::kNone:
break;
}
+1
View File
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(audiomonitor)
add_subdirectory(bezier)
add_subdirectory(clickablelabel)
add_subdirectory(collapsebutton)
add_subdirectory(colorbutton)
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2021 Olive Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
widget/bezier/bezierwidget.cpp
widget/bezier/bezierwidget.h
PARENT_SCOPE
)
+101
View File
@@ -0,0 +1,101 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "bezierwidget.h"
#include <QGridLayout>
#include <QGroupBox>
namespace olive {
BezierWidget::BezierWidget(QWidget *parent) :
QWidget{parent}
{
QGridLayout *layout = new QGridLayout(this);
int row = 0;
layout->addWidget(new QLabel(tr("Center:")), row, 0);
x_slider_ = new FloatSlider();
connect(x_slider_, &FloatSlider::ValueChanged, this, &BezierWidget::ValueChanged);
layout->addWidget(x_slider_, row, 1);
y_slider_ = new FloatSlider();
connect(y_slider_, &FloatSlider::ValueChanged, this, &BezierWidget::ValueChanged);
layout->addWidget(y_slider_, row, 2);
row++;
QGroupBox *bezier_group = new QGroupBox(tr("Bezier"));
layout->addWidget(bezier_group, row, 0, 1, 3);
QGridLayout *bezier_layout = new QGridLayout(bezier_group);
row = 0;
bezier_layout->addWidget(new QLabel(tr("In:")), row, 0);
cp1_x_slider_ = new FloatSlider();
connect(cp1_x_slider_, &FloatSlider::ValueChanged, this, &BezierWidget::ValueChanged);
bezier_layout->addWidget(cp1_x_slider_, row, 1);
cp1_y_slider_ = new FloatSlider();
connect(cp1_y_slider_, &FloatSlider::ValueChanged, this, &BezierWidget::ValueChanged);
bezier_layout->addWidget(cp1_y_slider_, row, 2);
row++;
bezier_layout->addWidget(new QLabel(tr("Out:")), row, 0);
cp2_x_slider_ = new FloatSlider();
connect(cp2_x_slider_, &FloatSlider::ValueChanged, this, &BezierWidget::ValueChanged);
bezier_layout->addWidget(cp2_x_slider_, row, 1);
cp2_y_slider_ = new FloatSlider();
connect(cp2_y_slider_, &FloatSlider::ValueChanged, this, &BezierWidget::ValueChanged);
bezier_layout->addWidget(cp2_y_slider_, row, 2);
}
Bezier BezierWidget::GetValue() const
{
Bezier b;
b.set_x(x_slider_->GetValue());
b.set_y(y_slider_->GetValue());
b.set_cp1_x(cp1_x_slider_->GetValue());
b.set_cp1_y(cp1_y_slider_->GetValue());
b.set_cp2_x(cp2_x_slider_->GetValue());
b.set_cp2_y(cp2_y_slider_->GetValue());
return b;
}
void BezierWidget::SetValue(const Bezier &b)
{
x_slider_->SetValue(b.x());
y_slider_->SetValue(b.y());
cp1_x_slider_->SetValue(b.cp1_x());
cp1_y_slider_->SetValue(b.cp1_y());
cp2_x_slider_->SetValue(b.cp2_x());
cp2_y_slider_->SetValue(b.cp2_y());
}
}
+74
View File
@@ -0,0 +1,74 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef BEZIERWIDGET_H
#define BEZIERWIDGET_H
#include <QCheckBox>
#include <QWidget>
#include "common/bezier.h"
#include "widget/slider/floatslider.h"
namespace olive {
class BezierWidget : public QWidget
{
Q_OBJECT
public:
explicit BezierWidget(QWidget *parent = nullptr);
Bezier GetValue() const;
void SetValue(const Bezier &b);
FloatSlider *x_slider() const { return x_slider_; }
FloatSlider *y_slider() const { return y_slider_; }
FloatSlider *cp1_x_slider() const { return cp1_x_slider_; }
FloatSlider *cp1_y_slider() const { return cp1_y_slider_; }
FloatSlider *cp2_x_slider() const { return cp2_x_slider_; }
FloatSlider *cp2_y_slider() const { return cp2_y_slider_; }
signals:
void ValueChanged();
private:
FloatSlider *x_slider_;
FloatSlider *y_slider_;
FloatSlider *cp1_x_slider_;
FloatSlider *cp1_y_slider_;
FloatSlider *cp2_x_slider_;
FloatSlider *cp2_y_slider_;
};
}
#endif // BEZIERWIDGET_H
@@ -33,6 +33,7 @@
#include "nodeparamviewtextedit.h"
#include "nodeparamviewundo.h"
#include "undo/undostack.h"
#include "widget/bezier/bezierwidget.h"
#include "widget/colorbutton/colorbutton.h"
#include "widget/filefield/filefield.h"
#include "widget/slider/floatslider.h"
@@ -158,6 +159,19 @@ void NodeParamViewWidgetBridge::CreateWidgets()
connect(font_combobox, &QFontComboBox::currentFontChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
break;
}
case NodeValue::kBezier:
{
BezierWidget *bezier = new BezierWidget();
widgets_.append(bezier);
connect(bezier->x_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
connect(bezier->y_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
connect(bezier->cp1_x_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
connect(bezier->cp1_y_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
connect(bezier->cp2_x_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
connect(bezier->cp2_y_slider(), &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
break;
}
}
// Check all properties
@@ -220,10 +234,8 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int
}
}
void NodeParamViewWidgetBridge::ProcessSlider(NumericSliderBase *slider, const QVariant &value)
void NodeParamViewWidgetBridge::ProcessSlider(NumericSliderBase *slider, int slider_track, const QVariant &value)
{
int slider_track = widgets_.indexOf(slider);
if (slider->IsDragging()) {
// While we're dragging, we block the input's normal signalling and create our own
@@ -371,11 +383,38 @@ void NodeParamViewWidgetBridge::WidgetCallback()
if (cb->itemData(i, Qt::AccessibleDescriptionRole).toString() == QStringLiteral("separator")) {
index--;
}
}
SetInputValue(index, 0);
break;
}
case NodeValue::kBezier:
{
// Widget is a FloatSlider (child of BezierWidget)
BezierWidget *bw = static_cast<BezierWidget*>(widgets_.first());
FloatSlider *fs = static_cast<FloatSlider*>(sender());
int index = -1;
if (fs == bw->x_slider()) {
index = 0;
} else if (fs == bw->y_slider()) {
index = 1;
} else if (fs == bw->cp1_x_slider()) {
index = 2;
} else if (fs == bw->cp1_y_slider()) {
index = 3;
} else if (fs == bw->cp2_x_slider()) {
index = 4;
} else if (fs == bw->cp2_y_slider()) {
index = 5;
}
if (index != -1) {
ProcessSlider(fs, index, fs->GetValue());
}
break;
}
}
}
@@ -509,6 +548,12 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
cb->blockSignals(false);
break;
}
case NodeValue::kBezier:
{
BezierWidget* bw = static_cast<BezierWidget*>(widgets_.first());
bw->SetValue(GetInnerInput().GetValueAtTime(node_time).value<Bezier>());
break;
}
}
}
@@ -64,7 +64,11 @@ private:
void SetInputValueInternal(const QVariant& value, int track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key);
void ProcessSlider(NumericSliderBase* slider, const QVariant& value);
void ProcessSlider(NumericSliderBase* slider, int slider_track, const QVariant& value);
void ProcessSlider(NumericSliderBase* slider, const QVariant& value)
{
ProcessSlider(slider, widgets_.indexOf(slider), value);
}
void SetProperty(const QString &key, const QVariant &value);