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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ public:
|
||||
return input_being_dragged;
|
||||
}
|
||||
|
||||
const QVariant &GetStartValue() const
|
||||
{
|
||||
return start_value_;
|
||||
}
|
||||
|
||||
private:
|
||||
NodeKeyframeTrackReference input_;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user