From 3f7381f7cb52887754f5e8eec4a6d9c950bdce16 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 24 Jan 2022 22:17:19 -0800 Subject: [PATCH] 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. --- app/common/bezier.cpp | 30 +++ app/common/bezier.h | 45 ++++ app/node/generator/polygon/polygon.cpp | 207 +++++++++++------- app/node/generator/polygon/polygon.h | 27 ++- app/node/inputdragger.h | 5 + app/node/value.cpp | 39 ++++ app/node/value.h | 7 + app/render/opengl/openglrenderer.cpp | 1 + app/widget/CMakeLists.txt | 1 + app/widget/bezier/CMakeLists.txt | 22 ++ app/widget/bezier/bezierwidget.cpp | 101 +++++++++ app/widget/bezier/bezierwidget.h | 74 +++++++ .../nodeparamviewwidgetbridge.cpp | 51 ++++- .../nodeparamview/nodeparamviewwidgetbridge.h | 6 +- 14 files changed, 523 insertions(+), 93 deletions(-) create mode 100644 app/widget/bezier/CMakeLists.txt create mode 100644 app/widget/bezier/bezierwidget.cpp create mode 100644 app/widget/bezier/bezierwidget.h diff --git a/app/common/bezier.cpp b/app/common/bezier.cpp index a1d900d57..b1a5f5fba 100644 --- a/app/common/bezier.cpp +++ b/app/common/bezier.cpp @@ -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 diff --git a/app/common/bezier.h b/app/common/bezier.h index 16705901f..776c72436 100644 --- a/app/common/bezier.h +++ b/app/common/bezier.h @@ -22,6 +22,7 @@ #define BEZIER_H #include +#include #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 diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index e962aaef1..451430403 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -21,7 +21,6 @@ #include "polygon.h" #include -#include #include #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 points = job.GetValue(kPointsInput).data().value< QVector >(); - if (!points.isEmpty()) { - path.moveTo(points.first().data().value().toPointF()); - - // TODO: Implement bezier - for (int i=1; i().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 points = GetGizmoCoordinates(db, scale); - QVector rects = GetGizmoRects(points); + QVector points = row[kPointsInput].data().value< QVector >(); - 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 lines(points.size() * 2); + + for (int i=0; i(); + + 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 points = GetGizmoCoordinates(db, scale); - QVector rects = GetGizmoRects(points); + // First, look for main points - for (int i=0;iAt(i); - gizmo_drag_start_ = points.at(i); - return true; + for (int i=0; i PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const -{ - // FIXME: Should Get() use a `kArray` type instead of a `kVec2` type? - QVector array_tbl = db[kPointsInput].Get(NodeValue::kVec2).value< QVector >(); - QVector points(array_tbl.size()); - - for (int i=0;i(); - - 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 PolygonGenerator::GetGizmoRects(const QVector &points) const +void PolygonGenerator::GizmoRelease(MultiUndoCommand *command) { - QVector 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;icubicTo(before.ToPointF() + before.ControlPoint2ToPointF(), + after.ToPointF() + after.ControlPoint1ToPointF(), + after.ToPointF()); +} + +QPainterPath PolygonGenerator::GeneratePath(const QVector &points) +{ + QPainterPath path; + + if (!points.isEmpty()) { + const Bezier &first_pt = points.first().data().value(); + path.moveTo(first_pt.ToPointF()); + + for (int i=1; i(), points.at(i).data().value()); + } + + AddPointToPath(&path, points.last().data().value(), first_pt); } - return rects; + return path; } } diff --git a/app/node/generator/polygon/polygon.h b/app/node/generator/polygon/polygon.h index e91491d6a..e7da30009 100644 --- a/app/node/generator/polygon/polygon.h +++ b/app/node/generator/polygon/polygon.h @@ -21,6 +21,9 @@ #ifndef POLYGONGENERATOR_H #define POLYGONGENERATOR_H +#include + +#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 GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D &scale) const; + static void AddPointToPath(QPainterPath *path, const Bezier &before, const Bezier &after); - QVector GetGizmoRects(const QVector& points) const; + static QPainterPath GeneratePath(const QVector &points); - NodeInput* gizmo_drag_; + QPainterPath gizmo_polygon_path_; + QVector gizmo_position_handles_; + QVector gizmo_bezier_handles_; + + QVector gizmo_x_active_; + QVector gizmo_y_active_; + QVector gizmo_x_draggers_; + QVector gizmo_y_draggers_; QPointF gizmo_drag_start_; - NodeInputDragger gizmo_x_dragger_; - NodeInputDragger gizmo_y_dragger_; - }; } diff --git a/app/node/inputdragger.h b/app/node/inputdragger.h index 26c3ee084..6ab0010f9 100644 --- a/app/node/inputdragger.h +++ b/app/node/inputdragger.h @@ -46,6 +46,11 @@ public: return input_being_dragged; } + const QVariant &GetStartValue() const + { + return start_value_; + } + private: NodeKeyframeTrackReference input_; diff --git a/app/node/value.cpp b/app/node/value.cpp index d9b1dfb34..3cc567a92 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -26,6 +26,7 @@ #include #include +#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(); + + 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().toString(); } else if (data_type == kTexture @@ -116,6 +126,7 @@ QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value) case kVec3: return ValueToBytesInternal(value); case kVec4: return ValueToBytesInternal(value); case kCombo: return ValueToBytesInternal(value); + case kBezier: return ValueToBytesInternal(value); case kVideoParams: return value.value().toBytes(); @@ -174,6 +185,17 @@ QVector NodeValue::split_normal_value_into_track_values(Type type, con vals.replace(3, c.alpha()); break; } + case kBezier: + { + Bezier b = value.value(); + 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: diff --git a/app/node/value.h b/app/node/value.h index 4c6959c9b..5ef2ff54f 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -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 * diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index a339bb23e..37e43e88d 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -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; } diff --git a/app/widget/CMakeLists.txt b/app/widget/CMakeLists.txt index 915214d9e..9faf9c462 100644 --- a/app/widget/CMakeLists.txt +++ b/app/widget/CMakeLists.txt @@ -15,6 +15,7 @@ # along with this program. If not, see . add_subdirectory(audiomonitor) +add_subdirectory(bezier) add_subdirectory(clickablelabel) add_subdirectory(collapsebutton) add_subdirectory(colorbutton) diff --git a/app/widget/bezier/CMakeLists.txt b/app/widget/bezier/CMakeLists.txt new file mode 100644 index 000000000..7f73ab2ba --- /dev/null +++ b/app/widget/bezier/CMakeLists.txt @@ -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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + widget/bezier/bezierwidget.cpp + widget/bezier/bezierwidget.h + PARENT_SCOPE +) diff --git a/app/widget/bezier/bezierwidget.cpp b/app/widget/bezier/bezierwidget.cpp new file mode 100644 index 000000000..2f2dec995 --- /dev/null +++ b/app/widget/bezier/bezierwidget.cpp @@ -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 . + +***/ + +#include "bezierwidget.h" + +#include +#include + +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()); +} + +} diff --git a/app/widget/bezier/bezierwidget.h b/app/widget/bezier/bezierwidget.h new file mode 100644 index 000000000..c052fd3b3 --- /dev/null +++ b/app/widget/bezier/bezierwidget.h @@ -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 . + +***/ + +#ifndef BEZIERWIDGET_H +#define BEZIERWIDGET_H + +#include +#include + +#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 diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 658b43118..05a5cccce 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -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(widgets_.first()); + FloatSlider *fs = static_cast(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(widgets_.first()); + bw->SetValue(GetInnerInput().GetValueAtTime(node_time).value()); + break; + } } } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index 106244d7b..0f80a443b 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -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);