From 181235f6cedf2558538ce7ed6408150c1c419cc3 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 5 Jan 2021 11:20:38 +1100 Subject: [PATCH] began new infrastructure Yep, this is another one of my "famous" sweeping rewrites. Expect things to break. Goals for this are: - Greatly simplify node connections (particularly with arrays) so the code requires less maintenance/is more stable - Redesign node structure to address issues where UI would stall for lengthy periods of time - Less reliance on shared ptrs/greater reliance on QObject system for inheritance/memory management - General code cleanup and improvements --- app/common/rational.h | 2 +- app/common/timerange.h | 2 +- app/common/xmlutils.cpp | 6 +- app/common/xmlutils.h | 12 +- app/config/config.cpp | 118 +- app/config/config.h | 8 +- .../keyframeproperties/keyframeproperties.cpp | 8 +- .../keyframeproperties/keyframeproperties.h | 4 +- app/node/CMakeLists.txt | 12 +- app/node/audio/pan/pan.cpp | 16 +- app/node/audio/volume/volume.cpp | 12 +- app/node/block/block.cpp | 53 +- app/node/block/block.h | 2 - app/node/block/clip/clip.cpp | 21 +- app/node/block/clip/clip.h | 2 +- .../diptocolor/diptocolortransition.cpp | 3 +- app/node/block/transition/transition.cpp | 117 +- app/node/block/transition/transition.h | 8 +- app/node/connectable.cpp | 77 + app/node/connectable.h | 96 ++ app/node/distort/crop/cropdistortnode.cpp | 54 +- .../transform/transformdistortnode.cpp | 47 +- app/node/edge.cpp | 50 - app/node/edge.h | 87 -- app/node/filter/blur/blur.cpp | 34 +- app/node/filter/mosaic/mosaicfilternode.cpp | 21 +- app/node/filter/stroke/stroke.cpp | 30 +- app/node/generator/matrix/matrix.cpp | 49 +- app/node/generator/polygon/polygon.cpp | 50 +- app/node/generator/polygon/polygon.h | 2 +- app/node/generator/solid/solid.cpp | 8 +- app/node/generator/text/text.cpp | 44 +- app/node/graph.cpp | 131 +- app/node/graph.h | 53 +- app/node/input.cpp | 1268 ++++++----------- app/node/input.h | 512 ++++--- app/node/input/media/media.cpp | 22 +- app/node/input/media/media.h | 2 - app/node/input/time/timeinput.cpp | 7 +- app/node/inputarray.cpp | 238 ---- app/node/inputarray.h | 79 - app/node/inputdragger.cpp | 36 +- app/node/inputdragger.h | 6 +- app/node/inputimmediate.cpp | 243 ++++ app/node/inputimmediate.h | 168 +++ app/node/keyframe.cpp | 38 +- app/node/keyframe.h | 29 +- app/node/math/math/math.cpp | 13 +- app/node/math/math/math.h | 4 +- app/node/math/math/mathbase.cpp | 112 +- app/node/math/math/mathbase.h | 6 +- app/node/math/merge/merge.cpp | 31 +- app/node/math/trigonometry/trigonometry.cpp | 16 +- app/node/node.cpp | 632 +++----- app/node/node.h | 185 +-- app/node/nodecopypaste.cpp | 2 +- app/node/output.cpp | 71 - app/node/output.h | 58 - app/node/output/track/CMakeLists.txt | 4 +- app/node/output/track/track.cpp | 275 ++-- app/node/output/track/track.h | 17 +- app/node/output/track/tracklist.cpp | 226 +-- app/node/output/track/tracklist.h | 20 +- app/node/output/viewer/viewer.cpp | 92 +- app/node/output/viewer/viewer.h | 12 +- app/node/param.cpp | 289 ---- app/node/param.h | 437 ------ app/node/traverser.cpp | 85 +- app/node/value.cpp | 340 ++++- app/node/value.h | 311 +++- app/panel/node/node.cpp | 4 +- app/panel/node/node.h | 6 +- app/project/item/folder/folder.h | 2 - app/project/item/item.cpp | 21 +- app/project/item/item.h | 4 +- app/project/item/sequence/sequence.cpp | 20 +- app/project/project.cpp | 2 +- app/render/CMakeLists.txt | 1 - app/render/job/acceleratedjob.h | 41 +- app/render/job/samplejob.h | 2 +- app/render/opengl/openglrenderer.cpp | 74 +- app/render/previewautocacher.cpp | 294 ++-- app/render/previewautocacher.h | 52 +- app/render/renderer.cpp | 10 +- app/render/renderprocessor.cpp | 22 +- app/render/shadervalue.h | 55 - app/task/precache/precachetask.cpp | 2 +- app/timeline/trackreference.h | 2 +- app/widget/CMakeLists.txt | 2 +- .../curvewidget/beziercontrolpointitem.cpp | 10 +- .../curvewidget/beziercontrolpointitem.h | 6 +- app/widget/curvewidget/curveview.cpp | 192 +-- app/widget/curvewidget/curveview.h | 8 +- app/widget/curvewidget/curvewidget.cpp | 4 +- app/widget/keyframeview/keyframeview.cpp | 2 +- app/widget/keyframeview/keyframeview.h | 2 +- app/widget/keyframeview/keyframeviewbase.cpp | 50 +- app/widget/keyframeview/keyframeviewbase.h | 6 +- app/widget/keyframeview/keyframeviewitem.cpp | 10 +- app/widget/keyframeview/keyframeviewitem.h | 6 +- app/widget/keyframeview/keyframeviewundo.cpp | 10 +- app/widget/keyframeview/keyframeviewundo.h | 10 +- app/widget/nodeparamview/nodeparamview.cpp | 1 - app/widget/nodeparamview/nodeparamview.h | 2 - .../nodeparamviewarraywidget.cpp | 8 +- .../nodeparamview/nodeparamviewarraywidget.h | 6 +- .../nodeparamviewconnectedlabel.cpp | 26 +- .../nodeparamviewconnectedlabel.h | 6 +- .../nodeparamview/nodeparamviewitem.cpp | 112 +- app/widget/nodeparamview/nodeparamviewitem.h | 28 +- .../nodeparamviewkeyframecontrol.cpp | 71 +- .../nodeparamviewkeyframecontrol.h | 13 +- .../nodeparamview/nodeparamviewundo.cpp | 73 +- app/widget/nodeparamview/nodeparamviewundo.h | 34 +- .../nodeparamviewwidgetbridge.cpp | 230 ++- .../nodeparamview/nodeparamviewwidgetbridge.h | 4 +- app/widget/nodetableview/nodetableview.cpp | 9 +- app/widget/nodetreeview/nodetreeview.cpp | 5 +- app/widget/nodeview/CMakeLists.txt | 13 +- app/widget/nodeview/nodeview.cpp | 879 +----------- app/widget/nodeview/nodeview.h | 147 +- app/widget/nodeview/nodeviewcommon.h | 58 - app/widget/nodeview/nodeviewedge.cpp | 223 --- app/widget/nodeview/nodeviewedge.h | 121 -- app/widget/nodeview/nodeviewitem.cpp | 702 --------- app/widget/nodeview/nodeviewitem.h | 179 --- .../nodeview/nodeviewitemwidgetproxy.cpp | 41 - app/widget/nodeview/nodeviewitemwidgetproxy.h | 49 - app/widget/nodeview/nodeviewscene.cpp | 313 ---- app/widget/nodeview/nodeviewscene.h | 151 -- app/widget/nodeview/nodeviewundo.cpp | 137 +- app/widget/nodeview/nodeviewundo.h | 98 +- .../projectexplorer/projectexplorer.cpp | 14 +- app/widget/projectexplorer/projectexplorer.h | 4 +- .../projectexplorer/projectexplorerundo.cpp | 2 +- .../projectexplorer/projectexplorerundo.h | 2 +- app/widget/scope/histogram/histogram.cpp | 10 +- app/widget/scope/scopebase/scopebase.cpp | 2 +- app/widget/scope/waveform/waveform.cpp | 8 +- app/widget/timelinewidget/timelinewidget.cpp | 53 +- app/widget/timelinewidget/timelinewidget.h | 2 +- app/widget/timelinewidget/tool/add.cpp | 4 +- app/widget/timelinewidget/tool/import.cpp | 8 +- app/widget/timelinewidget/tool/pointer.cpp | 4 - app/widget/timelinewidget/tool/transition.cpp | 9 +- app/widget/timelinewidget/undo/undo.cpp | 135 +- app/widget/timelinewidget/undo/undo.h | 5 +- app/widget/timetarget/timetarget.cpp | 8 +- app/widget/timetarget/timetarget.h | 4 +- app/widget/viewer/footageviewer.cpp | 12 +- app/widget/viewer/viewer.cpp | 10 +- app/widget/viewer/viewerdisplay.cpp | 6 +- 152 files changed, 3932 insertions(+), 8166 deletions(-) create mode 100644 app/node/connectable.cpp create mode 100644 app/node/connectable.h delete mode 100644 app/node/edge.cpp delete mode 100644 app/node/edge.h delete mode 100644 app/node/inputarray.cpp delete mode 100644 app/node/inputarray.h create mode 100644 app/node/inputimmediate.cpp create mode 100644 app/node/inputimmediate.h delete mode 100644 app/node/output.cpp delete mode 100644 app/node/output.h delete mode 100644 app/node/param.cpp delete mode 100644 app/node/param.h delete mode 100644 app/render/shadervalue.h delete mode 100644 app/widget/nodeview/nodeviewcommon.h delete mode 100644 app/widget/nodeview/nodeviewedge.cpp delete mode 100644 app/widget/nodeview/nodeviewedge.h delete mode 100644 app/widget/nodeview/nodeviewitem.cpp delete mode 100644 app/widget/nodeview/nodeviewitem.h delete mode 100644 app/widget/nodeview/nodeviewitemwidgetproxy.cpp delete mode 100644 app/widget/nodeview/nodeviewitemwidgetproxy.h delete mode 100644 app/widget/nodeview/nodeviewscene.cpp delete mode 100644 app/widget/nodeview/nodeviewscene.h diff --git a/app/common/rational.h b/app/common/rational.h index 9b8f05e1e..0d548523b 100644 --- a/app/common/rational.h +++ b/app/common/rational.h @@ -138,7 +138,7 @@ private: #define RATIONAL_MIN rational(INT64_MIN, 1) #define RATIONAL_MAX rational(INT64_MAX, 1) -uint qHash(const rational& r, uint seed); +uint qHash(const rational& r, uint seed = 0); } diff --git a/app/common/timerange.h b/app/common/timerange.h index a14ceb69d..c00721cb6 100644 --- a/app/common/timerange.h +++ b/app/common/timerange.h @@ -137,7 +137,7 @@ private: }; -uint qHash(const TimeRange& r, uint seed); +uint qHash(const TimeRange& r, uint seed = 0); } diff --git a/app/common/xmlutils.cpp b/app/common/xmlutils.cpp index 0ff66750a..f068d230a 100644 --- a/app/common/xmlutils.cpp +++ b/app/common/xmlutils.cpp @@ -29,13 +29,13 @@ namespace olive { void XMLConnectNodes(const XMLNodeData &xml_node_data, QUndoCommand *command) { foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { - NodeOutput* out = xml_node_data.output_ptrs.value(con.output); + Node* out = xml_node_data.node_ptrs.value(con.output); if (out) { if (command) { - new NodeEdgeAddCommand(out, con.input, command); + new NodeEdgeAddCommand(out, con.input, con.element, command); } else { - NodeParam::ConnectEdge(out, con.input); + Node::ConnectEdge(out, con.input, con.element); } } } diff --git a/app/common/xmlutils.h b/app/common/xmlutils.h index 25a67c56c..e6a290fbd 100644 --- a/app/common/xmlutils.h +++ b/app/common/xmlutils.h @@ -29,24 +29,23 @@ namespace olive { class Block; -class Node; -class NodeParam; -class NodeInput; -class NodeOutput; class Item; +class Node; +class NodeInput; #define XMLAttributeLoop(reader, item) \ - QXmlStreamAttributes __attributes = reader->attributes(); \ - foreach (const QXmlStreamAttribute& item, __attributes) + foreach (const QXmlStreamAttribute& item, reader->attributes()) struct XMLNodeData { struct SerializedConnection { NodeInput* input; + int element; quintptr output; }; struct FootageConnection { NodeInput* input; + int element; quintptr footage; }; @@ -56,7 +55,6 @@ struct XMLNodeData { }; QHash node_ptrs; - QHash output_ptrs; QList desired_connections; QHash footage_ptrs; QList footage_connections; diff --git a/app/config/config.cpp b/app/config/config.cpp index 4efa8bc54..45f219c90 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -43,7 +43,7 @@ Config::Config() SetDefaults(); } -void Config::SetEntryInternal(const QString &key, NodeParam::DataType type, const QVariant &data) +void Config::SetEntryInternal(const QString &key, NodeValue::Type type, const QVariant &data) { config_map_[key] = {type, data}; } @@ -61,67 +61,67 @@ Config &Config::Current() void Config::SetDefaults() { config_map_.clear(); - SetEntryInternal(QStringLiteral("Style"), NodeParam::kString, StyleManager::kDefaultStyle); - SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeParam::kInt, Timecode::kTimecodeDropFrame); - SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeParam::kRational, QVariant::fromValue(rational(2))); - SetEntryInternal(QStringLiteral("HoverFocus"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeParam::kBoolean, true); - SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeParam::kInt, 1); - SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeParam::kInt, 10000); - SetEntryInternal(QStringLiteral("Language"), NodeParam::kString, QString()); - SetEntryInternal(QStringLiteral("ScrollZooms"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeParam::kBoolean, true); - SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeParam::kBoolean, true); - SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("PasteSeeks"), NodeParam::kBoolean, true); - SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeParam::kBoolean, true); - SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeParam::kBoolean, true); - SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("Autoscroll"), NodeParam::kInt, AutoScroll::kPage); - SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeParam::kBoolean, true); - SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeParam::kInt, ImportTool::kDWSAsk); - SetEntryInternal(QStringLiteral("Loop"), NodeParam::kBoolean, false); - SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeParam::kBoolean, true); + SetEntryInternal(QStringLiteral("Style"), NodeValue::kText, StyleManager::kDefaultStyle); + SetEntryInternal(QStringLiteral("TimecodeDisplay"), NodeValue::kInt, Timecode::kTimecodeDropFrame); + SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational, QVariant::fromValue(rational(2))); + SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true); + SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt, 1); + SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt, 10000); + SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString()); + SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("EnableSeekToImport"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("EditToolAlsoSeeks"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("EditToolSelectsLinks"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("EnableDragFilesToTimeline"), NodeValue::kBoolean, true); + SetEntryInternal(QStringLiteral("InvertTimelineScrollAxes"), NodeValue::kBoolean, true); + SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("PasteSeeks"), NodeValue::kBoolean, true); + SetEntryInternal(QStringLiteral("SelectAlsoSeeks"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("AutoSeekToBeginning"), NodeValue::kBoolean, true); + SetEntryInternal(QStringLiteral("DropFileOnMediaToReplace"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("AddDefaultEffectsToClips"), NodeValue::kBoolean, true); + SetEntryInternal(QStringLiteral("AutoscaleByDefault"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("Autoscroll"), NodeValue::kInt, AutoScroll::kPage); + SetEntryInternal(QStringLiteral("AutoSelectDivider"), NodeValue::kBoolean, true); + SetEntryInternal(QStringLiteral("SetNameWithMarker"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeValue::kInt, ImportTool::kDWSAsk); + SetEntryInternal(QStringLiteral("Loop"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeValue::kBoolean, true); - SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeParam::kInt, 1000); + SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000); - SetEntryInternal(QStringLiteral("NodeCatColor0"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.75, 0.75))); - SetEntryInternal(QStringLiteral("NodeCatColor1"), NodeParam::kColor, QVariant::fromValue(Color(0.25, 0.25, 0.25))); - SetEntryInternal(QStringLiteral("NodeCatColor2"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.75, 0.25))); - SetEntryInternal(QStringLiteral("NodeCatColor3"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.25, 0.75))); - SetEntryInternal(QStringLiteral("NodeCatColor4"), NodeParam::kColor, QVariant::fromValue(Color(0.25, 0.75, 0.75))); - SetEntryInternal(QStringLiteral("NodeCatColor5"), NodeParam::kColor, QVariant::fromValue(Color(0.50, 0.50, 0.50))); - SetEntryInternal(QStringLiteral("NodeCatColor6"), NodeParam::kColor, QVariant::fromValue(Color(0.25, 0.75, 0.25))); - SetEntryInternal(QStringLiteral("NodeCatColor7"), NodeParam::kColor, QVariant::fromValue(Color(0.25, 0.25, 0.75))); - SetEntryInternal(QStringLiteral("NodeCatColor8"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.25, 0.25))); - SetEntryInternal(QStringLiteral("NodeCatColor9"), NodeParam::kColor, QVariant::fromValue(Color(0.55, 0.55, 0.75))); - SetEntryInternal(QStringLiteral("NodeCatColor10"), NodeParam::kColor, QVariant::fromValue(Color(0.75, 0.55, 0.25))); + SetEntryInternal(QStringLiteral("NodeCatColor0"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.75, 0.75))); + SetEntryInternal(QStringLiteral("NodeCatColor1"), NodeValue::kColor, QVariant::fromValue(Color(0.25, 0.25, 0.25))); + SetEntryInternal(QStringLiteral("NodeCatColor2"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.75, 0.25))); + SetEntryInternal(QStringLiteral("NodeCatColor3"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.25, 0.75))); + SetEntryInternal(QStringLiteral("NodeCatColor4"), NodeValue::kColor, QVariant::fromValue(Color(0.25, 0.75, 0.75))); + SetEntryInternal(QStringLiteral("NodeCatColor5"), NodeValue::kColor, QVariant::fromValue(Color(0.50, 0.50, 0.50))); + SetEntryInternal(QStringLiteral("NodeCatColor6"), NodeValue::kColor, QVariant::fromValue(Color(0.25, 0.75, 0.25))); + SetEntryInternal(QStringLiteral("NodeCatColor7"), NodeValue::kColor, QVariant::fromValue(Color(0.25, 0.25, 0.75))); + SetEntryInternal(QStringLiteral("NodeCatColor8"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.25, 0.25))); + SetEntryInternal(QStringLiteral("NodeCatColor9"), NodeValue::kColor, QVariant::fromValue(Color(0.55, 0.55, 0.75))); + SetEntryInternal(QStringLiteral("NodeCatColor10"), NodeValue::kColor, QVariant::fromValue(Color(0.75, 0.55, 0.25))); - SetEntryInternal(QStringLiteral("AudioOutput"), NodeParam::kString, QString()); - SetEntryInternal(QStringLiteral("AudioInput"), NodeParam::kString, QString()); + SetEntryInternal(QStringLiteral("AudioOutput"), NodeValue::kText, QString()); + SetEntryInternal(QStringLiteral("AudioInput"), NodeValue::kText, QString()); - SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeParam::kRational, QVariant::fromValue(rational(1))); - SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeParam::kRational, QVariant::fromValue(rational(5))); + SetEntryInternal(QStringLiteral("DiskCacheBehind"), NodeValue::kRational, QVariant::fromValue(rational(1))); + SetEntryInternal(QStringLiteral("DiskCacheAhead"), NodeValue::kRational, QVariant::fromValue(rational(5))); - SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeParam::kInt, 1920); - SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeParam::kInt, 1080); - SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeParam::kRational, QVariant::fromValue(rational(1))); - SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeParam::kRational, QVariant::fromValue(rational(1001, 30000))); - SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeParam::kInt, VideoParams::kInterlaceNone); - SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeParam::kInt, 48000); - SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeParam::kInt, QVariant::fromValue(static_cast(AV_CH_LAYOUT_STEREO))); + SetEntryInternal(QStringLiteral("DefaultSequenceWidth"), NodeValue::kInt, 1920); + SetEntryInternal(QStringLiteral("DefaultSequenceHeight"), NodeValue::kInt, 1080); + SetEntryInternal(QStringLiteral("DefaultSequencePixelAspect"), NodeValue::kRational, QVariant::fromValue(rational(1))); + SetEntryInternal(QStringLiteral("DefaultSequenceFrameRate"), NodeValue::kRational, QVariant::fromValue(rational(1001, 30000))); + SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeValue::kInt, VideoParams::kInterlaceNone); + SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeValue::kInt, 48000); + SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeValue::kInt, QVariant::fromValue(static_cast(AV_CH_LAYOUT_STEREO))); // Online/offline settings - SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeParam::kInt, VideoParams::kFormatFloat32); - SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeParam::kInt, VideoParams::kFormatFloat16); + SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeValue::kInt, VideoParams::kFormatFloat32); + SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeValue::kInt, VideoParams::kFormatFloat16); } void Config::Load() @@ -181,7 +181,7 @@ void Config::Load() current_config_[key] = QVariant::fromValue(match.flipped()); } else { - current_config_[key] = NodeInput::StringToValue(current_config_.GetConfigEntryType(key), value, false); + current_config_[key] = NodeValue::StringToValue(current_config_.GetConfigEntryType(key), value, false); } } @@ -233,9 +233,9 @@ void Config::Save() while (iterator.hasNext()) { iterator.next(); - QString value = NodeInput::ValueToString(iterator.value().type, iterator.value().data, false); + QString value = NodeValue::ValueToString(iterator.value().type, iterator.value().data, false); - if (iterator.value().type == NodeParam::kNone) { + if (iterator.value().type == NodeValue::kNone) { qWarning() << "Config key" << iterator.key() << "had null type and was discarded"; } else { writer.writeTextElement(iterator.key(), value); @@ -264,7 +264,7 @@ QVariant &Config::operator[](const QString &key) return config_map_[key].data; } -NodeParam::DataType Config::GetConfigEntryType(const QString &key) const +NodeValue::Type Config::GetConfigEntryType(const QString &key) const { return config_map_[key].type; } diff --git a/app/config/config.h b/app/config/config.h index 5fedcdbd1..76f18b9c0 100644 --- a/app/config/config.h +++ b/app/config/config.h @@ -26,7 +26,7 @@ #include #include "common/timecodefunctions.h" -#include "node/param.h" +#include "node/value.h" namespace olive { @@ -44,17 +44,17 @@ public: QVariant& operator[](const QString&); - NodeParam::DataType GetConfigEntryType(const QString& key) const; + NodeValue::Type GetConfigEntryType(const QString& key) const; private: Config(); struct ConfigEntry { - NodeParam::DataType type; + NodeValue::Type type; QVariant data; }; - void SetEntryInternal(const QString& key, NodeParam::DataType type, const QVariant& data); + void SetEntryInternal(const QString& key, NodeValue::Type type, const QVariant& data); QMap config_map_; diff --git a/app/dialog/keyframeproperties/keyframeproperties.cpp b/app/dialog/keyframeproperties/keyframeproperties.cpp index 469734cfd..d0a71e654 100644 --- a/app/dialog/keyframeproperties/keyframeproperties.cpp +++ b/app/dialog/keyframeproperties/keyframeproperties.cpp @@ -30,7 +30,7 @@ namespace olive { -KeyframePropertiesDialog::KeyframePropertiesDialog(const QList &keys, const rational &timebase, QWidget *parent) : +KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector &keys, const rational &timebase, QWidget *parent) : QDialog(parent), keys_(keys), timebase_(timebase) @@ -92,8 +92,8 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QList for (int i=0;i 0) { - NodeKeyframePtr prev_key = keys_.at(i-1); - NodeKeyframePtr this_key = keys_.at(i); + NodeKeyframe* prev_key = keys_.at(i-1); + NodeKeyframe* this_key = keys_.at(i); // Determine if the keyframes are all the same time or not if (all_same_time) { @@ -199,7 +199,7 @@ void KeyframePropertiesDialog::accept() rational new_time = Timecode::timestamp_to_time(time_slider_->GetValue(), timebase_); int new_type = type_select_->currentData().toInt(); - foreach (NodeKeyframePtr key, keys_) { + foreach (NodeKeyframe* key, keys_) { if (time_slider_->isEnabled() && !time_slider_->IsTristate()) { new NodeParamSetKeyframeTimeCommand(key, new_time, command); } diff --git a/app/dialog/keyframeproperties/keyframeproperties.h b/app/dialog/keyframeproperties/keyframeproperties.h index 3ad6145af..b1008998e 100644 --- a/app/dialog/keyframeproperties/keyframeproperties.h +++ b/app/dialog/keyframeproperties/keyframeproperties.h @@ -35,7 +35,7 @@ class KeyframePropertiesDialog : public QDialog { Q_OBJECT public: - KeyframePropertiesDialog(const QList& keys, const rational& timebase, QWidget* parent = nullptr); + KeyframePropertiesDialog(const QVector& keys, const rational& timebase, QWidget* parent = nullptr); public slots: virtual void accept() override; @@ -43,7 +43,7 @@ public slots: private: void SetUpBezierSlider(FloatSlider *slider, bool all_same, double value); - const QList& keys_; + const QVector& keys_; rational timebase_; diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index e74c12f5b..2de2c99fb 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -25,28 +25,24 @@ add_subdirectory(output) set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/edge.h - node/edge.cpp + node/connectable.cpp + node/connectable.h node/factory.h node/factory.cpp node/graph.h node/graph.cpp node/input.h node/input.cpp - node/inputarray.h - node/inputarray.cpp node/inputdragger.h node/inputdragger.cpp + node/inputimmediate.h + node/inputimmediate.cpp node/keyframe.h node/keyframe.cpp node/node.h node/node.cpp node/nodecopypaste.h node/nodecopypaste.cpp - node/output.h - node/output.cpp - node/param.h - node/param.cpp node/traverser.h node/traverser.cpp node/value.h diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 71ae6ea6f..6665be2e0 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -24,14 +24,12 @@ namespace olive { PanNode::PanNode() { - samples_input_ = new NodeInput("samples_in", NodeParam::kSamples); - AddInput(samples_input_); + samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples); - panning_input_ = new NodeInput("panning_in", NodeParam::kFloat, 0.0); + panning_input_ = new NodeInput(this, QStringLiteral("panning_in"), NodeValue::kFloat, 0.0); panning_input_->setProperty("min", -1.0); panning_input_->setProperty("max", 1.0); panning_input_->setProperty("view", QStringLiteral("percent")); - AddInput(panning_input_); } Node *PanNode::copy() const @@ -69,8 +67,8 @@ NodeValueTable PanNode::Value(NodeValueDatabase &value) const NodeValueTable table = value.Merge(); if (job.HasSamples()) { - float pan_volume = job.GetValue(panning_input_).data.toFloat(); - if (panning_input_->is_static()) { + float pan_volume = job.GetValue(panning_input_).data().toFloat(); + if (panning_input_->IsStatic()) { if (!qIsNull(pan_volume) && job.samples()->audio_params().channel_count() == 2) { if (pan_volume > 0) { job.samples()->transform_volume_for_channel(0, 1.0f - pan_volume); @@ -79,9 +77,9 @@ NodeValueTable PanNode::Value(NodeValueDatabase &value) const } } - table.Push(NodeParam::kSamples, QVariant::fromValue(job.samples()), this); + table.Push(NodeValue::kSamples, QVariant::fromValue(job.samples()), this); } else { - table.Push(NodeParam::kSampleJob, QVariant::fromValue(job), this); + table.Push(NodeValue::kSampleJob, QVariant::fromValue(job), this); } } @@ -95,7 +93,7 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in return; } - float pan_val = values[panning_input_].Get(NodeParam::kFloat).toFloat(); + float pan_val = values[panning_input_].Get(NodeValue::kFloat).toFloat(); for (int i=0;iaudio_params().channel_count();i++) { output->data()[i][index] = input->data()[i][index]; diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 20c94b342..8197f2ec7 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -24,14 +24,12 @@ namespace olive { VolumeNode::VolumeNode() { - samples_input_ = new NodeInput(QStringLiteral("samples_in"), NodeParam::kSamples); - samples_input_->set_is_keyframable(false); - AddInput(samples_input_); + samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples); + samples_input_->SetKeyframable(false); - volume_input_ = new NodeInput(QStringLiteral("volume_in"), NodeParam::kFloat, 1.0); + volume_input_ = new NodeInput(this, QStringLiteral("volume_in"), NodeValue::kFloat, 1.0); volume_input_->setProperty("min", 0.0); volume_input_->setProperty("view", QStringLiteral("db")); - AddInput(volume_input_); } Node *VolumeNode::copy() const @@ -65,9 +63,9 @@ NodeValueTable VolumeNode::Value(NodeValueDatabase &value) const kOpMultiply, kPairSampleNumber, samples_input_, - value[samples_input_].TakeWithMeta(NodeParam::kSamples), + value[samples_input_].TakeWithMeta(NodeValue::kSamples), volume_input_, - value[volume_input_].TakeWithMeta(NodeParam::kFloat)); + value[volume_input_].TakeWithMeta(NodeValue::kFloat)); } void VolumeNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 29857d25f..ebd01c0b6 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -31,28 +31,24 @@ Block::Block() : previous_(nullptr), next_(nullptr) { - length_input_ = new NodeInput("length_in", NodeParam::kRational); - length_input_->set_connectable(false); - length_input_->set_is_keyframable(false); - AddInput(length_input_); + length_input_ = new NodeInput(this, QStringLiteral("length_in"), NodeValue::kRational); + length_input_->SetConnectable(false); + length_input_->SetKeyframable(false); disconnect(length_input_, &NodeInput::ValueChanged, this, &Block::InputChanged); connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthInputChanged); - media_in_input_ = new NodeInput("media_in_in", NodeParam::kRational); - media_in_input_->set_connectable(false); - media_in_input_->set_is_keyframable(false); - AddInput(media_in_input_); + media_in_input_ = new NodeInput(this, QStringLiteral("media_in_in"), NodeValue::kRational); + media_in_input_->SetConnectable(false); + media_in_input_->SetKeyframable(false); - enabled_input_ = new NodeInput("enabled_in", NodeParam::kBoolean); - enabled_input_->set_connectable(false); - enabled_input_->set_is_keyframable(false); - enabled_input_->set_standard_value(true); - AddInput(enabled_input_); + enabled_input_ = new NodeInput(this, QStringLiteral("enabled_in"), NodeValue::kBoolean); + enabled_input_->SetConnectable(false); + enabled_input_->SetKeyframable(false); + enabled_input_->SetStandardValue(true); - speed_input_ = new NodeInput("speed_in", NodeParam::kFloat); - speed_input_->set_standard_value(1.0); + speed_input_ = new NodeInput(this, QStringLiteral("speed_in"), NodeValue::kFloat); + speed_input_->SetStandardValue(1.0); speed_input_->setProperty("view", QStringLiteral("percent")); - AddInput(speed_input_); // A block's length must be greater than 0 set_length_and_media_out(1); @@ -85,7 +81,7 @@ void Block::set_out(const rational &out) rational Block::length() const { - return length_input_->get_standard_value().value(); + return length_input_->GetStandardValue().value(); } void Block::set_length_and_media_out(const rational &length) @@ -149,22 +145,22 @@ void Block::set_next(Block *next) rational Block::media_in() const { - return media_in_input_->get_standard_value().value(); + return media_in_input_->GetStandardValue().value(); } void Block::set_media_in(const rational &media_in) { - media_in_input_->set_standard_value(QVariant::fromValue(media_in)); + media_in_input_->SetStandardValue(QVariant::fromValue(media_in)); } bool Block::is_enabled() const { - return enabled_input_->get_standard_value().toBool(); + return enabled_input_->GetStandardValue().toBool(); } void Block::set_enabled(bool e) { - enabled_input_->set_standard_value(e); + enabled_input_->SetStandardValue(e); emit EnabledChanged(); } @@ -179,10 +175,10 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const rational local_time = sequence_time - in(); // FIXME: Doesn't handle reversing - if (speed_input_->is_keyframing() || speed_input_->is_connected()) { + if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) { // FIXME: We'll need to calculate the speed hoo boy } else { - double speed_value = speed_input_->get_standard_value().toDouble(); + double speed_value = speed_input_->GetStandardValue().toDouble(); if (qIsNull(speed_value)) { // Effectively holds the frame at the in point @@ -206,10 +202,10 @@ rational Block::MediaToSequenceTime(const rational &media_time) const rational sequence_time = media_time - media_in(); // FIXME: Doesn't handle reversing - if (speed_input_->is_keyframing() || speed_input_->is_connected()) { + if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) { // FIXME: We'll need to calculate the speed hoo boy } else { - double speed_value = speed_input_->get_standard_value().toDouble(); + double speed_value = speed_input_->GetStandardValue().toDouble(); if (qIsNull(speed_value)) { // Effectively holds the frame at the in point, also prevents divide by zero @@ -259,7 +255,7 @@ void Block::LengthChangedEvent(const rational &, const rational &, const Timelin void Block::set_length_internal(const rational &length) { - length_input_->set_standard_value(QVariant::fromValue(length)); + length_input_->SetStandardValue (QVariant::fromValue(length)); } void Block::LengthInputChanged() @@ -340,11 +336,6 @@ bool Block::HasLinks() return !linked_clips_.isEmpty(); } -bool Block::IsBlock() const -{ - return true; -} - void Block::Retranslate() { Node::Retranslate(); diff --git a/app/node/block/block.h b/app/node/block/block.h index 1f4362340..d7923c0c2 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -75,8 +75,6 @@ public: const QVector& linked_clips(); bool HasLinks(); - virtual bool IsBlock() const override; - virtual void Retranslate() override; NodeInput* length_input() const; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index cc73034e6..2b915e60b 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -24,9 +24,8 @@ namespace olive { ClipBlock::ClipBlock() { - texture_input_ = new NodeInput("buffer_in", NodeInput::kBuffer); - texture_input_->set_is_keyframable(false); - AddInput(texture_input_); + texture_input_ = new NodeInput(this, QStringLiteral("buffer_in"), NodeValue::kNone); + texture_input_->SetKeyframable(false); } Node *ClipBlock::copy() const @@ -59,18 +58,18 @@ NodeInput *ClipBlock::texture_input() const return texture_input_; } -void ClipBlock::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) +void ClipBlock::InvalidateCache(const TimeRange& range, const InputConnection& from) { // If signal is from texture input, transform all times from media time to sequence time - if (from == texture_input_) { + if (from.input == texture_input_) { // Adjust range from media time to sequence time rational start = MediaToSequenceTime(range.in()); rational end = MediaToSequenceTime(range.out()); - Block::InvalidateCache(TimeRange(start, end), from, source); + Block::InvalidateCache(TimeRange(start, end), from); } else { // Otherwise, pass signal along normally - Block::InvalidateCache(range, from, source); + Block::InvalidateCache(range, from); } } @@ -95,10 +94,10 @@ TimeRange ClipBlock::OutputTimeAdjustment(NodeInput *input, const TimeRange &inp NodeValueTable ClipBlock::Value(NodeValueDatabase &value) const { // We discard most values here except for the buffer we received - NodeValue data = value[texture_input()].GetWithMeta(NodeParam::kBuffer); + NodeValue data = value[texture_input()].GetWithMeta(NodeValue::kBuffer); NodeValueTable table; - if (data.type() != NodeParam::kNone) { + if (data.type() != NodeValue::kNone) { table.Push(data); } return table; @@ -113,10 +112,10 @@ void ClipBlock::Retranslate() void ClipBlock::Hash(QCryptographicHash &hash, const rational &time) const { - if (texture_input_->is_connected()) { + if (texture_input_->IsConnected()) { rational t = InputTimeAdjustment(texture_input_, TimeRange(time, time)).in(); - texture_input_->get_connected_node()->Hash(hash, t); + texture_input_->GetConnectedNode()->Hash(hash, t); } } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index adedaaac6..e4f376f14 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -44,7 +44,7 @@ public: NodeInput* texture_input() const; - virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override; + virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override; virtual TimeRange InputTimeAdjustment(NodeInput* input, const TimeRange& input_time) const override; diff --git a/app/node/block/transition/diptocolor/diptocolortransition.cpp b/app/node/block/transition/diptocolor/diptocolortransition.cpp index 6925ffa25..cbad89c2a 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.cpp +++ b/app/node/block/transition/diptocolor/diptocolortransition.cpp @@ -24,8 +24,7 @@ namespace olive { DipToColorTransition::DipToColorTransition() { - color_input_ = new NodeInput(QStringLiteral("color_in"), NodeParam::kColor, QVariant::fromValue(Color(0, 0, 0))); - AddInput(color_input_); + color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0))); } Node *DipToColorTransition::copy() const diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index eacffe377..ab54c068d 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -28,22 +28,19 @@ TransitionBlock::TransitionBlock() : connected_out_block_(nullptr), connected_in_block_(nullptr) { - out_block_input_ = new NodeInput(QStringLiteral("out_block_in"), NodeParam::kBuffer); - out_block_input_->set_is_keyframable(false); - connect(out_block_input_, &NodeParam::EdgeAdded, this, &TransitionBlock::BlockConnected); - connect(out_block_input_, &NodeParam::EdgeRemoved, this, &TransitionBlock::BlockDisconnected); - AddInput(out_block_input_); + out_block_input_ = new NodeInput(this, QStringLiteral("out_block_in"), NodeValue::kNone); + out_block_input_->SetKeyframable(false); + connect(out_block_input_, &NodeInput::InputConnected, this, &TransitionBlock::InBlockConnected); + connect(out_block_input_, &NodeInput::InputDisconnected, this, &TransitionBlock::InBlockDisconnected); - in_block_input_ = new NodeInput(QStringLiteral("in_block_in"), NodeParam::kBuffer); - in_block_input_->set_is_keyframable(false); - connect(in_block_input_, &NodeParam::EdgeAdded, this, &TransitionBlock::BlockConnected); - connect(in_block_input_, &NodeParam::EdgeRemoved, this, &TransitionBlock::BlockDisconnected); - AddInput(in_block_input_); + in_block_input_ = new NodeInput(this, QStringLiteral("in_block_in"), NodeValue::kNone); + in_block_input_->SetKeyframable(false); + connect(in_block_input_, &NodeInput::InputConnected, this, &TransitionBlock::OutBlockConnected); + connect(in_block_input_, &NodeInput::InputDisconnected, this, &TransitionBlock::OutBlockDisconnected); - curve_input_ = new NodeInput(QStringLiteral("curve_in"), NodeParam::kCombo); - curve_input_->set_is_keyframable(false); - curve_input_->set_connectable(false); - AddInput(curve_input_); + curve_input_ = new NodeInput(this, QStringLiteral("curve_in"), NodeValue::kCombo); + curve_input_->SetKeyframable(false); + curve_input_->SetConnectable(false); } Block::Type TransitionBlock::type() const @@ -161,57 +158,55 @@ void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &t { // Provides total transition progress from 0.0 (start) - 1.0 (end) job->InsertValue(QStringLiteral("ove_tprog_all"), - ShaderValue(GetTotalProgress(time), NodeParam::kFloat)); + NodeValue(NodeValue::kFloat, GetTotalProgress(time), this)); // Provides progress of out section from 1.0 (start) - 0.0 (end) job->InsertValue(QStringLiteral("ove_tprog_out"), - ShaderValue(GetOutProgress(time), NodeParam::kFloat)); + NodeValue(NodeValue::kFloat, GetOutProgress(time), this)); // Provides progress of in section from 0.0 (start) - 1.0 (end) job->InsertValue(QStringLiteral("ove_tprog_in"), - ShaderValue(GetInProgress(time), NodeParam::kFloat)); + NodeValue(NodeValue::kFloat, GetInProgress(time), this)); } -void TransitionBlock::BlockConnected(NodeEdgePtr edge) +void TransitionBlock::OutBlockConnected(Node *node) { - if (!edge->output()->parentNode()->IsBlock()) { - return; - } - - Block* block = static_cast(edge->output()->parentNode()); - - if (edge->input() == out_block_input_) { - connected_out_block_ = block; - } else { - connected_in_block_ = block; - } + // If node is not a block, this will just be null + connected_out_block_ = dynamic_cast(node); } -void TransitionBlock::BlockDisconnected(NodeEdgePtr edge) +void TransitionBlock::OutBlockDisconnected() { - if (edge->input() == out_block_input_) { - connected_out_block_ = nullptr; - } else { - connected_in_block_ = nullptr; - } + connected_out_block_ = nullptr; +} + +void TransitionBlock::InBlockConnected(Node *node) +{ + // If node is not a block, this will just be null + connected_in_block_ = dynamic_cast(node); +} + +void TransitionBlock::InBlockDisconnected() +{ + connected_in_block_ = nullptr; } NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const { - NodeParam::DataType data_type; + NodeValue::Type data_type; - if (out_block_input()->is_connected()) { - data_type = value[out_block_input()].GetWithMeta(NodeParam::kBuffer).type(); - } else if (in_block_input()->is_connected()) { - data_type = value[in_block_input()].GetWithMeta(NodeParam::kBuffer).type(); + if (out_block_input()->IsConnected()) { + data_type = value[out_block_input()].GetWithMeta(NodeValue::kBuffer).type(); + } else if (in_block_input()->IsConnected()) { + data_type = value[in_block_input()].GetWithMeta(NodeValue::kBuffer).type(); } else { - data_type = NodeParam::kNone; + data_type = NodeValue::kNone; } - NodeParam::DataType job_type; + NodeValue::Type job_type; QVariant push_job; - if (data_type == NodeParam::kTexture) { + if (data_type == NodeValue::kTexture) { // This must be a visual transition ShaderJob job; @@ -219,21 +214,21 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const job.InsertValue(in_block_input(), value); job.InsertValue(curve_input_, value); - double time = value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_in")).toDouble(); + double time = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble(); InsertTransitionTimes(&job, time); ShaderJobEvent(value, job); - job_type = NodeParam::kShaderJob; + job_type = NodeValue::kShaderJob; push_job = QVariant::fromValue(job); - } else if (data_type == NodeParam::kSamples) { + } else if (data_type == NodeValue::kSamples) { // This must be an audio transition - SampleBufferPtr from_samples = value[out_block_input()].Take(NodeParam::kBuffer).value(); - SampleBufferPtr to_samples = value[in_block_input()].Take(NodeParam::kBuffer).value(); + SampleBufferPtr from_samples = value[out_block_input()].Take(NodeValue::kSamples).value(); + SampleBufferPtr to_samples = value[in_block_input()].Take(NodeValue::kSamples).value(); if (from_samples || to_samples) { - double time_in = value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_in")).toDouble(); - double time_out = value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_out")).toDouble(); + double time_in = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble(); + double time_out = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_out")).toDouble(); const AudioParams& params = (from_samples) ? from_samples->audio_params() : to_samples->audio_params(); @@ -242,7 +237,7 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const SampleBufferPtr out_samples = SampleBuffer::CreateAllocated(params, nb_samples); SampleJobEvent(from_samples, to_samples, out_samples, time_in); - job_type = NodeParam::kSamples; + job_type = NodeValue::kSamples; push_job = QVariant::fromValue(out_samples); } } @@ -259,19 +254,13 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const TransitionBlock *GetBlockTransitionInternal(Block *block, Timeline::MovementMode mode) { // See if this block outputs to a transition - foreach (NodeEdgePtr edge, block->output()->edges()) { - Node* connected_node = edge->input()->parentNode(); + foreach (const NodeConnectable::InputConnection& conn, block->edges()) { + TransitionBlock* transition = dynamic_cast(conn.input->parent()); - if (connected_node->IsBlock()) { - Block* connected_block = static_cast(connected_node); - - if (connected_block->type() == Block::kTransition) { - TransitionBlock* connected_transition = static_cast(connected_block); - - if ((mode == Timeline::kTrimIn && edge->input() == connected_transition->in_block_input()) - || (mode == Timeline::kTrimOut && edge->input() == connected_transition->out_block_input())) { - return connected_transition; - } + if (transition) { + if ((mode == Timeline::kTrimIn && conn.input == transition->in_block_input()) + || (mode == Timeline::kTrimOut && conn.input == transition->out_block_input())) { + return transition; } } } @@ -305,7 +294,7 @@ void TransitionBlock::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferP double TransitionBlock::TransformCurve(double linear) const { - switch (static_cast(curve_input_->get_standard_value().toInt())) { + switch (static_cast(curve_input_->GetStandardValue().toInt())) { case kLinear: break; case kExponential: diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index 8a6acca7e..196824157 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -85,9 +85,13 @@ private: Block* connected_in_block_; private slots: - void BlockConnected(NodeEdgePtr edge); + void OutBlockConnected(Node* node); - void BlockDisconnected(NodeEdgePtr edge); + void OutBlockDisconnected(); + + void InBlockConnected(Node* node); + + void InBlockDisconnected(); }; diff --git a/app/node/connectable.cpp b/app/node/connectable.cpp new file mode 100644 index 000000000..bc49f7c65 --- /dev/null +++ b/app/node/connectable.cpp @@ -0,0 +1,77 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 "connectable.h" + +#include "input.h" +#include "node.h" + +namespace olive { + +void NodeConnectable::ConnectEdge(Node *output, NodeInput *input, int element) +{ + InputConnection conn_to_in = {input, element}; + + // Connection exists + if (output->output_connections_.contains(conn_to_in)) { + qDebug() << "Ignored connect that already exists"; + return; + } + + // Ensure a connection isn't getting overwritten + Q_ASSERT(!input->input_connections_.contains(element)); + + // Insert connections in both sides + output->output_connections_.append(conn_to_in); + input->input_connections_.insert(element, output); + + // Emit signals + emit input->InputConnected(output, element); + emit output->OutputConnected(input, element); +} + +void NodeConnectable::DisconnectEdge(Node *output, NodeInput *input, int element) +{ + InputConnection conn_to_in = {input, element}; + + // Connection exists + if (!output->output_connections_.contains(conn_to_in)) { + qDebug() << "Ignored disconnect that doesn't exist"; + return; + } + + // Assertions to ensure connection exists + Q_ASSERT(input->input_connections_.value(element) == output); + + // Remove connections from both sides + output->output_connections_.removeOne(conn_to_in); + input->input_connections_.remove(element); + + // Emit signals + emit input->InputDisconnected(output, element); + emit output->OutputDisconnected(input, element); +} + +uint qHash(const NodeConnectable::InputConnection &r, uint seed) +{ + return ::qHash(r.input, seed) ^ ::qHash(r.element, seed); +} + +} diff --git a/app/node/connectable.h b/app/node/connectable.h new file mode 100644 index 000000000..c7f8e812a --- /dev/null +++ b/app/node/connectable.h @@ -0,0 +1,96 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 CONNECTABLE_H +#define CONNECTABLE_H + +#include +#include +#include + +namespace olive { + +class Node; +class NodeInput; + +class NodeConnectable : public QObject +{ + Q_OBJECT +public: + NodeConnectable() = default; + + struct InputConnection { + InputConnection(NodeInput* i = nullptr, int e = -1) + { + input = i; + element = e; + } + + bool operator==(const InputConnection& rhs) const + { + return input == rhs.input && element == rhs.element; + } + + NodeInput* input; + int element; + }; + + static void ConnectEdge(Node* output, NodeInput* input, int element = -1); + + static void DisconnectEdge(Node* output, NodeInput* input, int element = -1); + + struct Edge { + Node* output; + NodeInput* input; + int element; + }; + +signals: + void OutputConnected(NodeInput* destination, int element); + + void OutputDisconnected(NodeInput* destination, int element); + + void InputConnected(Node* source, int element); + + void InputDisconnected(Node* source, int element); + +protected: + const QVector& output_connections() const + { + return output_connections_; + } + + const QHash& input_connections() const + { + return input_connections_; + } + +private: + QVector output_connections_; + + QHash input_connections_; + +}; + +uint qHash(const NodeConnectable::InputConnection& r, uint seed = 0); + +} + +#endif // CONNECTABLE_H diff --git a/app/node/distort/crop/cropdistortnode.cpp b/app/node/distort/crop/cropdistortnode.cpp index 4c7c8b446..fd9eb23ae 100644 --- a/app/node/distort/crop/cropdistortnode.cpp +++ b/app/node/distort/crop/cropdistortnode.cpp @@ -26,36 +26,30 @@ namespace olive { CropDistortNode::CropDistortNode() { - texture_input_ = new NodeInput(QStringLiteral("tex_in"), NodeParam::kTexture); - AddInput(texture_input_); + texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); - left_input_ = new NodeInput(QStringLiteral("left_in"), NodeParam::kFloat, 0.0); + left_input_ = new NodeInput(this, QStringLiteral("left_in"), NodeValue::kFloat, 0.0); left_input_->setProperty("min", 0.0); left_input_->setProperty("max", 1.0); left_input_->setProperty("view", QStringLiteral("percent")); - AddInput(left_input_); - top_input_ = new NodeInput(QStringLiteral("top_in"), NodeParam::kFloat, 0.0); + top_input_ = new NodeInput(this, QStringLiteral("top_in"), NodeValue::kFloat, 0.0); top_input_->setProperty("min", 0.0); top_input_->setProperty("max", 1.0); top_input_->setProperty("view", QStringLiteral("percent")); - AddInput(top_input_); - right_input_ = new NodeInput(QStringLiteral("right_in"), NodeParam::kFloat, 0.0); + right_input_ = new NodeInput(this, QStringLiteral("right_in"), NodeValue::kFloat, 0.0); right_input_->setProperty("min", 0.0); right_input_->setProperty("max", 1.0); right_input_->setProperty("view", QStringLiteral("percent")); - AddInput(right_input_); - bottom_input_ = new NodeInput(QStringLiteral("bottom_in"), NodeParam::kFloat, 0.0); + bottom_input_ = new NodeInput(this, QStringLiteral("bottom_in"), NodeValue::kFloat, 0.0); bottom_input_->setProperty("min", 0.0); bottom_input_->setProperty("max", 1.0); bottom_input_->setProperty("view", QStringLiteral("percent")); - AddInput(bottom_input_); - feather_input_ = new NodeInput(QStringLiteral("feather_in"), NodeParam::kFloat, 0.0); + feather_input_ = new NodeInput(this, QStringLiteral("feather_in"), NodeValue::kFloat, 0.0); feather_input_->setProperty("min", 0.0); - AddInput(feather_input_); } void CropDistortNode::Retranslate() @@ -78,19 +72,19 @@ NodeValueTable CropDistortNode::Value(NodeValueDatabase &value) const job.InsertValue(bottom_input_, value); job.InsertValue(feather_input_, value); job.InsertValue(QStringLiteral("resolution_in"), - ShaderValue(value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")), NodeParam::kVec2)); + NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this)); job.SetAlphaChannelRequired(true); NodeValueTable table = value.Merge(); - if (!job.GetValue(texture_input_).data.isNull()) { - if (!qIsNull(job.GetValue(left_input_).data.toDouble()) - || !qIsNull(job.GetValue(right_input_).data.toDouble()) - || !qIsNull(job.GetValue(top_input_).data.toDouble()) - || !qIsNull(job.GetValue(bottom_input_).data.toDouble())) { - table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this); + if (!job.GetValue(texture_input_).data().isNull()) { + if (!qIsNull(job.GetValue(left_input_).data().toDouble()) + || !qIsNull(job.GetValue(right_input_).data().toDouble()) + || !qIsNull(job.GetValue(top_input_).data().toDouble()) + || !qIsNull(job.GetValue(bottom_input_).data().toDouble())) { + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { - table.Push(NodeParam::kTexture, job.GetValue(texture_input_).data, this); + table.Push(NodeValue::kTexture, job.GetValue(texture_input_).data(), this); } } @@ -105,16 +99,16 @@ ShaderCode CropDistortNode::GetShaderCode(const QString &shader_id) const void CropDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) { - QVector2D resolution = db[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value(); + QVector2D resolution = db[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value(); const double handle_radius = GetGizmoHandleRadius(p->transform()); p->setPen(QPen(Qt::white, 0)); - double left_pt = resolution.x() * db[left_input_].Get(NodeParam::kFloat).toDouble(); - double top_pt = resolution.y() * db[top_input_].Get(NodeParam::kFloat).toDouble(); - double right_pt = resolution.x() * (1.0 - db[right_input_].Get(NodeParam::kFloat).toDouble()); - double bottom_pt = resolution.y() * (1.0 - db[bottom_input_].Get(NodeParam::kFloat).toDouble()); + double left_pt = resolution.x() * db[left_input_].Get(NodeValue::kFloat).toDouble(); + double top_pt = resolution.y() * db[top_input_].Get(NodeValue::kFloat).toDouble(); + double right_pt = resolution.x() * (1.0 - db[right_input_].Get(NodeValue::kFloat).toDouble()); + double bottom_pt = resolution.y() * (1.0 - db[bottom_input_].Get(NodeValue::kFloat).toDouble()); double center_x_pt = lerp(left_pt, right_pt, 0.5); double center_y_pt = lerp(top_pt, bottom_pt, 0.5); @@ -161,7 +155,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoLeft; - gizmo_start_.append(db[left_input_].Get(NodeParam::kFloat)); + gizmo_start_.append(db[left_input_].Get(NodeValue::kFloat)); } if (gizmo_active[kGizmoScaleTopLeft] @@ -170,7 +164,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoTop; - gizmo_start_.append(db[top_input_].Get(NodeParam::kFloat)); + gizmo_start_.append(db[top_input_].Get(NodeValue::kFloat)); } if (gizmo_active[kGizmoScaleTopRight] @@ -179,7 +173,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoRight; - gizmo_start_.append(db[right_input_].Get(NodeParam::kFloat)); + gizmo_start_.append(db[right_input_].Get(NodeValue::kFloat)); } if (gizmo_active[kGizmoScaleBottomLeft] @@ -188,11 +182,11 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoBottom; - gizmo_start_.append(db[bottom_input_].Get(NodeParam::kFloat)); + gizmo_start_.append(db[bottom_input_].Get(NodeValue::kFloat)); } if (gizmo_drag_ > kGizmoNone) { - gizmo_res_ = db[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value(); + gizmo_res_ = db[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value(); gizmo_drag_start_ = p; return true; diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 46abe9cf4..d0070cc79 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -28,14 +28,11 @@ namespace olive { TransformDistortNode::TransformDistortNode() { - autoscale_input_ = new NodeInput(QStringLiteral("autoscale_in"), NodeParam::kCombo, 0); - AddInput(autoscale_input_); + autoscale_input_ = new NodeInput(this, QStringLiteral("autoscale_in"), NodeValue::kCombo, 0); - interpolation_input_ = new NodeInput(QStringLiteral("interpolation_in"), NodeParam::kCombo, 2); - AddInput(interpolation_input_); + interpolation_input_ = new NodeInput(this, QStringLiteral("interpolation_in"), NodeValue::kCombo, 2); - texture_input_ = new NodeInput(QStringLiteral("tex_in"), NodeParam::kTexture); - AddInput(texture_input_); + texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); } void TransformDistortNode::Retranslate() @@ -56,7 +53,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false); // Pop texture - TexturePtr texture = value[texture_input_].Take(NodeParam::kTexture).value(); + TexturePtr texture = value[texture_input_].Take(NodeValue::kTexture).value(); // Merge table NodeValueTable table = value.Merge(); @@ -64,9 +61,9 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const // If we have a texture, generate a matrix and make it happen if (texture) { // Adjust our matrix by the resolutions involved - QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value(); + QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value(); QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height()); - AutoScaleType autoscale = static_cast(value[autoscale_input_].Get(NodeParam::kCombo).toInt()); + AutoScaleType autoscale = static_cast(value[autoscale_input_].Get(NodeValue::kCombo).toInt()); QMatrix4x4 real_matrix = AdjustMatrixByResolutions(generated_matrix, sequence_res, @@ -75,19 +72,19 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const if (real_matrix.isIdentity()) { // We don't expect any changes, just push as normal - table.Push(NodeParam::kTexture, QVariant::fromValue(texture), this); + table.Push(NodeValue::kTexture, QVariant::fromValue(texture), this); } else { // The matrix will transform things ShaderJob job; - job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(texture), NodeParam::kTexture)); - job.InsertValue(QStringLiteral("ove_mvpmat"), ShaderValue(real_matrix, NodeParam::kMatrix)); - job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(value[interpolation_input_].Get(NodeParam::kCombo).toInt())); + job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture), this)); + job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, real_matrix, this)); + job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(value[interpolation_input_].Get(NodeValue::kCombo).toInt())); // FIXME: This should be optimized, we can use matrix math to determine if this operation will // end up with gaps in the screen that will require an alpha channel. job.SetAlphaChannelRequired(true); - table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this); + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } } @@ -123,10 +120,10 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) if (scaling) { // Dragging scale handle - gizmo_start_ = {db[scale_input()].Get(NodeParam::kVec2)}; + gizmo_start_ = {db[scale_input()].Get(NodeValue::kVec2)}; gizmo_drag_ = scale_input(); - gizmo_scale_uniform_ = db[uniform_scale_input()].Get(NodeParam::kBoolean).toBool(); + gizmo_scale_uniform_ = db[uniform_scale_input()].Get(NodeValue::kBoolean).toBool(); if (gizmo_scale_active[kGizmoScaleTopLeft] || gizmo_scale_active[kGizmoScaleTopRight] || gizmo_scale_active[kGizmoScaleBottomLeft] || gizmo_scale_active[kGizmoScaleBottomRight]) { @@ -138,8 +135,8 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) } // Store texture size - QVector2D texture_sz = db[texture_input()].Get(NodeParam::kTexture).value(); - gizmo_scale_anchor_ = db[anchor_input()].Get(NodeParam::kVec2).value() + texture_sz/2; + QVector2D texture_sz = db[texture_input()].Get(NodeValue::kTexture).value(); + gizmo_scale_anchor_ = db[anchor_input()].Get(NodeValue::kVec2).value() + texture_sz/2; if (gizmo_scale_active[kGizmoScaleTopRight] || gizmo_scale_active[kGizmoScaleBottomRight] @@ -163,8 +160,8 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) } else if (gizmo_anchor_pt_.contains(p)) { // Dragging the anchor point specifically - gizmo_start_ = {db[anchor_input()].Get(NodeParam::kVec2), - db[position_input()].Get(NodeParam::kVec2)}; + gizmo_start_ = {db[anchor_input()].Get(NodeValue::kVec2), + db[position_input()].Get(NodeValue::kVec2)}; gizmo_drag_ = anchor_input(); // Store current matrix @@ -175,7 +172,7 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) } else if (gizmo_rect_.containsPoint(p, Qt::OddEvenFill)) { // Dragging the main rectangle - gizmo_start_ = {db[position_input()].Get(NodeParam::kVec2)}; + gizmo_start_ = {db[position_input()].Get(NodeValue::kVec2)}; gizmo_drag_ = position_input(); return true; @@ -183,7 +180,7 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) } else { // Dragging rotation - gizmo_start_ = {db[rotation_input()].Get(NodeParam::kFloat)}; + gizmo_start_ = {db[rotation_input()].Get(NodeValue::kFloat)}; gizmo_drag_ = rotation_input(); gizmo_start_angle_ = qAtan2(gizmo_drag_pos_.y() - gizmo_anchor_pt_.center().y(), gizmo_drag_pos_.x() - gizmo_anchor_pt_.center().x()); @@ -364,15 +361,15 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) p->setPen(QPen(Qt::white, 0)); // Get the sequence resolution - QVector2D sequence_res = db[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value(); + QVector2D sequence_res = db[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value(); QVector2D sequence_half_res = sequence_res/2; QPointF sequence_half_res_pt = sequence_half_res.toPointF(); // GizmoTraverser just returns the sizes of the textures and no other data - QVector2D tex_sz = db[texture_input_].Get(NodeParam::kTexture).value(); + QVector2D tex_sz = db[texture_input_].Get(NodeValue::kTexture).value(); // Retrieve autoscale value - AutoScaleType autoscale = static_cast(db[autoscale_input_].Get(NodeParam::kCombo).toInt()); + AutoScaleType autoscale = static_cast(db[autoscale_input_].Get(NodeValue::kCombo).toInt()); // Fold values into a matrix for the rectangle QMatrix4x4 rectangle_matrix; diff --git a/app/node/edge.cpp b/app/node/edge.cpp deleted file mode 100644 index ed6f8efbe..000000000 --- a/app/node/edge.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "edge.h" - -#include "input.h" -#include "node.h" -#include "output.h" - -namespace olive { - -NodeEdge::NodeEdge(NodeOutput *output, NodeInput *input) -{ - output_ = ParamToConnection(output); - input_ = ParamToConnection(input); -} - -NodeOutput *NodeEdge::output() const -{ - return output_.node->GetOutputWithID(output_.id); -} - -NodeInput *NodeEdge::input() const -{ - return input_.node->GetInputWithID(input_.id); -} - -NodeEdge::Connection NodeEdge::ParamToConnection(NodeParam *param) -{ - return {param->parentNode(), param->id()}; -} - -} diff --git a/app/node/edge.h b/app/node/edge.h deleted file mode 100644 index a3f377741..000000000 --- a/app/node/edge.h +++ /dev/null @@ -1,87 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 EDGE_H -#define EDGE_H - -#include -#include - -#include "common/define.h" - -namespace olive { - -class Node; -class NodeInput; -class NodeOutput; -class NodeParam; - -/** - * @brief A connection between two node parameters (a NodeOutput and a NodeInput) - * - * To simplify memory management, it's recommended to use NodeEdgePtr instead of raw pointers when working with - * NodeEdge. - */ -class NodeEdge -{ -public: - /** - * @brief Create a node edge connecting an output to an input - */ - NodeEdge(NodeOutput* output, NodeInput* input); - - Node* output_node() const - { - return output_.node; - } - - Node* input_node() const - { - return input_.node; - } - - /** - * @brief Return the output parameter this edge is connected to - */ - NodeOutput* output() const; - - /** - * @brief Return the input parameter this edge is connected to - */ - NodeInput* input() const; - -private: - struct Connection { - Node* node; - QString id; - }; - - static Connection ParamToConnection(NodeParam* param); - - Connection output_; - Connection input_; - -}; - -using NodeEdgePtr = std::shared_ptr; - -} - -#endif // EDGE_H diff --git a/app/node/filter/blur/blur.cpp b/app/node/filter/blur/blur.cpp index 70e07d79f..b3f8afd30 100644 --- a/app/node/filter/blur/blur.cpp +++ b/app/node/filter/blur/blur.cpp @@ -24,24 +24,18 @@ namespace olive { BlurFilterNode::BlurFilterNode() { - texture_input_ = new NodeInput("tex_in", NodeParam::kTexture); - AddInput(texture_input_); + texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); - method_input_ = new NodeInput("method_in", NodeParam::kCombo, 0); - AddInput(method_input_); + method_input_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo, 0); - radius_input_ = new NodeInput("radius_in", NodeParam::kFloat, 10.0f); + radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f); radius_input_->setProperty("min", 0.0f); - AddInput(radius_input_); - horiz_input_ = new NodeInput("horiz_in", NodeParam::kBoolean, true); - AddInput(horiz_input_); + horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kBoolean, true); - vert_input_ = new NodeInput("vert_in", NodeParam::kBoolean, true); - AddInput(vert_input_); + vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kBoolean, true); - repeat_edge_pixels_input_ = new NodeInput("repeat_edge_pixels_in", NodeParam::kBoolean, false); - AddInput(repeat_edge_pixels_input_); + repeat_edge_pixels_input_ = new NodeInput(this, QStringLiteral("repeat_edge_pixels_in"), NodeValue::kBoolean, false); } Node *BlurFilterNode::copy() const @@ -97,32 +91,32 @@ NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const job.InsertValue(vert_input_, value); job.InsertValue(repeat_edge_pixels_input_, value); job.InsertValue(QStringLiteral("resolution_in"), - ShaderValue(value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")), NodeParam::kVec2)); + NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this)); NodeValueTable table = value.Merge(); // If there's no texture, no need to run an operation - if (!job.GetValue(texture_input_).data.isNull()) { + if (!job.GetValue(texture_input_).data().isNull()) { // Check if radius > 0, and both "horiz" and/or "vert" are enabled - if ((job.GetValue(horiz_input_).data.toBool() || job.GetValue(vert_input_).data.toBool()) - && job.GetValue(radius_input_).data.toDouble() > 0.0) { + if ((job.GetValue(horiz_input_).data().toBool() || job.GetValue(vert_input_).data().toBool()) + && job.GetValue(radius_input_).data().toDouble() > 0.0) { // Set iteration count to 2 if we're blurring both horizontally and vertically - if (job.GetValue(horiz_input_).data.toBool() && job.GetValue(vert_input_).data.toBool()) { + if (job.GetValue(horiz_input_).data().toBool() && job.GetValue(vert_input_).data().toBool()) { job.SetIterations(2, texture_input_); } // If we're not repeating pixels, expect an alpha channel to appear - if (!job.GetValue(repeat_edge_pixels_input_).data.toBool()) { + if (!job.GetValue(repeat_edge_pixels_input_).data().toBool()) { job.SetAlphaChannelRequired(true); } - table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this); + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { // If we're not performing the blur job, just push the texture - table.Push(job.GetValue(texture_input_), this); + table.Push(job.GetValue(texture_input_)); } } diff --git a/app/node/filter/mosaic/mosaicfilternode.cpp b/app/node/filter/mosaic/mosaicfilternode.cpp index b1e633b63..b03532cdc 100644 --- a/app/node/filter/mosaic/mosaicfilternode.cpp +++ b/app/node/filter/mosaic/mosaicfilternode.cpp @@ -24,16 +24,13 @@ namespace olive { MosaicFilterNode::MosaicFilterNode() { - tex_input_ = new NodeInput(QStringLiteral("tex_in"), NodeParam::kTexture); - AddInput(tex_input_); + tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); - horiz_input_ = new NodeInput(QStringLiteral("horiz_in"), NodeParam::kFloat, 32.0); + horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kFloat, 32.0); horiz_input_->setProperty("min", 1.0); - AddInput(horiz_input_); - vert_input_ = new NodeInput(QStringLiteral("vert_in"), NodeParam::kFloat, 18.0); + vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kFloat, 18.0); vert_input_->setProperty("min", 1.0); - AddInput(vert_input_); } void MosaicFilterNode::Retranslate() @@ -56,15 +53,15 @@ NodeValueTable MosaicFilterNode::Value(NodeValueDatabase &value) const NodeValueTable table = value.Merge(); - if (!job.GetValue(tex_input_).data.isNull()) { - TexturePtr texture = job.GetValue(tex_input_).data.value(); + if (!job.GetValue(tex_input_).data().isNull()) { + TexturePtr texture = job.GetValue(tex_input_).data().value(); if (texture - && job.GetValue(horiz_input_).data.toInt() != texture->width() - && job.GetValue(vert_input_).data.toInt() != texture->height()) { - table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this); + && job.GetValue(horiz_input_).data().toInt() != texture->width() + && job.GetValue(vert_input_).data().toInt() != texture->height()) { + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { - table.Push(job.GetValue(tex_input_), this); + table.Push(job.GetValue(tex_input_)); } } diff --git a/app/node/filter/stroke/stroke.cpp b/app/node/filter/stroke/stroke.cpp index edc74b4c5..806676c99 100644 --- a/app/node/filter/stroke/stroke.cpp +++ b/app/node/filter/stroke/stroke.cpp @@ -26,26 +26,22 @@ namespace olive { StrokeFilterNode::StrokeFilterNode() { - tex_input_ = new NodeInput("tex_in", NodeParam::kTexture); - AddInput(tex_input_); + tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); - color_input_ = new NodeInput("color_in", - NodeParam::kColor, + color_input_ = new NodeInput(this, + QStringLiteral("color_in"), + NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f))); - AddInput(color_input_); - radius_input_ = new NodeInput("radius_in", NodeParam::kFloat, 10.0f); + radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f); radius_input_->setProperty("min", 0.0f); - AddInput(radius_input_); - opacity_input_ = new NodeInput("opacity_in", NodeParam::kFloat, 1.0f); + opacity_input_ = new NodeInput(this, QStringLiteral("opacity_in"), NodeValue::kFloat, 1.0f); opacity_input_->setProperty("view", QStringLiteral("percent")); opacity_input_->setProperty("min", 0.0f); opacity_input_->setProperty("max", 1.0f); - AddInput(opacity_input_); - inner_input_ = new NodeInput("inner_in", NodeParam::kBoolean, false); - AddInput(inner_input_); + inner_input_ = new NodeInput(this, QStringLiteral("inner_in"), NodeValue::kBoolean, false); } Node *StrokeFilterNode::copy() const @@ -92,16 +88,16 @@ NodeValueTable StrokeFilterNode::Value(NodeValueDatabase &value) const job.InsertValue(opacity_input_, value); job.InsertValue(inner_input_, value); job.InsertValue(QStringLiteral("resolution_in"), - ShaderValue(value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")), NodeParam::kVec2)); + NodeValue(NodeValue::kVec2, value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")), this)); NodeValueTable table = value.Merge(); - if (!job.GetValue(tex_input_).data.isNull()) { - if (job.GetValue(radius_input_).data.toDouble() > 0.0 - && job.GetValue(opacity_input_).data.toDouble() > 0.0) { - table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this); + if (!job.GetValue(tex_input_).data().isNull()) { + if (job.GetValue(radius_input_).data().toDouble() > 0.0 + && job.GetValue(opacity_input_).data().toDouble() > 0.0) { + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { - table.Push(job.GetValue(tex_input_), this); + table.Push(job.GetValue(tex_input_)); } } diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index 52cecfad2..78c3200f3 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -27,26 +27,21 @@ namespace olive { MatrixGenerator::MatrixGenerator() { - position_input_ = new NodeInput("pos_in", NodeParam::kVec2, QVector2D()); - AddInput(position_input_); + position_input_ = new NodeInput(this, QStringLiteral("pos_in"), NodeValue::kVec2, QVector2D()); - rotation_input_ = new NodeInput("rot_in", NodeParam::kFloat, 0.0f); - AddInput(rotation_input_); + rotation_input_ = new NodeInput(this, QStringLiteral("rot_in"), NodeValue::kFloat, 0.0f); - scale_input_ = new NodeInput("scale_in", NodeParam::kVec2, QVector2D(1.0f, 1.0f)); + scale_input_ = new NodeInput(this, QStringLiteral("scale_in"), NodeValue::kVec2, QVector2D(1.0f, 1.0f)); scale_input_->setProperty("min", QVector2D(0, 0)); scale_input_->setProperty("view", QStringLiteral("percent")); scale_input_->setProperty("disabley", true); - AddInput(scale_input_); - uniform_scale_input_ = new NodeInput("uniform_scale_in", NodeParam::kBoolean, true); - uniform_scale_input_->set_is_keyframable(false); - uniform_scale_input_->set_connectable(false); + uniform_scale_input_ = new NodeInput(this, QStringLiteral("uniform_scale_in"), NodeValue::kBoolean, true); + uniform_scale_input_->SetKeyframable(false); + uniform_scale_input_->SetConnectable(false); connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &MatrixGenerator::UniformScaleChanged); - AddInput(uniform_scale_input_); - anchor_input_ = new NodeInput("anchor_in", NodeParam::kVec2, QVector2D()); - AddInput(anchor_input_); + anchor_input_ = new NodeInput(this, QStringLiteral("anchor_in"), NodeValue::kVec2, QVector2D()); } Node *MatrixGenerator::copy() const @@ -93,7 +88,7 @@ NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const // Push matrix output QMatrix4x4 mat = GenerateMatrix(value, true, false, false, false); NodeValueTable output = value.Merge(); - output.Push(NodeParam::kMatrix, mat, this); + output.Push(NodeValue::kMatrix, mat, this); return output; } @@ -106,47 +101,47 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool take, if (!ignore_anchor) { if (take) { // Take and store - anchor = value[anchor_input_].Take(NodeParam::kVec2).value(); + anchor = value[anchor_input_].Take(NodeValue::kVec2).value(); } else { // Get and store - anchor = value[anchor_input_].Get(NodeParam::kVec2).value(); + anchor = value[anchor_input_].Get(NodeValue::kVec2).value(); } } else if (take) { // Just take - value[anchor_input_].Take(NodeParam::kVec2).value(); + value[anchor_input_].Take(NodeValue::kVec2).value(); } if (!ignore_scale) { if (take) { - scale = value[scale_input_].Take(NodeParam::kVec2).value(); + scale = value[scale_input_].Take(NodeValue::kVec2).value(); } else { - scale = value[scale_input_].Get(NodeParam::kVec2).value(); + scale = value[scale_input_].Get(NodeValue::kVec2).value(); } } else if (take) { - value[scale_input_].Take(NodeParam::kVec2).value(); + value[scale_input_].Take(NodeValue::kVec2).value(); } if (!ignore_position) { if (take) { - position = value[position_input_].Take(NodeParam::kVec2).value(); + position = value[position_input_].Take(NodeValue::kVec2).value(); } else { - position = value[position_input_].Get(NodeParam::kVec2).value(); + position = value[position_input_].Get(NodeValue::kVec2).value(); } } else if (take) { - value[position_input_].Take(NodeParam::kVec2).value(); + value[position_input_].Take(NodeValue::kVec2).value(); } if (take) { return GenerateMatrix(position, - value[rotation_input_].Take(NodeParam::kFloat).toFloat(), + value[rotation_input_].Take(NodeValue::kFloat).toFloat(), scale, - value[uniform_scale_input_].Take(NodeParam::kBoolean).toBool(), + value[uniform_scale_input_].Take(NodeValue::kBoolean).toBool(), anchor); } else { return GenerateMatrix(position, - value[rotation_input_].Get(NodeParam::kFloat).toFloat(), + value[rotation_input_].Get(NodeValue::kFloat).toFloat(), scale, - value[uniform_scale_input_].Get(NodeParam::kBoolean).toBool(), + value[uniform_scale_input_].Get(NodeValue::kBoolean).toBool(), anchor); } @@ -183,7 +178,7 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos, void MatrixGenerator::UniformScaleChanged() { - scale_input_->setProperty("disabley", uniform_scale_input_->get_standard_value().toBool()); + scale_input_->setProperty("disabley", uniform_scale_input_->GetStandardValue().toBool()); } } diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index aa1afee4c..ea8f46ca5 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -27,31 +27,23 @@ namespace olive { PolygonGenerator::PolygonGenerator() { - points_input_ = new NodeInputArray("points_in", NodeParam::kVec2); - AddInput(points_input_); + points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2); + points_input_->SetIsArray(true); - color_input_ = new NodeInput("color_in", NodeParam::kColor); - AddInput(color_input_); + color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0))); - // Default to "a color" that isn't - color_input_->set_standard_value(1.0, 0); - color_input_->set_standard_value(1.0, 1); - color_input_->set_standard_value(1.0, 2); - color_input_->set_standard_value(1.0, 3); - - // FIXME: Test code - points_input_->SetSize(5); - points_input_->At(0)->set_standard_value(960, 0); - points_input_->At(0)->set_standard_value(240, 1); - points_input_->At(1)->set_standard_value(640, 0); - points_input_->At(1)->set_standard_value(480, 1); - points_input_->At(2)->set_standard_value(760, 0); - points_input_->At(2)->set_standard_value(800, 1); - points_input_->At(3)->set_standard_value(1100, 0); - points_input_->At(3)->set_standard_value(800, 1); - points_input_->At(4)->set_standard_value(1280, 0); - points_input_->At(4)->set_standard_value(480, 1); - // End test + // The Default Pentagon(tm) + points_input_->ArrayResize(5); + points_input_->SetStandardValueOnTrack(960, 0, 0); + points_input_->SetStandardValueOnTrack(240, 1, 0); + points_input_->SetStandardValueOnTrack(640, 0, 1); + points_input_->SetStandardValueOnTrack(480, 1, 1); + points_input_->SetStandardValueOnTrack(760, 0, 2); + points_input_->SetStandardValueOnTrack(800, 1, 2); + points_input_->SetStandardValueOnTrack(1100, 0, 3); + points_input_->SetStandardValueOnTrack(800, 1, 3); + points_input_->SetStandardValueOnTrack(1280, 0, 4); + points_input_->SetStandardValueOnTrack(480, 1, 4); } Node *PolygonGenerator::copy() const @@ -98,11 +90,11 @@ NodeValueTable PolygonGenerator::Value(NodeValueDatabase &value) const job.InsertValue(points_input_, value); job.InsertValue(color_input_, value); - job.InsertValue(QStringLiteral("resolution_in"), ShaderValue(value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")), NodeParam::kVec2)); + job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution"))); job.SetAlphaChannelRequired(true); NodeValueTable table = value.Merge(); - table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this); + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); return table; } @@ -175,10 +167,12 @@ void PolygonGenerator::GizmoRelease() QVector PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const { - QVector points(points_input_->GetSize()); + // FIXME: Should Get() use a `kArray` type instead of a `kVec2` type? + QVector array_tbl = db[points_input_].Get(NodeValue::kVec2).value< QVector >(); + QVector points(array_tbl.size()); - for (int i=0;iGetSize();i++) { - QVector2D v = db[points_input_->At(i)].Get(NodeParam::kVec2).value(); + for (int i=0;iArraySize();i++) { + QVector2D v = array_tbl.at(i).Get(NodeValue::kVec2).value(); v *= scale; diff --git a/app/node/generator/polygon/polygon.h b/app/node/generator/polygon/polygon.h index e14519d58..6ab1fbc7e 100644 --- a/app/node/generator/polygon/polygon.h +++ b/app/node/generator/polygon/polygon.h @@ -56,7 +56,7 @@ private: QVector GetGizmoRects(const QVector& points) const; - NodeInputArray* points_input_; + NodeInput* points_input_; NodeInput* color_input_; diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index c722b910a..11afc2ce2 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -27,10 +27,10 @@ namespace olive { SolidGenerator::SolidGenerator() { // Default to a color that isn't black - color_input_ = new NodeInput("color_in", - NodeInput::kColor, + color_input_ = new NodeInput(this, + QStringLiteral("color_in"), + NodeValue::kColor, QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f))); - AddInput(color_input_); } Node *SolidGenerator::copy() const @@ -69,7 +69,7 @@ NodeValueTable SolidGenerator::Value(NodeValueDatabase &value) const job.InsertValue(color_input_, value); NodeValueTable table = value.Merge(); - table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this); + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); return table; } diff --git a/app/node/generator/text/text.cpp b/app/node/generator/text/text.cpp index c221f709f..55746b5cb 100644 --- a/app/node/generator/text/text.cpp +++ b/app/node/generator/text/text.cpp @@ -32,29 +32,29 @@ enum TextVerticalAlign { TextGenerator::TextGenerator() { - text_input_ = new NodeInput(QStringLiteral("text_in"), - NodeParam::kText, + text_input_ = new NodeInput(this, + QStringLiteral("text_in"), + NodeValue::kText, tr("Sample Text")); - AddInput(text_input_); - color_input_ = new NodeInput(QStringLiteral("color_in"), - NodeParam::kColor, + color_input_ = new NodeInput(this, + QStringLiteral("color_in"), + NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0))); - AddInput(color_input_); - valign_input_ = new NodeInput(QStringLiteral("valign_in"), - NodeParam::kCombo, + valign_input_ = new NodeInput(this, + QStringLiteral("valign_in"), + NodeValue::kCombo, 1); - AddInput(valign_input_); - font_input_ = new NodeInput(QStringLiteral("font_in"), - NodeParam::kFont); - AddInput(font_input_); + font_input_ = new NodeInput(this, + QStringLiteral("font_in"), + NodeValue::kFont); - font_size_input_ = new NodeInput(QStringLiteral("font_size_in"), - NodeParam::kFloat, + font_size_input_ = new NodeInput(this, + QStringLiteral("font_size_in"), + NodeValue::kFloat, 72.0f); - AddInput(font_size_input_); } Node *TextGenerator::copy() const @@ -104,8 +104,8 @@ NodeValueTable TextGenerator::Value(NodeValueDatabase &value) const NodeValueTable table = value.Merge(); - if (!job.GetValue(text_input_).data.toString().isEmpty()) { - table.Push(NodeParam::kGenerateJob, QVariant::fromValue(job), this); + if (!job.GetValue(text_input_).data().toString().isEmpty()) { + table.Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this); } return table; @@ -124,14 +124,14 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const // Set default font QFont default_font; - default_font.setFamily(job.GetValue(font_input_).data.toString()); - default_font.setPointSizeF(job.GetValue(font_size_input_).data.toFloat()); + default_font.setFamily(job.GetValue(font_input_).data().toString()); + default_font.setPointSizeF(job.GetValue(font_size_input_).data().toFloat()); text_doc.setDefaultFont(default_font); // Center by default text_doc.setDefaultTextOption(QTextOption(Qt::AlignCenter)); - text_doc.setHtml(job.GetValue(text_input_).data.toString()); + text_doc.setHtml(job.GetValue(text_input_).data().toString()); // Align to 80% width because that's considered the "title safe" area int tenth_of_width = frame->video_params().width() / 10; @@ -144,7 +144,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const // Push 10% inwards to compensate for title safe area p.translate(tenth_of_width, 0); - TextVerticalAlign valign = static_cast(job.GetValue(valign_input_).data.toInt()); + TextVerticalAlign valign = static_cast(job.GetValue(valign_input_).data().toInt()); int doc_height = text_doc.size().height(); switch (valign) { @@ -165,7 +165,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const text_doc.drawContents(&p); // Transplant alpha channel to frame - Color rgb = job.GetValue(color_input_).data.value(); + Color rgb = job.GetValue(color_input_).data().value(); for (int x=0; xwidth(); x++) { for (int y=0; yheight(); y++) { uchar src_alpha = img.bits()[img.bytesPerLine() * y + x]; diff --git a/app/node/graph.cpp b/app/node/graph.cpp index 37b674d64..d855917d2 100644 --- a/app/node/graph.cpp +++ b/app/node/graph.cpp @@ -22,8 +22,7 @@ namespace olive { -NodeGraph::NodeGraph() : - operation_stack_(0) +NodeGraph::NodeGraph() { } @@ -35,133 +34,25 @@ void NodeGraph::Clear() node_children_.clear(); } -void NodeGraph::AddNode(Node *node) +void NodeGraph::childEvent(QChildEvent *event) { - if (ContainsNode(node)) { - return; - } + Item::childEvent(event); - node->setParent(this); + Node* node = dynamic_cast(event->child()); - connect(node, &Node::EdgeAdded, this, &NodeGraph::SignalEdgeAdded); - connect(node, &Node::EdgeRemoved, this, &NodeGraph::SignalEdgeRemoved); + if (node) { + if (event->type() == QEvent::ChildAdded) { - node_children_.append(node); + node_children_.append(node); + emit NodeAdded(node); - emit NodeAdded(node); -} + } else if (event->type() == QEvent::ChildRemoved) { -void NodeGraph::BeginOperation() -{ - operation_stack_++; -} + node_children_.removeOne(node); + emit NodeRemoved(node); -void NodeGraph::EndOperation() -{ - operation_stack_--; - - if (!operation_stack_) { - // Signal everything that we cached during the operation - - // First, signal the removed edges - foreach (NodeEdgePtr e, cached_removed_edges_) { - emit EdgeRemoved(e); } - cached_removed_edges_.clear(); - - // Next, signal the removed nodes - foreach (Node* n, cached_removed_nodes_) { - emit NodeRemoved(n); - } - cached_removed_nodes_.clear(); - - // Next, signal the added nodes - foreach (Node* n, cached_added_nodes_) { - emit NodeAdded(n); - } - cached_added_nodes_.clear(); - - // Finally, signal the added edges - foreach (NodeEdgePtr e, cached_added_edges_) { - emit EdgeAdded(e); - } - cached_added_edges_.clear(); } } -void NodeGraph::SignalNodeAdded(Node* node) -{ - if (!operation_stack_) { - emit NodeAdded(node); - } else if (!cached_removed_nodes_.removeOne(node)) { - // If we already removed this node during the operation (appending a signal to - // cached_removed_nodes_), we just remove that instead of appending a new signal. However if we - // didn't (removeOne returning false), only then do we append an add signal - cached_added_nodes_.append(node); - } -} - -void NodeGraph::SignalNodeRemoved(Node *node) -{ - if (!operation_stack_) { - emit NodeRemoved(node); - } else if (!cached_added_nodes_.removeOne(node)) { - // See SignalNodeAdded() for explanation of this - cached_removed_nodes_.append(node); - } -} - -void NodeGraph::SignalEdgeAdded(NodeEdgePtr edge) -{ - if (!operation_stack_) { - emit EdgeAdded(edge); - } else if (!cached_removed_edges_.removeOne(edge)) { - // See SignalNodeAdded() for explanation of this - cached_added_edges_.append(edge); - } -} - -void NodeGraph::SignalEdgeRemoved(NodeEdgePtr edge) -{ - if (!operation_stack_) { - emit EdgeRemoved(edge); - } else if (!cached_added_edges_.removeOne(edge)) { - // See SignalNodeAdded() for explanation of this - cached_removed_edges_.append(edge); - } -} - -void NodeGraph::TakeNode(Node *node, QObject* new_parent) -{ - if (!ContainsNode(node)) { - return; - } - - if (!node->CanBeDeleted()) { - qWarning() << "Tried to delete a Node that's been flagged as not deletable"; - return; - } - - node->DisconnectAll(); - - disconnect(node, &Node::EdgeAdded, this, &NodeGraph::EdgeAdded); - disconnect(node, &Node::EdgeRemoved, this, &NodeGraph::EdgeRemoved); - - node->setParent(new_parent); - - node_children_.removeAll(node); - - emit NodeRemoved(node); -} - -const QList &NodeGraph::nodes() const -{ - return node_children_; -} - -bool NodeGraph::ContainsNode(Node *n) const -{ - return (n->parent() == this); -} - } diff --git a/app/node/graph.h b/app/node/graph.h index 36d431a13..7b51e944e 100644 --- a/app/node/graph.h +++ b/app/node/graph.h @@ -46,32 +46,13 @@ public: */ void Clear(); - /** - * @brief Add a node to this graph - * - * The node will get added to this graph. It is not automatically connected to anything, any connections will need to - * be made manually after the node is added. The graph takes ownership of the Node. - */ - void AddNode(Node* node); - - /** - * @brief Removes a Node from the graph BUT doesn't destroy it. Ownership is passed to `new_parent`. - */ - void TakeNode(Node* node, QObject* new_parent = nullptr); - /** * @brief Retrieve a complete list of the nodes belonging to this graph */ - const QList& nodes() const; - - /** - * @brief Returns whether a certain Node is in the graph or not - */ - bool ContainsNode(Node* n) const; - - void BeginOperation(); - - void EndOperation(); + const QVector& nodes() const + { + return node_children_; + } signals: /** @@ -84,31 +65,11 @@ signals: */ void NodeRemoved(Node* node); - /** - * @brief Signal emitted when a member node of this graph has been connected to another (creating an "edge") - */ - void EdgeAdded(NodeEdgePtr edge); - - /** - * @brief Signal emitted when a member node of this graph has been disconnected from another (removing an "edge") - */ - void EdgeRemoved(NodeEdgePtr edge); +protected: + virtual void childEvent(QChildEvent* event) override; private: - QList node_children_; - - int operation_stack_; - - QList cached_added_nodes_; - QList cached_removed_nodes_; - QList cached_added_edges_; - QList cached_removed_edges_; - -private slots: - void SignalNodeAdded(Node *node); - void SignalNodeRemoved(Node* node); - void SignalEdgeAdded(NodeEdgePtr edge); - void SignalEdgeRemoved(NodeEdgePtr edge); + QVector node_children_; }; diff --git a/app/node/input.cpp b/app/node/input.cpp index c49b85d63..2a66d27c8 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -20,187 +20,105 @@ #include "input.h" -#include -#include -#include -#include - #include "common/bezier.h" #include "common/lerp.h" -#include "common/tohex.h" #include "common/xmlutils.h" #include "node.h" -#include "output.h" -#include "inputarray.h" #include "project/item/footage/stream.h" -#include "render/color.h" + +#define super NodeConnectable namespace olive { -NodeInput::NodeInput(const QString& id, const DataType &type, const QVector &default_value) : - NodeParam(id) +NodeInput::NodeInput(Node* parent, const QString& id, NodeValue::Type type, const QVector &default_value) { - Init(type); - - if (!default_value.isEmpty()) { - SetDefaultValue(default_value); - } + Init(parent, id, type, default_value); } -NodeInput::NodeInput(const QString &id, const NodeParam::DataType &type, const QVariant &default_value) : - NodeParam(id) +NodeInput::NodeInput(Node *parent, const QString &id, NodeValue::Type type, const QVariant &default_value) { - Init(type); - SetDefaultValue(split_normal_value_into_track_values(default_value)); + Init(parent, id, type, NodeValue::split_normal_value_into_track_values(type, default_value)); } -NodeInput::NodeInput(const QString &id, const NodeParam::DataType &type) : - NodeParam(id) +NodeInput::NodeInput(Node* parent, const QString &id, NodeValue::Type type) { - Init(type); + Init(parent, id, type, QVector()); } -bool NodeInput::IsArray() const +NodeInput::~NodeInput() { - return false; + // Disconnect everything + DisconnectAll(); + + // Delete instances + delete primary_; + qDeleteAll(subinputs_); } -NodeParam::Type NodeInput::type() -{ - return kInput; -} - -QString NodeInput::name() +QString NodeInput::name() const { if (name_.isEmpty()) { return tr("Input"); } - return NodeParam::name(); + return name_; +} + +void NodeInput::DisconnectAll() +{ + auto copied_edges = edges(); + + for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { + DisconnectEdge(it.value(), this, it.key()); + } } void NodeInput::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) { - { - XMLAttributeLoop(reader, attr) { - if (cancelled && *cancelled) { - return; - } - - if (attr.name() == QStringLiteral("keyframing")) { - set_is_keyframing(attr.value().toInt()); - } - } - } - while (XMLReadNextStartElement(reader)) { if (cancelled && *cancelled) { return; } - if (reader->name() == QStringLiteral("standard")) { - // Load standard value - int val_index = 0; - - while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } - - if (reader->name() == QStringLiteral("value")) { - QString value_text = reader->readElementText(); - - if (value_text.isEmpty()) { - standard_value_.replace(val_index, QVariant()); - } else { - standard_value_.replace(val_index, StringToValue(value_text, xml_node_data.footage_connections)); - } - - val_index++; - } else { - reader->skipCurrentElement(); + if (reader->name() == QStringLiteral("primary")) { + // Load primary immediate + LoadImmediate(reader, -1, xml_node_data, cancelled); + } else if (reader->name() == QStringLiteral("subelements")) { + // Load subelements + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("count")) { + ArrayResize(attr.value().toInt()); } } - } else if (reader->name() == QStringLiteral("keyframes")) { - int track = 0; + + int element_counter = 0; while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } + if (reader->name() == QStringLiteral("element")) { + LoadImmediate(reader, element_counter, xml_node_data, cancelled); - if (reader->name() == QStringLiteral("track")) { - while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } - - if (reader->name() == QStringLiteral("key")) { - rational key_time; - NodeKeyframe::Type key_type; - QVariant key_value; - QPointF key_in_handle; - QPointF key_out_handle; - - XMLAttributeLoop(reader, attr) { - if (cancelled && *cancelled) { - return; - } - - if (attr.name() == QStringLiteral("time")) { - key_time = rational::fromString(attr.value().toString()); - } else if (attr.name() == QStringLiteral("type")) { - key_type = static_cast(attr.value().toInt()); - } else if (attr.name() == QStringLiteral("inhandlex")) { - key_in_handle.setX(attr.value().toDouble()); - } else if (attr.name() == QStringLiteral("inhandley")) { - key_in_handle.setY(attr.value().toDouble()); - } else if (attr.name() == QStringLiteral("outhandlex")) { - key_out_handle.setX(attr.value().toDouble()); - } else if (attr.name() == QStringLiteral("outhandley")) { - key_out_handle.setY(attr.value().toDouble()); - } - } - - key_value = StringToValue(reader->readElementText(), xml_node_data.footage_connections); - - NodeKeyframePtr key = NodeKeyframe::Create(key_time, key_value, key_type, track); - key->set_bezier_control_in(key_in_handle); - key->set_bezier_control_out(key_out_handle); - key->set_parent(this); - keyframe_tracks_[track].append(key); - } else { - reader->skipCurrentElement(); - } - } - - track++; + element_counter++; } else { reader->skipCurrentElement(); } } } else if (reader->name() == QStringLiteral("connections")) { + // Load connections while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } - if (reader->name() == QStringLiteral("connection")) { - xml_node_data.desired_connections.append({this, reader->readElementText().toULongLong()}); + int ele = -1; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("element")) { + ele = attr.value().toInt(); + } + } + + xml_node_data.desired_connections.append({this, ele, reader->readElementText().toULongLong()}); } else { reader->skipCurrentElement(); } } - } else if (reader->name() == QStringLiteral("csinput")) { - setProperty("col_input", reader->readElementText()); - } else if (reader->name() == QStringLiteral("csdisplay")) { - setProperty("col_display", reader->readElementText()); - } else if (reader->name() == QStringLiteral("csview")) { - setProperty("col_view", reader->readElementText()); - } else if (reader->name() == QStringLiteral("cslook")) { - setProperty("col_look", reader->readElementText()); - } else if (reader->name() == QStringLiteral("custom")) { - LoadInternal(reader, xml_node_data, cancelled); } else { reader->skipCurrentElement(); } @@ -209,14 +127,213 @@ void NodeInput::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const void NodeInput::Save(QXmlStreamWriter *writer) const { - writer->writeAttribute("id", id()); + writer->writeAttribute(QStringLiteral("id"), id()); - writer->writeAttribute("keyframing", QString::number(keyframing_)); + writer->writeStartElement(QStringLiteral("primary")); + + SaveImmediate(writer, -1); + + writer->writeEndElement(); // primary + + writer->writeStartElement(QStringLiteral("subelements")); + + writer->writeAttribute(QStringLiteral("count"), QString::number(array_size_)); + + for (int i=0; iwriteStartElement(QStringLiteral("element")); + + SaveImmediate(writer, i); + + writer->writeEndElement(); // element + } + + writer->writeEndElement(); // subelements + + writer->writeStartElement(QStringLiteral("connections")); + + for (auto it=input_connections().cbegin(); it!=input_connections().cend(); it++) { + writer->writeStartElement(QStringLiteral("connection")); + + writer->writeAttribute(QStringLiteral("element"), QString::number(it.key())); + + writer->writeCharacters(QString::number(reinterpret_cast(it.value()))); + + writer->writeEndElement(); // connection + } + + writer->writeEndElement(); // connections +} + +Node *NodeInput::parent() const +{ + return static_cast(QObject::parent()); +} + +bool NodeInput::event(QEvent *e) +{ + if (e->type() == QEvent::DynamicPropertyChange) { + QByteArray key = static_cast(e)->propertyName(); + emit PropertyChanged(key, property(key)); + return true; + } + + return QObject::event(e); +} + +void NodeInput::childEvent(QChildEvent *event) +{ + super::childEvent(event); + + NodeKeyframe* key = dynamic_cast(event->child()); + + if (key) { + if (event->type() == QEvent::ChildAdded) { + GetImmediate(key->element())->insert_keyframe(key); + + connect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged); + connect(key, &NodeKeyframe::ValueChanged, this, &NodeInput::KeyframeValueChanged); + connect(key, &NodeKeyframe::TypeChanged, this, &NodeInput::KeyframeTypeChanged); + connect(key, &NodeKeyframe::BezierControlInChanged, this, &NodeInput::KeyframeBezierInChanged); + connect(key, &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::KeyframeBezierOutChanged); + + emit KeyframeAdded(key); + emit ValueChanged(get_range_affected_by_keyframe(key), key->element()); + } else if (event->type() == QEvent::ChildRemoved) { + TimeRange time_affected = get_range_affected_by_keyframe(key); + + disconnect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged); + disconnect(key, &NodeKeyframe::ValueChanged, this, &NodeInput::KeyframeValueChanged); + disconnect(key, &NodeKeyframe::TypeChanged, this, &NodeInput::KeyframeTypeChanged); + disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &NodeInput::KeyframeBezierInChanged); + disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::KeyframeBezierOutChanged); + + GetImmediate(key->element())->remove_keyframe(key); + + emit KeyframeRemoved(key); + emit ValueChanged(time_affected, key->element()); + } + } +} + +void NodeInput::Init(Node* parent, const QString &id, NodeValue::Type type, const QVector& default_val) +{ + setParent(parent); + + id_ = id; + keyframable_ = true; + connectable_ = true; + default_value_ = default_val; + array_size_ = 0; + data_type_ = type; + + primary_ = new NodeInputImmediate(type, default_value_); +} + +void NodeInput::LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) +{ + if (reader->name() == QStringLiteral("standard")) { + // Load standard value + int val_index = 0; + + while (XMLReadNextStartElement(reader)) { + if (cancelled && *cancelled) { + return; + } + + if (reader->name() == QStringLiteral("value")) { + QString value_text = reader->readElementText(); + QVariant value_on_track; + + if (!value_text.isEmpty()) { + value_on_track = StringToValue(value_text, xml_node_data.footage_connections, element); + } + + SetStandardValueOnTrack(value_on_track, val_index, element); + + val_index++; + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("keyframing")) { + SetIsKeyframing(reader->readElementText().toInt(), element); + } else if (reader->name() == QStringLiteral("keyframes")) { + int track = 0; + + while (XMLReadNextStartElement(reader)) { + if (cancelled && *cancelled) { + return; + } + + if (reader->name() == QStringLiteral("track")) { + while (XMLReadNextStartElement(reader)) { + if (cancelled && *cancelled) { + return; + } + + if (reader->name() == QStringLiteral("key")) { + rational key_time; + NodeKeyframe::Type key_type; + QVariant key_value; + QPointF key_in_handle; + QPointF key_out_handle; + + XMLAttributeLoop(reader, attr) { + if (cancelled && *cancelled) { + return; + } + + if (attr.name() == QStringLiteral("time")) { + key_time = rational::fromString(attr.value().toString()); + } else if (attr.name() == QStringLiteral("type")) { + key_type = static_cast(attr.value().toInt()); + } else if (attr.name() == QStringLiteral("inhandlex")) { + key_in_handle.setX(attr.value().toDouble()); + } else if (attr.name() == QStringLiteral("inhandley")) { + key_in_handle.setY(attr.value().toDouble()); + } else if (attr.name() == QStringLiteral("outhandlex")) { + key_out_handle.setX(attr.value().toDouble()); + } else if (attr.name() == QStringLiteral("outhandley")) { + key_out_handle.setY(attr.value().toDouble()); + } + } + + key_value = StringToValue(reader->readElementText(), xml_node_data.footage_connections, element); + + NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, this); + key->set_bezier_control_in(key_in_handle); + key->set_bezier_control_out(key_out_handle); + } else { + reader->skipCurrentElement(); + } + } + + track++; + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("csinput")) { + setProperty("col_input", reader->readElementText()); + } else if (reader->name() == QStringLiteral("csdisplay")) { + setProperty("col_display", reader->readElementText()); + } else if (reader->name() == QStringLiteral("csview")) { + setProperty("col_view", reader->readElementText()); + } else if (reader->name() == QStringLiteral("cslook")) { + setProperty("col_look", reader->readElementText()); + } else { + reader->skipCurrentElement(); + } +} + +void NodeInput::SaveImmediate(QXmlStreamWriter* writer, int element) const +{ + writer->writeTextElement(QStringLiteral("keyframing"), QString::number(IsKeyframing(element))); // Write standard value writer->writeStartElement("standard"); - foreach (const QVariant& v, standard_value_) { + foreach (const QVariant& v, GetSplitStandardValue(element)) { writer->writeTextElement("value", ValueToString(v)); } @@ -225,10 +342,10 @@ void NodeInput::Save(QXmlStreamWriter *writer) const // Write keyframes writer->writeStartElement("keyframes"); - foreach (const KeyframeTrack& track, keyframe_tracks()) { + foreach (const NodeKeyframeTrack& track, GetKeyframeTracks(element)) { writer->writeStartElement("track"); - foreach (NodeKeyframePtr key, track) { + foreach (NodeKeyframe* key, track) { writer->writeStartElement("key"); writer->writeAttribute("time", key->time().toString()); @@ -248,221 +365,34 @@ void NodeInput::Save(QXmlStreamWriter *writer) const writer->writeEndElement(); // keyframes - if (data_type_ == NodeParam::kColor) { + if (data_type_ == NodeValue::kColor) { // Save color management information writer->writeTextElement(QStringLiteral("csinput"), property("col_input").toString()); writer->writeTextElement(QStringLiteral("csdisplay"), property("col_display").toString()); writer->writeTextElement(QStringLiteral("csview"), property("col_view").toString()); writer->writeTextElement(QStringLiteral("cslook"), property("col_look").toString()); } - - SaveConnections(writer); - - writer->writeStartElement(QStringLiteral("custom")); - SaveInternal(writer); - writer->writeEndElement(); // custom -} - -void NodeInput::SaveConnections(QXmlStreamWriter *writer) const -{ - writer->writeStartElement("connections"); - - foreach (NodeEdgePtr edge, edges_) { - writer->writeTextElement("connection", - QString::number(reinterpret_cast(edge->output()))); - } - - writer->writeEndElement(); // connections -} - -const NodeParam::DataType &NodeInput::data_type() const -{ - return data_type_; -} - -void NodeInput::LoadInternal(QXmlStreamReader* reader, XMLNodeData &, const QAtomicInt*) -{ - reader->skipCurrentElement(); -} - -void NodeInput::SaveInternal(QXmlStreamWriter*) const -{ -} - -bool NodeInput::event(QEvent *e) -{ - if (e->type() == QEvent::DynamicPropertyChange) { - QByteArray key = static_cast(e)->propertyName(); - emit PropertyChanged(key, property(key)); - return true; - } - - return QObject::event(e); -} - -void NodeInput::Init(DataType type) -{ - keyframable_ = true; - keyframing_ = false; - data_type_ = type; - - int track_size; - - switch (data_type_) { - case kVec2: - track_size = 2; - break; - case kVec3: - track_size = 3; - break; - case kVec4: - case kColor: - track_size = 4; - break; - default: - track_size = 1; - } - - keyframe_tracks_.resize(track_size); - standard_value_.resize(track_size); -} - -void NodeInput::SetDefaultValue(const QVector &default_value) -{ - default_value_ = default_value; - - for (int i=0;i(); - - return QStringLiteral("%1:%2").arg(QString::number(vec.x()), - QString::number(vec.y())); - } else if (!value_is_a_key_track && data_type == kVec3) { - QVector3D vec = value.value(); - - return QStringLiteral("%1:%2:%3").arg(QString::number(vec.x()), - QString::number(vec.y()), - QString::number(vec.z())); - } else if (!value_is_a_key_track && data_type == kVec4) { - QVector4D vec = value.value(); - - return QStringLiteral("%1:%2:%3:%4").arg(QString::number(vec.x()), - QString::number(vec.y()), - QString::number(vec.z()), - QString::number(vec.w())); - } else if (!value_is_a_key_track && data_type == kColor) { - Color c = value.value(); - - return QStringLiteral("%1:%2:%3:%4").arg(QString::number(c.red()), - QString::number(c.green()), - QString::number(c.blue()), - QString::number(c.alpha())); - } else if (data_type == kRational) { - return value.value().toString(); - } else if (data_type == kFootage) { - return QString::number(value.value()); - } else if (data_type == kTexture - || data_type == kSamples - || data_type == kBuffer) { - // These data types need no XML representation - return QString(); - } else if (data_type == kInt) { - return QString::number(value.value()); - } else { - if (value.canConvert()) { - return value.toString(); - } - - if (!value.isNull()) { - qWarning() << "Failed to convert type" << ToHex(data_type) << "to string"; - } - - return QString(); - } -} - -void NodeInput::ValidateVectorString(QStringList* list, int count) -{ - while (list->size() < count) { - list->append(QStringLiteral("0")); - } -} - -QVariant NodeInput::StringToValue(const DataType& data_type, const QString &string, bool value_is_a_key_track) -{ - if (!value_is_a_key_track && data_type == kVec2) { - QStringList vals = string.split(':'); - - ValidateVectorString(&vals, 2); - - return QVector2D(vals.at(0).toFloat(), vals.at(1).toFloat()); - } else if (!value_is_a_key_track && data_type == kVec3) { - QStringList vals = string.split(':'); - - ValidateVectorString(&vals, 3); - - return QVector3D(vals.at(0).toFloat(), vals.at(1).toFloat(), vals.at(2).toFloat()); - } else if (!value_is_a_key_track && data_type == kVec4) { - QStringList vals = string.split(':'); - - ValidateVectorString(&vals, 4); - - return QVector4D(vals.at(0).toFloat(), vals.at(1).toFloat(), vals.at(2).toFloat(), vals.at(3).toFloat()); - } else if (!value_is_a_key_track && data_type == kColor) { - QStringList vals = string.split(':'); - - 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 (data_type == kInt) { - return QVariant::fromValue(string.toLongLong()); - } else if (data_type == kRational) { - return QVariant::fromValue(rational::fromString(string)); - } else { - return string; - } } void NodeInput::GetDependencies(QVector &list, bool traverse, bool exclusive_only) const { - if (is_connected() - && (get_connected_output()->edges().size() == 1 || !exclusive_only)) { - Node* connected = get_connected_node(); + for (auto it=input_connections().cbegin(); it!=input_connections().cend(); it++) { + if (it.value()->edges().size() == 1 || !exclusive_only) { + Node* connected = it.value(); - if (!list.contains(connected)) { - list.append(connected); + if (!list.contains(connected)) { + list.append(connected); - if (traverse) { - QVector connected_inputs = connected->GetInputsIncludingArrays(); - - foreach (NodeInput* i, connected_inputs) { - i->GetDependencies(list, traverse, exclusive_only); + if (traverse) { + foreach (NodeInput* i, connected->parameters()) { + i->GetDependencies(list, traverse, exclusive_only); + } } } } } } -QVariant NodeInput::GetDefaultValue() const -{ - if (default_value_.isEmpty()) { - return QVariant(); - } - - return combine_track_values_into_normal_value(default_value_); -} - QVariant NodeInput::GetDefaultValueForTrack(int track) const { if (default_value_.isEmpty()) { @@ -491,68 +421,105 @@ QVector NodeInput::GetImmediateDependencies() const return GetDependencies(false, false); } -QVariant NodeInput::StringToValue(const QString &string, QList& footage_connections) +void NodeInput::ArrayInsert(int index) { - if (data_type_ == NodeParam::kFootage) { - footage_connections.append({this, string.toULongLong()}); + // Prepend new input + subinputs_.insert(index, new NodeInputImmediate(GetDataType(), default_value_)); + + // Move connections down + QHash copied_edges = edges(); + for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { + if (it.key() >= index) { + // Disconnect this and reconnect it one element down + DisconnectEdge(it.value(), this, it.key()); + ConnectEdge(it.value(), this, it.key() + 1); + } } - - return StringToValue(data_type_, string, true); } -NodeOutput *NodeInput::get_connected_output() const +void NodeInput::ArrayRemove(int index) { - if (!edges_.isEmpty()) { - return edges_.first()->output(); + // Remove subinput here + delete subinputs_.at(index); + subinputs_.removeAt(index); + + // Move connections up + QHash copied_edges = edges(); + for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { + if (it.key() >= index) { + // Disconnect this and reconnect it one element up if it's not the element being removed + DisconnectEdge(it.value(), this, it.key()); + + if (it.key() > index) { + ConnectEdge(it.value(), this, it.key() - 1); + } + } } - - return nullptr; } -Node *NodeInput::get_connected_node() const +void NodeInput::ArrayPrepend() { - NodeOutput* output = get_connected_output(); + ArrayInsert(0); +} - if (output) { - return output->parentNode(); +void NodeInput::ArrayResize(int size) +{ + if (array_size_ != size) { + if (array_size_ > size) { + // Decreasing in size, disconnect any extraneous edges + for (int i=size; i NodeInput::get_split_values_at_time(const rational &time) const +QVector NodeInput::GetSplitValuesAtTime(const rational &time, int element) const { QVector vals; - for (int i=0;itime() >= time) { // This time precedes any keyframe, so we just return the first value @@ -566,11 +533,11 @@ QVariant NodeInput::get_value_at_time_for_track(const rational& time, int track) // If we're here, the time must be somewhere in between the keyframes for (int i=0;itime() == time - || !type_can_be_interpolated(data_type()) + || !NodeValue::type_can_be_interpolated(data_type_) || (before->time() < time && before->type() == NodeKeyframe::kHold)) { // Time == keyframe time, so value is precise @@ -636,332 +603,166 @@ QVariant NodeInput::get_value_at_time_for_track(const rational& time, int track) } } - return standard_value_.at(track); + return GetStandardValueOnTrack(track, element); } -QList NodeInput::get_keyframe_at_time(const rational &time) const +bool NodeInput::IsKeyframable() const { - QList keys; + return keyframable_; +} - for (int i=0;iid() == dest->id()); + + CopyValuesOfElement(source, dest, -1); + + // Copy array size + dest->ArrayResize(source->ArraySize()); + + // If enabled, copy arrays too + if (traverse_arrays) { + for (int i=0; iArraySize(); i++) { + CopyValuesOfElement(source, dest, i); } } - return keys; -} - -NodeKeyframePtr NodeInput::get_keyframe_at_time_on_track(const rational &time, int track) const -{ - if (!is_using_standard_value(track)) { - foreach (NodeKeyframePtr key, keyframe_tracks_.at(track)) { - if (key->time() == time) { - return key; + // Copy connections + if (include_connections) { + if (traverse_arrays) { + // Copy all connections + for (auto it=source->input_connections().cbegin(); it!=source->input_connections().cend(); it++) { + ConnectEdge(it.value(), dest, it.key()); + } + } else { + // Just copy the primary connection (at -1) + if (source->IsConnected()) { + ConnectEdge(source->GetConnectedNode(), dest); } } } - - return nullptr; } -NodeKeyframePtr NodeInput::get_closest_keyframe_to_time_on_track(const rational &time, int track) const +void NodeInput::CopyValuesOfElement(NodeInput *src, NodeInput *dst, int element) { - if (is_using_standard_value(track)) { - return nullptr; - } + // Copy standard value + dst->SetSplitStandardValue(src->GetSplitStandardValue(element), element); - const KeyframeTrack& key_track = keyframe_tracks_.at(track); - - if (time <= key_track.first()->time()) { - return key_track.first(); - } - - if (time >= key_track.last()->time()) { - return key_track.last(); - } - - for (int i=1;itime() <= time && next_key->time() >= time) { - // Return whichever is closer - rational prev_diff = time - prev_key->time(); - rational next_diff = next_key->time() - time; - - if (next_diff < prev_diff) { - return next_key; - } else { - return prev_key; - } + // Copy keyframes + foreach (const NodeKeyframeTrack& track, src->GetImmediate(element)->keyframe_tracks()) { + foreach (NodeKeyframe* key, track) { + key->copy(dst); } } - return nullptr; -} - -NodeKeyframePtr NodeInput::get_closest_keyframe_before_time(const rational &time) const -{ - NodeKeyframePtr key = nullptr; - - foreach (const KeyframeTrack& track, keyframe_tracks_) { - foreach (NodeKeyframePtr k, track) { - if (k->time() >= time) { - break; - } else if (!key || k->time() > key->time()) { - key = k; - } - } + // Copy keyframing state + if (src->IsKeyframable()) { + dst->SetIsKeyframing(src->IsKeyframing(element), element); } - return key; + emit dst->ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX), element); } -NodeKeyframePtr NodeInput::get_closest_keyframe_after_time(const rational &time) const +QStringList NodeInput::get_combobox_strings() const { - NodeKeyframePtr key = nullptr; - - foreach (const KeyframeTrack& track, keyframe_tracks_) { - for (int i=track.size()-1;i>=0;i--) { - NodeKeyframePtr k = track.at(i); - - if (k->time() <= time) { - break; - } else if (!key || k->time() < key->time()) { - key = k; - } - } - } - - return key; + return property("combo_str").toStringList(); } -NodeKeyframe::Type NodeInput::get_best_keyframe_type_for_time(const rational &time, int track) const +void NodeInput::set_combobox_strings(const QStringList &strings) { - NodeKeyframePtr closest_key = get_closest_keyframe_to_time_on_track(time, track); - - if (closest_key) { - return closest_key->type(); - } - - return NodeKeyframe::kDefaultType; -} - -int NodeInput::get_number_of_keyframe_tracks() const -{ - return keyframe_tracks_.size(); -} - -NodeKeyframePtr NodeInput::get_earliest_keyframe() const -{ - NodeKeyframePtr earliest = nullptr; - - foreach (const KeyframeTrack& track, keyframe_tracks_) { - if (!track.isEmpty()) { - NodeKeyframePtr earliest_in_track = track.first(); - - if (!earliest - || earliest_in_track->time() < earliest->time()) { - earliest = earliest_in_track; - } - } - } - - return earliest; -} - -NodeKeyframePtr NodeInput::get_latest_keyframe() const -{ - NodeKeyframePtr latest = nullptr; - - foreach (const KeyframeTrack& track, keyframe_tracks_) { - if (!track.isEmpty()) { - NodeKeyframePtr latest_in_track = track.last(); - - if (!latest - || latest_in_track->time() > latest->time()) { - latest = latest_in_track; - } - } - } - - return latest; -} - -void NodeInput::insert_keyframe(NodeKeyframePtr key) -{ - Q_ASSERT(is_keyframable()); - - insert_keyframe_internal(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); - - emit_range_affected_by_keyframe(key.get()); -} - -void NodeInput::remove_keyframe(NodeKeyframePtr key) -{ - Q_ASSERT(is_keyframable()); - - TimeRange time_affected = get_range_affected_by_keyframe(key.get()); - - 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); - - keyframe_tracks_[key->track()].removeOne(key); - key->set_parent(nullptr); - - emit KeyframeRemoved(key); - emit_time_range(time_affected); -} - -NodeKeyframePtr NodeInput::get_keyframe_shared_ptr_from_raw(NodeKeyframe* raw) const -{ - foreach (const KeyframeTrack& track, keyframe_tracks_) { - foreach (NodeKeyframePtr key, track) { - if (key.get() == raw) { - return key; - } - } - } - - return nullptr; -} - -void NodeInput::KeyframeTimeChanged() -{ - NodeKeyframe* key = static_cast(sender()); - int keyframe_index = FindIndexOfKeyframeFromRawPtr(key); - - Q_ASSERT(keyframe_index > -1); - - TimeRange original_range = get_range_around_index(keyframe_index, key->track()); - - if (!(original_range.in() < key->time() && original_range.out() > key->time())) { - // This keyframe needs resorting, store it and remove it from the list - NodeKeyframePtr key_shared_ptr = keyframe_tracks_.at(key->track()).at(keyframe_index); - - keyframe_tracks_[key->track()].removeAt(keyframe_index); - - // Automatically insertion sort - insert_keyframe_internal(key_shared_ptr); - - // Invalidate new area that the keyframe has been moved to - emit_time_range(get_range_around_index(FindIndexOfKeyframeFromRawPtr(key), key->track())); - } - - // Invalidate entire area surrounding the keyframe (either where it currently is, or where it used to be before it - // was resorted in the if block above) - emit_time_range(original_range); -} - -void NodeInput::KeyframeValueChanged() -{ - emit_range_affected_by_keyframe(static_cast(sender())); -} - -void NodeInput::KeyframeTypeChanged() -{ - NodeKeyframe* key = static_cast(sender()); - int keyframe_index = FindIndexOfKeyframeFromRawPtr(key); - - if (keyframe_tracks_.at(key->track()).size() == 1) { - // If there are no other frames, the interpolation won't do anything - return; - } - - // Invalidate entire range - emit_time_range(get_range_around_index(keyframe_index, key->track())); + setProperty("combo_str", strings); } void NodeInput::KeyframeBezierInChanged() { NodeKeyframe* key = static_cast(sender()); - int keyframe_index = FindIndexOfKeyframeFromRawPtr(key); + const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); + int keyframe_index = track.indexOf(key); rational start = RATIONAL_MIN; rational end = key->time(); if (keyframe_index > 0) { - start = keyframe_tracks_.at(key->track()).at(keyframe_index - 1)->time(); + start = track.at(keyframe_index - 1)->time(); } - emit ValueChanged(TimeRange(start, end)); + emit ValueChanged(TimeRange(start, end), key->element()); } void NodeInput::KeyframeBezierOutChanged() { NodeKeyframe* key = static_cast(sender()); - int keyframe_index = FindIndexOfKeyframeFromRawPtr(key); + const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); + int keyframe_index = track.indexOf(key); rational start = key->time(); rational end = RATIONAL_MAX; - if (keyframe_index < keyframe_tracks_.at(key->track()).size() - 1) { - end = keyframe_tracks_.at(key->track()).at(keyframe_index + 1)->time(); + if (keyframe_index < track.size() - 1) { + end = track.at(keyframe_index + 1)->time(); } - emit ValueChanged(TimeRange(start, end)); + emit ValueChanged(TimeRange(start, end), key->element()); } -int NodeInput::FindIndexOfKeyframeFromRawPtr(NodeKeyframe *raw_ptr) const +void NodeInput::KeyframeTimeChanged() { - const KeyframeTrack& track = keyframe_tracks_.at(raw_ptr->track()); + NodeKeyframe* key = static_cast(sender()); + NodeInputImmediate* immediate = GetImmediate(key->element()); + TimeRange original_range = get_range_affected_by_keyframe(key); - for (int i=0;itime() && original_range.out() > key->time())) { + // This keyframe needs resorting, store it and remove it from the list + immediate->remove_keyframe(key); + + // Automatically insertion sort + immediate->insert_keyframe(key); + + // Invalidate new area that the keyframe has been moved to + invalidate_range.insert(get_range_affected_by_keyframe(key)); } - return -1; + // Invalidate entire area surrounding the keyframe (either where it currently is, or where it used to be before it + // was resorted in the if block above) + foreach (const TimeRange& r, invalidate_range) { + emit ValueChanged(r, key->element()); + } } -void NodeInput::insert_keyframe_internal(NodeKeyframePtr key) +void NodeInput::KeyframeValueChanged() { - KeyframeTrack& key_track = keyframe_tracks_[key->track()]; + NodeKeyframe* key = static_cast(sender()); + emit ValueChanged(get_range_affected_by_keyframe(key), key->element()); +} - key->set_parent(this); +void NodeInput::KeyframeTypeChanged() +{ + NodeKeyframe* key = static_cast(sender()); + const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); - for (int i=0;itime() != key->time()); - - if (compare->time() > key->time()) { - key_track.insert(i, key); - return; - } + if (track.size() == 1) { + // If there are no other frames, the interpolation won't do anything + return; } - key_track.append(key); -} - -bool NodeInput::is_using_standard_value(int track) const -{ - return (!is_keyframing() || keyframe_tracks_.at(track).isEmpty()); + // Invalidate entire range + emit ValueChanged(get_range_around_index(track.indexOf(key), key->track(), key->element()), key->element()); } TimeRange NodeInput::get_range_affected_by_keyframe(NodeKeyframe *key) const { - int keyframe_index = FindIndexOfKeyframeFromRawPtr(key); + const NodeKeyframeTrack& key_track = GetTrackFromKeyframe(key); + int keyframe_index = key_track.indexOf(key); - TimeRange range = get_range_around_index(keyframe_index, key->track()); - - const KeyframeTrack& key_track = keyframe_tracks_.at(key->track()); + TimeRange range = get_range_around_index(keyframe_index, key->track(), key->element()); // If a previous key exists and it's a hold, we don't need to invalidate those frames if (key_track.size() > 1 @@ -973,12 +774,12 @@ TimeRange NodeInput::get_range_affected_by_keyframe(NodeKeyframe *key) const return range; } -TimeRange NodeInput::get_range_around_index(int index, int track) const +TimeRange NodeInput::get_range_around_index(int index, int track, int element) const { rational range_begin = RATIONAL_MIN; rational range_end = RATIONAL_MAX; - const KeyframeTrack& key_track = keyframe_tracks_.at(track); + const NodeKeyframeTrack& key_track = GetImmediate(element)->keyframe_tracks().at(track); if (key_track.size() > 1) { if (index > 0) { @@ -994,215 +795,18 @@ TimeRange NodeInput::get_range_around_index(int index, int track) const return TimeRange(range_begin, range_end); } -void NodeInput::emit_time_range(const TimeRange &range) +QString NodeInput::ValueToString(const QVariant &value) const { - emit ValueChanged(range); + return NodeValue::ValueToString(data_type_, value, true); } -void NodeInput::emit_range_affected_by_keyframe(NodeKeyframe *key) +QVariant NodeInput::StringToValue(const QString &string, QList& footage_connections, int element) { - emit_time_range(get_range_affected_by_keyframe(key)); -} - -bool NodeInput::has_keyframe_at_time(const rational &time) const -{ - if (!is_keyframing()) { - return false; + if (data_type_ == NodeValue::kFootage) { + footage_connections.append({this, element, string.toULongLong()}); } - // Loop through keyframes to see if any match - foreach (const KeyframeTrack& track, keyframe_tracks_) { - foreach (NodeKeyframePtr key, track) { - if (key->time() == time) { - return true; - } - } - } - - // None match - return false; -} - -bool NodeInput::is_keyframing() const -{ - return keyframing_; -} - -void NodeInput::set_is_keyframing(bool k) -{ - keyframing_ = k; - - emit KeyframeEnableChanged(keyframing_); -} - -bool NodeInput::is_keyframable() const -{ - return keyframable_; -} - -bool NodeInput::is_static() const -{ - return !(this->is_connected() || this->is_keyframing()); -} - -QVariant NodeInput::get_standard_value() const -{ - return combine_track_values_into_normal_value(standard_value_); -} - -const QVector &NodeInput::get_split_standard_value() const -{ - return standard_value_; -} - -void NodeInput::set_standard_value(const QVariant &value, int track) -{ - standard_value_.replace(track, value); - - if (is_using_standard_value(track)) { - // If this standard value is being used, we need to send a value changed signal - emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX)); - } -} - -const QVector &NodeInput::keyframe_tracks() const -{ - return keyframe_tracks_; -} - -void NodeInput::set_is_keyframable(bool k) -{ - keyframable_ = k; -} - -void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections, bool traverse_arrays) -{ - Q_ASSERT(source->id() == dest->id()); - - // Copy standard value - dest->standard_value_ = source->standard_value_; - - // Copy keyframes - for (int i=0;ikeyframe_tracks_.size();i++) { - dest->keyframe_tracks_[i].clear(); - foreach (NodeKeyframePtr key, source->keyframe_tracks_.at(i)) { - NodeKeyframePtr key_copy = key->copy(); - key_copy->set_parent(dest); - dest->keyframe_tracks_[i].append(key_copy); - } - } - - // Copy keyframing state - dest->set_is_keyframing(source->is_keyframing()); - - // Copy connections - if (include_connections && source->get_connected_output() != nullptr) { - ConnectEdge(source->get_connected_output(), dest); - } - - // If these inputs are an array, copy the subparams too - if (dest->IsArray()) { - NodeInputArray* src_array = static_cast(source); - NodeInputArray* dst_array = static_cast(dest); - - dst_array->SetSize(src_array->GetSize()); - - if (traverse_arrays) { - for (int i=0;iGetSize();i++) { - CopyValues(src_array->At(i), dst_array->At(i), include_connections); - } - } - } - - emit dest->ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX)); -} - -QVector NodeInput::split_normal_value_into_track_values(const QVariant &value) const -{ - QVector vals(get_number_of_keyframe_tracks()); - - switch (data_type_) { - case kVec2: - { - QVector2D vec = value.value(); - vals.replace(0, vec.x()); - vals.replace(1, vec.y()); - break; - } - case kVec3: - { - QVector3D vec = value.value(); - vals.replace(0, vec.x()); - vals.replace(1, vec.y()); - vals.replace(2, vec.z()); - break; - } - case kVec4: - { - QVector4D vec = value.value(); - vals.replace(0, vec.x()); - vals.replace(1, vec.y()); - vals.replace(2, vec.z()); - vals.replace(3, vec.w()); - break; - } - case kColor: - { - Color c = value.value(); - vals.replace(0, c.red()); - vals.replace(1, c.green()); - vals.replace(2, c.blue()); - vals.replace(3, c.alpha()); - break; - } - default: - vals.replace(0, value); - } - - return vals; -} - -QVariant NodeInput::combine_track_values_into_normal_value(const QVector &split) const -{ - switch (data_type_) { - case kVec2: - { - return QVector2D(split.at(0).toFloat(), - split.at(1).toFloat()); - } - case kVec3: - { - return QVector3D(split.at(0).toFloat(), - split.at(1).toFloat(), - split.at(2).toFloat()); - } - case kVec4: - { - return QVector4D(split.at(0).toFloat(), - split.at(1).toFloat(), - split.at(2).toFloat(), - split.at(3).toFloat()); - } - case kColor: - { - return QVariant::fromValue(Color(split.at(0).toFloat(), - split.at(1).toFloat(), - split.at(2).toFloat(), - split.at(3).toFloat())); - } - default: - return split.first(); - } -} - -QStringList NodeInput::get_combobox_strings() const -{ - return property("combo_str").toStringList(); -} - -void NodeInput::set_combobox_strings(const QStringList &strings) -{ - setProperty("combo_str", strings); + return NodeValue::StringToValue(data_type_, string, true); } } diff --git a/app/node/input.h b/app/node/input.h index 0522be416..7f19ca1e3 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -23,19 +23,21 @@ #include "common/timerange.h" #include "keyframe.h" -#include "param.h" +#include "node/connectable.h" +#include "node/inputimmediate.h" +#include "node/value.h" namespace olive { +class Node; + /** * @brief A node parameter designed to take either user input or data from another node */ -class NodeInput : public NodeParam +class NodeInput : public NodeConnectable { Q_OBJECT public: - using KeyframeTrack = QList; - /** * @brief NodeInput Constructor * @@ -45,22 +47,46 @@ public: * saving/loading data from this Node so that parameter order can be changed without issues loading data saved by an * older version. This of course assumes that parameters don't change their ID. */ - NodeInput(const QString &id, const DataType& type, const QVector& default_value); - NodeInput(const QString &id, const DataType& type, const QVariant& default_value); - NodeInput(const QString &id, const DataType& type); + NodeInput(Node* parent, const QString &id, NodeValue::Type type, const QVector& default_value); + NodeInput(Node* parent, const QString &id, NodeValue::Type type, const QVariant& default_value); + NodeInput(Node* parent, const QString &id, NodeValue::Type type); - virtual bool IsArray() const; + virtual ~NodeInput() override; + + const QString& id() const + { + return id_; + } + + QString name() const; + + void set_name(const QString& name) + { + name_ = name; + + emit NameChanged(name_); + } + + bool IsArray() const + { + return is_array_; + } + + void SetIsArray(bool e) + { + is_array_ = e; + } + + void DisconnectAll(); + + void Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled); + + void Save(QXmlStreamWriter* writer) const; /** - * @brief Returns kInput + * @brief Deliberately shadow QObject parent, since we only expect NodeInput to have Node as a parent */ - virtual Type type() override; - - virtual QString name() override; - - virtual void Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled) override; - - virtual void Save(QXmlStreamWriter* writer) const override; + Node* parent() const; /** * @brief The data type this parameter outputs @@ -68,190 +94,160 @@ public: * This can be used in conjunction with NodeInput::can_accept_type() to determine whether this parameter can be * connected to it. */ - const DataType& data_type() const; + NodeValue::Type GetDataType() const + { + return data_type_; + } + + void SetDataType(NodeValue::Type type) + { + data_type_ = type; + + emit DataTypeChanged(type); + } + + const QHash& edges() const + { + return input_connections(); + } + + bool IsConnected(int element = -1) const + { + return input_connections().contains(element); + } /** - * @brief If this input is connected to an output, retrieve the output parameter - * - * @return - * - * The output parameter if connected or nullptr if not + * @brief Returns TRUE if the value is expected to always be the same (i.e. no keyframes and + * not connected to anything) */ - NodeOutput* get_connected_output() const; + bool IsStatic(int element = -1) const + { + return !IsConnected(element) && !GetImmediate(element)->is_keyframing(); + } - /** - * @brief If this input is connected to an output, retrieve the Node whose output is connected - * - * @return - * - * The connected Node if connected or nullptr if not - */ - Node* get_connected_node() const; + Node* GetConnectedNode(int element = -1) const + { + return input_connections().value(element); + } - /** - * @brief Calculate what the stored value should be at a certain time - * - * If this is a multi-track data type (e.g. kVec2), this will automatically combine the result into a QVector2D. - */ - QVariant get_value_at_time(const rational& time) const; + bool IsConnectable() const + { + return connectable_; + } - QVector get_split_values_at_time(const rational& time) const; + void SetConnectable(bool e) + { + connectable_ = e; + } - /** - * @brief Calculate the stored value for a specific track - * - * For most data types, there is only one track (e.g. `track == 0`), but multi-track data types like kVec2 will - * produce the X value on track 0 and the Y value on track 1. - */ - QVariant get_value_at_time_for_track(const rational& time, int track) const; + const QVector &GetKeyframeTracks(int element = -1) const + { + return GetImmediate(element)->keyframe_tracks(); + } - /** - * @brief Retrieve a list of keyframe objects for all tracks at a given time - * - * List may be empty if this input is not keyframing or has no keyframes at this time. - */ - QList get_keyframe_at_time(const rational& time) const; + NodeKeyframe* GetKeyframeAtTimeOnTrack(const rational& time, int track, int element) const + { + return GetImmediate(element)->get_keyframe_at_time_on_track(time, track); + } - /** - * @brief Retrieve the keyframe object at a given time for a given track - * - * @return - * - * The keyframe object at this time or nullptr if there isn't one or if is_keyframing() is false. - */ - NodeKeyframePtr get_keyframe_at_time_on_track(const rational& time, int track) const; - - /** - * @brief Gets the closest keyframe to a time - * - * If is_keyframing() is false or keyframes_ is empty, this will return nullptr. - */ - NodeKeyframePtr get_closest_keyframe_to_time_on_track(const rational& time, int track) const; - - /** - * @brief Get closest keyframe that's before the time on any track - * - * If no keyframe is before this time, returns nullptr. - */ - NodeKeyframePtr get_closest_keyframe_before_time(const rational& time) const; - - /** - * @brief Get closest keyframe that's before the time on any track - * - * If no keyframe is before this time, returns nullptr. - */ - NodeKeyframePtr get_closest_keyframe_after_time(const rational& time) const; - - /** - * @brief A heuristic to determine what type a keyframe should be if it's inserted at a certain time (between keyframes) - */ - NodeKeyframe::Type get_best_keyframe_type_for_time(const rational& time, int track) const; - - /** - * @brief Retrieve the number of - */ - int get_number_of_keyframe_tracks() const; - - /** - * @brief Gets the earliest keyframe on any track - */ - NodeKeyframePtr get_earliest_keyframe() const; - - /** - * @brief Gets the latest keyframe on any track - */ - NodeKeyframePtr get_latest_keyframe() const; - - /** - * @brief Inserts a keyframe at the given time and returns a reference to it - */ - void insert_keyframe(NodeKeyframePtr key); - - /** - * @brief Removes the keyframe - */ - void remove_keyframe(NodeKeyframePtr key); - - /** - * @brief Hacky convenience function to turn a raw pointer into a shared pointer - */ - NodeKeyframePtr get_keyframe_shared_ptr_from_raw(NodeKeyframe *raw) const; - - /** - * @brief Return whether a keyframe exists at this time - * - * If is_keyframing() is false, this will always return false. This checks all tracks and will return true if *any* - * track has a keyframe. - */ - bool has_keyframe_at_time(const rational &time) const; - - /** - * @brief Return whether keyframing is enabled on this input or not - */ - bool is_keyframing() const; - - /** - * @brief Set whether keyframing is enabled on this input or not - */ - void set_is_keyframing(bool k); + NodeKeyframe::Type GetBestKeyframeTypeForTime(const rational& time, int track, int element) const + { + return GetImmediate(element)->get_best_keyframe_type_for_time(time, track); + } /** * @brief Return whether this input can be keyframed or not */ - bool is_keyframable() const; + bool IsKeyframable() const; - /** - * @brief Returns whether the value that this input returns is always the same or is expected to change - * - * Equivalent to `!(is_connected() || is_keyframing())` - */ - bool is_static() const; + bool IsKeyframing(int element = -1) const + { + return GetImmediate(element)->is_keyframing(); + } /** * @brief Get non-keyframed value */ - QVariant get_standard_value() const; + QVariant GetStandardValue(int element = -1) const + { + return NodeValue::combine_track_values_into_normal_value(data_type_, GetSplitStandardValue(element)); + } /** * @brief Get non-keyframed value split into components (the way it's stored) */ - const QVector& get_split_standard_value() const; + const QVector& GetSplitStandardValue(int element = -1) const + { + return GetImmediate(element)->get_split_standard_value(); + } + + QVariant GetStandardValueOnTrack(int track, int element = -1) const + { + return GetImmediate(element)->get_split_standard_value().at(track); + } /** * @brief Set non-keyframed value + * + * This is the value used if keyframing is not enabled. If keyframing is enabled, it is + * overwritten. */ - void set_standard_value(const QVariant& value, int track = 0); + void SetStandardValue(const QVariant& value, int element = -1) + { + SetSplitStandardValue(NodeValue::split_normal_value_into_track_values(data_type_, value), element); + } - /** - * @brief Return list of keyframes in this parameter - */ - const QVector &keyframe_tracks() const; + void SetSplitStandardValue(const QVector& value, int element = -1) + { + GetImmediate(element)->set_split_standard_value(value); + + for (int i=0; iset_standard_value_on_track(value, track); + + if (IsUsingStandardValue(track, element)) { + // If this standard value is being used, we need to send a value changed signal + emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX), element); + } + } /** * @brief Set whether this input can be keyframed or not */ - void set_is_keyframable(bool k); + void SetKeyframable(bool k); /** * @brief Copy all values including keyframe information and connections from another NodeInput */ static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true, bool traverse_arrays = true); - QVector split_normal_value_into_track_values(const QVariant &value) const; - - QVariant combine_track_values_into_normal_value(const QVector& split) const; + static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int element); QStringList get_combobox_strings() const; void set_combobox_strings(const QStringList& strings); - static QString ValueToString(const DataType& data_type, const QVariant& value, bool value_is_a_key_track); - - static QVariant StringToValue(const DataType &data_type, const QString &string, bool value_is_a_key_track); - void GetDependencies(QVector &list, bool traverse, bool exclusive_only) const; - QVariant GetDefaultValue() const; + QVariant GetDefaultValue() const + { + return NodeValue::combine_track_values_into_normal_value(data_type_, default_value_); + } + + const QVector& GetSplitDefaultValue() const + { + return default_value_; + } QVariant GetDefaultValueForTrack(int track) const; @@ -261,57 +257,139 @@ public: QVector GetImmediateDependencies() const; + NodeKeyframe* GetEarliestKeyframe(int element = -1) + { + return GetImmediate(element)->get_earliest_keyframe(); + } + + NodeKeyframe* GetLatestKeyframe(int element = -1) + { + return GetImmediate(element)->get_latest_keyframe(); + } + + bool HasKeyframeAtTime(const rational& time, int element = -1) + { + return GetImmediate(element)->has_keyframe_at_time(time); + } + + QVector GetKeyframesAtTime(const rational& time, int element = -1) + { + return GetImmediate(element)->get_keyframe_at_time(time); + } + + void SetIsKeyframing(bool keyframing, int element) + { + Q_ASSERT(IsKeyframable()); + + GetImmediate(element)->set_is_keyframing(keyframing); + + emit KeyframeEnableChanged(keyframing, element); + } + + void ArrayAppend() + { + ArrayResize(ArraySize() + 1); + } + + void ArrayInsert(int index); + + void ArrayRemove(int index); + + void ArrayPrepend(); + + void ArrayResize(int size); + + int ArraySize() const + { + return array_size_; + } + + void ArrayRemoveLast(); + + int GetNumberOfKeyframeTracks() const + { + return NodeValue::get_number_of_keyframe_tracks(data_type_); + } + + /** + * @brief Calculate what the stored value should be at a certain time + * + * If this is a multi-track data type (e.g. kVec2), this will automatically combine the result into a QVector2D. + */ + QVariant GetValueAtTime(const rational& time, int element = -1) const; + + QVector GetSplitValuesAtTime(const rational& time, int element = -1) const; + + /** + * @brief Calculate the stored value for a specific track + * + * For most data types, there is only one track (e.g. `track == 0`), but multi-track data types like kVec2 will + * produce the X value on track 0 and the Y value on track 1. + */ + QVariant GetValueAtTimeForTrack(const rational& time, int track, int element = -1) const; + + NodeKeyframe* GetClosestKeyframeBeforeTime(const rational& time, int element = -1) const + { + return GetImmediate(element)->get_closest_keyframe_before_time(time); + } + + NodeKeyframe* GetClosestKeyframeAfterTime(const rational& time, int element = -1) const + { + return GetImmediate(element)->get_closest_keyframe_after_time(time); + } + signals: - void ValueChanged(const olive::TimeRange& range); + void NameChanged(const QString& name); - void KeyframeEnableChanged(bool); + void ValueChanged(const olive::TimeRange& range, int element); - void KeyframeAdded(NodeKeyframePtr key); + void KeyframeAdded(NodeKeyframe* key); - void KeyframeRemoved(NodeKeyframePtr key); + void KeyframeRemoved(NodeKeyframe* key); void PropertyChanged(const QString& s, const QVariant& v); + void ArraySizeChanged(int size); + + void KeyframeEnableChanged(bool enabled, int element); + + void DataTypeChanged(NodeValue::Type type); + protected: - virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled); - - virtual void SaveInternal(QXmlStreamWriter* writer) const; - virtual bool event(QEvent* e) override; -private: - void Init(NodeParam::DataType type); + virtual void childEvent(QChildEvent* e) override; - void SetDefaultValue(const QVector &default_value); +private: + void Init(Node *parent, const QString& id, NodeValue::Type type, const QVector &default_val); + + void LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled); + + void SaveImmediate(QXmlStreamWriter *writer, int element) const; + + const NodeInputImmediate* GetImmediate(int element = -1) const + { + return element > -1 ? subinputs_.at(element) : primary_; + } + + NodeInputImmediate* GetImmediate(int element = -1) + { + return element > -1 ? subinputs_[element] : primary_; + } + + const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const + { + return GetImmediate(key->element())->keyframe_tracks().at(key->track()); + } + + bool IsUsingStandardValue(int track = 0, int element = -1) const + { + return GetImmediate(element)->is_using_standard_value(track); + } QString ValueToString(const QVariant& value) const; - QVariant StringToValue(const QString &string, QList &footage_connections); - - void SaveConnections(QXmlStreamWriter* writer) const; - - static void ValidateVectorString(QStringList* list, int count); - - /** - * @brief Returns whether a data type can be interpolated or not - */ - static bool type_can_be_interpolated(DataType type); - - /** - * @brief We use Qt signals/slots for keyframe communication but store them as shared ptrs. This function converts - * a raw ptr to a list index - */ - int FindIndexOfKeyframeFromRawPtr(NodeKeyframe* raw_ptr) const; - - /** - * @brief Internal insert function, automatically does an insertion sort based on the keyframe's time - */ - void insert_keyframe_internal(NodeKeyframePtr key); - - /** - * @brief Return whether the standard value should be used over keyframe data - */ - bool is_using_standard_value(int track) const; + QVariant StringToValue(const QString &string, QList &footage_connections, int element); /** * @brief Intelligently determine how what time range is affected by a keyframe @@ -321,51 +399,39 @@ private: /** * @brief Gets a time range between the previous and next keyframes of index */ - TimeRange get_range_around_index(int index, int track) const; + TimeRange get_range_around_index(int index, int track, int element) const; + + NodeInputImmediate* primary_; + + QVector subinputs_; + + QVector default_value_; /** - * @brief Convenience function - equivalent to calling `emit ValueChanged(range.in(), range.out())` + * @brief Unique identifier of this input within this node */ - void emit_time_range(const TimeRange& range); + QString id_; /** - * @brief Convenience function - equivalent to calling `emit_time_range(get_range_affected_by_keyframe(key))` + * @brief User displayable name of input */ - void emit_range_affected_by_keyframe(NodeKeyframe* key); - - /** - * @brief Internal list of accepted data types - * - * Use can_accept_type() to check if a type is in this list - */ - DataType data_type_; + QString name_; /** * @brief Internal keyframable value */ bool keyframable_; - /** - * @brief Non-keyframed value - */ - QVector standard_value_; + bool connectable_; + + bool is_array_; + + int array_size_; /** - * @brief Default value that can be reset if the user requests + * @brief Default data type */ - QVector default_value_; - - /** - * @brief Internal keyframe array - * - * If keyframing is enabled, this data is used instead of standard_value. - */ - QVector< QList > keyframe_tracks_; - - /** - * @brief Internal keyframing enabled setting - */ - bool keyframing_; + NodeValue::Type data_type_; private slots: /** diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index ed80a6582..09211a58e 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -28,11 +28,10 @@ namespace olive { MediaInput::MediaInput() : connected_footage_(nullptr) { - footage_input_ = new NodeInput("footage_in", NodeInput::kFootage); - footage_input_->set_connectable(false); - footage_input_->set_is_keyframable(false); + footage_input_ = new NodeInput(this, QStringLiteral("footage_in"), NodeValue::kFootage); + footage_input_->SetConnectable(false); + footage_input_->SetKeyframable(false); connect(footage_input_, &NodeInput::ValueChanged, this, &MediaInput::FootageChanged); - AddInput(footage_input_); } QVector MediaInput::Category() const @@ -42,17 +41,12 @@ QVector MediaInput::Category() const Stream *MediaInput::stream() const { - return Node::ValueToPtr(footage_input_->get_standard_value()); + return Node::ValueToPtr(footage_input_->GetStandardValue()); } void MediaInput::SetStream(Stream* s) { - footage_input_->set_standard_value(Node::PtrToValue(s)); -} - -bool MediaInput::IsMedia() const -{ - return true; + footage_input_->SetStandardValue(Node::PtrToValue(s)); } void MediaInput::Retranslate() @@ -68,7 +62,7 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const rational media_duration = Timecode::timestamp_to_time(connected_footage_->duration(), connected_footage_->timebase()); - table.Push(NodeInput::kRational, QVariant::fromValue(media_duration), this, "length"); + table.Push(NodeValue::kRational, QVariant::fromValue(media_duration), this, false, QStringLiteral("length")); } return table; @@ -76,7 +70,7 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const void MediaInput::FootageChanged() { - Stream* new_footage = footage_input_->get_standard_value().value(); + Stream* new_footage = footage_input_->GetStandardValue().value(); if (new_footage == connected_footage_) { return; @@ -95,7 +89,7 @@ void MediaInput::FootageChanged() void MediaInput::FootageParametersChanged() { - InvalidateCache(TimeRange(0, RATIONAL_MAX), footage_input_, footage_input_); + InvalidateCache(TimeRange(0, RATIONAL_MAX), InputConnection(footage_input_)); } } diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index 2767e4f0b..6dc2b8270 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -61,8 +61,6 @@ public: Stream* stream() const; void SetStream(Stream *s); - virtual bool IsMedia() const override; - virtual void Retranslate() override; virtual NodeValueTable Value(NodeValueDatabase& value) const override; diff --git a/app/node/input/time/timeinput.cpp b/app/node/input/time/timeinput.cpp index 101e738e8..705f6fc16 100644 --- a/app/node/input/time/timeinput.cpp +++ b/app/node/input/time/timeinput.cpp @@ -55,9 +55,10 @@ NodeValueTable TimeInput::Value(NodeValueDatabase &value) const { NodeValueTable table = value.Merge(); - table.Push(NodeParam::kFloat, - value[QStringLiteral("global")].Get(NodeParam::kFloat, QStringLiteral("time_in")), + table.Push(NodeValue::kFloat, + value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")), this, + false, QStringLiteral("time")); return table; @@ -68,7 +69,7 @@ void TimeInput::Hash(QCryptographicHash &hash, const rational &time) const Node::Hash(hash, time); // Make sure time is hashed - hash.addData(NodeParam::ValueToBytes(NodeParam::kRational, QVariant::fromValue(time))); + hash.addData(NodeValue::ValueToBytes(NodeValue::kRational, QVariant::fromValue(time))); } } diff --git a/app/node/inputarray.cpp b/app/node/inputarray.cpp deleted file mode 100644 index 6811e8633..000000000 --- a/app/node/inputarray.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "inputarray.h" - -#include - -#include "common/xmlutils.h" -#include "node.h" - -namespace olive { - -NodeInputArray::NodeInputArray(const QString &id, const DataType &type, const QVariant &default_value) : - NodeInput(id, type, default_value), - default_value_(default_value) -{ -} - -NodeInputArray::~NodeInputArray() -{ - // Clear all connected edges (make sure our override is called) - DisconnectAll(); -} - -bool NodeInputArray::IsArray() const -{ - return true; -} - -int NodeInputArray::GetSize() const -{ - return sub_params_.size(); -} - -void NodeInputArray::Prepend() -{ - InsertAt(0); -} - -void NodeInputArray::SetSize(int size) -{ - int old_size = GetSize(); - - if (size == old_size) { - return; - } - - if (size < old_size) { - // If the new size is less, delete all extraneous parameters - for (int i=size;iDisconnectAll(); - } - - for (int i=size;i old_size) { - // If the new size is greater, create valid parameters for each slot - for (int i=old_size;iHasParamWithID(sub_id)); - - NodeInput* new_param = new NodeInput(sub_id, data_type(), default_value_); - new_param->setParent(this); - sub_params_.replace(i, new_param); - - connect(new_param, &NodeInput::ValueChanged, this, &NodeInput::ValueChanged); - connect(new_param, &NodeInput::EdgeAdded, this, &NodeInputArray::SubParamEdgeAdded); - connect(new_param, &NodeInput::EdgeRemoved, this, &NodeInputArray::SubParamEdgeRemoved); - } - } - - emit SizeChanged(size); -} - -bool NodeInputArray::ContainsSubParameter(NodeInput *input) const -{ - return sub_params_.contains(input); -} - -int NodeInputArray::IndexOfSubParameter(NodeInput *input) const -{ - return sub_params_.indexOf(input); -} - -NodeInput *NodeInputArray::At(int index) const -{ - return sub_params_.at(index); -} - -NodeInput *NodeInputArray::First() const -{ - return sub_params_.first(); -} - -NodeInput *NodeInputArray::Last() const -{ - return sub_params_.last(); -} - -const QVector &NodeInputArray::sub_params() -{ - return sub_params_; -} - -void NodeInputArray::DisconnectAll() -{ - NodeParam::DisconnectAll(); - - foreach (NodeInput* input, sub_params_) { - input->DisconnectAll(); - } -} - -void NodeInputArray::InsertAt(int index) -{ - // Add another input at the end - Append(); - - // Shift all connections from index down - for (int i=sub_params_.size()-1;i>index;i--) { - NodeInput* this_param = sub_params_.at(i); - NodeInput* prev_param = sub_params_.at(i-1); - - if (this_param->is_connected()) { - // Disconnect whatever is at this parameter (presumably its connection has already been copied so we can just remove it) - NodeParam::DisconnectEdge(this_param->edges().first()); - } - - if (prev_param->is_connected()) { - // Get edge here (only one since it's an input) - NodeEdgePtr edge = prev_param->edges().first(); - - // Disconnect it - NodeParam::DisconnectEdge(edge); - - // Create a new edge between it and the next one down - NodeParam::ConnectEdge(edge->output(), - this_param); - } - } -} - -void NodeInputArray::Append() -{ - SetSize(GetSize() + 1); -} - -void NodeInputArray::RemoveLast() -{ - SetSize(GetSize() - 1); -} - -void NodeInputArray::RemoveAt(int index) -{ - // Shift all connections from index down - for (int i=index;iis_connected()) { - // Disconnect current edge - NodeParam::DisconnectEdge(this_param->edges().first()); - } - - if (i < sub_params_.size() - 1) { - NodeInput* next_param = sub_params_.at(i + 1); - if (next_param->is_connected()) { - // Get edge from next param - NodeEdgePtr edge = next_param->edges().first(); - - // Disconnect it - NodeParam::DisconnectEdge(edge); - - // Reconnect it to this param - NodeParam::ConnectEdge(edge->output(), - this_param); - } - } - } - - RemoveLast(); -} - -void NodeInputArray::LoadInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt* cancelled) -{ - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("subparameters")) { - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("input")) { - Append(); - At(GetSize() - 1)->Load(reader, xml_node_data, cancelled); - } else { - reader->skipCurrentElement(); - } - } - } else { - reader->skipCurrentElement(); - } - } -} - -void NodeInputArray::SaveInternal(QXmlStreamWriter *writer) const -{ - writer->writeStartElement("subparameters"); - - foreach (NodeInput* sub, sub_params_) { - writer->writeStartElement(QStringLiteral("input")); - sub->Save(writer); - writer->writeEndElement(); - } - - writer->writeEndElement(); // subparameters -} - -} diff --git a/app/node/inputarray.h b/app/node/inputarray.h deleted file mode 100644 index 4af48cd5c..000000000 --- a/app/node/inputarray.h +++ /dev/null @@ -1,79 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 INPUTARRAY_H -#define INPUTARRAY_H - -#include "input.h" - -namespace olive { - -class NodeInputArray : public NodeInput -{ - Q_OBJECT -public: - NodeInputArray(const QString &id, const DataType& type, const QVariant& default_value = 0); - - virtual ~NodeInputArray() override; - - virtual bool IsArray() const override; - - int GetSize() const; - - void Prepend(); - void Append(); - void InsertAt(int index); - void RemoveLast(); - void RemoveAt(int index); - void SetSize(int size); - - bool ContainsSubParameter(NodeInput* input) const; - int IndexOfSubParameter(NodeInput* input) const; - - NodeInput* First() const; - NodeInput* Last() const; - NodeInput* At(int index) const; - - const QVector& sub_params(); - - virtual void DisconnectAll() override; - -signals: - void SizeChanged(int size); - - void SubParamEdgeAdded(NodeEdgePtr edge); - - void SubParamEdgeRemoved(NodeEdgePtr edge); - -protected: - virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt *cancelled) override; - - virtual void SaveInternal(QXmlStreamWriter* writer) const override; - -private: - QVector sub_params_; - - QVariant default_value_; - -}; - -} - -#endif // INPUTARRAY_H diff --git a/app/node/inputdragger.cpp b/app/node/inputdragger.cpp index 265994c46..d71bfbc9c 100644 --- a/app/node/inputdragger.cpp +++ b/app/node/inputdragger.cpp @@ -37,7 +37,7 @@ bool NodeInputDragger::IsStarted() const return input_; } -void NodeInputDragger::Start(NodeInput *input, const rational &time, int track) +void NodeInputDragger::Start(NodeInput *input, const rational &time, int track, int element) { Q_ASSERT(!input_); @@ -45,27 +45,23 @@ void NodeInputDragger::Start(NodeInput *input, const rational &time, int track) input_ = input; time_ = time; track_ = track; + element_ = element; // Cache current value - start_value_ = input_->get_value_at_time_for_track(time, track); + start_value_ = input_->GetValueAtTimeForTrack(time, track, element_); // Determine whether we are creating a keyframe or not - if (input_->is_keyframing()) { - dragging_key_ = input_->get_keyframe_at_time_on_track(time, track); + if (input_->IsKeyframing(element_)) { + dragging_key_ = input_->GetKeyframeAtTimeOnTrack(time, track, element_); drag_created_key_ = !dragging_key_; if (drag_created_key_) { - dragging_key_ = NodeKeyframe::Create(time, - start_value_, - input_->get_best_keyframe_type_for_time(time, track), - track); - - // We disable default signal emitting during the drag - //input_->blockSignals(true); - input_->insert_keyframe(dragging_key_); - //input_->blockSignals(false); - - emit input_->KeyframeAdded(dragging_key_); + dragging_key_ = new NodeKeyframe(time, + start_value_, + input_->GetBestKeyframeTypeForTime(time, track, element_), + track, + element_, + input_); } } } @@ -95,10 +91,10 @@ void NodeInputDragger::Drag(QVariant value) //input_->blockSignals(true); - if (input_->is_keyframing()) { + if (input_->IsKeyframing(element_)) { dragging_key_->set_value(value); } else { - input_->set_standard_value(value, track_); + input_->SetStandardValueOnTrack(value, track_, element_); } //input_->blockSignals(false); @@ -112,10 +108,10 @@ void NodeInputDragger::End() QUndoCommand* command = new QUndoCommand(); - if (input_->is_keyframing()) { + if (input_->IsKeyframing(element_)) { if (drag_created_key_) { // We created a keyframe in this process - new NodeParamInsertKeyframeCommand(input_, dragging_key_, true, command); + new NodeParamInsertKeyframeCommand(input_, dragging_key_, command); } // We just set a keyframe's value @@ -124,7 +120,7 @@ void NodeInputDragger::End() new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_, command); } else { // We just set the standard value - new NodeParamSetStandardValueCommand(input_, track_, end_value_, start_value_, command); + new NodeParamSetStandardValueCommand(input_, track_, element_, end_value_, start_value_, command); } Core::instance()->undo_stack()->push(command); diff --git a/app/node/inputdragger.h b/app/node/inputdragger.h index bd9f80eff..fd8efb6e7 100644 --- a/app/node/inputdragger.h +++ b/app/node/inputdragger.h @@ -32,7 +32,7 @@ public: bool IsStarted() const; - void Start(NodeInput* input, const rational& time, int track); + void Start(NodeInput* input, const rational& time, int track, int element = -1); void Drag(QVariant value); @@ -45,11 +45,13 @@ private: rational time_; + int element_; + QVariant start_value_; QVariant end_value_; - NodeKeyframePtr dragging_key_; + NodeKeyframe* dragging_key_; bool drag_created_key_; diff --git a/app/node/inputimmediate.cpp b/app/node/inputimmediate.cpp new file mode 100644 index 000000000..c48e271f2 --- /dev/null +++ b/app/node/inputimmediate.cpp @@ -0,0 +1,243 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 "inputimmediate.h" + +#include "common/bezier.h" +#include "common/lerp.h" +#include "common/tohex.h" +#include "input.h" + +namespace olive { + +NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const QVector &default_val) : + keyframing_(false) +{ + int track_size = NodeValue::get_number_of_keyframe_tracks(type); + + keyframe_tracks_.resize(track_size); + standard_value_.resize(track_size); + + set_split_standard_value(default_val); +} + +void NodeInputImmediate::set_standard_value_on_track(const QVariant &value, int track) +{ + standard_value_.replace(track, value); +} + +void NodeInputImmediate::set_split_standard_value(const QVector &value) +{ + for (int i=0; i NodeInputImmediate::get_keyframe_at_time(const rational &time) const +{ + QVector keys; + + for (int i=0;itime() == time) { + return key; + } + } + } + + return nullptr; +} + +NodeKeyframe* NodeInputImmediate::get_closest_keyframe_to_time_on_track(const rational &time, int track) const +{ + if (is_using_standard_value(track)) { + return nullptr; + } + + const NodeKeyframeTrack& key_track = keyframe_tracks_.at(track); + + if (time <= key_track.first()->time()) { + return key_track.first(); + } + + if (time >= key_track.last()->time()) { + return key_track.last(); + } + + for (int i=1;itime() <= time && next_key->time() >= time) { + // Return whichever is closer + rational prev_diff = time - prev_key->time(); + rational next_diff = next_key->time() - time; + + if (next_diff < prev_diff) { + return next_key; + } else { + return prev_key; + } + } + } + + return nullptr; +} + +NodeKeyframe *NodeInputImmediate::get_closest_keyframe_before_time(const rational &time) const +{ + NodeKeyframe* key = nullptr; + + foreach (const NodeKeyframeTrack& track, keyframe_tracks_) { + foreach (NodeKeyframe* k, track) { + if (k->time() >= time) { + break; + } else if (!key || k->time() > key->time()) { + key = k; + } + } + } + + return key; +} + +NodeKeyframe* NodeInputImmediate::get_closest_keyframe_after_time(const rational &time) const +{ + NodeKeyframe* key = nullptr; + + foreach (const NodeKeyframeTrack& track, keyframe_tracks_) { + for (int i=track.size()-1;i>=0;i--) { + NodeKeyframe* k = track.at(i); + + if (k->time() <= time) { + break; + } else if (!key || k->time() < key->time()) { + key = k; + } + } + } + + return key; +} + +NodeKeyframe::Type NodeInputImmediate::get_best_keyframe_type_for_time(const rational &time, int track) const +{ + NodeKeyframe* closest_key = get_closest_keyframe_to_time_on_track(time, track); + + if (closest_key) { + return closest_key->type(); + } + + return NodeKeyframe::kDefaultType; +} + +bool NodeInputImmediate::has_keyframe_at_time(const rational &time) const +{ + if (!is_keyframing()) { + return false; + } + + // Loop through keyframes to see if any match + foreach (const NodeKeyframeTrack& track, keyframe_tracks_) { + foreach (NodeKeyframe* key, track) { + if (key->time() == time) { + return true; + } + } + } + + // None match + return false; +} + +NodeKeyframe *NodeInputImmediate::get_earliest_keyframe() const +{ + NodeKeyframe* earliest = nullptr; + + foreach (const NodeKeyframeTrack& track, keyframe_tracks_) { + if (!track.isEmpty()) { + NodeKeyframe* earliest_in_track = track.first(); + + if (!earliest + || earliest_in_track->time() < earliest->time()) { + earliest = earliest_in_track; + } + } + } + + return earliest; +} + +NodeKeyframe *NodeInputImmediate::get_latest_keyframe() const +{ + NodeKeyframe* latest = nullptr; + + foreach (const NodeKeyframeTrack& track, keyframe_tracks_) { + if (!track.isEmpty()) { + NodeKeyframe* latest_in_track = track.last(); + + if (!latest + || latest_in_track->time() > latest->time()) { + latest = latest_in_track; + } + } + } + + return latest; +} + +void NodeInputImmediate::insert_keyframe(NodeKeyframe* key) +{ + NodeKeyframeTrack& key_track = keyframe_tracks_[key->track()]; + + for (int i=0;itime() != key->time()); + + if (compare->time() > key->time()) { + key_track.insert(i, key); + return; + } + } + + key_track.append(key); +} + +void NodeInputImmediate::remove_keyframe(NodeKeyframe *key) +{ + keyframe_tracks_[key->track()].removeOne(key); +} + +} diff --git a/app/node/inputimmediate.h b/app/node/inputimmediate.h new file mode 100644 index 000000000..d9678e149 --- /dev/null +++ b/app/node/inputimmediate.h @@ -0,0 +1,168 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 NODEINPUTIMMEDIATE_H +#define NODEINPUTIMMEDIATE_H + +#include "common/timerange.h" +#include "common/xmlutils.h" +#include "node/keyframe.h" +#include "node/value.h" + +namespace olive { + +class NodeInput; + +class NodeInputImmediate +{ +public: + NodeInputImmediate(NodeValue::Type type, const QVector& default_val); + + /** + * @brief Internal insert function, automatically does an insertion sort based on the keyframe's time + */ + void insert_keyframe(NodeKeyframe* key); + + void remove_keyframe(NodeKeyframe* key); + + /** + * @brief Get non-keyframed value split into components (the way it's stored) + */ + const QVector& get_split_standard_value() const + { + return standard_value_; + } + + void set_standard_value_on_track(const QVariant &value, int track = 0); + + void set_split_standard_value(const QVector& value); + + /** + * @brief Retrieve a list of keyframe objects for all tracks at a given time + * + * List may be empty if this input is not keyframing or has no keyframes at this time. + */ + QVector get_keyframe_at_time(const rational& time) const; + + /** + * @brief Retrieve the keyframe object at a given time for a given track + * + * @return + * + * The keyframe object at this time or nullptr if there isn't one or if is_keyframing() is false. + */ + NodeKeyframe* get_keyframe_at_time_on_track(const rational& time, int track) const; + + /** + * @brief Gets the closest keyframe to a time + * + * If is_keyframing() is false or keyframes_ is empty, this will return nullptr. + */ + NodeKeyframe* get_closest_keyframe_to_time_on_track(const rational& time, int track) const; + + /** + * @brief Get closest keyframe that's before the time on any track + * + * If no keyframe is before this time, returns nullptr. + */ + NodeKeyframe* get_closest_keyframe_before_time(const rational& time) const; + + /** + * @brief Get closest keyframe that's before the time on any track + * + * If no keyframe is before this time, returns nullptr. + */ + NodeKeyframe* get_closest_keyframe_after_time(const rational& time) const; + + /** + * @brief A heuristic to determine what type a keyframe should be if it's inserted at a certain time (between keyframes) + */ + NodeKeyframe::Type get_best_keyframe_type_for_time(const rational& time, int track) const; + + /** + * @brief Return list of keyframes in this parameter + */ + const QVector &keyframe_tracks() const + { + return keyframe_tracks_; + } + + /** + * @brief Return whether keyframing is enabled on this input or not + */ + bool is_keyframing() const + { + return keyframing_; + } + + /** + * @brief Set whether keyframing is enabled on this input or not + */ + void set_is_keyframing(bool k) + { + keyframing_ = k; + } + + /** + * @brief Gets the earliest keyframe on any track + */ + NodeKeyframe* get_earliest_keyframe() const; + + /** + * @brief Gets the latest keyframe on any track + */ + NodeKeyframe* get_latest_keyframe() const; + + /** + * @brief Return whether a keyframe exists at this time + * + * If is_keyframing() is false, this will always return false. This checks all tracks and will return true if *any* + * track has a keyframe. + */ + bool has_keyframe_at_time(const rational &time) const; + + bool is_using_standard_value(int track) const + { + return (!is_keyframing() || keyframe_tracks_.at(track).isEmpty()); + } + +private: + /** + * @brief Non-keyframed value + */ + QVector standard_value_; + + /** + * @brief Internal keyframe array + * + * If keyframing is enabled, this data is used instead of standard_value. + */ + QVector keyframe_tracks_; + + /** + * @brief Internal keyframing enabled setting + */ + bool keyframing_; + +}; + +} + +#endif // NODEINPUTIMMEDIATE_H diff --git a/app/node/keyframe.cpp b/app/node/keyframe.cpp index dd3ae2de3..7eb7931c3 100644 --- a/app/node/keyframe.cpp +++ b/app/node/keyframe.cpp @@ -20,34 +20,37 @@ #include "keyframe.h" +#include "input.h" + namespace olive { const NodeKeyframe::Type NodeKeyframe::kDefaultType = kLinear; -NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, const NodeKeyframe::Type &type, const int &track) : - parent_(nullptr), +NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, const NodeKeyframe::Type &type, const int &track, int element, QObject *parent) : time_(time), value_(value), type_(type), bezier_control_in_(QPointF(-1.0, 0.0)), bezier_control_out_(QPointF(1.0, 0.0)), - track_(track) + track_(track), + element_(element) { + setParent(parent); } -NodeKeyframePtr NodeKeyframe::Create(const rational &time, const QVariant &value, const NodeKeyframe::Type &type, const int& track) +NodeKeyframe *NodeKeyframe::copy(QObject* parent) const { - return std::make_shared(time, value, type, track); -} - -NodeKeyframePtr NodeKeyframe::copy() const -{ - NodeKeyframePtr copy = std::make_shared(time_, value_, type_, track_); + NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element_, parent); copy->bezier_control_in_ = bezier_control_in_; copy->bezier_control_out_ = bezier_control_out_; return copy; } +NodeInput *NodeKeyframe::parent() const +{ + return static_cast(QObject::parent()); +} + const rational &NodeKeyframe::time() const { return time_; @@ -121,11 +124,6 @@ void NodeKeyframe::set_bezier_control(NodeKeyframe::BezierType type, const QPoin } } -const int &NodeKeyframe::track() const -{ - return track_; -} - NodeKeyframe::BezierType NodeKeyframe::get_opposing_bezier_type(NodeKeyframe::BezierType type) { if (type == kInHandle) { @@ -135,14 +133,4 @@ NodeKeyframe::BezierType NodeKeyframe::get_opposing_bezier_type(NodeKeyframe::Be } } -NodeInput *NodeKeyframe::parent() const -{ - return parent_; -} - -void NodeKeyframe::set_parent(NodeInput *parent) -{ - parent_ = parent; -} - } diff --git a/app/node/keyframe.h b/app/node/keyframe.h index 517defe6d..aa19c38d9 100644 --- a/app/node/keyframe.h +++ b/app/node/keyframe.h @@ -30,9 +30,7 @@ namespace olive { class NodeInput; - -class NodeKeyframe; -using NodeKeyframePtr = std::shared_ptr; +class NodeInputImmediate; /** * @brief A point of data to be used at a certain time and interpolated with other data @@ -63,11 +61,11 @@ public: /** * @brief NodeKeyframe Constructor */ - NodeKeyframe(const rational& time, const QVariant& value, const Type& type, const int& track); + NodeKeyframe(const rational& time, const QVariant& value, const Type& type, const int& track, int element, QObject* parent = nullptr); - static NodeKeyframePtr Create(const rational& time, const QVariant& value, const Type& type, const int &track); + NodeKeyframe* copy(QObject* parent = nullptr) const; - NodeKeyframePtr copy() const; + NodeInput* parent() const; /** * @brief The time this keyframe is set at @@ -111,16 +109,21 @@ public: * For the majority of keyfreames, this will be 0, but for some types, such as kVec2, this will be 0 for X keyframes * and 1 for Y keyframes, etc. */ - const int& track() const; + int track() const + { + return track_; + } + + int element() const + { + return element_; + } /** * @brief Convenience function for getting the opposite handle type (e.g. kInHandle <-> kOutHandle) */ static BezierType get_opposing_bezier_type(BezierType type); - NodeInput* parent() const; - void set_parent(NodeInput* parent); - signals: /** * @brief Signal emitted when this keyframe's time is changed @@ -148,8 +151,6 @@ signals: void BezierControlOutChanged(const QPointF& d); private: - NodeInput* parent_; - rational time_; QVariant value_; @@ -162,8 +163,12 @@ private: int track_; + int element_; + }; +using NodeKeyframeTrack = QVector; + } Q_DECLARE_METATYPE(olive::NodeKeyframe::Type) diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 41d30bbf8..58c9c97fd 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -24,20 +24,17 @@ namespace olive { MathNode::MathNode() { - method_in_ = new NodeInput(QStringLiteral("method_in"), NodeParam::kCombo); - method_in_->set_connectable(false); - method_in_->set_is_keyframable(false); - AddInput(method_in_); + method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo); + method_in_->SetConnectable(false); + method_in_->SetKeyframable(false); - param_a_in_ = new NodeInput(QStringLiteral("param_a_in"), NodeParam::kFloat, 0.0); + param_a_in_ = new NodeInput(this, QStringLiteral("param_a_in"), NodeValue::kFloat, 0.0); param_a_in_->setProperty("decimalplaces", 8); param_a_in_->setProperty("autotrim", true); - AddInput(param_a_in_); - param_b_in_ = new NodeInput(QStringLiteral("param_b_in"), NodeParam::kFloat, 0.0); + param_b_in_ = new NodeInput(this, QStringLiteral("param_b_in"), NodeValue::kFloat, 0.0); param_b_in_->setProperty("decimalplaces", 8); param_b_in_->setProperty("autotrim", true); - AddInput(param_b_in_); } Node *MathNode::copy() const diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index 5d9584d77..bf78b59c7 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -44,12 +44,12 @@ public: Operation GetOperation() const { - return static_cast(method_in_->get_standard_value().toInt()); + return static_cast(method_in_->GetStandardValue().toInt()); } void SetOperation(Operation o) { - method_in_->set_standard_value(o); + method_in_->SetStandardValue(o); } NodeInput* param_a_in() const diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index bb0015409..d81e3bcb9 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -35,16 +35,16 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp Operation op = static_cast(code_id.at(0).toInt()); Pairing pairing = static_cast(code_id.at(1).toInt()); - NodeParam::DataType type_a = static_cast(code_id.at(2).toInt()); - NodeParam::DataType type_b = static_cast(code_id.at(3).toInt()); + NodeValue::Type type_a = static_cast(code_id.at(2).toInt()); + NodeValue::Type type_b = static_cast(code_id.at(3).toInt()); QString operation, frag, vert; if (pairing == kPairTextureMatrix && op == kOpMultiply) { // Override the operation for this operation since we multiply texture COORDS by the matrix rather than - NodeParam* tex_in = (type_a == NodeParam::kTexture) ? param_a_in : param_b_in; - NodeParam* mat_in = (type_a == NodeParam::kTexture) ? param_b_in : param_a_in; + NodeInput* tex_in = (type_a == NodeValue::kTexture) ? param_a_in : param_b_in; + NodeInput* mat_in = (type_a == NodeValue::kTexture) ? param_b_in : param_a_in; // No-op frag shader (can we return QString() instead?) operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id()); @@ -78,7 +78,7 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp case kOpPower: if (pairing == kPairTextureNumber) { // The "number" in this operation has to be declared a vec4 - if (type_a & NodeParam::kNumber) { + if (NodeValue::type_is_numeric(type_a)) { operation = QStringLiteral("pow(%2, vec4(%1))"); } else { operation = QStringLiteral("pow(%1, vec4(%2))"); @@ -111,23 +111,23 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp return ShaderCode(frag, vert); } -QString MathNodeBase::GetShaderUniformType(const NodeParam::DataType &type) +QString MathNodeBase::GetShaderUniformType(const olive::NodeValue::Type &type) { switch (type) { - case NodeParam::kTexture: + case NodeValue::kTexture: return QStringLiteral("sampler2D"); - case NodeParam::kColor: + case NodeValue::kColor: return QStringLiteral("vec4"); - case NodeParam::kMatrix: + case NodeValue::kMatrix: return QStringLiteral("mat4"); default: return QStringLiteral("float"); } } -QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const NodeParam::DataType &type, const QString& coord_op) +QString MathNodeBase::GetShaderVariableCall(const QString &input_id, const NodeValue::Type &type, const QString& coord_op) { - if (type == NodeParam::kTexture) { + if (type == NodeValue::kTexture) { return QStringLiteral("texture(%1, ove_texcoord%2)").arg(input_id, coord_op); } @@ -138,26 +138,26 @@ QVector4D MathNodeBase::RetrieveVector(const NodeValue &val) { // QVariant doesn't know that QVector*D can convert themselves so we do it here switch (val.type()) { - case NodeParam::kVec2: + case NodeValue::kVec2: return val.data().value(); - case NodeParam::kVec3: + case NodeValue::kVec3: return val.data().value(); - case NodeParam::kVec4: + case NodeValue::kVec4: default: return val.data().value(); } } -void MathNodeBase::PushVector(NodeValueTable *output, NodeParam::DataType type, const QVector4D &vec) const +void MathNodeBase::PushVector(NodeValueTable *output, olive::NodeValue::Type type, const QVector4D &vec) const { switch (type) { - case NodeParam::kVec2: + case NodeValue::kVec2: output->Push(type, QVector2D(vec), this); break; - case NodeParam::kVec3: + case NodeValue::kVec3: output->Push(type, QVector3D(vec), this); break; - case NodeParam::kVec4: + case NodeValue::kVec4: output->Push(type, vec, this); break; default: @@ -173,13 +173,13 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o case kPairNumberNumber: { - if (val_a.type() == NodeParam::kRational && val_b.type() == NodeParam::kRational && operation != kOpPower) { + if (val_a.type() == NodeValue::kRational && val_b.type() == NodeValue::kRational && operation != kOpPower) { // Preserve rationals - output.Push(NodeParam::kRational, + output.Push(NodeValue::kRational, QVariant::fromValue(PerformAddSubMultDiv(operation, val_a.data().value(), val_b.data().value())), this); } else { - output.Push(NodeParam::kFloat, + output.Push(NodeValue::kFloat, PerformAll(operation, RetrieveNumber(val_a), RetrieveNumber(val_b)), this); } @@ -198,8 +198,8 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o case kPairMatrixVec: { - QMatrix4x4 matrix = (val_a.type() == NodeParam::kMatrix) ? val_a.data().value() : val_b.data().value(); - QVector4D vec = (val_a.type() == NodeParam::kMatrix) ? RetrieveVector(val_b) : RetrieveVector(val_a); + QMatrix4x4 matrix = (val_a.type() == NodeValue::kMatrix) ? val_a.data().value() : val_b.data().value(); + QVector4D vec = (val_a.type() == NodeValue::kMatrix) ? RetrieveVector(val_b) : RetrieveVector(val_a); // Only valid operation is multiply PushVector(&output, @@ -210,8 +210,8 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o case kPairVecNumber: { - QVector4D vec = (val_a.type() & NodeParam::kVector) ? RetrieveVector(val_a) : RetrieveVector(val_b); - float number = RetrieveNumber((val_a.type() & NodeParam::kMatrix) ? val_b : val_a); + QVector4D vec = (NodeValue::type_is_vector(val_a.type()) ? RetrieveVector(val_a) : RetrieveVector(val_b)); + float number = RetrieveNumber((val_a.type() & NodeValue::kMatrix) ? val_b : val_a); // Only multiply and divide are valid operations PushVector(&output, val_a.type(), PerformMultDiv(operation, vec, number)); @@ -222,7 +222,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o { QMatrix4x4 mat_a = val_a.data().value(); QMatrix4x4 mat_b = val_b.data().value(); - output.Push(NodeParam::kMatrix, PerformAddSubMult(operation, mat_a, mat_b), this); + output.Push(NodeValue::kMatrix, PerformAddSubMult(operation, mat_a, mat_b), this); break; } @@ -232,18 +232,18 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o Color col_b = val_b.data().value(); // Only add and subtract are valid operations - output.Push(NodeParam::kColor, QVariant::fromValue(PerformAddSub(operation, col_a, col_b)), this); + output.Push(NodeValue::kColor, QVariant::fromValue(PerformAddSub(operation, col_a, col_b)), this); break; } case kPairNumberColor: { - Color col = (val_a.type() == NodeParam::kColor) ? val_a.data().value() : val_b.data().value(); - float num = (val_a.type() == NodeParam::kColor) ? val_b.data().toFloat() : val_a.data().toFloat(); + Color col = (val_a.type() == NodeValue::kColor) ? val_a.data().value() : val_b.data().value(); + float num = (val_a.type() == NodeValue::kColor) ? val_b.data().toFloat() : val_a.data().toFloat(); // Only multiply and divide are valid operations - output.Push(NodeParam::kColor, QVariant::fromValue(PerformMult(operation, col, num)), this); + output.Push(NodeValue::kColor, QVariant::fromValue(PerformMult(operation, col, num)), this); break; } @@ -277,7 +277,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o } } - output.Push(NodeParam::kSamples, QVariant::fromValue(mixed_samples), this); + output.Push(NodeValue::kSamples, QVariant::fromValue(mixed_samples), this); break; } @@ -297,8 +297,8 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o bool operation_is_noop = false; - const NodeValue& number_val = val_a.type() == NodeParam::kTexture ? val_b : val_a; - const NodeValue& texture_val = val_a.type() == NodeParam::kTexture ? val_a : val_b; + const NodeValue& number_val = val_a.type() == NodeValue::kTexture ? val_b : val_a; + const NodeValue& texture_val = val_a.type() == NodeValue::kTexture ? val_a : val_b; TexturePtr texture = texture_val.data().value(); if (!texture) { @@ -309,7 +309,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o } } else if (pairing == kPairTextureMatrix) { // Only allow matrix multiplication - QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeParam::kVec2, QStringLiteral("resolution")).value(); + QVector2D sequence_res = value[QStringLiteral("global")].Get(NodeValue::kVec2, QStringLiteral("resolution")).value(); QVector2D texture_res(texture->params().width() * texture->pixel_aspect_ratio().toDouble(), texture->params().height()); QMatrix4x4 adjusted_matrix = TransformDistortNode::AdjustMatrixByResolutions(number_val.data().value(), @@ -320,8 +320,8 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o operation_is_noop = true; } else { // Replace with adjusted matrix - job.InsertValue(val_a.type() == NodeParam::kTexture ? param_b_in : param_a_in, - ShaderValue(adjusted_matrix, NodeParam::kMatrix)); + job.InsertValue(val_a.type() == NodeValue::kTexture ? param_b_in : param_a_in, + NodeValue(NodeValue::kMatrix, adjusted_matrix, this)); // It's likely an alpha channel will result from this operation job.SetAlphaChannelRequired(true); @@ -333,7 +333,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o output.Push(texture_val); } else { // Push shader job - output.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this); + output.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } break; } @@ -341,16 +341,16 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o case kPairSampleNumber: { // Queue a sample job - const NodeValue& number_val = val_a.type() == NodeParam::kSamples ? val_b : val_a; - NodeInput* number_param = val_a.type() == NodeParam::kSamples ? param_b_in : param_a_in; + const NodeValue& number_val = val_a.type() == NodeValue::kSamples ? val_b : val_a; + NodeInput* number_param = val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in; float number = RetrieveNumber(number_val); - SampleJob job(val_a.type() == NodeParam::kSamples ? val_a : val_b); - job.InsertValue(number_param, ShaderValue(number, NodeParam::kFloat)); + SampleJob job(val_a.type() == NodeValue::kSamples ? val_a : val_b); + job.InsertValue(number_param, NodeValue(NodeValue::kFloat, number, this)); if (job.HasSamples()) { - if (number_param->is_static()) { + if (number_param->IsStatic()) { if (!NumberIsNoOp(operation, number)) { for (int i=0;iaudio_params().channel_count();i++) { for (int j=0;jsample_count();j++) { @@ -359,9 +359,9 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o } } - output.Push(NodeParam::kSamples, QVariant::fromValue(job.samples()), this); + output.Push(NodeValue::kSamples, QVariant::fromValue(job.samples()), this); } else { - output.Push(NodeParam::kSampleJob, QVariant::fromValue(job), this); + output.Push(NodeValue::kSampleJob, QVariant::fromValue(job), this); } } break; @@ -378,12 +378,12 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBase::Operation operation, NodeInput *param_a_in, NodeInput *param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const { // This function is only used for sample+number pairing - NodeValue number_val = values[param_a_in].GetWithMeta(NodeParam::kNumber); + NodeValue number_val = values[param_a_in].GetWithMeta(NodeValue::kNumber); - if (number_val.type() == NodeParam::kNone) { - number_val = values[param_b_in].GetWithMeta(NodeParam::kNumber); + if (number_val.type() == NodeValue::kNone) { + number_val = values[param_b_in].GetWithMeta(NodeValue::kNumber); - if (number_val.type() == NodeParam::kNone) { + if (number_val.type() == NodeValue::kNone) { return; } } @@ -397,7 +397,7 @@ void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBas float MathNodeBase::RetrieveNumber(const NodeValue &val) { - if (val.type() == NodeParam::kRational) { + if (val.type() == NodeValue::kRational) { return val.data().value().toDouble(); } else { return val.data().toFloat(); @@ -467,32 +467,32 @@ QVector MathNodeBase::PairingCalculator::GetPairLikelihood(const NodeValueT QVector likelihood(kPairCount, -1); for (int i=0;i static T PerformAddSubMultDiv(Operation operation, T a, U b); - static QString GetShaderUniformType(const NodeParam::DataType& type); + static QString GetShaderUniformType(const NodeValue::Type& type); - static QString GetShaderVariableCall(const QString& input_id, const NodeParam::DataType& type, const QString &coord_op = QString()); + static QString GetShaderVariableCall(const QString& input_id, const NodeValue::Type& type, const QString &coord_op = QString()); static QVector4D RetrieveVector(const NodeValue& val); @@ -111,7 +111,7 @@ protected: ShaderCode GetShaderCodeInternal(const QString &shader_id, NodeInput* param_a_in, NodeInput* param_b_in) const; - void PushVector(NodeValueTable* output, NodeParam::DataType type, const QVector4D& vec) const; + void PushVector(NodeValueTable* output, NodeValue::Type type, const QVector4D& vec) const; NodeValueTable ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, NodeInput* param_a_in, const NodeValue &val_a, NodeInput* param_b_in, const NodeValue& val_b) const; diff --git a/app/node/math/merge/merge.cpp b/app/node/math/merge/merge.cpp index 877b73a31..073bb4f01 100644 --- a/app/node/math/merge/merge.cpp +++ b/app/node/math/merge/merge.cpp @@ -24,11 +24,9 @@ namespace olive { MergeNode::MergeNode() { - base_in_ = new NodeInput("base_in", NodeParam::kTexture); - AddInput(base_in_); + base_in_ = new NodeInput(this, QStringLiteral("base_in"), NodeValue::kTexture); - blend_in_ = new NodeInput("blend_in", NodeParam::kTexture); - AddInput(blend_in_); + blend_in_ = new NodeInput(this, QStringLiteral("blend_in"), NodeValue::kTexture); } Node *MergeNode::copy() const @@ -77,19 +75,19 @@ NodeValueTable MergeNode::Value(NodeValueDatabase &value) const NodeValueTable table = value.Merge(); - TexturePtr base_tex = job.GetValue(base_in_).data.value(); - TexturePtr blend_tex = job.GetValue(blend_in_).data.value(); + TexturePtr base_tex = job.GetValue(base_in_).data().value(); + TexturePtr blend_tex = job.GetValue(blend_in_).data().value(); if (base_tex || blend_tex) { if (!base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount)) { // We only have a blend texture or the blend texture is RGB only, no need to alpha over - table.Push(job.GetValue(blend_in_), this); + table.Push(job.GetValue(blend_in_)); } else if (!blend_tex) { // We only have a base texture, no need to alpha over - table.Push(job.GetValue(base_in_), this); + table.Push(job.GetValue(base_in_)); } else { // We have both textures, push the job - table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this); + table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } } @@ -108,12 +106,19 @@ NodeInput *MergeNode::blend_in() const void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const { - if (base_in_->is_connected()) { - base_in_->get_connected_node()->Hash(hash, time); + // If only one of these is connected, the merge is a no-op, so we only leave a fingerprint if + // both are connected + if (base_in_->IsConnected() && blend_in_->IsConnected()) { + // Leave fingerprint of merge node + hash.addData(id().toUtf8()); } - if (blend_in_->is_connected()) { - blend_in_->get_connected_node()->Hash(hash, time); + if (base_in_->IsConnected()) { + base_in_->GetConnectedNode()->Hash(hash, time); + } + + if (blend_in_->IsConnected()) { + blend_in_->GetConnectedNode()->Hash(hash, time); } } diff --git a/app/node/math/trigonometry/trigonometry.cpp b/app/node/math/trigonometry/trigonometry.cpp index 89119db60..c464d0f0e 100644 --- a/app/node/math/trigonometry/trigonometry.cpp +++ b/app/node/math/trigonometry/trigonometry.cpp @@ -24,13 +24,11 @@ namespace olive { TrigonometryNode::TrigonometryNode() { - method_in_ = new NodeInput(QStringLiteral("method_in"), NodeParam::kCombo); - method_in_->set_connectable(false); - method_in_->set_is_keyframable(false); - AddInput(method_in_); + method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo); + method_in_->SetConnectable(false); + method_in_->SetKeyframable(false); - x_in_ = new NodeInput(QStringLiteral("x_in"), NodeParam::kFloat, 0.0); - AddInput(x_in_); + x_in_ = new NodeInput(this, QStringLiteral("x_in"), NodeValue::kFloat, 0.0); } olive::Node *olive::TrigonometryNode::copy() const @@ -79,11 +77,11 @@ void TrigonometryNode::Retranslate() NodeValueTable TrigonometryNode::Value(NodeValueDatabase &value) const { - float x = value[x_in_].Take(NodeParam::kFloat).toFloat(); + float x = value[x_in_].Take(NodeValue::kFloat).toFloat(); NodeValueTable table = value.Merge(); - switch (static_cast(method_in_->get_standard_value().toInt())) { + switch (static_cast(method_in_->GetStandardValue().toInt())) { case kOpSine: x = qSin(x); break; @@ -113,7 +111,7 @@ NodeValueTable TrigonometryNode::Value(NodeValueDatabase &value) const break; } - table.Push(NodeParam::kFloat, x, this); + table.Push(NodeValue::kFloat, x, this); return table; } diff --git a/app/node/node.cpp b/app/node/node.cpp index fd198ebbb..6074a74a4 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -37,27 +37,16 @@ namespace olive { Node::Node() : can_be_deleted_(true) { - output_ = new NodeOutput("node_out"); - AddParameter(output_); } Node::~Node() { DisconnectAll(); +} - // We delete in the Node destructor rather than relying on the QObject system because the parameter may need to - // perform actions on this Node object and we want them to be done before the Node object is fully destroyed - foreach (NodeParam* param, params_) { - - // We disconnect input signals because these will try to send invalidate cache signals that may involve the derived - // class (which is now destroyed). Any node that this is connected to will handle cache invalidation so it's a waste - // of time anyway. - if (param->type() == NodeParam::kInput) { - DisconnectInput(static_cast(param)); - } - - delete param; - } +NodeGraph *Node::parent() const +{ + return static_cast(QObject::parent()); } void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled) @@ -67,7 +56,7 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto return; } - if (reader->name() == QStringLiteral("input") || reader->name() == QStringLiteral("output")) { + if (reader->name() == QStringLiteral("input")) { QString param_id; XMLAttributeLoop(reader, attr) { @@ -83,13 +72,7 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto continue; } - NodeParam* param; - - if (reader->name() == QStringLiteral("input")) { - param = GetInputWithID(param_id); - } else { - param = GetOutputWithID(param_id); - } + NodeInput* param = GetInputWithID(param_id); if (!param) { qDebug() << "No parameter in" << id() << "with parameter" << param_id; @@ -135,19 +118,12 @@ void Node::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("label"), GetLabel()); - foreach (NodeParam* param, parameters()) { - switch (param->type()) { - case NodeParam::kInput: - writer->writeStartElement(QStringLiteral("input")); - break; - case NodeParam::kOutput: - writer->writeStartElement(QStringLiteral("output")); - break; - } + foreach (NodeInput* input, inputs_) { + writer->writeStartElement(QStringLiteral("input")); - param->Save(writer); + input->Save(writer); - writer->writeEndElement(); // input/output + writer->writeEndElement(); // input } writer->writeStartElement(QStringLiteral("custom")); @@ -170,30 +146,33 @@ void Node::Retranslate() { } -void Node::AddParameter(NodeParam *param) +void Node::RemoveNodesAndExclusiveDependencies(Node *node, QUndoCommand *command) { - // Ensure no other param with this ID has been added to this Node (since that defeats the purpose) - Q_ASSERT(!HasParamWithID(param->id())); + // Remove main node + RemoveNodeAndDisconnect(node, command); - if (params_.contains(param)) { - return; + // Remove exclusive dependencies + QVector deps = node->GetExclusiveDependencies(); + foreach (Node* d, deps) { + RemoveNodeAndDisconnect(d, command); + } +} + +void Node::RemoveNodeAndDisconnect(Node *node, QUndoCommand *command) +{ + // Disconnect everything + foreach (const InputConnection& conn, node->output_connections()) { + new NodeEdgeRemoveCommand(node, conn.input, conn.element, command); } - param->setParent(this); - - // Keep main output as the last parameter, assume if there are no parameters that this is the output parameter - if (params_.isEmpty()) { - params_.append(param); - } else { - params_.insert(params_.size()-1, param); + foreach (NodeInput* input, node->inputs_) { + for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { + new NodeEdgeRemoveCommand(it.value(), input, it.key(), command); + } } - connect(param, &NodeParam::EdgeAdded, this, &Node::EdgeAdded); - connect(param, &NodeParam::EdgeRemoved, this, &Node::EdgeRemoved); - - if (param->type() == NodeParam::kInput) { - ConnectInput(static_cast(param)); - } + // Remove node + new NodeRemoveCommand(node, command); } NodeValueTable Node::Value(NodeValueDatabase &value) const @@ -201,32 +180,26 @@ NodeValueTable Node::Value(NodeValueDatabase &value) const return value.Merge(); } -void Node::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) +void Node::InvalidateCache(const TimeRange &range, const InputConnection &from) { Q_UNUSED(from) - SendInvalidateCache(range, source); + SendInvalidateCache(range); } void Node::BeginOperation() { - foreach (NodeParam* param, params_) { - if (param->type() == NodeParam::kOutput) { - foreach (NodeEdgePtr edge, param->edges()) { - edge->input()->parentNode()->BeginOperation(); - } - } + // Ripple through graph + foreach (const InputConnection& conn, output_connections()) { + conn.input->parent()->BeginOperation(); } } void Node::EndOperation() { - foreach (NodeParam* param, params_) { - if (param->type() == NodeParam::kOutput) { - foreach (NodeEdgePtr edge, param->edges()) { - edge->input()->parentNode()->EndOperation(); - } - } + // Ripple through graph + foreach (const InputConnection& conn, output_connections()) { + conn.input->parent()->EndOperation(); } } @@ -260,7 +233,7 @@ QVector Node::CopyDependencyGraph(const QVector &nodes, QUndoCom if (command) { new NodeAddCommand(graph, c, command); } else { - graph->AddNode(c); + c->setParent(graph); } // Store in array at the same index as source @@ -274,27 +247,20 @@ QVector Node::CopyDependencyGraph(const QVector &nodes, QUndoCom void Node::CopyDependencyGraph(const QVector &src, const QVector &dst, QUndoCommand *command) { - int nb_nodes = src.size(); + for (int i=0; iinputs()) { + for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { + int connection_index = src.indexOf(it.value()); - for (int i=0; i inputs = src.at(i)->GetInputsIncludingArrays(); - - for (int j=0; jget_connected_node() == src.at(j)) { + if (connection_index > -1) { // Found a connection - NodeOutput* copy_output = dst.at(j)->GetOutputWithID(input->get_connected_output()->id()); - NodeInput* copy_input = dst.at(i)->GetInputWithID(input->id()); + Node* dst_output = dst.at(connection_index); + NodeInput* dst_input = dst.at(i)->GetInputWithID(input->id()); if (command) { - new NodeEdgeAddCommand(copy_output, copy_input, command); + new NodeEdgeAddCommand(dst_output, dst_input, it.key(), command); } else { - NodeParam::ConnectEdge(copy_output, copy_input); + ConnectEdge(dst_output, dst_input, it.key()); } } } @@ -302,23 +268,19 @@ void Node::CopyDependencyGraph(const QVector &src, const QVector } } -void Node::SendInvalidateCache(const TimeRange &range, NodeInput *source) +void Node::SendInvalidateCache(const TimeRange &range) { - // Loop through all parameters (there should be no children that are not NodeParams) - foreach (NodeParam* param, params_) { - // If the Node is an output, relay the signal to any Nodes that are connected to it - if (param->type() == NodeParam::kOutput) { - foreach (NodeEdgePtr edge, param->edges()) { - NodeInput* connected_input = edge->input(); - Node* connected_node = connected_input->parentNode(); - - // Send clear cache signal to the Node - connected_node->InvalidateCache(range, connected_input, source); - } - } + foreach (const InputConnection& conn, output_connections()) { + // Send clear cache signal to the Node + conn.input->parent()->InvalidateCache(range, conn); } } +void Node::IgnoreConnectionSignalsFrom(NodeInput *input) +{ + ignore_connections_.append(input); +} + void Node::LoadInternal(QXmlStreamReader *reader, XMLNodeData &) { reader->skipCurrentElement(); @@ -330,43 +292,7 @@ void Node::SaveInternal(QXmlStreamWriter *) const QVector Node::GetInputsToHash() const { - return GetInputsIncludingArrays(); -} - -void GetInputsIncludingArraysInternal(NodeInputArray* array, QVector& list) -{ - foreach (NodeInput* input, array->sub_params()) { - list.append(input); - - if (input->IsArray()) { - GetInputsIncludingArraysInternal(static_cast(input), list); - } - } -} - -QVector Node::GetInputsIncludingArrays() const -{ - QVector inputs; - - foreach (NodeParam* param, params_) { - if (param->type() == NodeParam::kInput) { - NodeInput* input = static_cast(param); - - inputs.append(input); - - if (input->IsArray()) { - GetInputsIncludingArraysInternal(static_cast(input), inputs); - } - } - } - - return inputs; -} - -QVector Node::GetOutputs() const -{ - // The current design only uses one output per node. This function returns a list just in case that changes. - return {output_}; + return inputs_; } bool Node::HasGizmos() const @@ -414,63 +340,9 @@ void Node::Hash(QCryptographicHash &hash, const rational& time) const foreach (NodeInput* input, inputs) { // For each input, try to hash its value - - // Get time adjustment - // For a single frame, we only care about one of the times - rational input_time = InputTimeAdjustment(input, TimeRange(time, time)).in(); - - if (input->is_connected()) { - // Traverse down this edge - input->get_connected_node()->Hash(hash, input_time); - } else { - // Grab the value at this time - QVariant value = input->get_value_at_time(input_time); - hash.addData(NodeParam::ValueToBytes(input->data_type(), value)); - } - - // We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer - if (input->data_type() == NodeParam::kFootage) { - Stream* stream = Node::ValueToPtr(input->get_standard_value()); - - if (stream) { - // Add footage details to hash - - // Footage filename - hash.addData(stream->footage()->filename().toUtf8()); - - // Footage last modified date - hash.addData(QString::number(stream->footage()->timestamp()).toUtf8()); - - // Footage stream - hash.addData(QString::number(stream->index()).toUtf8()); - - if (stream->type() == Stream::kVideo) { - VideoStream* image_stream = static_cast(stream); - - // Current color config and space - hash.addData(image_stream->footage()->project()->color_manager()->GetConfigFilename().toUtf8()); - hash.addData(image_stream->colorspace().toUtf8()); - - // Alpha associated setting - hash.addData(QString::number(image_stream->premultiplied_alpha()).toUtf8()); - - // Pixel aspect ratio - hash.addData(reinterpret_cast(&image_stream->pixel_aspect_ratio()), sizeof(rational)); - } - - // Footage timestamp - if (stream->type() == Stream::kVideo) { - VideoStream* video_stream = static_cast(stream); - - int64_t video_ts = Timecode::time_to_timestamp(input_time, video_stream->timebase()); - - // Add timestamp in units of the video stream's timebase - hash.addData(reinterpret_cast(&video_ts), sizeof(int64_t)); - - // Add start time - used for both image sequences and video streams - hash.addData(QString::number(video_stream->start_time()).toUtf8()); - } - } + HashInputElement(hash, input, -1, time); + for (int i=0; iArraySize(); i++) { + HashInputElement(hash, input, i, time); } } } @@ -479,19 +351,14 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections) { Q_ASSERT(source->id() == destination->id()); - const QVector& src_param = source->params_; - const QVector& dst_param = destination->params_; + const QVector& src_param = source->inputs_; + const QVector& dst_param = destination->inputs_; for (int i=0;i(src_param.at(i)); + NodeInput* dst = static_cast(dst_param.at(i)); - if (p->type() == NodeParam::kInput) { - NodeInput* src = static_cast(p); - - NodeInput* dst = static_cast(dst_param.at(i)); - - NodeInput::CopyValues(src, dst, include_connections); - } + NodeInput::CopyValues(src, dst, include_connections); } destination->SetPosition(source->GetPosition()); @@ -508,31 +375,6 @@ void Node::SetCanBeDeleted(bool s) can_be_deleted_ = s; } -bool Node::IsBlock() const -{ - return false; -} - -bool Node::IsTrack() const -{ - return false; -} - -bool Node::IsMedia() const -{ - return false; -} - -const QVector& Node::parameters() const -{ - return params_; -} - -int Node::IndexOfParameter(NodeParam *param) const -{ - return params_.indexOf(param); -} - /** * @brief Recursively collects dependencies of Node `n` and appends them to QList `list` * @@ -541,17 +383,91 @@ int Node::IndexOfParameter(NodeParam *param) const * TRUE to recursively traverse each node for a complete dependency graph. FALSE to return only the immediate * dependencies. */ -QVector Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const { - QVector inputs = GetInputsIncludingArrays(); +QVector Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const +{ QVector list; - foreach (NodeInput* i, inputs) { + foreach (NodeInput* i, inputs_) { i->GetDependencies(list, traverse, exclusive_only); } return list; } +void Node::HashInputElement(QCryptographicHash &hash, NodeInput *input, int element, const rational &time) const +{ + // Get time adjustment + // For a single frame, we only care about one of the times + rational input_time = InputTimeAdjustment(input, TimeRange(time, time)).in(); + + if (input->IsConnected(element)) { + // Traverse down this edge + input->GetConnectedNode(element)->Hash(hash, input_time); + } else { + // Grab the value at this time + QVariant value = input->GetValueAtTime(input_time, element); + hash.addData(NodeValue::ValueToBytes(input->GetDataType(), value)); + } + + // We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer + if (input->GetDataType() == NodeValue::kFootage) { + Stream* stream = Node::ValueToPtr(input->GetStandardValue(element)); + + if (stream) { + // Add footage details to hash + + // Footage filename + hash.addData(stream->footage()->filename().toUtf8()); + + // Footage last modified date + hash.addData(QString::number(stream->footage()->timestamp()).toUtf8()); + + // Footage stream + hash.addData(QString::number(stream->index()).toUtf8()); + + if (stream->type() == Stream::kVideo) { + VideoStream* image_stream = static_cast(stream); + + // Current color config and space + hash.addData(image_stream->footage()->project()->color_manager()->GetConfigFilename().toUtf8()); + hash.addData(image_stream->colorspace().toUtf8()); + + // Alpha associated setting + hash.addData(QString::number(image_stream->premultiplied_alpha()).toUtf8()); + + // Pixel aspect ratio + hash.addData(reinterpret_cast(&image_stream->pixel_aspect_ratio()), sizeof(rational)); + } + + // Footage timestamp + if (stream->type() == Stream::kVideo) { + VideoStream* video_stream = static_cast(stream); + + int64_t video_ts = Timecode::time_to_timestamp(input_time, video_stream->timebase()); + + // Add timestamp in units of the video stream's timebase + hash.addData(reinterpret_cast(&video_ts), sizeof(int64_t)); + + // Add start time - used for both image sequences and video streams + hash.addData(QString::number(video_stream->start_time()).toUtf8()); + } + } + } +} + +void Node::InputConnectionChanged(Node *source, int element) +{ + Q_UNUSED(source) + + NodeInput* input = static_cast(sender()); + + if (ignore_connections_.contains(input)) { + return; + } + + InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), {input, element}); +} + QVector Node::GetDependencies() const { return GetDependenciesInternal(true, false); @@ -586,9 +502,7 @@ void Node::GenerateFrame(FramePtr frame, const GenerateJob &job) const NodeInput *Node::GetInputWithID(const QString &id) const { - QVector inputs = GetInputsIncludingArrays(); - - foreach (NodeInput* i, inputs) { + foreach (NodeInput* i, inputs_) { if (i->id() == id) { return i; } @@ -597,31 +511,15 @@ NodeInput *Node::GetInputWithID(const QString &id) const return nullptr; } -NodeOutput *Node::GetOutputWithID(const QString &id) const -{ - foreach (NodeParam* p, params_) { - if (p->type() == NodeParam::kOutput - && p->id() == id) { - return static_cast(p); - } - } - - return nullptr; -} - bool Node::OutputsTo(Node *n, bool recursively) const { - QVector outputs = GetOutputs(); + foreach (const InputConnection& conn, output_connections()) { + Node* connected = conn.input->parent(); - foreach (NodeOutput* output, outputs) { - foreach (NodeEdgePtr edge, output->edges()) { - Node* connected = edge->input()->parentNode(); - - if (connected == n) { - return true; - } else if (recursively && connected->OutputsTo(n, recursively)) { - return true; - } + if (connected == n) { + return true; + } else if (recursively && connected->OutputsTo(n, recursively)) { + return true; } } @@ -630,39 +528,28 @@ bool Node::OutputsTo(Node *n, bool recursively) const bool Node::OutputsTo(const QString &id, bool recursively) const { - QVector outputs = GetOutputs(); + foreach (const InputConnection& conn, output_connections()) { + Node* connected = conn.input->parent(); - foreach (NodeOutput* output, outputs) { - foreach (NodeEdgePtr edge, output->edges()) { - Node* connected = edge->input()->parentNode(); - - if (connected->id() == id) { - return true; - } else if (recursively && connected->OutputsTo(id, recursively)) { - return true; - } + if (connected->id() == id) { + return true; + } else if (recursively && connected->OutputsTo(id, recursively)) { + return true; } } return false; } -bool Node::OutputsTo(NodeInput *input, bool recursively, bool include_arrays) const +bool Node::OutputsTo(NodeInput *input, bool recursively) const { - QVector outputs = GetOutputs(); + foreach (const InputConnection& conn, output_connections()) { + NodeInput* connected = conn.input; - foreach (NodeOutput* output, outputs) { - foreach (NodeEdgePtr edge, output->edges()) { - NodeInput* connected = edge->input(); - - if (connected == input) { - return true; - } else if (include_arrays && input->IsArray() - && static_cast(input)->sub_params().contains(connected)) { - return true; - } else if (recursively && connected->parentNode()->OutputsTo(input, recursively, include_arrays)) { - return true; - } + if (connected == input) { + return true; + } else if (recursively && connected->parent()->OutputsTo(input, recursively)) { + return true; } } @@ -671,11 +558,9 @@ bool Node::OutputsTo(NodeInput *input, bool recursively, bool include_arrays) co bool Node::InputsFrom(Node *n, bool recursively) const { - QVector inputs = GetInputsIncludingArrays(); - - foreach (NodeInput* input, inputs) { - foreach (NodeEdgePtr edge, input->edges()) { - Node* connected = edge->output()->parentNode(); + foreach (NodeInput* input, inputs_) { + for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { + Node* connected = it.value(); if (connected == n) { return true; @@ -690,11 +575,9 @@ bool Node::InputsFrom(Node *n, bool recursively) const bool Node::InputsFrom(const QString &id, bool recursively) const { - QVector inputs = GetInputsIncludingArrays(); - - foreach (NodeInput* input, inputs) { - foreach (NodeEdgePtr edge, input->edges()) { - Node* connected = edge->output()->parentNode(); + foreach (NodeInput* input, inputs_) { + for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { + Node* connected = it.value(); if (connected->id() == id) { return true; @@ -712,17 +595,13 @@ int Node::GetRoutesTo(Node *n) const bool outputs_directly = false; int routes = 0; - QVector outputs = GetOutputs(); + foreach (const InputConnection& conn, edges()) { + Node* connected_node = conn.input->parent(); - foreach (NodeOutput* o, outputs) { - foreach (NodeEdgePtr edge, o->edges()) { - Node* connected_node = edge->input()->parentNode(); - - if (connected_node == n) { - outputs_directly = true; - } else { - routes += connected_node->GetRoutesTo(n); - } + if (connected_node == n) { + outputs_directly = true; + } else { + routes += connected_node->GetRoutesTo(n); } } @@ -733,30 +612,11 @@ int Node::GetRoutesTo(Node *n) const return routes; } -bool Node::HasInputs() const -{ - return HasParamOfType(NodeParam::kInput, false); -} - -bool Node::HasOutputs() const -{ - return HasParamOfType(NodeParam::kOutput, false); -} - -bool Node::HasConnectedInputs() const -{ - return HasParamOfType(NodeParam::kInput, true); -} - -bool Node::HasConnectedOutputs() const -{ - return HasParamOfType(NodeParam::kOutput, true); -} - void Node::DisconnectAll() { - foreach (NodeParam* param, params_) { - param->DisconnectAll(); + // Disconnect outputs (inputs will be disconnected in their respective destructors) + while (!edges().isEmpty()) { + DisconnectEdge(this, edges().first().input, edges().first().element); } } @@ -793,19 +653,16 @@ QString Node::GetCategoryName(const CategoryID &c) return tr("Uncategorized"); } -QVector Node::TransformTimeTo(const TimeRange &time, Node *target, NodeParam::Type direction) +QVector Node::TransformTimeTo(const TimeRange &time, Node *target, bool input_dir) { QVector paths_found; - if (direction == NodeParam::kInput) { - // Get list of all inputs - QVector inputs = GetInputsIncludingArrays(); - + if (input_dir) { // If this input is connected, traverse it to see if we stumble across the specified `node` - foreach (NodeInput* input, inputs) { - if (input->is_connected()) { + foreach (NodeInput* input, inputs_) { + for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { TimeRange input_adjustment = InputTimeAdjustment(input, time); - Node* connected = input->get_connected_node(); + Node* connected = input->GetConnectedNode(it.key()); if (connected == target) { // We found the target, no need to keep traversing @@ -814,28 +671,21 @@ QVector Node::TransformTimeTo(const TimeRange &time, Node *target, No } } else { // We did NOT find the target, traverse this - paths_found.append(connected->TransformTimeTo(input_adjustment, target, direction)); + paths_found.append(connected->TransformTimeTo(input_adjustment, target, input_dir)); } } } } else { - // Get list of all outputs - QVector outputs = GetOutputs(); - // If this input is connected, traverse it to see if we stumble across the specified `node` - foreach (NodeOutput* output, outputs) { - if (output->is_connected()) { - foreach (NodeEdgePtr edge, output->edges()) { - Node* input_node = edge->input()->parentNode(); + foreach (const InputConnection& conn, edges()) { + Node* input_node = conn.input->parent(); - TimeRange output_adjustment = input_node->OutputTimeAdjustment(edge->input(), time); + TimeRange output_adjustment = input_node->OutputTimeAdjustment(conn.input, time); - if (input_node == target) { - paths_found.append(output_adjustment); - } else { - paths_found.append(input_node->TransformTimeTo(output_adjustment, target, direction)); - } - } + if (input_node == target) { + paths_found.append(output_adjustment); + } else { + paths_found.append(input_node->TransformTimeTo(output_adjustment, target, input_dir)); } } } @@ -850,10 +700,8 @@ QVariant Node::PtrToValue(void *ptr) bool Node::HasParamWithID(const QString &id) const { - foreach (NodeParam* p, params_) - { - if (p->id() == id) - { + foreach (NodeInput* i, inputs_) { + if (i->id() == id) { return true; } } @@ -861,11 +709,6 @@ bool Node::HasParamWithID(const QString &id) const return false; } -NodeOutput *Node::output() const -{ - return output_; -} - const QPointF &Node::GetPosition() const { return position_; @@ -878,63 +721,9 @@ void Node::SetPosition(const QPointF &pos) emit PositionChanged(position_); } -void Node::AddInput(NodeInput *input) +void Node::InputChanged(const TimeRange& range, int element) { - AddParameter(input); -} - -bool Node::HasParamOfType(NodeParam::Type type, bool must_be_connected) const -{ - foreach (NodeParam* p, params_) { - if (p->type() == type - && (p->is_connected() || !must_be_connected)) { - return true; - } - } - - return false; -} - -void Node::ConnectInput(NodeInput *input) -{ - connect(input, &NodeInput::ValueChanged, this, &Node::InputChanged); - connect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged); - connect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged); - - if (input->IsArray()) { - NodeInputArray* array = static_cast(input); - - connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged); - connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged); - connect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded); - connect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved); - } -} - -void Node::DisconnectInput(NodeInput *input) -{ - if (input->IsArray()) { - NodeInputArray* array = static_cast(input); - - disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::InputConnectionChanged); - disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::InputConnectionChanged); - disconnect(array, &NodeInputArray::SubParamEdgeAdded, this, &Node::EdgeAdded); - disconnect(array, &NodeInputArray::SubParamEdgeRemoved, this, &Node::EdgeRemoved); - } - - disconnect(input, &NodeInput::ValueChanged, this, &Node::InputChanged); - disconnect(input, &NodeInput::EdgeAdded, this, &Node::InputConnectionChanged); - disconnect(input, &NodeInput::EdgeRemoved, this, &Node::InputConnectionChanged); -} - -void Node::InputChanged(const TimeRange& range) -{ - InvalidateCache(range, static_cast(sender()), static_cast(sender())); -} - -void Node::InputConnectionChanged(NodeEdgePtr edge) -{ - InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), edge->input(), edge->input()); + InvalidateCache(range, InputConnection(static_cast(sender()), element)); } QRectF Node::CreateGizmoHandleRect(const QPointF &pt, int radius) @@ -967,4 +756,33 @@ void Node::DrawAndExpandGizmoHandles(QPainter *p, int handle_radius, QRectF *rec } } +void Node::childEvent(QChildEvent *event) +{ + NodeInput* input = dynamic_cast(event->child()); + + if (input) { + if (event->type() == QEvent::ChildAdded) { + // Ensure no other param with this ID has been added to this Node (since that defeats the purpose) + Q_ASSERT(!HasParamWithID(input->id())); + + // Keep main output as the last parameter, assume if there are no parameters that this is the output parameter + inputs_.append(input); + + connect(input, &NodeInput::InputConnected, this, &Node::InputConnected); + connect(input, &NodeInput::InputDisconnected, this, &Node::InputDisconnected); + connect(input, &NodeInput::ValueChanged, this, &Node::InputChanged); + connect(input, &NodeInput::InputConnected, this, &Node::InputConnectionChanged); + connect(input, &NodeInput::InputDisconnected, this, &Node::InputConnectionChanged); + } else if (event->type() == QEvent::ChildRemoved) { + disconnect(input, &NodeInput::InputConnected, this, &Node::InputConnected); + disconnect(input, &NodeInput::InputDisconnected, this, &Node::InputDisconnected); + disconnect(input, &NodeInput::ValueChanged, this, &Node::InputChanged); + disconnect(input, &NodeInput::InputConnected, this, &Node::InputConnectionChanged); + disconnect(input, &NodeInput::InputDisconnected, this, &Node::InputConnectionChanged); + + inputs_.removeOne(input); + } + } +} + } diff --git a/app/node/node.h b/app/node/node.h index 6f51460e4..d692c728e 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -31,9 +31,8 @@ #include "codec/samplebuffer.h" #include "common/rational.h" #include "common/xmlutils.h" +#include "node/connectable.h" #include "node/input.h" -#include "node/inputarray.h" -#include "node/output.h" #include "node/value.h" #include "render/audioparams.h" #include "render/job/generatejob.h" @@ -43,6 +42,8 @@ namespace olive { +class NodeGraph; + /** * @brief A single processing unit that can be connected with others to create intricate processing systems * @@ -57,7 +58,7 @@ namespace olive { * This is a simple base class designed to contain all the functionality for this kind of processing connective unit. * It is an abstract class intended to be subclassed to create nodes with actual functionality. */ -class Node : public QObject +class Node : public NodeConnectable { Q_OBJECT public: @@ -91,6 +92,11 @@ public: */ virtual Node* copy() const = 0; + /** + * @brief Convenience function - assumes parent is a NodeGraph + */ + NodeGraph* parent() const; + /** * @brief Clear current node variables and replace them with */ @@ -151,13 +157,28 @@ public: /** * @brief Return a list of NodeParams */ - const QVector& parameters() const; + const QVector& parameters() const + { + return inputs_; + } + + const QVector& inputs() const + { + return inputs_; + } + + static void RemoveNodesAndExclusiveDependencies(Node* node, QUndoCommand* command); + + static void RemoveNodeAndDisconnect(Node* node, QUndoCommand* command); /** * @brief Return the index of a parameter * @return Parameter index or -1 if this parameter is not part of this Node */ - int IndexOfParameter(NodeParam* param) const; + int IndexOfParameter(NodeInput* param) const + { + return inputs_.indexOf(param); + } /** * @brief Return a list of all Nodes that this Node's inputs are connected to (does not include this Node) @@ -201,11 +222,6 @@ public: */ NodeInput* GetInputWithID(const QString& id) const; - /** - * @brief Returns the output with the specified ID (or nullptr if it doesn't exist) - */ - NodeOutput* GetOutputWithID(const QString& id) const; - /** * @brief Returns whether this Node outputs to `n` * @@ -228,7 +244,7 @@ public: /** * @brief Same as OutputsTo(Node*), but for a specific node input rather than just a node. */ - bool OutputsTo(NodeInput* input, bool recursively, bool include_arrays) const; + bool OutputsTo(NodeInput* input, bool recursively) const; /** * @brief Returns whether this node ever receives an input from a particular node instance @@ -245,26 +261,6 @@ public: */ int GetRoutesTo(Node* n) const; - /** - * @brief Return whether this Node has input parameters - */ - bool HasInputs() const; - - /** - * @brief Return whether this Node has output parameters - */ - bool HasOutputs() const; - - /** - * @brief Return whether this Node has input parameters and at least one of them is connected - */ - bool HasConnectedInputs() const; - - /** - * @brief Return whether this Node has output parameters and at least one of them is connected - */ - bool HasConnectedOutputs() const; - /** * @brief Severs all input and output connections */ @@ -278,7 +274,7 @@ public: /** * @brief Transforms time from this node through the connections it takes to get to the specified node */ - QVector TransformTimeTo(const TimeRange& time, Node* target, NodeParam::Type direction); + QVector TransformTimeTo(const TimeRange& time, Node* target, bool input_dir); /** * @brief Find nodes of a certain type that this Node takes inputs from @@ -312,7 +308,7 @@ public: * the DAG. Even if the time needs to be transformed somehow (e.g. converting media time to sequence time), you can * call this function with transformed time and relay the signal that way. */ - virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput* source); + virtual void InvalidateCache(const TimeRange& range, const InputConnection& from = InputConnection()); /** * @brief Limits cache invalidation temporarily @@ -363,31 +359,6 @@ public: */ void SetCanBeDeleted(bool s); - /** - * @brief Returns whether this Node is a "Block" type or not - * - * You shouldn't ever need to override this since all derivatives of Block will automatically have this set to true. - * It's just a more convenient way of checking than dynamic_casting. - */ - virtual bool IsBlock() const; - - /** - * @brief Returns whether this Node is a "Track" type or not - * - * You shouldn't ever need to override this since all derivatives of Track will automatically have this set to true. - * It's just a more convenient way of checking than dynamic_casting. - */ - virtual bool IsTrack() const; - - - /** - * @brief Returns whether this Node is a "Media" type or not - * - * You shouldn't ever need to override this since all derivatives of Media will automatically have this set to true. - * It's just a more convenient way of checking than dynamic_casting. - */ - virtual bool IsMedia() const; - /** * @brief The main processing function * @@ -408,16 +379,10 @@ public: */ bool HasParamWithID(const QString& id) const; - NodeOutput* output() const; - const QPointF& GetPosition() const; void SetPosition(const QPointF& pos); - QVector GetInputsIncludingArrays() const; - - QVector GetOutputs() const; - virtual bool HasGizmos() const; virtual void DrawGizmos(NodeValueDatabase& db, QPainter* p); @@ -431,12 +396,22 @@ public: virtual void Hash(QCryptographicHash& hash, const rational &time) const; + const QVector& edges() const + { + return output_connections(); + } + protected: - void AddInput(NodeInput* input); + void SendInvalidateCache(const TimeRange &range); - void ClearCachedValuesInParameters(const rational& start_range, const rational& end_range); - - void SendInvalidateCache(const TimeRange &range, NodeInput *source); + /** + * @brief Don't send cache invalidation signals if `input` is connected or disconnected + * + * By default, when a node is connected or disconnected from input, the Node assumes that the + * parameters has changed throughout the duration of the clip (essential from 0 to infinity). + * In some scenarios, it may be preferable to handle this signal separately in order to + */ + void IgnoreConnectionSignalsFrom(NodeInput* input); virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data); @@ -462,30 +437,14 @@ protected: static void DrawAndExpandGizmoHandles(QPainter* p, int handle_radius, QRectF* rects, int count); -protected slots: - void InputChanged(const olive::TimeRange &range); + virtual void childEvent(QChildEvent* event) override; - void InputConnectionChanged(NodeEdgePtr edge); +protected slots: + void InputChanged(const olive::TimeRange &range, int element); + + void InputConnectionChanged(Node* source, int element); signals: - /** - * @brief Signal emitted when a node is connected to another node (creating an "edge") - * - * @param edge - * - * The edge that was added - */ - void EdgeAdded(NodeEdgePtr edge); - - /** - * @brief Signal emitted when a node is disconnected from another node (removing an "edge") - * - * @param edge - * - * The edge that was removed - */ - void EdgeRemoved(NodeEdgePtr edge); - /** * @brief Signal emitted whenever the position is set through SetPosition() */ @@ -497,41 +456,25 @@ signals: void LabelChanged(const QString& s); private: - /** - * @brief Add a parameter to this node - * - * The Node takes ownership of this parameter. - * - * This can be either an output or an input at any time. Parameters will always appear in the order they're added. - */ - void AddParameter(NodeParam* param); - - bool HasParamOfType(NodeParam::Type type, bool must_be_connected) const; - - void ConnectInput(NodeInput* input); - - void DisconnectInput(NodeInput* input); - template static void FindInputNodeInternal(const Node* n, QVector& list); template static void FindOutputNodeInternal(const Node* n, QVector& list); - QVector GetDependenciesInternal(bool traverse, bool exclusive_only) const; + QVector GetDependenciesInternal(bool traverse, bool exclusive_only) const; - QVector params_; + void HashInputElement(QCryptographicHash& hash, NodeInput* input, int element, const rational& time) const; + + QVector inputs_; + + QVector ignore_connections_; /** * @brief Internal variable for whether this Node can be deleted or not */ bool can_be_deleted_; - /** - * @brief Primary node output - */ - NodeOutput* output_; - /** * @brief UI position for NodeViews */ @@ -542,23 +485,22 @@ private: */ QString label_; +private slots: + }; template void Node::FindInputNodeInternal(const Node* n, QVector &list) { - QVector inputs = n->GetInputsIncludingArrays(); - - foreach (NodeInput* input, inputs) { - if (input->is_connected()) { - Node* connected = input->get_connected_node(); - T* cast_test = dynamic_cast(connected); + foreach (NodeInput* input, n->inputs_) { + foreach (Node* edge, input->edges()) { + T* cast_test = dynamic_cast(edge); if (cast_test) { list.append(cast_test); } - FindInputNodeInternal(connected, list); + FindInputNodeInternal(edge, list); } } } @@ -580,9 +522,10 @@ T* Node::ValueToPtr(const QVariant &ptr) } template -void Node::FindOutputNodeInternal(const Node* n, QVector& list) { - foreach (NodeEdgePtr edge, n->output()->edges()) { - Node* connected = edge->input()->parentNode(); +void Node::FindOutputNodeInternal(const Node* n, QVector& list) +{ + foreach (const InputConnection& edge, n->edges()) { + Node* connected = static_cast(edge.input->parent()); T* cast_test = dynamic_cast(connected); if (cast_test) { diff --git a/app/node/nodecopypaste.cpp b/app/node/nodecopypaste.cpp index f78db63ff..b5b1f03ae 100644 --- a/app/node/nodecopypaste.cpp +++ b/app/node/nodecopypaste.cpp @@ -148,7 +148,7 @@ QVector NodeCopyPasteService::PasteNodesFromClipboard(Sequence *graph, Q foreach (Item* item, footage) { foreach (Stream* s, static_cast(item)->streams()) { if (s == loaded_stream) { - con.input->set_standard_value(Node::PtrToValue(s)); + con.input->SetStandardValue(Node::PtrToValue(s), con.element); found = true; break; } diff --git a/app/node/output.cpp b/app/node/output.cpp deleted file mode 100644 index 3f9320c67..000000000 --- a/app/node/output.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "output.h" - -#include "common/xmlutils.h" -#include "node/node.h" - -namespace olive { - -NodeOutput::NodeOutput(const QString &id) : - NodeParam(id) -{ -} - -NodeParam::Type NodeOutput::type() -{ - return kOutput; -} - -QString NodeOutput::name() -{ - if (name_.isEmpty()) { - return tr("Output"); - } - - return NodeParam::name(); -} - -void NodeOutput::Load(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) -{ - XMLAttributeLoop(reader, attr) { - if (cancelled && *cancelled) { - return; - } - - if (attr.name() == "ptr") { - quintptr saved_ptr = attr.value().toULongLong(); - - xml_node_data.output_ptrs.insert(saved_ptr, this); - } - } - - reader->skipCurrentElement(); -} - -void NodeOutput::Save(QXmlStreamWriter *writer) const -{ - writer->writeAttribute("id", id()); - - writer->writeAttribute("ptr", QString::number(reinterpret_cast(this))); -} - -} diff --git a/app/node/output.h b/app/node/output.h deleted file mode 100644 index 26ad2141a..000000000 --- a/app/node/output.h +++ /dev/null @@ -1,58 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 NODEOUTPUT_H -#define NODEOUTPUT_H - -#include "common/timerange.h" -#include "param.h" - -namespace olive { - -/** - * @brief A node parameter designed to serve data to the input of another node - */ -class NodeOutput : public NodeParam -{ - Q_OBJECT -public: - /** - * @brief NodeOutput Constructor - */ - NodeOutput(const QString& id); - - /** - * @brief Returns kOutput - */ - virtual Type type() override; - - virtual QString name() override; - - virtual void Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled) override; - - virtual void Save(QXmlStreamWriter* writer) const override; - -private: - -}; - -} - -#endif // NODEOUTPUT_H diff --git a/app/node/output/track/CMakeLists.txt b/app/node/output/track/CMakeLists.txt index 660f66b0d..302cfa83d 100644 --- a/app/node/output/track/CMakeLists.txt +++ b/app/node/output/track/CMakeLists.txt @@ -16,9 +16,9 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/output/track/track.h node/output/track/track.cpp - node/output/track/tracklist.h + node/output/track/track.h node/output/track/tracklist.cpp + node/output/track/tracklist.h PARENT_SCOPE ) diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 28f12558e..4687d6c80 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -38,18 +38,18 @@ TrackOutput::TrackOutput() : index_(-1), locked_(false) { - block_input_ = new NodeInputArray("block_in", NodeParam::kAny); - block_input_->set_is_keyframable(false); - AddInput(block_input_); - connect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::BlockConnected); - connect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::BlockDisconnected); - disconnect(block_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackOutput::InputConnectionChanged); - disconnect(block_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackOutput::InputConnectionChanged); + block_input_ = new NodeInput(this, QStringLiteral("block_in"), NodeValue::kNone); + block_input_->SetKeyframable(false); + connect(block_input_, &NodeInput::InputConnected, this, &TrackOutput::BlockConnected); + connect(block_input_, &NodeInput::InputDisconnected, this, &TrackOutput::BlockDisconnected); - muted_input_ = new NodeInput("muted_in", NodeParam::kBoolean); - muted_input_->set_is_keyframable(false); + // Since blocks are time based, we can handle the invalidate timing a little more intelligently + // on our end + IgnoreConnectionSignalsFrom(block_input_); + + muted_input_ = new NodeInput(this, QStringLiteral("muted_in"), NodeValue::kBoolean); + muted_input_->SetKeyframable(false); connect(muted_input_, &NodeInput::ValueChanged, this, &TrackOutput::MutedInputValueChanged); - AddInput(muted_input_); // Set default height track_height_ = kTrackHeightDefault; @@ -245,21 +245,16 @@ QList TrackOutput::BlocksAtTimeRange(const TimeRange &range) const return list; } -const QList &TrackOutput::Blocks() const -{ - return block_cache_; -} - -void TrackOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) +void TrackOutput::InvalidateCache(const TimeRange& range, const InputConnection& from) { TimeRange limited; - if (block_input_->sub_params().contains(from) - && from->get_connected_node() - && from->get_connected_node()->IsBlock()) { - // Limit the range signal to the corresponding block - Block* b = static_cast(from->get_connected_node()); + Block* b; + if (from.input == block_input_ + && from.element >= 0 + && (b = dynamic_cast(from.input->GetConnectedNode(from.element)))) { + // Limit the range signal to the corresponding block if (range.out() <= b->in() || range.in() >= b->out()) { return; } @@ -269,7 +264,7 @@ void TrackOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeI limited = TimeRange(qMax(range.in(), rational(0)), qMin(range.out(), track_length())); } - Node::InvalidateCache(limited, from, source); + Node::InvalidateCache(limited, from); } void TrackOutput::InsertBlockBefore(Block* block, Block* after) @@ -294,13 +289,13 @@ void TrackOutput::PrependBlock(Block *block) { BeginOperation(); - block_input_->Prepend(); - NodeParam::ConnectEdge(block->output(), block_input_->First()); + block_input_->ArrayPrepend(); + Node::ConnectEdge(block, block_input_, 0); EndOperation(); // Everything has shifted at this point - Node::InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_); + Node::InvalidateCache(TimeRange(0, track_length()), InputConnection()); } void TrackOutput::InsertBlockAtIndex(Block *block, int index) @@ -308,26 +303,25 @@ void TrackOutput::InsertBlockAtIndex(Block *block, int index) BeginOperation(); int insert_index = GetInputIndexFromCacheIndex(index); - block_input_->InsertAt(insert_index); - NodeParam::ConnectEdge(block->output(), - block_input_->At(insert_index)); + block_input_->ArrayInsert(insert_index); + Node::ConnectEdge(block, block_input_, insert_index); EndOperation(); - Node::InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_); + Node::InvalidateCache(TimeRange(block->in(), track_length())); } void TrackOutput::AppendBlock(Block *block) { BeginOperation(); - block_input_->Append(); - NodeParam::ConnectEdge(block->output(), block_input_->Last()); + block_input_->ArrayAppend(); + Node::ConnectEdge(block, block_input_, block_input_->ArraySize() - 1); EndOperation(); // Invalidate area that block was added to - Node::InvalidateCache(TimeRange(block->in(), track_length()), block_input_, block_input_); + Node::InvalidateCache(TimeRange(block->in(), track_length())); } void TrackOutput::RippleRemoveBlock(Block *block) @@ -337,11 +331,11 @@ void TrackOutput::RippleRemoveBlock(Block *block) rational remove_in = block->in(); rational remove_out = block->out(); - block_input_->RemoveAt(GetInputIndexFromCacheIndex(block)); + block_input_->ArrayRemove(GetInputIndexFromCacheIndex(block)); EndOperation(); - Node::InvalidateCache(TimeRange(remove_in, qMax(track_length(), remove_out)), block_input_, block_input_); + Node::InvalidateCache(TimeRange(remove_in, qMax(track_length(), remove_out))); } void TrackOutput::ReplaceBlock(Block *old, Block *replace) @@ -350,30 +344,26 @@ void TrackOutput::ReplaceBlock(Block *old, Block *replace) int index_of_old_block = GetInputIndexFromCacheIndex(old); - NodeParam::DisconnectEdge(old->output(), - block_input_->At(index_of_old_block)); + DisconnectEdge(old, block_input_, index_of_old_block); - NodeParam::ConnectEdge(replace->output(), - block_input_->At(index_of_old_block)); + ConnectEdge(replace, block_input_, index_of_old_block); EndOperation(); if (old->length() == replace->length()) { - Node::InvalidateCache(TimeRange(replace->in(), replace->out()), block_input_, block_input_); + Node::InvalidateCache(TimeRange(replace->in(), replace->out())); } else { - Node::InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX), block_input_, block_input_); + Node::InvalidateCache(TimeRange(replace->in(), RATIONAL_MAX)); } } TrackOutput *TrackOutput::TrackFromBlock(const Block *block) { - NodeOutput* output = block->output(); + foreach (const InputConnection& conn, block->edges()) { + TrackOutput* track = dynamic_cast(conn.input->parent()); - foreach (NodeEdgePtr edge, output->edges()) { - Node* n = edge->input()->parentNode(); - - if (n->IsTrack()) { - return static_cast(n); + if (track) { + return track; } } @@ -385,11 +375,6 @@ const rational &TrackOutput::track_length() const return track_length_; } -bool TrackOutput::IsTrack() const -{ - return true; -} - QString TrackOutput::GetDefaultTrackName(Timeline::TrackType type, int index) { // Starts tracks at 1 rather than 0 @@ -409,7 +394,7 @@ QString TrackOutput::GetDefaultTrackName(Timeline::TrackType type, int index) bool TrackOutput::IsMuted() const { - return muted_input_->get_standard_value().toBool(); + return muted_input_->GetStandardValue().toBool(); } bool TrackOutput::IsLocked() const @@ -417,7 +402,7 @@ bool TrackOutput::IsLocked() const return locked_; } -NodeInputArray *TrackOutput::block_input() const +NodeInput *TrackOutput::block_input() const { return block_input_; } @@ -434,8 +419,8 @@ void TrackOutput::Hash(QCryptographicHash &hash, const rational &time) const void TrackOutput::SetMuted(bool e) { - muted_input_->set_standard_value(e); - Node::InvalidateCache(TimeRange(0, track_length()), block_input_, block_input_); + muted_input_->SetStandardValue(e); + Node::InvalidateCache(TimeRange(0, track_length())); } void TrackOutput::SetLocked(bool e) @@ -472,8 +457,8 @@ int TrackOutput::GetInputIndexFromCacheIndex(int cache_index) int TrackOutput::GetInputIndexFromCacheIndex(Block *block) { - for (int i=0; iGetSize(); i++) { - if (block_input_->At(i)->get_connected_node() == block) { + for (int i=0; iArraySize(); i++) { + if (block_input_->GetConnectedNode(i) == block) { return i; } } @@ -490,96 +475,128 @@ void TrackOutput::SetLengthInternal(const rational &r, bool invalidate) emit TrackLengthChanged(); if (invalidate) { - Node::InvalidateCache(invalidate_range, - block_input_, - block_input_); + Node::InvalidateCache(invalidate_range); } } } -void TrackOutput::BlockConnected(NodeEdgePtr edge) +void TrackOutput::BlockConnected(Node *node, int element) { - QList new_block_list; - - foreach (NodeInput* i, block_input_->sub_params()) { - Node* connected = i->get_connected_node(); - - if (connected - && connected->IsBlock() - && !new_block_list.contains(static_cast(connected))) { - Block* b = static_cast(connected); - - if (!new_block_list.isEmpty()) { - new_block_list.last()->set_next(b); - b->set_previous(new_block_list.last()); - } - - new_block_list.append(b); - - if (!block_cache_.contains(b)) { - // Make connections to this block - connect(b, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); - - emit BlockAdded(b); - } - } + if (element == -1) { + // User has replaced the entire array, we will invalidate everything + InputConnectionChanged(node, element); + return; } - if (!new_block_list.isEmpty()) { - new_block_list.first()->set_previous(nullptr); - new_block_list.last()->set_next(nullptr); + // Check if a block was connected, if not, ignore + Block* block = dynamic_cast(node); + + if (!block) { + return; } - int new_index; + Block *previous = nullptr, *next = nullptr; - for (new_index = 0; new_index < block_cache_.size(); new_index++) { - if (block_cache_.at(new_index) != new_block_list.at(new_index)) { + for (int i=element-1; i>=0; i--) { + // Find previous block + previous = dynamic_cast(block_input_->GetConnectedNode(i)); + + if (previous) { break; } } - block_cache_ = new_block_list; + // Find cache index + int cache_index; + if (previous) { + // Insert block just after the previous block we found + cache_index = block_cache_.indexOf(previous) + 1; - UpdateInOutFrom(new_index); + // Use current previous' next as our next + next = previous->next(); + } else { + // Didn't find a previous, so insert block at 0 (prepend it) + cache_index = 0; - InputConnectionChanged(edge); -} - -void TrackOutput::BlockDisconnected(NodeEdgePtr edge) -{ - Block* b = static_cast(edge->output_node()); - - if (block_cache_.contains(b)) { - block_cache_.removeOne(b); - - Block* previous = b->previous(); - Block* next = b->next(); - - if (previous) { - previous->set_next(next); + if (!block_cache_.isEmpty()) { + next = block_cache_.first(); } - - if (next) { - next->set_previous(previous); - } - - b->set_previous(nullptr); - b->set_next(nullptr); - - if (next) { - UpdateInOutFrom(block_cache_.indexOf(next)); - } else if (block_cache_.isEmpty()) { - SetLengthInternal(rational()); - } else { - SetLengthInternal(block_cache_.last()->out()); - } - - disconnect(b, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); - - emit BlockRemoved(b); } - InputConnectionChanged(edge); + // Insert at index + block_cache_.insert(cache_index, block); + + // Update previous/next + if (previous) { + previous->set_next(block); + block->set_previous(previous); + } + + if (next) { + block->set_next(next); + next->set_previous(block); + } + + // Update ins/outs + UpdateInOutFrom(cache_index); + + // Connect to the block + connect(block, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); + + // Invalidate cache now that block should have an in point + Node::InvalidateCache(TimeRange(block->in(), track_length())); + + // Emit block added signal + emit BlockAdded(block); +} + +void TrackOutput::BlockDisconnected(Node* node, int element) +{ + if (element == -1) { + // User has replaced the entire array, we will invalidate everything + InputConnectionChanged(node, element); + return; + } + + Block* b = dynamic_cast(node); + + if (!b) { + return; + } + + TimeRange invalidate_range(b->in(), track_length()); + + // FIXME: What happens if a user connects the same block twice? This must be addressed in the + // upcoming timeline rewrite. + block_cache_.removeOne(b); + + Block* previous = b->previous(); + Block* next = b->next(); + + if (previous) { + previous->set_next(next); + } + + if (next) { + next->set_previous(previous); + } + + b->set_previous(nullptr); + b->set_next(nullptr); + + if (next) { + UpdateInOutFrom(block_cache_.indexOf(next)); + } else if (block_cache_.isEmpty()) { + SetLengthInternal(rational()); + } else { + SetLengthInternal(block_cache_.last()->out()); + } + + disconnect(b, &Block::LengthChanged, this, &TrackOutput::BlockLengthChanged); + + emit BlockRemoved(b); + + Node::InvalidateCache(invalidate_range); } void TrackOutput::BlockLengthChanged() @@ -595,7 +612,7 @@ void TrackOutput::BlockLengthChanged() TimeRange invalidate_region(qMin(old_out, new_out), track_length()); - Node::InvalidateCache(invalidate_region, block_input_, block_input_); + Node::InvalidateCache(invalidate_region); } void TrackOutput::MutedInputValueChanged() diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index a5dd8b112..123d5cf3e 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -150,9 +150,12 @@ public: */ QList BlocksAtTimeRange(const TimeRange& range) const; - const QList &Blocks() const; + const QList &Blocks() const + { + return block_cache_; + } - virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput *source) override; + virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override; /** * @brief Adds Block `block` at the very beginning of the Sequence before all other clips @@ -200,15 +203,13 @@ public: const rational& track_length() const; - virtual bool IsTrack() const override; - static QString GetDefaultTrackName(Timeline::TrackType type, int index); bool IsMuted() const; bool IsLocked() const; - NodeInputArray* block_input() const; + NodeInput* block_input() const; virtual void Hash(QCryptographicHash& hash, const rational &time) const override; @@ -277,7 +278,7 @@ private: QList block_cache_; - NodeInputArray* block_input_; + NodeInput* block_input_; NodeInput* muted_input_; @@ -294,9 +295,9 @@ private: AudioVisualWaveform waveform_; private slots: - void BlockConnected(NodeEdgePtr edge); + void BlockConnected(Node* node, int element); - void BlockDisconnected(NodeEdgePtr edge); + void BlockDisconnected(Node* node, int element); void BlockLengthChanged(); diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index d4e5ae8fe..d0d1db55f 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -27,13 +27,13 @@ namespace olive { -TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, NodeInputArray *track_input) : +TrackList::TrackList(ViewerOutput *parent, const Timeline::TrackType &type, NodeInput *track_input) : QObject(parent), track_input_(track_input), type_(type) { - connect(track_input_, &NodeInputArray::SubParamEdgeAdded, this, &TrackList::TrackConnected); - connect(track_input_, &NodeInputArray::SubParamEdgeRemoved, this, &TrackList::TrackDisconnected); + connect(track_input_, &NodeInput::InputConnected, this, &TrackList::TrackConnected); + connect(track_input_, &NodeInput::InputDisconnected, this, &TrackList::TrackDisconnected); } const Timeline::TrackType &TrackList::type() const @@ -75,173 +75,91 @@ int TrackList::GetTrackCount() const return track_cache_.size(); } -TrackOutput* TrackList::AddTrack() +void TrackList::TrackConnected(Node *node, int element) { - TrackOutput* track = new TrackOutput(); - GetParentGraph()->AddNode(track); - - track_input_->Append(); - - // Connect this track directly to this output - NodeParam::ConnectEdge(track->output(), - track_input_->At(track_input_->GetSize() - 1)); - - // Auto-merge with previous track - if (track_input_->GetSize() > 1) { - TrackOutput* last_track = nullptr; - - for (int i=track_cache_.size()-1;i>=0;i--) { - TrackOutput* test_track = track_cache_.at(i); - - if (test_track && test_track != track) { - last_track = test_track; - break; - } - } - - if (last_track && last_track->output()->is_connected()) { - foreach (NodeEdgePtr edge, last_track->output()->edges()) { - if (!track_input_->ContainsSubParameter(edge->input())) { - switch (type_) { - case Timeline::kTrackTypeVideo: - { - MergeNode* blend = new MergeNode(); - GetParentGraph()->AddNode(blend); - - NodeParam::ConnectEdge(track->output(), blend->blend_in()); - NodeParam::ConnectEdge(last_track->output(), blend->base_in()); - NodeParam::ConnectEdge(blend->output(), edge->input()); - break; - } - case Timeline::kTrackTypeAudio: - { - MathNode* add = new MathNode(); - GetParentGraph()->AddNode(add); - - NodeParam::ConnectEdge(track->output(), add->param_a_in()); - NodeParam::ConnectEdge(last_track->output(), add->param_b_in()); - NodeParam::ConnectEdge(add->output(), edge->input()); - break; - } - default: - break; - } - } - } - } - } - - return track; -} - -void TrackList::RemoveTrack(QObject* new_parent) -{ - if (track_cache_.isEmpty()) { + if (element == -1) { + // FIXME: Should probably merge into Viewer so we can actually do this + //static_cast(parent())->InputConnectionChanged(node, element); return; } - TrackOutput* track = track_cache_.last(); + TrackOutput* track = dynamic_cast(node); - GetParentGraph()->TakeNode(track); - - if (!new_parent) { - delete track; - } else { - track->setParent(new_parent); + if (!track) { + return; } - track_input_->RemoveLast(); -} + // Find "real" index + TrackOutput* next = nullptr; + for (int i=element+1; iArraySize(); i++) { + next = dynamic_cast(track_input_->GetConnectedNode(i)); -void TrackList::TrackConnected(NodeEdgePtr edge) -{ - int input_index = track_input_->IndexOfSubParameter(edge->input()); - - Q_ASSERT(input_index >= 0); - - Node* connected_node = edge->output()->parentNode(); - - if (connected_node->IsTrack()) { - TrackOutput* connected_track = static_cast(connected_node); - - { - // Find "real" index - TrackOutput* next = nullptr; - for (int i=input_index+1; iGetSize(); i++) { - Node* that_track = track_input_->At(i)->get_connected_node(); - - if (that_track && that_track->IsTrack()) { - next = static_cast(that_track); - break; - } - } - - int track_index; - - if (next) { - // Insert track before "next" - track_index = track_cache_.indexOf(next); - track_cache_.insert(track_index, connected_track); - } else { - // No "next", this track must come at the end - track_index = track_cache_.size(); - track_cache_.append(connected_track); - } - - // Update track indexes in the list (including this track) - UpdateTrackIndexesFrom(track_index); + if (next) { + break; } - - connect(connected_track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock); - connect(connected_track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock); - connect(connected_track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength); - connect(connected_track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot); - - connected_track->set_track_type(type_); - - emit TrackListChanged(); - - // This function must be called after the track is added to track_cache_, since it uses track_cache_ to determine - // the track's index - emit TrackAdded(connected_track); - - UpdateTotalLength(); - } + + int track_index; + + if (next) { + // Insert track before "next" + track_index = track_cache_.indexOf(next); + track_cache_.insert(track_index, track); + } else { + // No "next", this track must come at the end + track_index = track_cache_.size(); + track_cache_.append(track); + } + + // Update track indexes in the list (including this track) + UpdateTrackIndexesFrom(track_index); + + connect(track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock); + connect(track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock); + connect(track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength); + connect(track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot); + + track->set_track_type(type_); + + emit TrackListChanged(); + + // This function must be called after the track is added to track_cache_, since it uses track_cache_ to determine + // the track's index + emit TrackAdded(track); + + UpdateTotalLength(); } -void TrackList::TrackDisconnected(NodeEdgePtr edge) +void TrackList::TrackDisconnected(Node *node, int element) { - int track_index = track_input_->IndexOfSubParameter(edge->input()); + Q_UNUSED(element) - Q_ASSERT(track_index >= 0); + TrackOutput* track = dynamic_cast(node); - Node* connected_node = edge->output()->parentNode(); - - if (connected_node->IsTrack()) { - TrackOutput* track = static_cast(connected_node); - - int index_of_track = track_cache_.indexOf(track); - track_cache_.removeAt(index_of_track); - - // Update indices for all subsequent tracks - UpdateTrackIndexesFrom(index_of_track); - - // Traverse through Tracks uncaching and disconnecting them - emit TrackRemoved(track); - - track->SetIndex(-1); - track->set_track_type(Timeline::kTrackTypeNone); - - disconnect(track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock); - disconnect(track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock); - disconnect(track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength); - disconnect(track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot); - - emit TrackListChanged(); - - UpdateTotalLength(); + if (!track) { + return; } + + int index_of_track = track_cache_.indexOf(track); + track_cache_.removeAt(index_of_track); + + // Update indices for all subsequent tracks + UpdateTrackIndexesFrom(index_of_track); + + // Traverse through Tracks uncaching and disconnecting them + emit TrackRemoved(track); + + track->SetIndex(-1); + track->set_track_type(Timeline::kTrackTypeNone); + + disconnect(track, &TrackOutput::BlockAdded, this, &TrackList::TrackAddedBlock); + disconnect(track, &TrackOutput::BlockRemoved, this, &TrackList::TrackRemovedBlock); + disconnect(track, &TrackOutput::TrackLengthChanged, this, &TrackList::UpdateTotalLength); + disconnect(track, &TrackOutput::TrackHeightChangedInPixels, this, &TrackList::TrackHeightChangedSlot); + + emit TrackListChanged(); + + UpdateTotalLength(); } void TrackList::UpdateTrackIndexesFrom(int index) diff --git a/app/node/output/track/tracklist.h b/app/node/output/track/tracklist.h index 5dd3dbe84..815806b65 100644 --- a/app/node/output/track/tracklist.h +++ b/app/node/output/track/tracklist.h @@ -31,10 +31,11 @@ namespace olive { class ViewerOutput; -class TrackList : public QObject { +class TrackList : public QObject +{ Q_OBJECT public: - TrackList(ViewerOutput *parent, const Timeline::TrackType& type, NodeInputArray* track_input); + TrackList(ViewerOutput *parent, const Timeline::TrackType& type, NodeInput* track_input); const Timeline::TrackType& type() const; @@ -42,16 +43,17 @@ public: TrackOutput* GetTrackAt(int index) const; - TrackOutput *AddTrack(); - - void RemoveTrack(QObject *new_parent); - const rational& GetTotalLength() const; int GetTrackCount() const; NodeGraph* GetParentGraph() const; + NodeInput* track_input() const + { + return track_input_; + } + signals: void BlockAdded(Block* block, int index); @@ -75,7 +77,7 @@ private: */ QVector track_cache_; - NodeInputArray* track_input_; + NodeInput* track_input_; rational total_length_; @@ -85,12 +87,12 @@ private slots: /** * @brief Slot for when the track connection is added */ - void TrackConnected(NodeEdgePtr edge); + void TrackConnected(Node* node, int element); /** * @brief Slot for when the track connection is removed */ - void TrackDisconnected(NodeEdgePtr edge); + void TrackDisconnected(Node* node, int element); /** * @brief Slot for when a connected Track has added a Block so we can update the UI diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 37b6529ad..e296e159c 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -29,11 +29,9 @@ ViewerOutput::ViewerOutput() : audio_playback_cache_(this), operation_stack_(0) { - texture_input_ = new NodeInput("tex_in", NodeInput::kTexture); - AddInput(texture_input_); + texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); - samples_input_ = new NodeInput("samples_in", NodeInput::kSamples); - AddInput(samples_input_); + samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples); // Create TrackList instances track_inputs_.resize(Timeline::kTrackTypeCount); @@ -41,10 +39,8 @@ ViewerOutput::ViewerOutput() : for (int i=0;i(i), track_input); @@ -52,7 +48,7 @@ ViewerOutput::ViewerOutput() : connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache); connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength); connect(list, &TrackList::BlockAdded, this, &ViewerOutput::TrackListAddedBlock); - connect(list, &TrackList::BlockRemoved, this, &ViewerOutput::SignalBlockRemoved); + connect(list, &TrackList::BlockRemoved, this, &ViewerOutput::BlockRemoved); connect(list, &TrackList::TrackAdded, this, &ViewerOutput::TrackListAddedTrack); connect(list, &TrackList::TrackRemoved, this, &ViewerOutput::TrackRemoved); connect(list, &TrackList::TrackHeightChanged, this, &ViewerOutput::TrackHeightChangedSlot); @@ -112,17 +108,15 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &to) ShiftAudioCache(from, to); } -void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *source) +void ViewerOutput::InvalidateCache(const TimeRange& range, const InputConnection& from) { - emit GraphChangedFrom(source); - if (operation_stack_ == 0) { - if (from == texture_input_ || from == samples_input_) { + if (from.input == texture_input_ || from.input == samples_input_) { TimeRange invalidated_range(qMax(rational(), range.in()), qMin(GetLength(), range.out())); if (invalidated_range.in() != invalidated_range.out()) { - if (from == texture_input_) { + if (from.input == texture_input_) { video_frame_cache_.Invalidate(invalidated_range); } else { audio_playback_cache_.Invalidate(invalidated_range); @@ -133,7 +127,7 @@ void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, Node VerifyLength(); } - Node::InvalidateCache(range, from, source); + Node::InvalidateCache(range, from); } void ViewerOutput::set_video_params(const VideoParams &video) @@ -215,26 +209,33 @@ void ViewerOutput::VerifyLength() NodeTraverser traverser; - rational video_length; + rational video_length, audio_length, subtitle_length; - if (texture_input_->is_connected()) { - NodeValueTable t = traverser.GenerateTable(texture_input_->get_connected_node(), 0, 0); - video_length = t.Get(NodeParam::kNumber, "length").value(); + { + video_length = track_lists_.at(Timeline::kTrackTypeVideo)->GetTotalLength(); + + if (video_length.isNull() && texture_input_->IsConnected()) { + NodeValueTable t = traverser.GenerateTable(texture_input_->GetConnectedNode(), 0, 0); + video_length = t.Get(NodeValue::kRational, "length").value(); + } + + video_frame_cache_.SetLength(video_length); } - rational audio_length; + { + audio_length = track_lists_.at(Timeline::kTrackTypeAudio)->GetTotalLength(); - if (samples_input_->is_connected()) { - NodeValueTable t = traverser.GenerateTable(samples_input_->get_connected_node(), 0, 0); - audio_length = t.Get(NodeParam::kNumber, "length").value(); + if (audio_length.isNull() && samples_input_->IsConnected()) { + NodeValueTable t = traverser.GenerateTable(samples_input_->GetConnectedNode(), 0, 0); + audio_length = t.Get(NodeValue::kRational, "length").value(); + } + + audio_playback_cache_.SetLength(audio_length); } - video_length = qMax(video_length, track_lists_.at(Timeline::kTrackTypeVideo)->GetTotalLength()); - audio_length = qMax(audio_length, track_lists_.at(Timeline::kTrackTypeAudio)->GetTotalLength()); - rational subtitle_length = track_lists_.at(Timeline::kTrackTypeSubtitle)->GetTotalLength(); - - video_frame_cache_.SetLength(video_length); - audio_playback_cache_.SetLength(audio_length); + { + subtitle_length = track_lists_.at(Timeline::kTrackTypeSubtitle)->GetTotalLength(); + } rational real_length = qMax(subtitle_length, qMax(video_length, audio_length)); @@ -283,27 +284,6 @@ void ViewerOutput::set_media_name(const QString &name) emit MediaNameChanged(media_name_); } -void ViewerOutput::SignalBlockAdded(Block *block, const TrackReference& track) -{ - if (!operation_stack_) { - emit BlockAdded(block, track); - } else { - cached_block_removed_.removeOne(block); - cached_block_added_.insert(block, track); - } -} - -void ViewerOutput::SignalBlockRemoved(Block *block) -{ - if (!operation_stack_) { - emit BlockRemoved({block}); - } else { - // We keep track of all blocks that are removed, even if we don't end up signalling them - cached_block_added_.remove(block); - cached_block_removed_.append(block); - } -} - void ViewerOutput::BeginOperation() { operation_stack_++; @@ -315,23 +295,13 @@ void ViewerOutput::EndOperation() { operation_stack_--; - if (!operation_stack_) { - for (auto it=cached_block_added_.cbegin(); it!=cached_block_added_.cend(); it++) { - emit BlockAdded(it.key(), it.value()); - } - cached_block_added_.clear(); - - emit BlockRemoved(cached_block_removed_); - cached_block_removed_.clear(); - } - Node::EndOperation(); } void ViewerOutput::TrackListAddedBlock(Block *block, int index) { Timeline::TrackType type = static_cast(sender())->type(); - SignalBlockAdded(block, TrackReference(type, index)); + emit BlockAdded(block, TrackReference(type, index)); } void ViewerOutput::TrackListAddedTrack(TrackOutput *track) diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 66d111bd0..b49e74276 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -68,7 +68,7 @@ public: return samples_input_; } - virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override; + virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override; const VideoParams& video_params() const { return video_params_; @@ -127,8 +127,6 @@ public: signals: void TimebaseChanged(const rational&); - void GraphChangedFrom(NodeInput* source); - void LengthChanged(const rational& length); void SizeChanged(int width, int height); @@ -141,7 +139,7 @@ signals: void AudioParamsChanged(); void BlockAdded(Block* block, TrackReference track); - void BlockRemoved(const QList& blocks); + void BlockRemoved(Block* block); void TrackAdded(TrackOutput* track, Timeline::TrackType type); void TrackRemoved(TrackOutput* track); @@ -151,9 +149,6 @@ signals: void MediaNameChanged(const QString& name); private: - QMap cached_block_added_; - QList cached_block_removed_; - QUuid uuid_; NodeInput* texture_input_; @@ -191,9 +186,6 @@ private slots: void TrackHeightChangedSlot(int index, int height); - void SignalBlockAdded(Block *block, const TrackReference &track); - void SignalBlockRemoved(Block *block); - }; } diff --git a/app/node/param.cpp b/app/node/param.cpp deleted file mode 100644 index 3d1ce2c17..000000000 --- a/app/node/param.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "param.h" - -#include -#include -#include -#include -#include - -#include "node/node.h" -#include "node/input.h" -#include "node/output.h" -#include "render/color.h" - -namespace olive { - -NodeParam::NodeParam(const QString &id) : - id_(id), - connectable_(true) -{ - Q_ASSERT(!id_.isEmpty()); -} - -NodeParam::~NodeParam() -{ - // Clear all connected edges - DisconnectAll(); -} - -const QString NodeParam::id() const -{ - return id_; -} - -QString NodeParam::name() -{ - if (name_.isEmpty()) { - return tr("Value"); - } - - return name_; -} - -void NodeParam::set_name(const QString &name) -{ - name_ = name; -} - -Node *NodeParam::parentNode() const -{ - QObject* p = parent(); - - while (p) { - Node* cast_test = dynamic_cast(p); - if (cast_test) { - return cast_test; - } else { - p = p->parent(); - } - } - - return nullptr; -} - -int NodeParam::index() -{ - return parentNode()->IndexOfParameter(this); -} - -bool NodeParam::is_connected() const -{ - return !edges_.isEmpty(); -} - -bool NodeParam::is_connectable() const -{ - return connectable_; -} - -void NodeParam::set_connectable(bool connectable) -{ - connectable_ = connectable; -} - -const QVector &NodeParam::edges() -{ - return edges_; -} - -void NodeParam::DisconnectAll() -{ - while (!edges_.isEmpty()) { - DisconnectEdge(edges_.last()); - } -} - -NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input) -{ - if (!input->is_connectable()) { - return nullptr; - } - - // If the input can only accept one input (the default) and has one already, disconnect it - DisconnectForNewOutput(input); - - // Make sure it's not a duplicate of an edge that already exists - foreach (NodeEdgePtr existing, input->edges()) { - if (existing->output() == output) { - return nullptr; - } - } - - // Ensure both nodes are in the same graph - Q_ASSERT(output->parentNode()->parent() == input->parentNode()->parent()); - - NodeEdgePtr edge = std::make_shared(output, input); - - // The nodes should never be the same, and since we lock both nodes here, this can lead to a entire program freeze - // that's difficult to diagnose. This makes that issue very clear. - Q_ASSERT(output->parentNode() != input->parentNode()); - - output->edges_.append(edge); - input->edges_.append(edge); - - // Emit a signal than an edge was added (only one signal needs emitting) - emit input->EdgeAdded(edge); - - return edge; -} - -void NodeParam::DisconnectEdge(NodeEdgePtr edge) -{ - NodeOutput* output = edge->output(); - NodeInput* input = edge->input(); - - output->edges_.removeOne(edge); - input->edges_.removeOne(edge); - - emit input->EdgeRemoved(edge); -} - -void NodeParam::DisconnectEdge(NodeOutput *output, NodeInput *input) -{ - for (int i=0;iedges_.size();i++) { - NodeEdgePtr edge = output->edges_.at(i); - if (edge->input() == input) { - DisconnectEdge(edge); - break; - } - } -} - -NodeEdgePtr NodeParam::DisconnectForNewOutput(NodeInput *input) -{ - // If the input can only accept one input (the default) and has one already, disconnect it - if (!input->edges_.isEmpty()) { - NodeEdgePtr edge = input->edges_.first(); - - DisconnectEdge(edge); - - return edge; - } - - return nullptr; -} - -QString NodeParam::GetPrettyDataTypeName(const NodeParam::DataType &type) -{ - switch (type) { - case kNone: - return tr("None"); - case kInt: - case kCombo: - return tr("Integer"); - case kFloat: - return tr("Float"); - case kRational: - return tr("Rational"); - case kBoolean: - return tr("Boolean"); - case kColor: - return tr("Color"); - case kMatrix: - return tr("Matrix"); - case kText: - return tr("Text"); - case kFont: - return tr("Font"); - case kFile: - return tr("File"); - case kTexture: - return tr("Texture"); - case kSamples: - return tr("Samples"); - case kFootage: - return tr("Footage"); - case kVec2: - return tr("Vector 2D"); - case kVec3: - return tr("Vector 3D"); - case kVec4: - return tr("Vector 4D"); - - case kDecimal: - case kNumber: - case kString: - case kBuffer: - case kVector: - case kShaderJob: - case kSampleJob: - case kGenerateJob: - case kAny: - break; - } - - return tr("Unknown"); -} - -QByteArray NodeParam::ValueToBytes(const NodeParam::DataType &type, const QVariant &value) -{ - switch (type) { - case kInt: return ValueToBytesInternal(value); - case kFloat: return ValueToBytesInternal(value); - case kColor: return ValueToBytesInternal(value); - case kText: return value.toString().toUtf8(); - case kBoolean: return ValueToBytesInternal(value); - case kFont: return value.toString().toUtf8(); - case kFile: return value.toString().toUtf8(); - case kMatrix: return ValueToBytesInternal(value); - case kRational: return ValueToBytesInternal(value); - case kVec2: return ValueToBytesInternal(value); - case kVec3: return ValueToBytesInternal(value); - case kVec4: return ValueToBytesInternal(value); - case kCombo: return ValueToBytesInternal(value); - - // These types have no persistent input - case kNone: - case kFootage: - case kTexture: - case kSamples: - case kDecimal: - case kNumber: - case kString: - case kBuffer: - case kVector: - case kShaderJob: - case kSampleJob: - case kGenerateJob: - case kAny: - break; - } - - return QByteArray(); -} - -template -QByteArray NodeParam::ValueToBytesInternal(const QVariant &v) -{ - QByteArray bytes; - - int size_of_type = sizeof(T); - - bytes.resize(size_of_type); - T raw_val = v.value(); - memcpy(bytes.data(), &raw_val, static_cast(size_of_type)); - - return bytes; -} - -} diff --git a/app/node/param.h b/app/node/param.h deleted file mode 100644 index 0852fa7df..000000000 --- a/app/node/param.h +++ /dev/null @@ -1,437 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 NODEPARAM_H -#define NODEPARAM_H - -#include -#include -#include -#include - -#include "common/rational.h" -#include "common/xmlutils.h" -#include "node/edge.h" - -namespace olive { - -class Node; - -/** - * @brief A base parameter of a Node - * - * The main data points of a Node. NodeParams are added to Nodes so that Node::Process() can use data acquired either - * directly as a value set by the user, or through the output of another NodeParam. - * - * This is an abstract base class. In most cases you'll want NodeInput or NodeOutput. - */ -class NodeParam : public QObject -{ - Q_OBJECT -public: - /** - * @brief The type of parameter this is - */ - enum Type { - kInput, - kOutput - }; - - /** - * @brief The types of data that can be passed between Nodes - */ - enum DataType { - kNone = 0x0, - - /** - ****************************** SPECIFIC IDENTIFIERS ****************************** - */ - - /** - * Integer type - * - * Resolves to int64_t. - */ - kInt = 0x1, - - /** - * Decimal (floating-point) type - * - * Resolves to `double`. - */ - kFloat = 0x2, - - /** - * Decimal (rational) type - * - * Resolves to `double`. - */ - kRational = 0x4, - - /** - * Boolean type - * - * Resolves to `bool`. - */ - kBoolean = 0x8, - - /** - * Floating-point type - * - * Resolves to `Color`. - * - * Colors passed around the nodes should always be in reference space and preferably use - */ - kColor = 0x10, - - /** - * Matrix type - * - * Resolves to `QMatrix4x4`. - */ - kMatrix = 0x20, - - /** - * Text type - * - * Resolves to `QString`. - */ - kText = 0x40, - - /** - * Font type - * - * Resolves to `QFont`. - */ - kFont = 0x80, - - /** - * File type - * - * Resolves to a `QString` containing an absolute file path. - */ - kFile = 0x100, - - /** - * Image buffer type - * - * True value type depends on the render engine used. - */ - kTexture = 0x200, - - /** - * Audio samples type - * - * Resolves to `SampleBufferPtr`. - */ - kSamples = 0x400, - - /** - * Footage stream identifier type - * - * Resolves to `StreamPtr`. - */ - kFootage = 0x800, - - /** - * Two-dimensional vector (XY) type - * - * Resolves to `QVector2D`. - */ - kVec2 = 0x1000, - - /** - * Three-dimensional vector (XYZ) type - * - * Resolves to `QVector3D`. - */ - kVec3 = 0x2000, - - /** - * Four-dimensional vector (XYZW) type - * - * Resolves to `QVector4D`. - */ - kVec4 = 0x4000, - - /** - * ComboBox type - * - * Resolves to `int` - the index currently selected - */ - kCombo = 0x8000, - - /** - * Job type - * - * An internal type used to indicate to the renderer that an accelerated shader job needs to - * run. This value will usually be taken from a table and a kTexture value will be pushed to - * take its place. - */ - kShaderJob = 0x10000, - - /** - * Job type - * - * An internal type used to indicate to the renderer that an accelerated sample job needs to - * take place. This value will usually be taken from a table and a kSamples value will be - * pushed to take its place. - */ - kSampleJob = 0x20000, - - /** - * Job type - * - * An internal type used to indicate to the renderer that an accelerated sample job needs to - * take place. This value will usually be taken from a table and a kSamples value will be - * pushed to take its place. - */ - kGenerateJob = 0x40000, - - /** - ****************************** BROAD IDENTIFIERS ****************************** - */ - - /** - * Identifier for type that contains a decimal number - * - * Includes kFloat and kRational. - */ - kDecimal = 0x6, - - /** - * Identifier for type that contains a number of any kind (whole or decimal) - * - * Includes kInt, kFloat, and kRational. - */ - kNumber = 0x7, - - /** - * Identifier for type that contains a text string of any kind. - * - * Includes kText and kFile. - */ - kString = 0x140, - - /** - * Identifier for type that contains a either an image or audio buffer - * - * Includes kTexture and kSamples. - */ - kBuffer = 0x600, - - /** - * Identifier for type that contains a vector (two- to four-dimensional) - * - * Includes kVec2, kVec3, kVec4, and kColor. - */ - kVector = 0x7010, - - /** - * Identifier for any type - * - * Matches with all types except for kNone - */ - kAny = 0xFFFFFFFF - }; - - /** - * @brief NodeParam Constructor - */ - NodeParam(const QString& id); - - virtual ~NodeParam() override; - - /** - * @brief Load function - */ - virtual void Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled) = 0; - - /** - * @brief Save function - */ - virtual void Save(QXmlStreamWriter* writer) const = 0; - - /** - * @brief Return ID of this parameter - */ - const QString id() const; - - /** - * @brief The type of node parameter this is - * - * This must be set in subclasses, but most of the time you should probably subclass from NodeInput and NodeOutput - * anyway. - */ - virtual Type type() = 0; - - /** - * @brief Name of this parameter to be shown to the user - */ - virtual QString name(); - void set_name(const QString& name); - - /** - * @brief Node parent object - * - * Nodes and NodeParams use the QObject parent-child system. This function is a convenience function for - * static_cast(QObject::parent()) - */ - Node* parentNode() const; - - /** - * @brief Return the row index of this parameter in the parent node (primarily used for UI drawing functions) - */ - int index(); - - /** - * @brief Returns whether anything is connected to this parameter or not - */ - bool is_connected() const; - - bool is_connectable() const; - void set_connectable(bool connectable); - - /** - * @brief Return a list of edges (aka connections to other nodes) - * - * This list can't be modified directly. Use ConnectEdge() and DisconnectEdge() instead for that. - */ - const QVector& edges(); - - /** - * @brief Disconnect any edges connecting this parameter to other parameters - */ - virtual void DisconnectAll(); - - /** - * @brief Connect an output parameter to an input parameter - * - * This function makes no attempt to check whether the two NodeParams have compatible data types. This should be done - * beforehand or behavior is undefined. - * - * If the input already has an edge connected and can't accept multiple edges, that edge is disconnected before an - * attempt at a new connection is made. This function returns the new NodeEdge created by this connection. - * - * If the input *can* accept multiple edges but is already connected to this output, no new connection is made (since - * the connection already exists). In this situation, nullptr is returned. - * - * This function emits EdgeAdded(). - */ - static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input); - - /** - * @brief Disconnect an edge - * - * This function emits EdgeRemoved(NodeEdgePtr edge). - * - * @param edge - * - * Edge to disconnect. - */ - static void DisconnectEdge(NodeEdgePtr edge); - - /** - * @brief Disconnect an edge - * - * Sometimes this function is preferable if you don't know what the edge object is (or with undo commands where the - * edge object may change despite the connection being between the same parameters). - * - * This function emits EdgeRemoved(NodeEdgePtr edge). - * - * @param edge - * - * Edge to disconnect. - */ - static void DisconnectEdge(NodeOutput* output, NodeInput* input); - - /** - * @brief If an input has an edge and can't take multiple, this function disconnects them and returns the edge object - * - * This is used just before a connection is about to be made. If an input is already connected to an output, but - * can't take multiple inputs, that connection will need to be removed before the new connection can be made. - * This function check if it's necessary to remove the edge from an input before connecting a new edge, and removes - * and returns it if so. - * - * If the input does NOT have anything connected, or it does but the input CAN accept multiple connections, nothing - * is disconnected and nullptr is returned. - */ - static NodeEdgePtr DisconnectForNewOutput(NodeInput* input); - - /** - * @brief Get a human-readable translated name for a certain data type - */ - static QString GetPrettyDataTypeName(const DataType &type); - - /** - * @brief Convert a value from a NodeParam into bytes - */ - static QByteArray ValueToBytes(const DataType &type, const QVariant& value); - -signals: - /** - * @brief Signal emitted when an edge is added to this parameter - * - * See ConnectEdge() for usage. Only one of the two parameters needs to emit this signal when a connection is made, - * because otherwise two of exactly the same signal will be emitted. - */ - void EdgeAdded(NodeEdgePtr edge); - - /** - * @brief Signal emitted when an edge is removed from this parameter - * - * See DisconnectEdge() for usage. Only one of the two parameters needs to emit this signal when a connection is - * removed, because otherwise two of exactly the same signal will be emitted. - */ - void EdgeRemoved(NodeEdgePtr edge); - -protected: - /** - * @brief Internal list of edges - */ - QVector edges_; - - /** - * @brief Internal name string - */ - QString name_; - -private: - /** - * @brief Internal function for returning a value in the form of bytes - */ - template - static QByteArray ValueToBytesInternal(const QVariant& v); - - /** - * @brief Internal ID string - */ - QString id_; - - /** - * @brief Internal connectable value - */ - bool connectable_; - -}; - -} - -#endif // NODEPARAM_H diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index 7aa6face0..8eecf64be 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -29,9 +29,7 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa NodeValueDatabase database; // We need to insert tables into the database for each input - QVector inputs = node->GetInputsIncludingArrays(); - - foreach (NodeInput* input, inputs) { + foreach (NodeInput* input, node->parameters()) { if (IsCancelled()) { return NodeValueDatabase(); } @@ -48,26 +46,57 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa NodeValueTable NodeTraverser::ProcessInput(NodeInput* input, const TimeRange& range) { - if (input->is_connected()) { + // If input is connected, retrieve value directly + if (input->IsConnected()) { + // Value will equal something from the connected node, follow it - return GenerateTable(input->get_connected_node(), range); - } else if (!input->IsArray()) { - // Push onto the table the value at this time from the input - QVariant input_value = input->get_value_at_time(range.in()); + return GenerateTable(input->GetConnectedNode(), range); + + } else { + + // Store node + Node* node = static_cast(input->parent()); + + QVariant return_val; + + if (input->IsArray()) { + + // Value is an array, we will return a list of NodeValueTables + QVector array_tbl(input->ArraySize()); + + for (int i=0; iIsConnected(i)) { + sub_tbl = GenerateTable(input->GetConnectedNode(i), range); + } else { + QVariant input_value = input->GetValueAtTime(range.in(), i); + sub_tbl.Push(input->GetDataType(), input_value, node); + } + } + + return_val = QVariant::fromValue(array_tbl); + + } else { + + // Not connected or an array, just pull the immediate + return_val = input->GetValueAtTime(range.in()); + + } + + NodeValueTable return_table; + return_table.Push(input->GetDataType(), return_val, node, true); + return return_table; - NodeValueTable table; - table.Push(input->data_type(), input_value, input->parentNode()); - return table; } - - return NodeValueTable(); } NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& range) { - if (n->IsTrack()) { + const TrackOutput* track = dynamic_cast(n); + if (track) { // If the range is not wholly contained in this Block, we'll need to do some extra processing - return GenerateBlockTable(static_cast(n), range); + return GenerateBlockTable(track, range); } // FIXME: Cache certain values here if we've already processed them before @@ -156,9 +185,9 @@ void NodeTraverser::AddGlobalsToDatabase(NodeValueDatabase &db, const TimeRange& { // Insert global variables NodeValueTable global; - global.Push(NodeParam::kFloat, range.in().toDouble(), nullptr, QStringLiteral("time_in")); - global.Push(NodeParam::kFloat, range.out().toDouble(), nullptr, QStringLiteral("time_out")); - global.Push(NodeParam::kVec2, GenerateResolution(), nullptr, QStringLiteral("resolution")); + global.Push(NodeValue::kFloat, range.in().toDouble(), nullptr, false, QStringLiteral("time_in")); + global.Push(NodeValue::kFloat, range.out().toDouble(), nullptr, false, QStringLiteral("time_out")); + global.Push(NodeValue::kVec2, GenerateResolution(), nullptr, false, QStringLiteral("resolution")); db.Insert(QStringLiteral("global"), global); } @@ -170,7 +199,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N // Convert footage to image/sample buffers QVariant cached_frame = GetCachedFrame(node, range.in()); if (!cached_frame.isNull()) { - output_params.Push(NodeParam::kTexture, cached_frame, node); + output_params.Push(NodeValue::kTexture, cached_frame, node); // No more to do here got_cached_frame = true; @@ -187,7 +216,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N const NodeValue& v = output_params.at(i); QList* take_this_value_list = nullptr; - if (v.type() == NodeParam::kFootage) { + if (v.type() == NodeValue::kFootage) { Stream* s = Node::ValueToPtr(v.data()); if (s) { @@ -197,11 +226,11 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N take_this_value_list = &audio_footage_to_retrieve; } } - } else if (v.type() == NodeParam::kShaderJob) { + } else if (v.type() == NodeValue::kShaderJob) { take_this_value_list = &shader_jobs_to_run; - } else if (v.type() == NodeParam::kSampleJob) { + } else if (v.type() == NodeValue::kSampleJob) { take_this_value_list = &sample_jobs_to_run; - } else if (v.type() == NodeParam::kGenerateJob) { + } else if (v.type() == NodeValue::kGenerateJob) { take_this_value_list = &generate_jobs_to_run; } @@ -221,7 +250,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N QVariant value = ProcessVideoFootage(stream, range.in()); if (!value.isNull()) { - output_params.Push(NodeParam::kTexture, value, node); + output_params.Push(NodeValue::kTexture, value, node); } } } @@ -231,7 +260,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N QVariant value = ProcessShader(node, range, v.data().value()); if (!value.isNull()) { - output_params.Push(NodeParam::kTexture, value, node); + output_params.Push(NodeValue::kTexture, value, node); } } @@ -240,7 +269,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N QVariant value = ProcessFrameGeneration(node, v.data().value()); if (!value.isNull()) { - output_params.Push(NodeParam::kTexture, value, node); + output_params.Push(NodeValue::kTexture, value, node); } } } @@ -254,7 +283,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N QVariant value = ProcessAudioFootage(stream, range); if (!value.isNull()) { - output_params.Push(NodeParam::kSamples, value, node); + output_params.Push(NodeValue::kSamples, value, node); } } } @@ -264,7 +293,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N QVariant value = ProcessSamples(node, range, v.data().value()); if (!value.isNull()) { - output_params.Push(NodeParam::kSamples, value, node); + output_params.Push(NodeValue::kSamples, value, node); } } } diff --git a/app/node/value.cpp b/app/node/value.cpp index 3c52d3ea0..93a822e01 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -20,8 +20,307 @@ #include "value.h" +#include +#include +#include +#include + +#include "common/tohex.h" +#include "node/input.h" +#include "render/color.h" + namespace olive { +const QVector NodeValue::kNumber = {kFloat, kInt, kRational}; +const QVector NodeValue::kBuffer = {kTexture, kSamples}; +const QVector NodeValue::kVector = {kVec2, kVec3, kVec4, kColor}; + +QString NodeValue::ValueToString(Type data_type, const QVariant &value, bool value_is_a_key_track) +{ + if (!value_is_a_key_track && data_type == kVec2) { + QVector2D vec = value.value(); + + return QStringLiteral("%1:%2").arg(QString::number(vec.x()), + QString::number(vec.y())); + } else if (!value_is_a_key_track && data_type == kVec3) { + QVector3D vec = value.value(); + + return QStringLiteral("%1:%2:%3").arg(QString::number(vec.x()), + QString::number(vec.y()), + QString::number(vec.z())); + } else if (!value_is_a_key_track && data_type == kVec4) { + QVector4D vec = value.value(); + + return QStringLiteral("%1:%2:%3:%4").arg(QString::number(vec.x()), + QString::number(vec.y()), + QString::number(vec.z()), + QString::number(vec.w())); + } else if (!value_is_a_key_track && data_type == kColor) { + Color c = value.value(); + + return QStringLiteral("%1:%2:%3:%4").arg(QString::number(c.red()), + QString::number(c.green()), + QString::number(c.blue()), + QString::number(c.alpha())); + } else if (data_type == kRational) { + return value.value().toString(); + } else if (data_type == kFootage) { + return QString::number(value.value()); + } else if (data_type == kTexture + || data_type == kSamples) { + // These data types need no XML representation + return QString(); + } else if (data_type == kInt) { + return QString::number(value.value()); + } else { + if (value.canConvert()) { + return value.toString(); + } + + if (!value.isNull()) { + qWarning() << "Failed to convert type" << ToHex(data_type) << "to string"; + } + + return QString(); + } +} + +template +QByteArray ValueToBytesInternal(const QVariant &v) +{ + QByteArray bytes; + + int size_of_type = sizeof(T); + + bytes.resize(size_of_type); + T raw_val = v.value(); + memcpy(bytes.data(), &raw_val, static_cast(size_of_type)); + + return bytes; +} + +QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value) +{ + switch (type) { + case kInt: return ValueToBytesInternal(value); + case kFloat: return ValueToBytesInternal(value); + case kColor: return ValueToBytesInternal(value); + case kText: return value.toString().toUtf8(); + case kBoolean: return ValueToBytesInternal(value); + case kFont: return value.toString().toUtf8(); + case kFile: return value.toString().toUtf8(); + case kMatrix: return ValueToBytesInternal(value); + case kRational: return ValueToBytesInternal(value); + case kVec2: return ValueToBytesInternal(value); + case kVec3: return ValueToBytesInternal(value); + case kVec4: return ValueToBytesInternal(value); + case kCombo: return ValueToBytesInternal(value); + + // These types have no persistent input + case kNone: + case kFootage: + case kTexture: + case kSamples: + case kShaderJob: + case kSampleJob: + case kGenerateJob: + break; + } + + return QByteArray(); +} + +QVector NodeValue::split_normal_value_into_track_values(Type type, const QVariant &value) +{ + QVector vals(get_number_of_keyframe_tracks(type)); + + switch (type) { + case kVec2: + { + QVector2D vec = value.value(); + vals.replace(0, vec.x()); + vals.replace(1, vec.y()); + break; + } + case kVec3: + { + QVector3D vec = value.value(); + vals.replace(0, vec.x()); + vals.replace(1, vec.y()); + vals.replace(2, vec.z()); + break; + } + case kVec4: + { + QVector4D vec = value.value(); + vals.replace(0, vec.x()); + vals.replace(1, vec.y()); + vals.replace(2, vec.z()); + vals.replace(3, vec.w()); + break; + } + case kColor: + { + Color c = value.value(); + vals.replace(0, c.red()); + vals.replace(1, c.green()); + vals.replace(2, c.blue()); + vals.replace(3, c.alpha()); + break; + } + default: + vals.replace(0, value); + } + + return vals; +} + +QVariant NodeValue::combine_track_values_into_normal_value(Type type, const QVector &split) +{ + switch (type) { + case kVec2: + { + return QVector2D(split.at(0).toFloat(), + split.at(1).toFloat()); + } + case kVec3: + { + return QVector3D(split.at(0).toFloat(), + split.at(1).toFloat(), + split.at(2).toFloat()); + } + case kVec4: + { + return QVector4D(split.at(0).toFloat(), + split.at(1).toFloat(), + split.at(2).toFloat(), + split.at(3).toFloat()); + } + case kColor: + { + return QVariant::fromValue(Color(split.at(0).toFloat(), + split.at(1).toFloat(), + split.at(2).toFloat(), + split.at(3).toFloat())); + } + default: + return split.first(); + } +} + +int NodeValue::get_number_of_keyframe_tracks(Type type) +{ + switch (type) { + case NodeValue::kVec2: + return 2; + case NodeValue::kVec3: + return 3; + case NodeValue::kVec4: + case NodeValue::kColor: + return 4; + default: + return 1; + } +} + +QVariant NodeValue::StringToValue(Type data_type, const QString &string, bool value_is_a_key_track) +{ + if (!value_is_a_key_track && data_type == kVec2) { + QStringList vals = string.split(':'); + + ValidateVectorString(&vals, 2); + + return QVector2D(vals.at(0).toFloat(), vals.at(1).toFloat()); + } else if (!value_is_a_key_track && data_type == kVec3) { + QStringList vals = string.split(':'); + + ValidateVectorString(&vals, 3); + + return QVector3D(vals.at(0).toFloat(), vals.at(1).toFloat(), vals.at(2).toFloat()); + } else if (!value_is_a_key_track && data_type == kVec4) { + QStringList vals = string.split(':'); + + ValidateVectorString(&vals, 4); + + return QVector4D(vals.at(0).toFloat(), vals.at(1).toFloat(), vals.at(2).toFloat(), vals.at(3).toFloat()); + } else if (!value_is_a_key_track && data_type == kColor) { + QStringList vals = string.split(':'); + + 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 (data_type == kInt) { + return QVariant::fromValue(string.toLongLong()); + } else if (data_type == kRational) { + return QVariant::fromValue(rational::fromString(string)); + } else { + return string; + } +} + +void NodeValue::ValidateVectorString(QStringList* list, int count) +{ + while (list->size() < count) { + list->append(QStringLiteral("0")); + } +} + +QString NodeValue::GetPrettyDataTypeName(Type type) +{ + switch (type) { + case kNone: + return QCoreApplication::translate("NodeValue", "None"); + case kInt: + case kCombo: + return QCoreApplication::translate("NodeValue", "Integer"); + case kFloat: + return QCoreApplication::translate("NodeValue", "Float"); + case kRational: + return QCoreApplication::translate("NodeValue", "Rational"); + case kBoolean: + return QCoreApplication::translate("NodeValue", "Boolean"); + case kColor: + return QCoreApplication::translate("NodeValue", "Color"); + case kMatrix: + return QCoreApplication::translate("NodeValue", "Matrix"); + case kText: + return QCoreApplication::translate("NodeValue", "Text"); + case kFont: + return QCoreApplication::translate("NodeValue", "Font"); + case kFile: + return QCoreApplication::translate("NodeValue", "File"); + case kTexture: + return QCoreApplication::translate("NodeValue", "Texture"); + case kSamples: + return QCoreApplication::translate("NodeValue", "Samples"); + case kFootage: + return QCoreApplication::translate("NodeValue", "Footage"); + case kVec2: + return QCoreApplication::translate("NodeValue", "Vector 2D"); + case kVec3: + return QCoreApplication::translate("NodeValue", "Vector 3D"); + case kVec4: + return QCoreApplication::translate("NodeValue", "Vector 4D"); + + case kShaderJob: + case kSampleJob: + case kGenerateJob: + break; + } + + return QCoreApplication::translate("NodeValue", "Unknown"); +} + +NodeValueTable &NodeValueDatabase::operator[](const NodeInput *input) +{ + return tables_[input->id()]; +} + +void NodeValueDatabase::Insert(const NodeInput *key, const NodeValueTable &value) +{ + tables_.insert(key->id(), value); +} + NodeValueTable NodeValueDatabase::Merge() const { QHash copy = tables_; @@ -32,31 +331,7 @@ NodeValueTable NodeValueDatabase::Merge() const return NodeValueTable::Merge(copy.values()); } -NodeValue::NodeValue() : - type_(NodeParam::kNone), - from_(nullptr) -{ -} - -NodeValue::NodeValue(const NodeParam::DataType &type, const QVariant &data, const Node *from, const QString &tag) : - type_(type), - data_(data), - from_(from), - tag_(tag) -{ -} - -bool NodeValue::operator==(const NodeValue &rhs) const -{ - return type_ == rhs.type_ && tag_ == rhs.tag_ && data_ == rhs.data_; -} - -QVariant NodeValueTable::Get(const NodeParam::DataType &type, const QString &tag) const -{ - return GetWithMeta(type, tag).data(); -} - -NodeValue NodeValueTable::GetWithMeta(const NodeParam::DataType &type, const QString &tag) const +NodeValue NodeValueTable::GetWithMeta(const QVector &type, const QString &tag) const { int value_index = GetInternal(type, tag); @@ -67,12 +342,7 @@ NodeValue NodeValueTable::GetWithMeta(const NodeParam::DataType &type, const QSt return NodeValue(); } -QVariant NodeValueTable::Take(const NodeParam::DataType &type, const QString &tag) -{ - return TakeWithMeta(type, tag).data(); -} - -NodeValue NodeValueTable::TakeWithMeta(const NodeParam::DataType &type, const QString &tag) +NodeValue NodeValueTable::TakeWithMeta(const QVector &type, const QString &tag) { int value_index = GetInternal(type, tag); @@ -83,7 +353,7 @@ NodeValue NodeValueTable::TakeWithMeta(const NodeParam::DataType &type, const QS return NodeValue(); } -bool NodeValueTable::Has(const NodeParam::DataType &type) const +bool NodeValueTable::Has(NodeValue::Type type) const { for (int i=values_.size() - 1;i>=0;i--) { const NodeValue& v = values_.at(i); @@ -110,8 +380,6 @@ void NodeValueTable::Remove(const NodeValue &v) NodeValueTable NodeValueTable::Merge(QList tables) { - - if (tables.size() == 1) { return tables.first(); } @@ -134,14 +402,14 @@ NodeValueTable NodeValueTable::Merge(QList tables) return merged_table; } -int NodeValueTable::GetInternal(const NodeParam::DataType &type, const QString &tag) const +int NodeValueTable::GetInternal(const QVector& types, const QString &tag) const { int index = -1; for (int i=values_.size() - 1;i>=0;i--) { const NodeValue& v = values_.at(i); - if (v.type() & type) { + if (types.contains(v.type())) { index = i; if (tag.isEmpty() || tag == v.tag()) { diff --git a/app/node/value.h b/app/node/value.h index 7ccc9c5e4..42a7fa8a9 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -22,19 +22,189 @@ #define VALUE_H #include - -#include "input.h" -#include "render/shadervalue.h" +#include namespace olive { +class Node; +class NodeInput; + class NodeValue { public: - NodeValue(); - NodeValue(const NodeParam::DataType& type, const QVariant& data, const Node* from, const QString& tag = QString()); + /** + * @brief The types of data that can be passed between Nodes + */ + enum Type { + kNone, - const NodeParam::DataType& type() const + /** + ****************************** SPECIFIC IDENTIFIERS ****************************** + */ + + /** + * Integer type + * + * Resolves to int64_t. + */ + kInt, + + /** + * Decimal (floating-point) type + * + * Resolves to `double`. + */ + kFloat, + + /** + * Decimal (rational) type + * + * Resolves to `double`. + */ + kRational, + + /** + * Boolean type + * + * Resolves to `bool`. + */ + kBoolean, + + /** + * Floating-point type + * + * Resolves to `Color`. + * + * Colors passed around the nodes should always be in reference space and preferably use + */ + kColor, + + /** + * Matrix type + * + * Resolves to `QMatrix4x4`. + */ + kMatrix, + + /** + * Text type + * + * Resolves to `QString`. + */ + kText, + + /** + * Font type + * + * Resolves to `QFont`. + */ + kFont, + + /** + * File type + * + * Resolves to a `QString` containing an absolute file path. + */ + kFile, + + /** + * Image buffer type + * + * True value type depends on the render engine used. + */ + kTexture, + + /** + * Audio samples type + * + * Resolves to `SampleBufferPtr`. + */ + kSamples, + + /** + * Footage stream identifier type + * + * Resolves to `StreamPtr`. + */ + kFootage, + + /** + * Two-dimensional vector (XY) type + * + * Resolves to `QVector2D`. + */ + kVec2, + + /** + * Three-dimensional vector (XYZ) type + * + * Resolves to `QVector3D`. + */ + kVec3, + + /** + * Four-dimensional vector (XYZW) type + * + * Resolves to `QVector4D`. + */ + kVec4, + + /** + * ComboBox type + * + * Resolves to `int` - the index currently selected + */ + kCombo, + + /** + * Job type + * + * An internal type used to indicate to the renderer that an accelerated shader job needs to + * run. This value will usually be taken from a table and a kTexture value will be pushed to + * take its place. + */ + kShaderJob, + + /** + * Job type + * + * An internal type used to indicate to the renderer that an accelerated sample job needs to + * take place. This value will usually be taken from a table and a kSamples value will be + * pushed to take its place. + */ + kSampleJob, + + /** + * Job type + * + * An internal type used to indicate to the renderer that an accelerated sample job needs to + * take place. This value will usually be taken from a table and a kSamples value will be + * pushed to take its place. + */ + kGenerateJob + }; + + static const QVector kNumber; + static const QVector kBuffer; + static const QVector kVector; + + NodeValue() : + type_(kNone), + from_(nullptr), + array_(false) + { + } + + NodeValue(Type type, const QVariant& data, const Node* from = nullptr, bool array = false, const QString& tag = QString()) : + type_(type), + data_(data), + from_(from), + tag_(tag), + array_(array) + { + } + + Type type() const { return type_; } @@ -54,13 +224,63 @@ public: return from_; } - bool operator==(const NodeValue& rhs) const; + bool array() const + { + return array_; + } + + bool operator==(const NodeValue& rhs) const + { + return type_ == rhs.type_ && tag_ == rhs.tag_ && data_ == rhs.data_; + } + + static QString GetPrettyDataTypeName(Type type); + + static QString ValueToString(Type data_type, const QVariant& value, bool value_is_a_key_track); + + static QVariant StringToValue(Type data_type, const QString &string, bool value_is_a_key_track); + + /** + * @brief Convert a value from a NodeParam into bytes + */ + static QByteArray ValueToBytes(Type type, const QVariant& value); + + static QVector split_normal_value_into_track_values(Type type, const QVariant &value); + + static QVariant combine_track_values_into_normal_value(Type type, const QVector& split); + + /** + * @brief Returns whether a data type can be interpolated or not + */ + static bool type_can_be_interpolated(NodeValue::Type type) + { + return type == kFloat + || type == kVec2 + || type == kVec3 + || type == kVec4 + || type == kColor; + } + + static bool type_is_numeric(NodeValue::Type type) + { + return kNumber.contains(type); + } + + static bool type_is_vector(NodeValue::Type type) + { + return kVector.contains(type); + } + + static int get_number_of_keyframe_tracks(Type type); + + static void ValidateVectorString(QStringList* list, int count); private: - NodeParam::DataType type_; + Type type_; QVariant data_; const Node* from_; QString tag_; + bool array_; }; @@ -69,24 +289,52 @@ class NodeValueTable public: NodeValueTable() = default; - QVariant Get(const NodeParam::DataType& type, const QString& tag = QString()) const; - NodeValue GetWithMeta(const NodeParam::DataType& type, const QString& tag = QString()) const; - QVariant Take(const NodeParam::DataType& type, const QString& tag = QString()); - NodeValue TakeWithMeta(const NodeParam::DataType& type, const QString& tag = QString()); + QVariant Get(NodeValue::Type type, const QString& tag = QString()) const + { + QVector types = {type}; + return Get(types, tag); + } + + QVariant Get(const QVector& type, const QString& tag = QString()) const + { + return GetWithMeta(type, tag).data(); + } + + NodeValue GetWithMeta(NodeValue::Type type, const QString& tag = QString()) const + { + QVector types = {type}; + return GetWithMeta(types, tag); + } + + NodeValue GetWithMeta(const QVector& type, const QString& tag = QString()) const; + + QVariant Take(NodeValue::Type type, const QString& tag = QString()) + { + QVector types = {type}; + return Take(types, tag); + } + + QVariant Take(const QVector& type, const QString& tag = QString()) + { + return TakeWithMeta(type, tag).data(); + } + + NodeValue TakeWithMeta(NodeValue::Type type, const QString& tag = QString()) + { + QVector types = {type}; + return TakeWithMeta(types, tag); + } + + NodeValue TakeWithMeta(const QVector& type, const QString& tag = QString()); void Push(const NodeValue& value) { values_.append(value); } - void Push(const NodeParam::DataType& type, const QVariant& data, const Node *from, const QString& tag = QString()) + void Push(NodeValue::Type type, const QVariant& data, const Node *from, bool array = false, const QString& tag = QString()) { - Push(NodeValue(type, data, from, tag)); - } - - void Push(const ShaderValue &value, const Node *from) - { - Push(value.type, value.data, from, value.tag); + Push(NodeValue(type, data, from, array, tag)); } void Prepend(const NodeValue& value) @@ -94,14 +342,9 @@ public: values_.prepend(value); } - void Prepend(const NodeParam::DataType& type, const QVariant& data, const Node *from, const QString& tag = QString()) + void Prepend(NodeValue::Type type, const QVariant& data, const Node *from, bool array = false, const QString& tag = QString()) { - Prepend(NodeValue(type, data, from, tag)); - } - - void Prepend(const ShaderValue &value, const Node *from) - { - Prepend(value.type, value.data, from, value.tag); + Prepend(NodeValue(type, data, from, array, tag)); } const NodeValue& at(int index) const @@ -118,7 +361,7 @@ public: return values_.size(); } - bool Has(const NodeParam::DataType& type) const; + bool Has(NodeValue::Type type) const; void Remove(const NodeValue& v); bool isEmpty() const @@ -129,7 +372,7 @@ public: static NodeValueTable Merge(QList tables); private: - int GetInternal(const NodeParam::DataType& type, const QString& tag) const; + int GetInternal(const QVector &type, const QString& tag) const; QList values_; @@ -145,20 +388,14 @@ public: return tables_[input_id]; } - NodeValueTable& operator[](const NodeInput* input) - { - return tables_[input->id()]; - } + NodeValueTable& operator[](const NodeInput* input); void Insert(const QString& key, const NodeValueTable &value) { tables_.insert(key, value); } - void Insert(const NodeInput* key, const NodeValueTable& value) - { - tables_.insert(key->id(), value); - } + void Insert(const NodeInput* key, const NodeValueTable& value); NodeValueTable Merge() const; @@ -184,6 +421,8 @@ private: }; +using NodeValueMap = QHash; + } Q_DECLARE_METATYPE(olive::NodeValue) diff --git a/app/panel/node/node.cpp b/app/panel/node/node.cpp index 7d4177db3..289ae8426 100644 --- a/app/panel/node/node.cpp +++ b/app/panel/node/node.cpp @@ -29,8 +29,8 @@ NodePanel::NodePanel(QWidget *parent) : node_view_ = new NodeView(this); // Connect node view signals to this panel - connect(node_view_, &NodeView::NodesSelected, this, &NodePanel::NodesSelected); - connect(node_view_, &NodeView::NodesDeselected, this, &NodePanel::NodesDeselected); + //connect(node_view_, &NodeView::NodesSelected, this, &NodePanel::NodesSelected); + //connect(node_view_, &NodeView::NodesDeselected, this, &NodePanel::NodesDeselected); // Set it as the main widget of this panel SetWidgetWithPadding(node_view_); diff --git a/app/panel/node/node.h b/app/panel/node/node.h index b0aa5e187..4d60af6ef 100644 --- a/app/panel/node/node.h +++ b/app/panel/node/node.h @@ -88,12 +88,14 @@ public slots: void SelectBlocks(const QVector& nodes) { - node_view_->SelectBlocks(nodes); + qDebug() << "Stub"; + //node_view_->SelectBlocks(nodes); } void DeselectBlocks(const QVector& nodes) { - node_view_->DeselectBlocks(nodes); + qDebug() << "Stub"; + //node_view_->DeselectBlocks(nodes); } signals: diff --git a/app/project/item/folder/folder.h b/app/project/item/folder/folder.h index fcd6a6d15..c2505bcf5 100644 --- a/app/project/item/folder/folder.h +++ b/app/project/item/folder/folder.h @@ -21,8 +21,6 @@ #ifndef FOLDER_H #define FOLDER_H -#include "node/param.h" -#include "project/item/footage/footage.h" #include "project/item/item.h" namespace olive { diff --git a/app/project/item/item.cpp b/app/project/item/item.cpp index 0495c3729..fdc6c667b 100644 --- a/app/project/item/item.cpp +++ b/app/project/item/item.cpp @@ -64,27 +64,18 @@ QString Item::rate() return QString(); } -const Item *Item::root() const -{ - const Item* item = this; - - while (item->item_parent()) { - item = item->item_parent(); - } - - return item; -} - Project *Item::project() const { - const Item* root_item = root(); - - return root_item->project_; + return project_; } void Item::set_project(Project *project) { project_ = project; + + foreach (Item* i, item_children_) { + i->set_project(project_); + } } QVector Item::get_children_of_type(Type type, bool recursive) const @@ -129,11 +120,13 @@ void Item::childEvent(QChildEvent *event) item_children_.append(cast_test); cast_test->item_parent_ = this; + cast_test->set_project(project_); } else if (event->type() == QEvent::ChildRemoved) { item_children_.removeOne(cast_test); cast_test->item_parent_ = nullptr; + cast_test->set_project(nullptr); } } diff --git a/app/project/item/item.h b/app/project/item/item.h index 3d64daa32..3547b0fb1 100644 --- a/app/project/item/item.h +++ b/app/project/item/item.h @@ -30,7 +30,6 @@ #include "common/threadedobject.h" #include "common/xmlutils.h" -#include "node/param.h" #include "project/item/footage/stream.h" namespace olive { @@ -101,9 +100,8 @@ public: return item_parent_; } - const Item* root() const; - Project* project() const; + void set_project(Project* project); QVector get_children_of_type(Type type, bool recursive) const; diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index e2767e537..98453eea4 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -42,7 +42,7 @@ Sequence::Sequence() { viewer_output_ = new ViewerOutput(); viewer_output_->SetCanBeDeleted(false); - AddNode(viewer_output_); + viewer_output_->setParent(this); } void Sequence::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt *cancelled) @@ -158,8 +158,7 @@ void Sequence::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint v if (node) { node->Load(reader, xml_node_data, cancelled); - - AddNode(node); + node->setParent(this); } } else { reader->skipCurrentElement(); @@ -222,10 +221,17 @@ void Sequence::Save(QXmlStreamWriter *writer) const void Sequence::add_default_nodes() { // Create tracks and connect them to the viewer - Node* video_track_output = viewer_output_->track_list(Timeline::kTrackTypeVideo)->AddTrack(); - Node* audio_track_output = viewer_output_->track_list(Timeline::kTrackTypeAudio)->AddTrack(); - NodeParam::ConnectEdge(video_track_output->output(), viewer_output_->texture_input()); - NodeParam::ConnectEdge(audio_track_output->output(), viewer_output_->samples_input()); + TrackOutput* video_track = new TrackOutput(); + video_track->setParent(this); + viewer_output_->track_input(Timeline::kTrackTypeVideo)->ArrayAppend(); + Node::ConnectEdge(video_track, viewer_output_->track_input(Timeline::kTrackTypeVideo), 0); + Node::ConnectEdge(video_track, viewer_output_->texture_input()); + + TrackOutput* audio_track = new TrackOutput(); + audio_track->setParent(this); + viewer_output_->track_input(Timeline::kTrackTypeAudio)->ArrayAppend(); + Node::ConnectEdge(audio_track, viewer_output_->track_input(Timeline::kTrackTypeAudio), 0); + Node::ConnectEdge(audio_track, viewer_output_->samples_input()); } Item::Type Sequence::type() const diff --git a/app/project/project.cpp b/app/project/project.cpp index c29e1b845..dddfddffc 100644 --- a/app/project/project.cpp +++ b/app/project/project.cpp @@ -88,7 +88,7 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint foreach (const XMLNodeData::FootageConnection& con, xml_node_data.footage_connections) { if (con.footage) { - con.input->set_standard_value(QVariant::fromValue(xml_node_data.footage_ptrs.value(con.footage))); + con.input->SetStandardValue(QVariant::fromValue(xml_node_data.footage_ptrs.value(con.footage)), con.element); } } } diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index 986160959..1f3c13314 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -52,7 +52,6 @@ set(OLIVE_SOURCES render/renderprocessor.cpp render/renderprocessor.h render/shadercode.h - render/shadervalue.h render/stillimagecache.h render/texture.cpp render/texture.h diff --git a/app/render/job/acceleratedjob.h b/app/render/job/acceleratedjob.h index e4b981e21..ca52b7c5e 100644 --- a/app/render/job/acceleratedjob.h +++ b/app/render/job/acceleratedjob.h @@ -22,8 +22,6 @@ #define ACCELERATEDJOB_H #include "node/input.h" -#include "node/inputarray.h" -#include "render/shadervalue.h" #include "node/value.h" namespace olive { @@ -32,58 +30,29 @@ class AcceleratedJob { public: AcceleratedJob() = default; - ShaderValue GetValue(NodeInput* input) const + NodeValue GetValue(NodeInput* input) const { return value_map_.value(input->id()); } - ShaderValue GetValue(const QString& input) const + NodeValue GetValue(const QString& input) const { return value_map_.value(input); } void InsertValue(NodeInput* input, NodeValueDatabase& value) { - ShaderValue shader_val; - - shader_val.type = input->data_type(); - shader_val.array = input->IsArray(); - - if (input->IsArray()) { - NodeInputArray* array = static_cast(input); - QVector values(array->GetSize()); - - for (int j=0;jGetSize();j++) { - NodeInput* subparam = array->At(j); - - values[j] = value[subparam].Take(subparam->data_type()); - } - - shader_val.data = QVariant::fromValue(values); - } else { - NodeValue node_val = value[input].TakeWithMeta(input->data_type()); - shader_val.data = node_val.data(); - shader_val.tag = node_val.tag(); - } - - InsertValue(input->id(), shader_val); + InsertValue(input->id(), value[input].TakeWithMeta(input->GetDataType())); } - void InsertValue(const QString& input, const ShaderValue& value) + void InsertValue(const QString& input, const NodeValue& value) { value_map_.insert(input, value); } - void InsertValue(NodeInput* input, const ShaderValue& value) - { - value_map_.insert(input->id(), value); - } - void InsertValue(NodeInput* input, const NodeValue& value) { - ShaderValue s(value.data(), value.type()); - s.tag = value.tag(); - value_map_.insert(input->id(), s); + value_map_.insert(input->id(), value); } const NodeValueMap &GetValues() const diff --git a/app/render/job/samplejob.h b/app/render/job/samplejob.h index 80cf5657e..c6d5d5eaf 100644 --- a/app/render/job/samplejob.h +++ b/app/render/job/samplejob.h @@ -40,7 +40,7 @@ public: SampleJob(NodeInput* from, NodeValueDatabase& db) { - samples_ = db[from].Take(NodeParam::kSamples).value(); + samples_ = db[from].Take(NodeValue::kSamples).value(); } SampleBufferPtr samples() const diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index a92bb10f2..af43e941f 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -367,51 +367,50 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video } // This variable is used in the shader, let's set it - const ShaderValue& value = it.value(); + const NodeValue& value = it.value(); - if (value.array) { + if (value.array()) { qWarning() << "FIXME: Array support is currently a stub"; } - switch (value.type) { - case NodeInput::kInt: + switch (value.type()) { + case NodeValue::kInt: // kInt technically specifies a LongLong, but OpenGL doesn't support those. This may lead to // over/underflows if the number is large enough, but the likelihood of that is quite low. - shader->setUniformValue(variable_location, value.data.toInt()); + shader->setUniformValue(variable_location, value.data().toInt()); break; - case NodeInput::kFloat: + case NodeValue::kFloat: // kFloat technically specifies a double but as above, OpenGL doesn't support those. - shader->setUniformValue(variable_location, value.data.toFloat()); + shader->setUniformValue(variable_location, value.data().toFloat()); break; - case NodeInput::kVec2: - shader->setUniformValue(variable_location, value.data.value()); + case NodeValue::kVec2: + shader->setUniformValue(variable_location, value.data().value()); break; - case NodeInput::kVec3: - shader->setUniformValue(variable_location, value.data.value()); + case NodeValue::kVec3: + shader->setUniformValue(variable_location, value.data().value()); break; - case NodeInput::kVec4: - shader->setUniformValue(variable_location, value.data.value()); + case NodeValue::kVec4: + shader->setUniformValue(variable_location, value.data().value()); break; - case NodeInput::kMatrix: - shader->setUniformValue(variable_location, value.data.value()); + case NodeValue::kMatrix: + shader->setUniformValue(variable_location, value.data().value()); break; - case NodeInput::kCombo: - shader->setUniformValue(variable_location, value.data.value()); + case NodeValue::kCombo: + shader->setUniformValue(variable_location, value.data().value()); break; - case NodeInput::kColor: + case NodeValue::kColor: { - Color color = value.data.value(); + Color color = value.data().value(); shader->setUniformValue(variable_location, color.red(), color.green(), color.blue(), color.alpha()); break; } - case NodeInput::kBoolean: - shader->setUniformValue(variable_location, value.data.toBool()); + case NodeValue::kBoolean: + shader->setUniformValue(variable_location, value.data().toBool()); break; - case NodeInput::kBuffer: - case NodeInput::kTexture: + case NodeValue::kTexture: { - TexturePtr texture = value.data.value(); + TexturePtr texture = value.data().value(); // Set value to bound texture shader->setUniformValue(variable_location, textures_to_bind.size()); @@ -433,21 +432,16 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video } break; } - case NodeInput::kSamples: - case NodeInput::kText: - case NodeInput::kRational: - case NodeInput::kFont: - case NodeInput::kFile: - case NodeInput::kDecimal: - case NodeInput::kNumber: - case NodeInput::kString: - case NodeInput::kVector: - case NodeInput::kShaderJob: - case NodeInput::kSampleJob: - case NodeInput::kGenerateJob: - case NodeInput::kFootage: - case NodeInput::kNone: - case NodeInput::kAny: + case NodeValue::kSamples: + case NodeValue::kText: + case NodeValue::kRational: + case NodeValue::kFont: + case NodeValue::kFile: + case NodeValue::kShaderJob: + case NodeValue::kSampleJob: + case NodeValue::kGenerateJob: + case NodeValue::kFootage: + case NodeValue::kNone: break; } } @@ -469,7 +463,7 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video // Ensure matrix is set, at least to identity shader->setUniformValue("ove_mvpmat", - job.GetValue(QStringLiteral("ove_mvpmat")).data.value()); + job.GetValue(QStringLiteral("ove_mvpmat")).data().value()); // Set the viewport to the "physical" resolution of the destination functions_->glViewport(0, 0, diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 8037b6a6f..5f6deec9d 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -61,66 +61,13 @@ void PreviewAutoCacher::SetPaused(bool paused) } } -void PreviewAutoCacher::NodeGraphChanged(NodeInput *source) -{ - // We need to determine: - // - If we don't have this input, assume that it's coming soon and ignore it - // - If we do, is this input a child of another input we're already copying? - // - Or are any of the queued inputs children of this one? - - // First we need to find our copy of the input being queued - Node* our_copy_node = copy_map_.value(source->parentNode()); - - // If we don't have this node yet, assume it's coming in a later copy in which case it'll be - // copied then - if (!our_copy_node) { - // Assert that there are updates coming - Q_ASSERT(!graph_update_queue_.isEmpty()); - return; - } - - // If we're here, we must have this node. Determine if we're already copying a "parent" of this - for (int i=0; iIsArray() && static_cast(source)->sub_params().contains(queued_input)) - || queued_input->parentNode()->OutputsTo(source, true, true)) { - // In which case, we don't need to queue it and can queue our own - graph_update_queue_.removeAt(i); - disconnect(queued_input, &NodeInput::destroyed, this, &PreviewAutoCacher::QueuedInputRemoved); - i--; - } - - // Check if the source is a member of this array, in which case it'll be copied eventually anyway - if (queued_input->IsArray() - && static_cast(queued_input)->sub_params().contains(source)) { - return; - } - - // Check if this dependency graph is already queued - if (source->parentNode()->OutputsTo(queued_input, true, true)) { - // In which case, no further copy is necessary - return; - } - } - - graph_update_queue_.append(source); - connect(source, &NodeInput::destroyed, this, &PreviewAutoCacher::QueuedInputRemoved); -} - void PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cache, const QVector ×, qint64 job_time) { std::vector existing_hashes; foreach (const rational& time, times) { // See if hash already exists in disk cache - QByteArray hash = RenderManager::Hash(viewer->texture_input()->get_connected_node(), viewer->video_params(), time); + QByteArray hash = RenderManager::Hash(viewer->texture_input()->GetConnectedNode(), viewer->video_params(), time); // Check memory list since disk checking is slow bool hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), hash) != existing_hashes.end()); @@ -292,13 +239,6 @@ void PreviewAutoCacher::VideoDownloaded() delete watcher; } -void PreviewAutoCacher::QueuedInputRemoved() -{ - NodeInput* i = static_cast(sender()); - disconnect(i, &NodeInput::destroyed, this, &PreviewAutoCacher::QueuedInputRemoved); - graph_update_queue_.removeOne(i); -} - void PreviewAutoCacher::VideoParamsChanged() { // In case the user is pressing the mouse at this exact moment @@ -324,27 +264,28 @@ void PreviewAutoCacher::SingleFrameFinished() delete watcher; } -//#define PRINT_UPDATE_QUEUE_INFO void PreviewAutoCacher::ProcessUpdateQueue() { -#ifdef PRINT_UPDATE_QUEUE_INFO - qint64 t = QDateTime::currentMSecsSinceEpoch(); - qDebug() << "Processing update queue of" << graph_update_queue_.size() << "elements:"; -#endif - - while (!graph_update_queue_.isEmpty()) { - NodeInput* i = graph_update_queue_.takeFirst(); -#ifdef PRINT_UPDATE_QUEUE_INFO - qDebug() << " " << i->parentNode()->id() << i->id(); -#endif - disconnect(i, &NodeInput::destroyed, this, &PreviewAutoCacher::QueuedInputRemoved); - - CopyNodeInputValue(i); + foreach (const QueuedJob& job, graph_update_queue_) { + switch (job.type) { + case QueuedJob::kNodeAdded: + AddNode(job.node); + break; + case QueuedJob::kNodeRemoved: + RemoveNode(job.node); + break; + case QueuedJob::kEdgeAdded: + AddEdge(job.node, job.input, job.element); + break; + case QueuedJob::kEdgeRemoved: + RemoveEdge(job.node, job.input, job.element); + break; + case QueuedJob::kValueChanged: + CopyValue(job.input, job.element); + break; + } } - -#ifdef PRINT_UPDATE_QUEUE_INFO - qDebug() << "Update queue took:" << (QDateTime::currentMSecsSinceEpoch() - t); -#endif + graph_update_queue_.clear(); last_update_time_ = QDateTime::currentMSecsSinceEpoch(); } @@ -356,6 +297,63 @@ bool PreviewAutoCacher::HasActiveJobs() const || !video_tasks_.isEmpty(); } +void PreviewAutoCacher::AddNode(Node *node) +{ + // Copy node + Node* copy = node->copy(); + + // Insert into map + copy_map_.insert(node, copy); + + // Copy parameters + Node::CopyInputs(node, copy, false); + + // Connect to each input + foreach (NodeInput* input, node->parameters()) { + connect(input, &NodeInput::InputConnected, this, &PreviewAutoCacher::EdgeAdded); + connect(input, &NodeInput::InputDisconnected, this, &PreviewAutoCacher::EdgeRemoved); + connect(input, &NodeInput::ValueChanged, this, &PreviewAutoCacher::ValueChanged); + } +} + +void PreviewAutoCacher::RemoveNode(Node *node) +{ + // Find our copy and remove it + Node* copy = copy_map_.take(node); + + // Delete it + delete copy; + + // Disconnect from inputs + foreach (NodeInput* input, node->parameters()) { + disconnect(input, &NodeInput::InputConnected, this, &PreviewAutoCacher::EdgeAdded); + disconnect(input, &NodeInput::InputDisconnected, this, &PreviewAutoCacher::EdgeRemoved); + disconnect(input, &NodeInput::ValueChanged, this, &PreviewAutoCacher::ValueChanged); + } +} + +void PreviewAutoCacher::AddEdge(Node *output, NodeInput *input, int element) +{ + Node* our_output = copy_map_.value(output); + NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id()); + + Node::ConnectEdge(our_output, our_input, element); +} + +void PreviewAutoCacher::RemoveEdge(Node *output, NodeInput *input, int element) +{ + Node* our_output = copy_map_.value(output); + NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id()); + + Node::DisconnectEdge(our_output, our_input, element); +} + +void PreviewAutoCacher::CopyValue(NodeInput *input, int element) +{ + NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id()); + NodeInput::CopyValuesOfElement(input, our_input, element); +} + void PreviewAutoCacher::SetPlayhead(const rational &playhead) { cache_range_ = TimeRange(playhead - Config::Current()["DiskCacheBehind"].value(), @@ -440,90 +438,31 @@ void PreviewAutoCacher::ClearVideoDownloadQueue(bool wait) } } -void PreviewAutoCacher::CopyNodeInputValue(NodeInput *input) +void PreviewAutoCacher::NodeAdded(Node *node) { - // Find our copy of this parameter - Node* our_copy_node = copy_map_.value(input->parentNode()); - Q_ASSERT(our_copy_node); - NodeInput* our_copy = our_copy_node->GetInputWithID(input->id()); - - // Copy the standard/keyframe values between these two inputs - NodeInput::CopyValues(input, - our_copy, - false, - false); - - // Handle connections - if (input->is_connected() || our_copy->is_connected()) { - // If one of the inputs is connected, it's likely this change came from connecting or - // disconnecting whatever was connected to it - - // We start by removing all old dependencies from the map - QVector old_deps = our_copy->GetExclusiveDependencies(); - foreach (Node* i, old_deps) { - copy_map_.take(copy_map_.key(i))->deleteLater(); - } - - // And clear any other edges - while (!our_copy->edges().isEmpty()) { - NodeParam::DisconnectEdge(our_copy->edges().first()); - } - - // Then we copy all node dependencies and connections (if there are any) - CopyNodeMakeConnection(input, our_copy); - } - - // Call on sub-elements too - if (input->IsArray()) { - foreach (NodeInput* i, static_cast(input)->sub_params()) { - CopyNodeInputValue(i); - } - } + graph_update_queue_.append({QueuedJob::kNodeAdded, node, nullptr, -1}); } -Node* PreviewAutoCacher::CopyNodeConnections(Node* src_node) +void PreviewAutoCacher::NodeRemoved(Node *node) { - // Check if this node is already in the map - Node* dst_node = copy_map_.value(src_node); - - // If not, create it now - if (!dst_node) { - dst_node = src_node->copy(); - - if (dst_node->IsTrack()) { - // Hack that ensures the track type is set since we don't bother copying the whole timeline - static_cast(dst_node)->set_track_type(static_cast(src_node)->track_type()); - } - - copy_map_.insert(src_node, dst_node); - } - - // Make sure its values are copied - Node::CopyInputs(src_node, dst_node, false); - - // Copy all connections - QVector src_node_inputs = src_node->GetInputsIncludingArrays(); - QVector dst_node_inputs = dst_node->GetInputsIncludingArrays(); - - for (int i=0;iis_connected()) { - Node* dst_node = CopyNodeConnections(src_input->get_connected_node()); + graph_update_queue_.append({QueuedJob::kEdgeAdded, output, static_cast(sender()), element}); +} - NodeOutput* corresponding_output = dst_node->GetOutputWithID(src_input->get_connected_output()->id()); +void PreviewAutoCacher::EdgeRemoved(Node *output, int element) +{ + graph_update_queue_.append({QueuedJob::kEdgeRemoved, output, static_cast(sender()), element}); +} - NodeParam::ConnectEdge(corresponding_output, - dst_input); - } +void PreviewAutoCacher::ValueChanged(const TimeRange &range, int element) +{ + Q_UNUSED(range) + + graph_update_queue_.append({QueuedJob::kValueChanged, nullptr, static_cast(sender()), element}); } void PreviewAutoCacher::TryRender() @@ -701,12 +640,13 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) video_params_changed_ = false; audio_params_changed_ = false; - // Disconnect signal (will be a no-op if the signal was never connected) - disconnect(viewer_node_, - &ViewerOutput::GraphChangedFrom, - this, - &PreviewAutoCacher::NodeGraphChanged); + // Disconnect signals for future node additions/deletions + NodeGraph* graph = viewer_node_->parent(); + connect(graph, &NodeGraph::NodeAdded, this, &PreviewAutoCacher::NodeAdded); + connect(graph, &NodeGraph::NodeRemoved, this, &PreviewAutoCacher::NodeRemoved); + + // Disconnect signal (will be a no-op if the signal was never connected) disconnect(viewer_node_, &ViewerOutput::VideoParamsChanged, this, @@ -732,28 +672,40 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) if (viewer_node_) { // Copy graph - copied_viewer_node_ = static_cast(viewer_node_->copy()); - copy_map_.insert(viewer_node_, copied_viewer_node_); + NodeGraph* graph = viewer_node_->parent(); + + // Add all nodes + foreach (Node* node, graph->nodes()) { + AddNode(node); + } + + // Find copied viewer node + copied_viewer_node_ = static_cast(copy_map_.value(viewer_node_)); // Copy parameters copied_viewer_node_->set_video_params(viewer_node_->video_params()); copied_viewer_node_->set_audio_params(viewer_node_->audio_params()); - // We begin an operation and never end it which prevents the copy from unnecessarily - // invalidating its own cache - copied_viewer_node_->BeginOperation(); + // Add all connections + foreach (Node* node, graph->nodes()) { + foreach (NodeInput* input, node->inputs()) { + for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { + AddEdge(it.value(), input, it.key()); + } + } + } - NodeGraphChanged(viewer_node_->texture_input()); - NodeGraphChanged(viewer_node_->samples_input()); - ProcessUpdateQueue(); + // Connect signals for future node additions/deletions + connect(graph, &NodeGraph::NodeAdded, this, &PreviewAutoCacher::NodeAdded); + connect(graph, &NodeGraph::NodeRemoved, this, &PreviewAutoCacher::NodeRemoved); + // Copy invalidated ranges - used to determine which frames need hashing invalidated_video_ = viewer_node_->video_frame_cache()->GetInvalidatedRanges(); invalidated_audio_ = viewer_node_->audio_playback_cache()->GetInvalidatedRanges(); - connect(viewer_node_, - &ViewerOutput::GraphChangedFrom, - this, - &PreviewAutoCacher::NodeGraphChanged); + // We begin an operation and never end it which prevents the copy from unnecessarily + // invalidating its own cache + copied_viewer_node_->BeginOperation(); connect(viewer_node_, &ViewerOutput::VideoParamsChanged, diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index 99c7aec70..e631a350c 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -4,6 +4,7 @@ #include #include "config/config.h" +#include "node/graph.h" #include "node/node.h" #include "node/output/viewer/viewer.h" #include "render/colormanager.h" @@ -93,15 +94,19 @@ public slots: /** * @brief Main handler for when the NodeGraph changes */ - void NodeGraphChanged(NodeInput *source); + void NodeAdded(Node* node); + + void NodeRemoved(Node* node); + + void EdgeAdded(Node* output, int element); + + void EdgeRemoved(Node* output, int element); + + void ValueChanged(const TimeRange& range, int element); private: static void GenerateHashes(ViewerOutput* viewer, FrameHashCache *cache, const QVector& times, qint64 job_time); - void CopyNodeInputValue(NodeInput* input); - Node *CopyNodeConnections(Node *src_node); - void CopyNodeMakeConnection(NodeInput *src_input, NodeInput *dst_input); - void TryRender(); /** @@ -114,12 +119,34 @@ private: bool HasActiveJobs() const; - QList graph_update_queue_; - QHash copy_map_; - ViewerOutput* copied_viewer_node_; + void AddNode(Node* node); + void RemoveNode(Node* node); + void AddEdge(Node* output, NodeInput* input, int element); + void RemoveEdge(Node* output, NodeInput* input, int element); + void CopyValue(NodeInput* input, int element); + + class QueuedJob { + public: + enum Type { + kNodeAdded, + kNodeRemoved, + kEdgeAdded, + kEdgeRemoved, + kValueChanged + }; + + Type type; + Node* node; + NodeInput* input; + int element; + }; ViewerOutput* viewer_node_; + QVector graph_update_queue_; + QHash copy_map_; + ViewerOutput* copied_viewer_node_; + bool paused_; TimeRange cache_range_; @@ -184,15 +211,6 @@ private slots: */ void VideoDownloaded(); - /** - * @brief Handler for when a NodeInput has been deleted so we clear it from the queue - * - * FIXME: This is hacky. It also might not be necessary anymore with recent changes to the - * node system, but I haven't tested yet. Either way, PreviewAutoCacher should probably - * be able to pick up on these sorts of things without such a slot. - */ - void QueuedInputRemoved(); - void VideoParamsChanged(); void AudioParamsChanged(); diff --git a/app/render/renderer.cpp b/app/render/renderer.cpp index f33dd3a7d..938a242e2 100644 --- a/app/render/renderer.cpp +++ b/app/render/renderer.cpp @@ -236,8 +236,8 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu ShaderJob job; - job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(source), NodeParam::kTexture)); - job.InsertValue(QStringLiteral("ove_mvpmat"), ShaderValue(matrix, NodeParam::kMatrix)); + job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(source))); + job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix)); AlphaAssociated associated; if (source->channel_count() == VideoParams::kRGBAChannelCount) { @@ -252,14 +252,14 @@ void Renderer::BlitColorManagedInternal(ColorProcessorPtr color_processor, Textu // No assoc/deassoc required associated = kAlphaNone; } - job.InsertValue(QStringLiteral("ove_maintex_alpha"), ShaderValue(associated, NodeParam::kInt)); + job.InsertValue(QStringLiteral("ove_maintex_alpha"), NodeValue(NodeValue::kInt, associated)); foreach (const ColorContext::LUT& l, color_ctx.lut3d_textures) { - job.InsertValue(l.name, ShaderValue(QVariant::fromValue(l.texture), NodeParam::kTexture)); + job.InsertValue(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture))); job.SetInterpolation(l.name, l.interpolation); } foreach (const ColorContext::LUT& l, color_ctx.lut1d_textures) { - job.InsertValue(l.name, ShaderValue(QVariant::fromValue(l.texture), NodeParam::kTexture)); + job.InsertValue(l.name, NodeValue(NodeValue::kTexture, QVariant::fromValue(l.texture))); job.SetInterpolation(l.name, l.interpolation); } diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 5c0c4c545..75a467bea 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -61,7 +61,7 @@ void RenderProcessor::Run() NodeValueTable table = ProcessInput(viewer->texture_input(), TimeRange(time, time + video_params.time_base())); - TexturePtr texture = table.Get(NodeParam::kTexture).value(); + TexturePtr texture = table.Get(NodeValue::kTexture).value(); // Set up output frame parameters VideoParams frame_params = ticket_->property("vparam").value(); @@ -112,8 +112,8 @@ void RenderProcessor::Run() } else { // No color transform, just blit ShaderJob job; - job.InsertValue(QStringLiteral("ove_maintex"), {QVariant::fromValue(texture), NodeParam::kTexture}); - job.InsertValue(QStringLiteral("ove_mvpmat"), {matrix, NodeParam::kMatrix}); + job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture))); + job.InsertValue(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix)); render_ctx_->BlitToTexture(default_shader_, job, blit_tex.get()); } @@ -135,7 +135,7 @@ void RenderProcessor::Run() NodeValueTable table = ProcessInput(viewer->samples_input(), time); - ticket_->Finish(table.Get(NodeParam::kSamples), IsCancelled()); + ticket_->Finish(table.Get(NodeValue::kSamples), IsCancelled()); break; } case RenderManager::kTypeVideoDownload: @@ -211,7 +211,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const TrackOutput *track, con // Destination buffer NodeValueTable table = GenerateTable(b, range_for_block); - SampleBufferPtr samples_from_this_block = table.Take(NodeParam::kSamples).value(); + SampleBufferPtr samples_from_this_block = table.Take(NodeValue::kSamples).value(); if (!samples_from_this_block) { // If we retrieved no samples from this block, do nothing @@ -219,10 +219,10 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const TrackOutput *track, con } // FIXME: Doesn't handle reversing - if (b->speed_input()->is_keyframing() || b->speed_input()->is_connected()) { + if (b->speed_input()->IsKeyframing() || b->speed_input()->IsConnected()) { // FIXME: We'll need to calculate the speed hoo boy } else { - double speed_value = b->speed_input()->get_standard_value().toDouble(); + double speed_value = b->speed_input()->GetStandardValue().toDouble(); if (qIsNull(speed_value)) { // Just silence, don't think there's any other practical application of 0 speed audio @@ -253,7 +253,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const TrackOutput *track, con ticket_->setProperty("waveforms", QVariant::fromValue(waveform_list)); } - merged_table.Push(NodeParam::kSamples, QVariant::fromValue(block_range_buffer), track); + merged_table.Push(NodeValue::kSamples, QVariant::fromValue(block_range_buffer), track); return merged_table; @@ -409,8 +409,8 @@ QVariant RenderProcessor::ProcessShader(const Node *node, const TimeRange &range bool input_textures_have_alpha = false; for (auto it=job.GetValues().cbegin(); it!=job.GetValues().cend(); it++) { - if (it.value().type == NodeParam::kTexture) { - TexturePtr tex = it.value().data.value(); + if (it.value().type() == NodeValue::kTexture) { + TexturePtr tex = it.value().data().value(); if (tex && tex->channel_count() == VideoParams::kRGBAChannelCount) { input_textures_have_alpha = true; break; @@ -458,7 +458,7 @@ QVariant RenderProcessor::ProcessSamples(const Node *node, const TimeRange &rang if (corresponding_input) { value = ProcessInput(corresponding_input, TimeRange(this_sample_time, this_sample_time)); } else { - value.Push(j.value(), node); + value.Push(j.value()); } value_db.Insert(j.key(), value); diff --git a/app/render/shadervalue.h b/app/render/shadervalue.h deleted file mode 100644 index 0b042173b..000000000 --- a/app/render/shadervalue.h +++ /dev/null @@ -1,55 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 SHADERVALUE_H -#define SHADERVALUE_H - -#include "node/param.h" - -namespace olive { - -struct ShaderValue -{ - ShaderValue() - { - type = NodeParam::kNone; - array = false; - } - - ShaderValue(QVariant data_in, NodeParam::DataType type_in, bool array_in = false) - { - data = data_in; - type = type_in; - array = array_in; - } - - NodeParam::DataType type; - QVariant data; - bool array; - - QString tag; - -}; - -using NodeValueMap = QHash; - -} - -#endif // SHADERVALUE_H diff --git a/app/task/precache/precachetask.cpp b/app/task/precache/precachetask.cpp index 708e167f8..374df8cfa 100644 --- a/app/task/precache/precachetask.cpp +++ b/app/task/precache/precachetask.cpp @@ -34,7 +34,7 @@ PreCacheTask::PreCacheTask(VideoStream *footage, Sequence* sequence) : video_node_ = new MediaInput(); video_node_->SetStream(footage); - NodeParam::ConnectEdge(video_node_->output(), viewer()->texture_input()); + Node::ConnectEdge(video_node_, viewer()->texture_input()); SetTitle(tr("Pre-caching %1:%2").arg(footage->footage()->filename(), QString::number(footage->index()))); diff --git a/app/timeline/trackreference.h b/app/timeline/trackreference.h index 827422fc6..6f81f0751 100644 --- a/app/timeline/trackreference.h +++ b/app/timeline/trackreference.h @@ -49,7 +49,7 @@ private: }; -uint qHash(const TrackReference& r, uint seed); +uint qHash(const TrackReference& r, uint seed = 0); } diff --git a/app/widget/CMakeLists.txt b/app/widget/CMakeLists.txt index 83659ae8e..15a58c87f 100644 --- a/app/widget/CMakeLists.txt +++ b/app/widget/CMakeLists.txt @@ -29,9 +29,9 @@ add_subdirectory(keyframeview) add_subdirectory(manageddisplay) add_subdirectory(menu) add_subdirectory(nodecombobox) +add_subdirectory(nodeparamview) add_subdirectory(nodetableview) add_subdirectory(nodetreeview) -add_subdirectory(nodeparamview) add_subdirectory(nodeview) add_subdirectory(panel) add_subdirectory(path) diff --git a/app/widget/curvewidget/beziercontrolpointitem.cpp b/app/widget/curvewidget/beziercontrolpointitem.cpp index 207a92e3e..66960ee33 100644 --- a/app/widget/curvewidget/beziercontrolpointitem.cpp +++ b/app/widget/curvewidget/beziercontrolpointitem.cpp @@ -29,7 +29,7 @@ namespace olive { -BezierControlPointItem::BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe::BezierType mode, QGraphicsItem *parent) : +BezierControlPointItem::BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem *parent) : QGraphicsRectItem(parent), key_(key), mode_(mode), @@ -38,12 +38,12 @@ BezierControlPointItem::BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe { setFlag(QGraphicsItem::ItemIsMovable); - connect(key.get(), &NodeKeyframe::TimeChanged, this, &BezierControlPointItem::UpdatePos); + connect(key, &NodeKeyframe::TimeChanged, this, &BezierControlPointItem::UpdatePos); if (mode_ == NodeKeyframe::kInHandle) { - connect(key.get(), &NodeKeyframe::BezierControlInChanged, this, &BezierControlPointItem::UpdatePos); + connect(key, &NodeKeyframe::BezierControlInChanged, this, &BezierControlPointItem::UpdatePos); } else { - connect(key.get(), &NodeKeyframe::BezierControlOutChanged, this, &BezierControlPointItem::UpdatePos); + connect(key, &NodeKeyframe::BezierControlOutChanged, this, &BezierControlPointItem::UpdatePos); } @@ -64,7 +64,7 @@ void BezierControlPointItem::SetYScale(double scale) UpdatePos(); } -NodeKeyframePtr BezierControlPointItem::key() const +NodeKeyframe* BezierControlPointItem::key() const { return key_; } diff --git a/app/widget/curvewidget/beziercontrolpointitem.h b/app/widget/curvewidget/beziercontrolpointitem.h index 6cca33d5a..cd29378cb 100644 --- a/app/widget/curvewidget/beziercontrolpointitem.h +++ b/app/widget/curvewidget/beziercontrolpointitem.h @@ -30,13 +30,13 @@ namespace olive { class BezierControlPointItem : public QObject, public QGraphicsRectItem { public: - BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe::BezierType mode, QGraphicsItem* parent = nullptr); + BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem* parent = nullptr); void SetXScale(double scale); void SetYScale(double scale); - NodeKeyframePtr key() const; + NodeKeyframe* key() const; const NodeKeyframe::BezierType& mode() const; @@ -50,7 +50,7 @@ protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; private: - NodeKeyframePtr key_; + NodeKeyframe* key_; NodeKeyframe::BezierType mode_; diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index e93a8fd3d..2370c6459 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -20,8 +20,9 @@ #include "curveview.h" -#include +#include #include +#include #include #include @@ -67,16 +68,9 @@ void CurveView::ConnectInput(NodeInput *input) return; } - // Add keyframes from this input - foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) { - foreach (NodeKeyframePtr key, track) { - this->AddKeyframe(key); - } - - if (!keyframe_colors_.contains(&track)) { - // Generate a random color for this input - keyframe_colors_.insert(&track, QColor::fromHsv(std::rand()%360, std::rand()%255, 255)); - } + // Add keyframes from each subelement including primary (-1) + for (int i=-1; iArraySize(); i++) { + ConnectInputElement(input, i); } // Append to the list @@ -89,9 +83,7 @@ void CurveView::ConnectInput(NodeInput *input) void CurveView::DisconnectNode(Node *node) { - QVector inputs = node->GetInputsIncludingArrays(); - - foreach (NodeInput* i, inputs) { + foreach (NodeInput* i, node->parameters()) { DisconnectInput(i); } } @@ -166,82 +158,89 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) // Draw keyframe lines foreach (NodeInput* input, connected_inputs_) { - if (input->is_keyframing()) { - foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) { - if (!track.isEmpty()) { - painter->setPen(QPen(keyframe_colors_.value(&track), qMax(1, fontMetrics().height() / 4))); + for (int j=-1; jArraySize(); j++) { + if (input->IsKeyframing(j)) { + const QVector& tracks = input->GetKeyframeTracks(j); - QVector keyframe_lines; + for (int k=0; kpos(); - keyframe_lines.append(QLineF(QPointF(scene_bottom_left.x(), first_key_pos.y()), first_key_pos)); + if (!track.isEmpty()) { + painter->setPen(QPen(keyframe_colors_.value(GetKeyframeTrackUniqueID(input, j, k)), + qMax(1, fontMetrics().height() / 4))); - // Draw lines between each keyframe - for (int i=1;i keyframe_lines; - KeyframeViewItem* before_item = item_map().value(before.get()); - KeyframeViewItem* after_item = item_map().value(after.get()); + // Draw straight line leading to first keyframe + QPointF first_key_pos = item_map().value(track.first())->pos(); + keyframe_lines.append(QLineF(QPointF(scene_bottom_left.x(), first_key_pos.y()), first_key_pos)); - if (before->type() == NodeKeyframe::kHold) { - // Draw a hold keyframe (basically a right angle) - keyframe_lines.append(QLineF(before_item->pos().x(), - before_item->pos().y(), - after_item->pos().x(), - before_item->pos().y())); - keyframe_lines.append(QLineF(after_item->pos().x(), - before_item->pos().y(), - after_item->pos().x(), - after_item->pos().y())); - } else if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { - // Draw a cubic bezier + // Draw lines between each keyframe + for (int i=1;ipos() + ScalePoint(before->bezier_control_out()); - QPointF after_control_point = after_item->pos() + ScalePoint(after->bezier_control_in()); + KeyframeViewItem* before_item = item_map().value(before); + KeyframeViewItem* after_item = item_map().value(after); - QPainterPath path; - path.moveTo(before_item->pos()); - path.cubicTo(before_control_point, after_control_point, after_item->pos()); - painter->drawPath(path); + if (before->type() == NodeKeyframe::kHold) { + // Draw a hold keyframe (basically a right angle) + keyframe_lines.append(QLineF(before_item->pos().x(), + before_item->pos().y(), + after_item->pos().x(), + before_item->pos().y())); + keyframe_lines.append(QLineF(after_item->pos().x(), + before_item->pos().y(), + after_item->pos().x(), + after_item->pos().y())); + } else if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { + // Draw a cubic bezier - } else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) { - // Draw a quadratic bezier + // Cubic beziers have two control points, so we can just use both + QPointF before_control_point = before_item->pos() + ScalePoint(before->bezier_control_out()); + QPointF after_control_point = after_item->pos() + ScalePoint(after->bezier_control_in()); - // Quadratic beziers have a single control point, we just have to determine which it is - QPointF key_anchor; - QPointF control_point; + QPainterPath path; + path.moveTo(before_item->pos()); + path.cubicTo(before_control_point, after_control_point, after_item->pos()); + painter->drawPath(path); + + } else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) { + // Draw a quadratic bezier + + // Quadratic beziers have a single control point, we just have to determine which it is + QPointF key_anchor; + QPointF control_point; + + if (before->type() == NodeKeyframe::kBezier) { + key_anchor = before_item->pos(); + control_point = before->bezier_control_out(); + } else { + key_anchor = after_item->pos(); + control_point = after->bezier_control_in(); + } + + // Scale control point + control_point = key_anchor + ScalePoint(control_point); + + // Create the path from both keyframes + QPainterPath path; + path.moveTo(before_item->pos()); + path.quadTo(control_point, after_item->pos()); + painter->drawPath(path); - if (before->type() == NodeKeyframe::kBezier) { - key_anchor = before_item->pos(); - control_point = before->bezier_control_out(); } else { - key_anchor = after_item->pos(); - control_point = after->bezier_control_in(); + // Linear to linear + keyframe_lines.append(QLineF(before_item->pos(), after_item->pos())); } - - // Scale control point - control_point = key_anchor + ScalePoint(control_point); - - // Create the path from both keyframes - QPainterPath path; - path.moveTo(before_item->pos()); - path.quadTo(control_point, after_item->pos()); - painter->drawPath(path); - - } else { - // Linear to linear - keyframe_lines.append(QLineF(before_item->pos(), after_item->pos())); } + + // Draw straight line leading from end keyframe + QPointF last_key_pos = item_map().value(track.last())->pos(); + keyframe_lines.append(QLineF(last_key_pos, QPointF(scene_top_right.x(), last_key_pos.y()))); + + painter->drawLines(keyframe_lines); } - - // Draw straight line leading from end keyframe - QPointF last_key_pos = item_map().value(track.last().get())->pos(); - keyframe_lines.append(QLineF(last_key_pos, QPointF(scene_top_right.x(), last_key_pos.y()))); - - painter->drawLines(keyframe_lines); } } } @@ -284,7 +283,7 @@ void CurveView::VerticalScaleChangedEvent(double scale) QMap::const_iterator iterator; for (iterator=item_map().begin();iterator!=item_map().end();iterator++) { - SetItemYFromKeyframeValue(iterator.value()->key().get(), iterator.value()); + SetItemYFromKeyframeValue(iterator.value()->key(), iterator.value()); } } @@ -306,6 +305,26 @@ void CurveView::ContextMenuEvent(Menu &m) //QAction* reset_zoom_action = m.addAction(tr("Reset Zoom")); } +void CurveView::ConnectInputElement(NodeInput *input, int element) +{ + const QVector& tracks = input->GetKeyframeTracks(element); + + for (int i=0; iAddKeyframe(key); + } + + uint h = GetKeyframeTrackUniqueID(input, element, i); + + if (!keyframe_colors_.contains(h)) { + // Generate a random color for this input + keyframe_colors_.insert(h, QColor::fromHsv(std::rand()%360, std::rand()%255, 255)); + } + } +} + qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key) { return GetItemYFromKeyframeValue(key->value().toDouble()); @@ -340,6 +359,11 @@ void CurveView::CreateBezierControlPoints(KeyframeViewItem* item) connect(bezier_out_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection); } +uint CurveView::GetKeyframeTrackUniqueID(NodeInput *input, int element, int track) +{ + return ::qHash(input) ^ ::qHash(element) ^ ::qHash(track); +} + void CurveView::KeyframeValueChanged() { NodeKeyframe* key = static_cast(sender()); @@ -398,10 +422,10 @@ void CurveView::ZoomToFit() double max_val = DBL_MIN; for (auto i=item_map().constBegin(); i!=item_map().constEnd(); i++) { - rational transformed_time = GetAdjustedTime(i.key()->parent()->parentNode(), + rational transformed_time = GetAdjustedTime(i.key()->parent()->parent(), GetTimeTarget(), i.key()->time(), - NodeParam::kOutput); + false); min_time = qMin(transformed_time, min_time); max_time = qMax(transformed_time, max_time); @@ -421,14 +445,14 @@ void CurveView::ZoomToFit() verticalScrollBar()->setValue(GetItemYFromKeyframeValue(max_val) - CalculatePaddingFromDimensionScale(this->height())); } -void CurveView::AddKeyframe(NodeKeyframePtr key) +void CurveView::AddKeyframe(NodeKeyframe* key) { KeyframeViewItem* item = AddKeyframeInternal(key); - SetItemYFromKeyframeValue(key.get(), item); - item->SetOverrideBrush(keyframe_colors_.value(&key->parent()->keyframe_tracks().at(key->track()))); + SetItemYFromKeyframeValue(key, item); + item->SetOverrideBrush(keyframe_colors_.value(GetKeyframeTrackUniqueID(key->parent(), key->element(), key->track()))); - connect(key.get(), &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged); - connect(key.get(), &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged); + connect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged); + connect(key, &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged); } } diff --git a/app/widget/curvewidget/curveview.h b/app/widget/curvewidget/curveview.h index 65a82ed52..afa0b13bb 100644 --- a/app/widget/curvewidget/curveview.h +++ b/app/widget/curvewidget/curveview.h @@ -45,7 +45,7 @@ public: void DisconnectInput(NodeInput* input); public slots: - void AddKeyframe(NodeKeyframePtr key); + void AddKeyframe(NodeKeyframe* key); void ZoomToFit(); @@ -63,6 +63,8 @@ protected: virtual void ContextMenuEvent(Menu &m) override; private: + void ConnectInputElement(NodeInput* input, int element); + qreal GetItemYFromKeyframeValue(NodeKeyframe* key); qreal GetItemYFromKeyframeValue(double value); @@ -74,7 +76,9 @@ private: void CreateBezierControlPoints(KeyframeViewItem *item); - QMap keyframe_colors_; + uint GetKeyframeTrackUniqueID(NodeInput* input, int element, int track); + + QMap keyframe_colors_; int text_padding_; diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index b8dcd1a1d..4f07d330b 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -215,9 +215,7 @@ void CurveWidget::UpdateBridgeTime(const int64_t ×tamp) void CurveWidget::ConnectNode(Node *n) { - QVector inputs = n->GetInputsIncludingArrays(); - - foreach (NodeInput* i, inputs) { + foreach (NodeInput* i, n->parameters()) { if (tree_view_->IsInputEnabled(i)) { view_->ConnectInput(i); } diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index dddf30c62..359d717b9 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -42,7 +42,7 @@ void KeyframeView::SceneRectUpdateEvent(QRectF &rect) rect.setHeight(max_scroll_); } -void KeyframeView::AddKeyframe(NodeKeyframePtr key, int y) +void KeyframeView::AddKeyframe(NodeKeyframe* key, int y) { QPoint global_pt(0, y); QPoint local_pt = mapFromGlobal(global_pt); diff --git a/app/widget/keyframeview/keyframeview.h b/app/widget/keyframeview/keyframeview.h index 5eef3934c..e41b6a0e0 100644 --- a/app/widget/keyframeview/keyframeview.h +++ b/app/widget/keyframeview/keyframeview.h @@ -42,7 +42,7 @@ protected: virtual void SceneRectUpdateEvent(QRectF& rect) override; public slots: - void AddKeyframe(NodeKeyframePtr key, int y); + void AddKeyframe(NodeKeyframe* key, int y); private: int max_scroll_; diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp index e2b6888e3..aa28fa5a5 100644 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ b/app/widget/keyframeview/keyframeviewbase.cpp @@ -63,11 +63,7 @@ void KeyframeViewBase::DeleteSelected() for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) { if (i.value()->isSelected()) { - NodeInput* input_parent = i.key()->parent(); - - new NodeParamRemoveKeyframeCommand(input_parent, - input_parent->get_keyframe_shared_ptr_from_raw(i.key()), - command); + new NodeParamRemoveKeyframeCommand(i.key(), command); } } @@ -76,38 +72,38 @@ void KeyframeViewBase::DeleteSelected() void KeyframeViewBase::RemoveKeyframesOfNode(Node *n) { - QVector inputs = n->GetInputsIncludingArrays(); - - foreach (NodeInput* i, inputs) { + foreach (NodeInput* i, n->inputs()) { RemoveKeyframesOfInput(i); } } -void KeyframeViewBase::RemoveKeyframesOfInput(NodeInput *i) +void KeyframeViewBase::RemoveKeyframesOfInput(NodeInput *input) { - foreach (const NodeInput::KeyframeTrack& track, i->keyframe_tracks()) { - foreach (NodeKeyframePtr key, track) { - RemoveKeyframe(key); + for (int i=-1; iArraySize(); i++) { + foreach (const NodeKeyframeTrack& track, input->GetKeyframeTracks(i)) { + foreach (NodeKeyframe* key, track) { + RemoveKeyframe(key); + } } } } -void KeyframeViewBase::RemoveKeyframe(NodeKeyframePtr key) +void KeyframeViewBase::RemoveKeyframe(NodeKeyframe* key) { - KeyframeAboutToBeRemoved(key.get()); + KeyframeAboutToBeRemoved(key); - delete item_map_.take(key.get()); + delete item_map_.take(key); } -KeyframeViewItem *KeyframeViewBase::AddKeyframeInternal(NodeKeyframePtr key) +KeyframeViewItem *KeyframeViewBase::AddKeyframeInternal(NodeKeyframe* key) { - KeyframeViewItem* item = item_map_.value(key.get()); + KeyframeViewItem* item = item_map_.value(key); if (!item) { item = new KeyframeViewItem(key); item->SetTimeTarget(GetTimeTarget()); item->SetScale(GetScale()); - item_map_.insert(key.get(), item); + item_map_.insert(key, item); scene()->addItem(item); } @@ -151,7 +147,7 @@ void KeyframeViewBase::mousePressEvent(QMouseEvent *event) selected_keys_.replace(i, {key, key->x(), - GetAdjustedTime(key->key()->parent()->parentNode(), GetTimeTarget(), key->key()->time(), NodeParam::kOutput), + GetAdjustedTime(key->key()->parent()->parent(), GetTimeTarget(), key->key()->time(), false), key->key()->value().toDouble()}); } } @@ -184,9 +180,9 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) //input_parent->blockSignals(true); rational node_time = GetAdjustedTime(GetTimeTarget(), - keypair.key->key()->parent()->parentNode(), + keypair.key->key()->parent()->parent(), CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()), - NodeParam::kInput); + true); keypair.key->key()->set_time(node_time); @@ -235,9 +231,9 @@ void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event) // Calculate the new time for this keyframe rational node_time = GetAdjustedTime(GetTimeTarget(), - keypair.key->key()->parent()->parentNode(), + keypair.key->key()->parent()->parent(), CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()), - NodeParam::kInput); + true); @@ -493,7 +489,7 @@ void KeyframeViewBase::ShowContextMenu() void KeyframeViewBase::ShowKeyframePropertiesDialog() { QList items = scene()->selectedItems(); - QList keys; + QVector keys; foreach (QGraphicsItem* item, items) { keys.append(static_cast(item)->key()); @@ -521,15 +517,15 @@ void KeyframeViewBase::AutoSelectKeyTimeNeighbors() rational key_time = key_item->key()->time(); - QList keys = key_item->key()->parent()->get_keyframe_at_time(key_time); + QVector keys = key_item->key()->parent()->GetKeyframesAtTime(key_time, key_item->key()->element()); - foreach (NodeKeyframePtr k, keys) { + foreach (NodeKeyframe* k, keys) { if (k == key_item->key()) { continue; } // Ensure this key is not already selected - KeyframeViewItem* item = item_map_.value(k.get()); + KeyframeViewItem* item = item_map_.value(k); item->setSelected(true); } diff --git a/app/widget/keyframeview/keyframeviewbase.h b/app/widget/keyframeview/keyframeviewbase.h index c81686a96..4b5d4edc6 100644 --- a/app/widget/keyframeview/keyframeviewbase.h +++ b/app/widget/keyframeview/keyframeviewbase.h @@ -42,13 +42,13 @@ public: void RemoveKeyframesOfNode(Node* n); - void RemoveKeyframesOfInput(NodeInput* i); + void RemoveKeyframesOfInput(NodeInput* input); public slots: - void RemoveKeyframe(NodeKeyframePtr key); + void RemoveKeyframe(NodeKeyframe* key); protected: - virtual KeyframeViewItem* AddKeyframeInternal(NodeKeyframePtr key); + virtual KeyframeViewItem* AddKeyframeInternal(NodeKeyframe* key); virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; diff --git a/app/widget/keyframeview/keyframeviewitem.cpp b/app/widget/keyframeview/keyframeviewitem.cpp index 783d94544..a52b815b8 100644 --- a/app/widget/keyframeview/keyframeviewitem.cpp +++ b/app/widget/keyframeview/keyframeviewitem.cpp @@ -30,7 +30,7 @@ namespace olive { -KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent) : +KeyframeViewItem::KeyframeViewItem(NodeKeyframe* key, QGraphicsItem *parent) : QGraphicsRectItem(parent), key_(key), scale_(1.0), @@ -39,8 +39,8 @@ KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent) : { setFlag(QGraphicsItem::ItemIsSelectable); - connect(key.get(), &NodeKeyframe::TimeChanged, this, &KeyframeViewItem::UpdatePos); - connect(key.get(), &NodeKeyframe::TypeChanged, this, &KeyframeViewItem::Redraw); + connect(key, &NodeKeyframe::TimeChanged, this, &KeyframeViewItem::UpdatePos); + connect(key, &NodeKeyframe::TypeChanged, this, &KeyframeViewItem::Redraw); int keyframe_size = QtUtils::QFontMetricsWidth(qApp->fontMetrics(), "Oi"); int half_sz = keyframe_size/2; @@ -69,7 +69,7 @@ void KeyframeViewItem::SetOverrideBrush(const QBrush &b) setBrush(b); } -NodeKeyframePtr KeyframeViewItem::key() const +NodeKeyframe* KeyframeViewItem::key() const { return key_; } @@ -117,7 +117,7 @@ void KeyframeViewItem::TimeTargetChangedEvent(Node *) void KeyframeViewItem::UpdatePos() { - rational adjusted = GetAdjustedTime(key_->parent()->parentNode(), GetTimeTarget(), key_->time(), NodeParam::kOutput); + rational adjusted = GetAdjustedTime(key_->parent()->parent(), GetTimeTarget(), key_->time(), false); setPos(adjusted.toDouble() * scale_, vert_center_); } diff --git a/app/widget/keyframeview/keyframeviewitem.h b/app/widget/keyframeview/keyframeviewitem.h index 102da3b61..8c053fd49 100644 --- a/app/widget/keyframeview/keyframeviewitem.h +++ b/app/widget/keyframeview/keyframeviewitem.h @@ -32,7 +32,7 @@ class KeyframeViewItem : public QObject, public QGraphicsRectItem, public TimeTa { Q_OBJECT public: - KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent = nullptr); + KeyframeViewItem(NodeKeyframe* key, QGraphicsItem *parent = nullptr); void SetOverrideY(qreal vertical_center); @@ -40,7 +40,7 @@ public: void SetOverrideBrush(const QBrush& b); - NodeKeyframePtr key() const; + NodeKeyframe* key() const; protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; @@ -48,7 +48,7 @@ protected: virtual void TimeTargetChangedEvent(Node* ) override; private: - NodeKeyframePtr key_; + NodeKeyframe* key_; double scale_; diff --git a/app/widget/keyframeview/keyframeviewundo.cpp b/app/widget/keyframeview/keyframeviewundo.cpp index 6e286768e..9f109fc80 100644 --- a/app/widget/keyframeview/keyframeviewundo.cpp +++ b/app/widget/keyframeview/keyframeviewundo.cpp @@ -26,7 +26,7 @@ namespace olive { -KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframePtr key, NodeKeyframe::Type type, QUndoCommand *parent) : +KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe::Type type, QUndoCommand *parent) : UndoCommand(parent), key_(key), old_type_(key->type()), @@ -36,7 +36,7 @@ KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframePtr key, NodeKeyframe Project *KeyframeSetTypeCommand::GetRelevantProject() const { - return static_cast(key_->parent()->parentNode()->parent())->project(); + return key_->parent()->parent()->parent()->project(); } void KeyframeSetTypeCommand::redo_internal() @@ -49,7 +49,7 @@ void KeyframeSetTypeCommand::undo_internal() key_->set_type(old_type_); } -KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand *parent) : +KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand *parent) : UndoCommand(parent), key_(key), mode_(mode), @@ -58,7 +58,7 @@ KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key { } -KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF &new_point, const QPointF &old_point, QUndoCommand *parent) : +KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF &new_point, const QPointF &old_point, QUndoCommand *parent) : UndoCommand(parent), key_(key), mode_(mode), @@ -69,7 +69,7 @@ KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key Project *KeyframeSetBezierControlPoint::GetRelevantProject() const { - return static_cast(key_->parent()->parentNode()->parent())->project(); + return key_->parent()->parent()->parent()->project(); } void KeyframeSetBezierControlPoint::redo_internal() diff --git a/app/widget/keyframeview/keyframeviewundo.h b/app/widget/keyframeview/keyframeviewundo.h index a6aa322b1..97d50fe59 100644 --- a/app/widget/keyframeview/keyframeviewundo.h +++ b/app/widget/keyframeview/keyframeviewundo.h @@ -28,7 +28,7 @@ namespace olive { class KeyframeSetTypeCommand : public UndoCommand { public: - KeyframeSetTypeCommand(NodeKeyframePtr key, NodeKeyframe::Type type, QUndoCommand* parent = nullptr); + KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe::Type type, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -37,7 +37,7 @@ protected: virtual void undo_internal() override; private: - NodeKeyframePtr key_; + NodeKeyframe* key_; NodeKeyframe::Type old_type_; @@ -47,8 +47,8 @@ private: class KeyframeSetBezierControlPoint : public UndoCommand { public: - KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand* parent = nullptr); - KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF& new_point, const QPointF& old_point, QUndoCommand* parent = nullptr); + KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand* parent = nullptr); + KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& new_point, const QPointF& old_point, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -57,7 +57,7 @@ protected: virtual void undo_internal() override; private: - NodeKeyframePtr key_; + NodeKeyframe* key_; NodeKeyframe::BezierType mode_; diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 69b37a310..b5805ae07 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -151,7 +151,6 @@ void NodeParamView::SelectNodes(const QVector &nodes) connect(item, &NodeParamViewItem::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); connect(item, &NodeParamViewItem::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged); - connect(item, &NodeParamViewItem::InputDoubleClicked, this, &NodeParamView::InputDoubleClicked); connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode); connect(item, &NodeParamViewItem::dockLocationChanged, this, &NodeParamView::QueueKeyframePositionUpdate); connect(item, &NodeParamViewItem::dockLocationChanged, this, &NodeParamView::SignalNodeOrder); diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 9c45fadfb..17bdab0a9 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -73,8 +73,6 @@ public: void DeleteSelected(); signals: - void InputDoubleClicked(NodeInput* input); - void RequestSelectNode(const QVector& target); void NodeOrderChanged(const QVector& nodes); diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp index dcbcb8e18..09fe3c96e 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp @@ -24,7 +24,7 @@ namespace olive { -NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInputArray* array, QWidget* parent) : +NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInput *array, QWidget* parent) : QWidget(parent), array_(array) { @@ -40,19 +40,19 @@ NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInputArray* array, QWidge layout->addWidget(plus_btn_); connect(plus_btn_, &QPushButton::clicked, this, &NodeParamViewArrayWidget::AddElement); - connect(array_, &NodeInputArray::SizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter); + connect(array_, &NodeInput::ArraySizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter); UpdateCounter(); } void NodeParamViewArrayWidget::UpdateCounter() { - count_lbl_->setText(tr("%1 element(s)").arg(array_->GetSize())); + count_lbl_->setText(tr("%1 element(s)").arg(array_->ArraySize())); } void NodeParamViewArrayWidget::AddElement() { - array_->Append(); + array_->ArrayResize(array_->ArraySize() + 1); } } diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.h b/app/widget/nodeparamview/nodeparamviewarraywidget.h index 1582f497d..64e6b9535 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.h +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.h @@ -25,7 +25,7 @@ #include #include -#include "node/inputarray.h" +#include "node/input.h" namespace olive { @@ -33,10 +33,10 @@ class NodeParamViewArrayWidget : public QWidget { Q_OBJECT public: - NodeParamViewArrayWidget(NodeInputArray* array, QWidget* parent = nullptr); + NodeParamViewArrayWidget(NodeInput* array, QWidget* parent = nullptr); private: - NodeInputArray* array_; + NodeInput* array_; QLabel* count_lbl_; diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index fba601f93..b65caf607 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -30,9 +30,10 @@ namespace olive { -NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, QWidget *parent) : +NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, int element, QWidget *parent) : QWidget(parent), - input_(input) + input_(input), + element_(element) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setSpacing(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral(" "))); @@ -55,18 +56,25 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, QWidg connected_to_lbl_->setForegroundRole(QPalette::Link); connected_to_lbl_->setFont(link_font); - UpdateConnected(); + UpdateConnected(nullptr, element_); - connect(input_, &NodeInput::EdgeAdded, this, &NodeParamViewConnectedLabel::UpdateConnected); - connect(input_, &NodeInput::EdgeRemoved, this, &NodeParamViewConnectedLabel::UpdateConnected); + connect(input_, &NodeInput::InputConnected, this, &NodeParamViewConnectedLabel::UpdateConnected); + connect(input_, &NodeInput::InputDisconnected, this, &NodeParamViewConnectedLabel::UpdateConnected); } -void NodeParamViewConnectedLabel::UpdateConnected() +void NodeParamViewConnectedLabel::UpdateConnected(Node *src, int element) { + Q_UNUSED(src) + + if (element_ != element) { + // Do nothing + return; + } + QString connection_str; - if (input_->is_connected()) { - connection_str = input_->get_connected_node()->Name(); + if (input_->IsConnected(element_)) { + connection_str = input_->GetConnectedNode(element_)->Name(); } else { connection_str = tr("Nothing"); } @@ -80,7 +88,7 @@ void NodeParamViewConnectedLabel::ShowLabelContextMenu() QAction* disconnect_action = m.addAction(tr("Disconnect")); connect(disconnect_action, &QAction::triggered, this, [this](){ - Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(input_->get_connected_output(), input_)); + Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(input_->GetConnectedNode(element_), input_, element_)); }); m.exec(QCursor::pos()); diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h index ffec58ecc..233456f4d 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h @@ -29,13 +29,13 @@ namespace olive { class NodeParamViewConnectedLabel : public QWidget { Q_OBJECT public: - NodeParamViewConnectedLabel(NodeInput* input, QWidget* parent = nullptr); + NodeParamViewConnectedLabel(NodeInput* input, int element, QWidget* parent = nullptr); signals: void ConnectionClicked(); private slots: - void UpdateConnected(); + void UpdateConnected(Node* src, int element); void ShowLabelContextMenu(); @@ -44,6 +44,8 @@ private: NodeInput* input_; + int element_; + }; } diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 8261d3720..10c1c58bf 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -43,17 +43,14 @@ NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) : this->setTitleBarWidget(title_bar_); // Create and add contents widget - QVector inputs; + QVector inputs; // Filter out inputs - foreach (NodeParam* p, node->parameters()) { - if (p->type() == NodeParam::kInput) { - inputs.append(static_cast(p)); - } + foreach (NodeInput* p, node->parameters()) { + inputs.append({p, -1}); } body_ = new NodeParamViewItemBody(inputs); - connect(body_, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItem::InputDoubleClicked); connect(body_, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItem::RequestSelectNode); connect(body_, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItem::RequestSetTime); connect(body_, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItem::KeyframeAdded); @@ -206,7 +203,7 @@ void NodeParamViewItemTitleBar::mouseDoubleClickEvent(QMouseEvent *event) collapse_btn_->click(); } -NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, QWidget *parent) : +NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, QWidget *parent) : QWidget(parent) { int row_count = 0; @@ -215,14 +212,13 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, QGridLayout* content_layout = new QGridLayout(this); - foreach (NodeInput* input, inputs) { + foreach (const NodeConnectable::InputConnection& conn, inputs) { InputUI ui_objects; // Add descriptor label ui_objects.main_label = new ClickableLabel(); - connect(ui_objects.main_label, &ClickableLabel::MouseDoubleClicked, this, &NodeParamViewItemBody::LabelDoubleClicked); - if (input->IsArray()) { + if (conn.input->IsArray()) { QHBoxLayout* array_label_layout = new QHBoxLayout(); array_label_layout->setMargin(0); @@ -233,7 +229,13 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, content_layout->addLayout(array_label_layout, row_count, 0); - NodeParamViewItemBody* sub_body = new NodeParamViewItemBody(static_cast(input)->sub_params()); + QVector subelements(conn.input->ArraySize()); + + for (int i=0; iArraySize(); i++) { + subelements[i] = {conn.input, i}; + } + + NodeParamViewItemBody* sub_body = new NodeParamViewItemBody(subelements); sub_bodies_.append(sub_body); sub_body->layout()->setMargin(0); content_layout->addWidget(sub_body, row_count + 1, 0, 1, max_col + 1); @@ -243,14 +245,13 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, connect(sub_body, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItemBody::KeyframeAdded); connect(sub_body, &NodeParamViewItemBody::KeyframeRemoved, this, &NodeParamViewItemBody::KeyframeRemoved); connect(sub_body, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItemBody::RequestSetTime); - connect(sub_body, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItemBody::InputDoubleClicked); connect(sub_body, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItemBody::RequestSelectNode); } else { content_layout->addWidget(ui_objects.main_label, row_count, 0); } // Create a widget/input bridge for this input - ui_objects.widget_bridge = new NodeParamViewWidgetBridge(input, this); + ui_objects.widget_bridge = new NodeParamViewWidgetBridge(conn.input, conn.element, this); // Add widgets for this parameter to the layout { @@ -261,42 +262,44 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector &inputs, } } - if (input->is_connectable()) { + if (conn.input->IsConnectable()) { // Create clickable label used when an input is connected - ui_objects.connected_label = new NodeParamViewConnectedLabel(input); + ui_objects.connected_label = new NodeParamViewConnectedLabel(conn.input, conn.element); connect(ui_objects.connected_label, &NodeParamViewConnectedLabel::ConnectionClicked, this, &NodeParamViewItemBody::ConnectionClicked); content_layout->addWidget(ui_objects.connected_label, row_count, 1); - connect(input, &NodeInput::EdgeAdded, this, &NodeParamViewItemBody::EdgeChanged); - connect(input, &NodeInput::EdgeRemoved, this, &NodeParamViewItemBody::EdgeChanged); + connect(conn.input, &NodeInput::InputConnected, this, &NodeParamViewItemBody::EdgeChanged); + connect(conn.input, &NodeInput::InputDisconnected, this, &NodeParamViewItemBody::EdgeChanged); } // Add keyframe control to this layout if parameter is keyframable - if (input->is_keyframable()) { + if (conn.input->IsKeyframable()) { // Hacky but effective way to make sure this widget is always as far right as possible int control_column = max_col; ui_objects.key_control = new NodeParamViewKeyframeControl(); - ui_objects.key_control->SetInput(input); + ui_objects.key_control->SetInput(conn.input, conn.element); content_layout->addWidget(ui_objects.key_control, row_count, control_column); connect(ui_objects.key_control, &NodeParamViewKeyframeControl::RequestSetTime, this, &NodeParamViewItemBody::RequestSetTime); - connect(input, &NodeInput::KeyframeEnableChanged, this, &NodeParamViewItemBody::InputKeyframeEnableChanged); - connect(input, &NodeInput::KeyframeAdded, this, &NodeParamViewItemBody::InputAddedKeyframe); - connect(input, &NodeInput::KeyframeRemoved, this, &NodeParamViewItemBody::KeyframeRemoved); + connect(conn.input, &NodeInput::KeyframeEnableChanged, this, &NodeParamViewItemBody::InputKeyframeEnableChanged); + connect(conn.input, &NodeInput::KeyframeAdded, this, &NodeParamViewItemBody::InputAddedKeyframe); + connect(conn.input, &NodeInput::KeyframeRemoved, this, &NodeParamViewItemBody::KeyframeRemoved); } - input_ui_map_.insert(input, ui_objects); + input_ui_map_.insert(conn, ui_objects); // Update "connected" label - if (input->is_connectable()) { - UpdateUIForEdgeConnection(input); + if (conn.input->IsConnectable()) { + for (int i=-1; iArraySize(); i++) { + UpdateUIForEdgeConnection(conn.input, i); + } } row_count++; // If the row count is an array, we put an extra body widget in the next row so we skip over it here - if (input->IsArray()) { + if (conn.input->IsArray()) { row_count++; } } @@ -336,10 +339,8 @@ void NodeParamViewItemBody::SetTime(const rational &time) void NodeParamViewItemBody::Retranslate() { - QMap::const_iterator i; - - for (i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) { - i.value().main_label->setText(tr("%1:").arg(i.key()->name())); + for (auto i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) { + i.value().main_label->setText(tr("%1:").arg(i.key().input->name())); } foreach (NodeParamViewItemBody* sb, sub_bodies_) { @@ -349,13 +350,11 @@ void NodeParamViewItemBody::Retranslate() void NodeParamViewItemBody::SignalAllKeyframes() { - QMap::const_iterator i; + for (auto i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) { + NodeInput* input = i.key().input; - for (i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) { - NodeInput* input = i.key(); - - foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) { - foreach (NodeKeyframePtr key, track) { + foreach (const NodeKeyframeTrack& track, input->GetKeyframeTracks(i.key().element)) { + foreach (NodeKeyframe* key, track) { InputAddedKeyframeInternal(input, key); } } @@ -366,30 +365,32 @@ void NodeParamViewItemBody::SignalAllKeyframes() } } -void NodeParamViewItemBody::EdgeChanged() +void NodeParamViewItemBody::EdgeChanged(Node* src, int element) { - UpdateUIForEdgeConnection(static_cast(sender())); + Q_UNUSED(src) + + UpdateUIForEdgeConnection(static_cast(sender()), element); } -void NodeParamViewItemBody::UpdateUIForEdgeConnection(NodeInput *input) +void NodeParamViewItemBody::UpdateUIForEdgeConnection(NodeInput *input, int element) { // Show/hide bridge widgets - const InputUI& ui_objects = input_ui_map_[input]; + const InputUI& ui_objects = input_ui_map_[{input, element}]; foreach (QWidget* w, ui_objects.widget_bridge->widgets()) { - w->setVisible(!input->is_connected()); + w->setVisible(!input->IsConnected(element)); } // Show/hide connection label - ui_objects.connected_label->setVisible(input->is_connected()); + ui_objects.connected_label->setVisible(input->IsConnected(element)); } -void NodeParamViewItemBody::InputKeyframeEnableChanged(bool e) +void NodeParamViewItemBody::InputKeyframeEnableChanged(bool e, int element) { NodeInput* input = static_cast(sender()); - foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) { - foreach (NodeKeyframePtr key, track) { + foreach (const NodeKeyframeTrack& track, input->GetKeyframeTracks(element)) { + foreach (NodeKeyframe* key, track) { if (e) { // Add a keyframe item for each keyframe InputAddedKeyframeInternal(input, key); @@ -401,7 +402,7 @@ void NodeParamViewItemBody::InputKeyframeEnableChanged(bool e) } } -void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframePtr key) +void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframe* key) { // Get NodeInput that emitted this signal NodeInput* input = static_cast(sender()); @@ -409,24 +410,11 @@ void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframePtr key) InputAddedKeyframeInternal(input, key); } -void NodeParamViewItemBody::LabelDoubleClicked() -{ - QMap::const_iterator iterator; - - for (iterator=input_ui_map_.begin(); iterator!=input_ui_map_.end(); iterator++) { - if (iterator.value().main_label == sender()) { - emit InputDoubleClicked(iterator.key()); - return; - } - } -} - void NodeParamViewItemBody::ConnectionClicked() { - QMap::const_iterator iterator; - for (iterator=input_ui_map_.begin(); iterator!=input_ui_map_.end(); iterator++) { + for (auto iterator=input_ui_map_.begin(); iterator!=input_ui_map_.end(); iterator++) { if (iterator.value().connected_label == sender()) { - Node* connected = iterator.key()->get_connected_node(); + Node* connected = iterator.key().input->parent(); if (connected) { emit RequestSelectNode({connected}); @@ -437,7 +425,7 @@ void NodeParamViewItemBody::ConnectionClicked() } } -void NodeParamViewItemBody::InputAddedKeyframeInternal(NodeInput *input, NodeKeyframePtr keyframe) +void NodeParamViewItemBody::InputAddedKeyframeInternal(NodeInput *input, NodeKeyframe* keyframe) { // Find its row in the parameters QLabel* lbl = input_ui_map_.value(input).main_label; diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index a71b46359..49e49e3a2 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -72,7 +72,7 @@ private: class NodeParamViewItemBody : public QWidget { Q_OBJECT public: - NodeParamViewItemBody(const QVector& inputs, QWidget* parent = nullptr); + NodeParamViewItemBody(const QVector& inputs, QWidget* parent = nullptr); void SetTimeTarget(Node* target); @@ -83,20 +83,18 @@ public: void SignalAllKeyframes(); signals: - void KeyframeAdded(NodeKeyframePtr key, int y); + void KeyframeAdded(NodeKeyframe* key, int y); - void KeyframeRemoved(NodeKeyframePtr key); + void KeyframeRemoved(NodeKeyframe* key); void RequestSetTime(const rational& time); - void InputDoubleClicked(NodeInput* input); - void RequestSelectNode(const QVector& node); private: - void UpdateUIForEdgeConnection(NodeInput* input); + void UpdateUIForEdgeConnection(NodeInput* input, int element); - void InputAddedKeyframeInternal(NodeInput* input, NodeKeyframePtr keyframe); + void InputAddedKeyframeInternal(NodeInput* input, NodeKeyframe* keyframe); struct InputUI { InputUI(); @@ -107,18 +105,16 @@ private: NodeParamViewKeyframeControl* key_control; }; - QMap input_ui_map_; + QHash input_ui_map_; QVector sub_bodies_; private slots: - void EdgeChanged(); + void EdgeChanged(Node *src, int element); - void InputKeyframeEnableChanged(bool e); + void InputKeyframeEnableChanged(bool e, int element); - void InputAddedKeyframe(NodeKeyframePtr key); - - void LabelDoubleClicked(); + void InputAddedKeyframe(NodeKeyframe* key); void ConnectionClicked(); @@ -153,14 +149,12 @@ public slots: void ToggleExpanded(); signals: - void KeyframeAdded(NodeKeyframePtr key, int y); + void KeyframeAdded(NodeKeyframe* key, int y); - void KeyframeRemoved(NodeKeyframePtr key); + void KeyframeRemoved(NodeKeyframe* key); void RequestSetTime(const rational& time); - void InputDoubleClicked(NodeInput* input); - void RequestSelectNode(const QVector& node); void PinToggled(bool e); diff --git a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp index 6e5e3e7ab..13e5203ef 100644 --- a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp +++ b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp @@ -67,16 +67,11 @@ NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(bool right_align, QWi connect(enable_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged); // Set defaults - SetInput(nullptr); + SetInput(nullptr, -1); ShowButtonsFromKeyframeEnable(false); } -NodeInput *NodeParamViewKeyframeControl::GetConnectedInput() const -{ - return input_; -} - -void NodeParamViewKeyframeControl::SetInput(NodeInput *input) +void NodeParamViewKeyframeControl::SetInput(NodeInput *input, int element) { if (input_ != nullptr) { disconnect(input_, &NodeInput::KeyframeEnableChanged, enable_key_btn_, &QPushButton::setChecked); @@ -85,10 +80,11 @@ void NodeParamViewKeyframeControl::SetInput(NodeInput *input) } input_ = input; + element_ = element; SetButtonsEnabled(input_); // Pick up keyframing value - enable_key_btn_->setChecked(input_ && input_->is_keyframing()); + enable_key_btn_->setChecked(input_ && input_->IsKeyframing(element_)); // Update buttons UpdateState(); @@ -126,12 +122,12 @@ void NodeParamViewKeyframeControl::SetButtonsEnabled(bool e) rational NodeParamViewKeyframeControl::GetCurrentTimeAsNodeTime() const { - return GetAdjustedTime(GetTimeTarget(), input_->parentNode(), time_, NodeParam::kInput); + return GetAdjustedTime(GetTimeTarget(), input_->parent(), time_, true); } rational NodeParamViewKeyframeControl::ConvertToViewerTime(const rational &r) const { - return GetAdjustedTime(input_->parentNode(), GetTimeTarget(), r, NodeParam::kOutput); + return GetAdjustedTime(input_->parent(), GetTimeTarget(), r, false); } void NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable(bool e) @@ -145,30 +141,34 @@ void NodeParamViewKeyframeControl::ToggleKeyframe(bool e) { rational node_time = GetCurrentTimeAsNodeTime(); - QList keys = input_->get_keyframe_at_time(node_time); + QVector keys = input_->GetKeyframesAtTime(node_time, element_); QUndoCommand* command = new QUndoCommand(); + int nb_tracks = input_->GetNumberOfKeyframeTracks(); + if (e && keys.isEmpty()) { // Add a keyframe here (one for each track) - for (int i=0;iget_number_of_keyframe_tracks();i++) { - NodeKeyframePtr key = NodeKeyframe::Create(node_time, - input_->get_value_at_time_for_track(node_time, i), - input_->get_best_keyframe_type_for_time(node_time, i), - i); + for (int i=0;iGetValueAtTimeForTrack(node_time, i, element_), + input_->GetBestKeyframeTypeForTime(node_time, i, element_), + i, + element_); new NodeParamInsertKeyframeCommand(input_, key, command); } } else if (!e && !keys.isEmpty()) { // Remove all keyframes at this time - foreach (NodeKeyframePtr key, keys) { - new NodeParamRemoveKeyframeCommand(input_, key, command); + foreach (NodeKeyframe* key, keys) { + new NodeParamRemoveKeyframeCommand(key, command); - if (input_->keyframe_tracks().at(key->track()).size() == 1) { + if (input_->GetKeyframeTracks(key->track()).size() == 1) { // If this was the last keyframe on this track, set the standard value to the value at this time too new NodeParamSetStandardValueCommand(input_, key->track(), - input_->get_value_at_time_for_track(node_time, key->track()), + element_, + input_->GetValueAtTimeForTrack(node_time, key->track(), element_), command); } } @@ -183,21 +183,21 @@ void NodeParamViewKeyframeControl::UpdateState() return; } - NodeKeyframePtr earliest_key = input_->get_earliest_keyframe(); - NodeKeyframePtr latest_key = input_->get_latest_keyframe(); + NodeKeyframe* earliest_key = input_->GetEarliestKeyframe(element_); + NodeKeyframe* latest_key = input_->GetLatestKeyframe(element_); rational node_time = GetCurrentTimeAsNodeTime(); prev_key_btn_->setEnabled(earliest_key && node_time > earliest_key->time()); next_key_btn_->setEnabled(latest_key && node_time < latest_key->time()); - toggle_key_btn_->setChecked(input_->has_keyframe_at_time(node_time)); + toggle_key_btn_->setChecked(input_->HasKeyframeAtTime(node_time, element_)); } void NodeParamViewKeyframeControl::GoToPreviousKey() { rational node_time = GetCurrentTimeAsNodeTime(); - NodeKeyframePtr previous_key = input_->get_closest_keyframe_before_time(node_time); + NodeKeyframe* previous_key = input_->GetClosestKeyframeBeforeTime(node_time, element_); if (previous_key) { rational key_time = ConvertToViewerTime(previous_key->time()); @@ -210,7 +210,7 @@ void NodeParamViewKeyframeControl::GoToNextKey() { rational node_time = GetCurrentTimeAsNodeTime(); - NodeKeyframePtr next_key = input_->get_closest_keyframe_after_time(node_time); + NodeKeyframe* next_key = input_->GetClosestKeyframeAfterTime(node_time, element_); if (next_key) { rational key_time = ConvertToViewerTime(next_key->time()); @@ -221,7 +221,7 @@ void NodeParamViewKeyframeControl::GoToNextKey() void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) { - if (e == input_->is_keyframing()) { + if (e == input_->IsKeyframing(element_)) { // No-op return; } @@ -233,13 +233,14 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) new NodeParamSetKeyframingCommand(input_, true, command); // Create one keyframe across all tracks here - QVector key_vals = input_->get_split_standard_value(); + const QVector& key_vals = input_->GetSplitStandardValue(element_); for (int i=0;i stored_vals = input_->get_split_values_at_time(GetCurrentTimeAsNodeTime()); + const QVector& stored_vals = input_->GetSplitValuesAtTime(GetCurrentTimeAsNodeTime(), element_); // Delete all keyframes - foreach (const NodeInput::KeyframeTrack& track, input_->keyframe_tracks()) { + foreach (const NodeKeyframeTrack& track, input_->GetKeyframeTracks(element_)) { for (int i=track.size()-1;i>=0;i--) { - new NodeParamRemoveKeyframeCommand(input_, track.at(i), command); + new NodeParamRemoveKeyframeCommand(track.at(i), command); } } // Update standard value for (int i=0;iis_keyframing()); + Q_ASSERT(setting != input_->IsKeyframing()); } Project *NodeParamSetKeyframingCommand::GetRelevantProject() const { - return static_cast(input_->parentNode()->parent())->project(); + return static_cast(input_->parent()->parent())->project(); } void NodeParamSetKeyframingCommand::redo_internal() { - input_->set_is_keyframing(setting_); + input_->SetIsKeyframing(setting_, element_); } void NodeParamSetKeyframingCommand::undo_internal() { - input_->set_is_keyframing(!setting_); + input_->SetIsKeyframing(!setting_, element_); } -NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& value, QUndoCommand* parent) : +NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value, QUndoCommand* parent) : UndoCommand(parent), key_(key), old_value_(key_->value()), @@ -56,7 +57,7 @@ NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframeP { } -NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) : +NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) : UndoCommand(parent), key_(key), old_value_(old_value), @@ -67,7 +68,7 @@ NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframeP Project *NodeParamSetKeyframeValueCommand::GetRelevantProject() const { - return static_cast(key_->parent()->parentNode()->parent())->project(); + return static_cast(key_->parent()->parent()->parent()->parent())->project(); } void NodeParamSetKeyframeValueCommand::redo_internal() @@ -80,63 +81,53 @@ void NodeParamSetKeyframeValueCommand::undo_internal() key_->set_value(old_value_); } -NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, QUndoCommand* parent) : +NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframe* keyframe, QUndoCommand* parent) : UndoCommand(parent), input_(input), - keyframe_(keyframe), - done_(false) -{ -} - -NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, bool already_done, QUndoCommand *parent) : - UndoCommand(parent), - input_(input), - keyframe_(keyframe), - done_(already_done) + keyframe_(keyframe) { + keyframe_->setParent(&memory_manager_); } Project *NodeParamInsertKeyframeCommand::GetRelevantProject() const { - return static_cast(input_->parentNode()->parent())->project(); + return static_cast(input_->parent()->parent())->project(); } void NodeParamInsertKeyframeCommand::redo_internal() { - if (!done_) { - input_->insert_keyframe(keyframe_); - } + keyframe_->setParent(input_); } void NodeParamInsertKeyframeCommand::undo_internal() { - input_->remove_keyframe(keyframe_); - done_ = false; + keyframe_->setParent(&memory_manager_); } -NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, QUndoCommand *parent) : +NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe, QUndoCommand *parent) : UndoCommand(parent), - input_(input), + input_(keyframe->parent()), keyframe_(keyframe) { } Project *NodeParamRemoveKeyframeCommand::GetRelevantProject() const { - return static_cast(input_->parentNode()->parent())->project(); + return static_cast(input_->parent()->parent())->project(); } void NodeParamRemoveKeyframeCommand::redo_internal() { - input_->remove_keyframe(keyframe_); + // Removes from input + keyframe_->setParent(&memory_manager_); } void NodeParamRemoveKeyframeCommand::undo_internal() { - input_->insert_keyframe(keyframe_); + keyframe_->setParent(input_); } -NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational &time, QUndoCommand *parent) : +NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational &time, QUndoCommand *parent) : UndoCommand(parent), key_(key), old_time_(key->time()), @@ -144,7 +135,7 @@ NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr { } -NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational &new_time, const rational &old_time, QUndoCommand *parent) : +NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational &new_time, const rational &old_time, QUndoCommand *parent) : UndoCommand(parent), key_(key), old_time_(old_time), @@ -154,7 +145,7 @@ NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr Project *NodeParamSetKeyframeTimeCommand::GetRelevantProject() const { - return static_cast(key_->parent()->parentNode()->parent())->project(); + return static_cast(key_->parent()->parent()->parent())->project(); } void NodeParamSetKeyframeTimeCommand::redo_internal() @@ -167,18 +158,20 @@ void NodeParamSetKeyframeTimeCommand::undo_internal() key_->set_time(old_time_); } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, const QVariant &value, QUndoCommand *parent) : +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &value, QUndoCommand *parent) : UndoCommand(parent), input_(input), + element_(element), track_(track), - old_value_(input_->get_standard_value()), + old_value_(input_->GetStandardValue(element_)), new_value_(value) { } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) : +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) : UndoCommand(parent), input_(input), + element_(element), track_(track), old_value_(old_value), new_value_(new_value) @@ -187,17 +180,17 @@ NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *in Project *NodeParamSetStandardValueCommand::GetRelevantProject() const { - return static_cast(input_->parentNode()->parent())->project(); + return static_cast(input_->parent()->parent())->project(); } void NodeParamSetStandardValueCommand::redo_internal() { - input_->set_standard_value(new_value_, track_); + input_->SetStandardValueOnTrack(new_value_, track_, element_); } void NodeParamSetStandardValueCommand::undo_internal() { - input_->set_standard_value(old_value_, track_); + input_->SetStandardValueOnTrack(old_value_, track_, element_); } } diff --git a/app/widget/nodeparamview/nodeparamviewundo.h b/app/widget/nodeparamview/nodeparamviewundo.h index 07d190a83..ad15c9892 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.h +++ b/app/widget/nodeparamview/nodeparamviewundo.h @@ -28,7 +28,7 @@ namespace olive { class NodeParamSetKeyframingCommand : public UndoCommand { public: - NodeParamSetKeyframingCommand(NodeInput* input, bool setting, QUndoCommand* parent = nullptr); + NodeParamSetKeyframingCommand(NodeInput* input, int element, bool setting, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -39,12 +39,13 @@ protected: private: NodeInput* input_; bool setting_; + int element_; + }; class NodeParamInsertKeyframeCommand : public UndoCommand { public: - NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, QUndoCommand *parent = nullptr); - NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, bool already_done, QUndoCommand *parent = nullptr); + NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframe* keyframe, QUndoCommand *parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -55,15 +56,15 @@ protected: private: NodeInput* input_; - NodeKeyframePtr keyframe_; + NodeKeyframe* keyframe_; - bool done_; + QObject memory_manager_; }; class NodeParamRemoveKeyframeCommand : public UndoCommand { public: - NodeParamRemoveKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, QUndoCommand *parent = nullptr); + NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe, QUndoCommand *parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -74,14 +75,16 @@ protected: private: NodeInput* input_; - NodeKeyframePtr keyframe_; + NodeKeyframe* keyframe_; + + QObject memory_manager_; }; class NodeParamSetKeyframeTimeCommand : public UndoCommand { public: - NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational& time, QUndoCommand* parent = nullptr); - NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational& new_time, const rational& old_time, QUndoCommand* parent = nullptr); + NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational& time, QUndoCommand* parent = nullptr); + NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational& new_time, const rational& old_time, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -90,7 +93,7 @@ protected: virtual void undo_internal() override; private: - NodeKeyframePtr key_; + NodeKeyframe* key_; rational old_time_; rational new_time_; @@ -99,8 +102,8 @@ private: class NodeParamSetKeyframeValueCommand : public UndoCommand { public: - NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& value, QUndoCommand* parent = nullptr); - NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr); + NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value, QUndoCommand* parent = nullptr); + NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -109,7 +112,7 @@ protected: virtual void undo_internal() override; private: - NodeKeyframePtr key_; + NodeKeyframe* key_; QVariant old_value_; QVariant new_value_; @@ -118,8 +121,8 @@ private: class NodeParamSetStandardValueCommand : public UndoCommand { public: - NodeParamSetStandardValueCommand(NodeInput* input, int track, const QVariant& value, QUndoCommand* parent = nullptr); - NodeParamSetStandardValueCommand(NodeInput* input, int track, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr); + NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& value, QUndoCommand* parent = nullptr); + NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -129,6 +132,7 @@ protected: private: NodeInput* input_; + int element_; int track_; QVariant old_value_; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 0717d6135..4d5f6fedc 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -40,9 +40,10 @@ namespace olive { -NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput *input, QObject *parent) : +NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput *input, int element, QObject *parent) : QObject(parent), - input_(input) + input_(input), + element_(element) { CreateWidgets(); @@ -69,30 +70,24 @@ void NodeParamViewWidgetBridge::CreateWidgets() if (input_->IsArray()) { - NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(static_cast(input_)); + NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(input_); widgets_.append(w); } else { // We assume the first data type is the "primary" type - switch (input_->data_type()) { + switch (input_->GetDataType()) { // None of these inputs have applicable UI widgets - case NodeParam::kNone: - case NodeParam::kAny: - case NodeParam::kTexture: - case NodeParam::kMatrix: - case NodeParam::kRational: - case NodeParam::kSamples: - case NodeParam::kDecimal: - case NodeParam::kNumber: - case NodeParam::kString: - case NodeParam::kBuffer: - case NodeParam::kVector: - case NodeParam::kShaderJob: - case NodeParam::kSampleJob: - case NodeParam::kGenerateJob: + case NodeValue::kNone: + case NodeValue::kTexture: + case NodeValue::kMatrix: + case NodeValue::kRational: + case NodeValue::kSamples: + case NodeValue::kShaderJob: + case NodeValue::kSampleJob: + case NodeValue::kGenerateJob: break; - case NodeParam::kInt: + case NodeValue::kInt: { IntegerSlider* slider = new IntegerSlider(); slider->SetDefaultValue(input_->GetDefaultValue()); @@ -101,27 +96,27 @@ void NodeParamViewWidgetBridge::CreateWidgets() connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); break; } - case NodeParam::kFloat: + case NodeValue::kFloat: { CreateSliders(1); break; } - case NodeParam::kVec2: + case NodeValue::kVec2: { CreateSliders(2); break; } - case NodeParam::kVec3: + case NodeValue::kVec3: { CreateSliders(3); break; } - case NodeParam::kVec4: + case NodeValue::kVec4: { CreateSliders(4); break; } - case NodeParam::kCombo: + case NodeValue::kCombo: { QComboBox* combobox = new QComboBox(); @@ -134,42 +129,42 @@ void NodeParamViewWidgetBridge::CreateWidgets() connect(combobox, static_cast(&QComboBox::currentIndexChanged), this, &NodeParamViewWidgetBridge::WidgetCallback); break; } - case NodeParam::kFile: + case NodeValue::kFile: // FIXME: File selector break; - case NodeParam::kColor: + case NodeValue::kColor: { // NOTE: Very convoluted way to get back to the project's color manager - ColorButton* color_button = new ColorButton(static_cast(input_->parentNode()->parent())->project()->color_manager()); + ColorButton* color_button = new ColorButton(input_->parent()->parent()->project()->color_manager()); widgets_.append(color_button); connect(color_button, &ColorButton::ColorChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); break; } - case NodeParam::kText: + case NodeValue::kText: { NodeParamViewRichText* line_edit = new NodeParamViewRichText(); widgets_.append(line_edit); connect(line_edit, &NodeParamViewRichText::textEdited, this, &NodeParamViewWidgetBridge::WidgetCallback); break; } - case NodeParam::kBoolean: + case NodeValue::kBoolean: { QCheckBox* check_box = new QCheckBox(); widgets_.append(check_box); connect(check_box, &QCheckBox::clicked, this, &NodeParamViewWidgetBridge::WidgetCallback); break; } - case NodeParam::kFont: + case NodeValue::kFont: { QFontComboBox* font_combobox = new QFontComboBox(); widgets_.append(font_combobox); connect(font_combobox, &QFontComboBox::currentFontChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); break; } - case NodeParam::kFootage: + case NodeValue::kFootage: { FootageComboBox* footage_combobox = new FootageComboBox(); - footage_combobox->SetRoot(static_cast(input_->parentNode()->parent())->project()->root()); + footage_combobox->SetRoot(input_->parent()->parent()->project()->root()); connect(footage_combobox, &FootageComboBox::FootageChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); @@ -207,22 +202,23 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int { rational node_time = GetCurrentTimeAsNodeTime(); - if (input_->is_keyframing()) { - NodeKeyframePtr existing_key = input_->get_keyframe_at_time_on_track(node_time, track); + if (input_->IsKeyframing(element_)) { + NodeKeyframe* existing_key = input_->GetKeyframeAtTimeOnTrack(node_time, track, element_); if (existing_key) { new NodeParamSetKeyframeValueCommand(existing_key, value, command); } else { // No existing key, create a new one - NodeKeyframePtr new_key = NodeKeyframe::Create(node_time, - value, - input_->get_best_keyframe_type_for_time(node_time, track), - track); + NodeKeyframe* new_key = new NodeKeyframe(node_time, + value, + input_->GetBestKeyframeTypeForTime(node_time, track, element_), + track, + element_); new NodeParamInsertKeyframeCommand(input_, new_key, command); } } else { - new NodeParamSetStandardValueCommand(input_, track, value, command); + new NodeParamSetStandardValueCommand(input_, track, element_, value, command); } } @@ -257,24 +253,18 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant void NodeParamViewWidgetBridge::WidgetCallback() { - switch (input_->data_type()) { + switch (input_->GetDataType()) { // None of these inputs have applicable UI widgets - case NodeParam::kNone: - case NodeParam::kAny: - case NodeParam::kTexture: - case NodeParam::kMatrix: - case NodeParam::kSamples: - case NodeParam::kRational: - case NodeParam::kDecimal: - case NodeParam::kNumber: - case NodeParam::kString: - case NodeParam::kVector: - case NodeParam::kBuffer: - case NodeParam::kShaderJob: - case NodeParam::kSampleJob: - case NodeParam::kGenerateJob: + case NodeValue::kNone: + case NodeValue::kTexture: + case NodeValue::kMatrix: + case NodeValue::kSamples: + case NodeValue::kRational: + case NodeValue::kShaderJob: + case NodeValue::kSampleJob: + case NodeValue::kGenerateJob: break; - case NodeParam::kInt: + case NodeValue::kInt: { // Widget is a IntegerSlider IntegerSlider* slider = static_cast(sender()); @@ -284,7 +274,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() ProcessSlider(slider, QVariant::fromValue(slider->GetValue() - offset)); break; } - case NodeParam::kFloat: + case NodeValue::kFloat: { // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); @@ -294,7 +284,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() ProcessSlider(slider, slider->GetValue() - offset); break; } - case NodeParam::kVec2: + case NodeValue::kVec2: { // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); @@ -304,7 +294,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]); break; } - case NodeParam::kVec3: + case NodeValue::kVec3: { // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); @@ -314,7 +304,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]); break; } - case NodeParam::kVec4: + case NodeValue::kVec4: { // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); @@ -324,10 +314,10 @@ void NodeParamViewWidgetBridge::WidgetCallback() ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]); break; } - case NodeParam::kFile: + case NodeValue::kFile: // FIXME: File selector break; - case NodeParam::kColor: + case NodeValue::kColor: { // Sender is a ColorButton ManagedColor c = static_cast(sender())->GetColor(); @@ -349,31 +339,31 @@ void NodeParamViewWidgetBridge::WidgetCallback() Core::instance()->undo_stack()->pushIfHasChildren(command); break; } - case NodeParam::kText: + case NodeValue::kText: { // Sender is a NodeParamViewRichText SetInputValue(static_cast(sender())->text(), 0); break; } - case NodeParam::kBoolean: + case NodeValue::kBoolean: { // Widget is a QCheckBox SetInputValue(static_cast(sender())->isChecked(), 0); break; } - case NodeParam::kFont: + case NodeValue::kFont: { // Widget is a QFontComboBox SetInputValue(static_cast(sender())->currentFont().family(), 0); break; } - case NodeParam::kFootage: + case NodeValue::kFootage: { // Widget is a FootageComboBox SetInputValue(QVariant::fromValue(static_cast(sender())->SelectedFootage()), 0); break; } - case NodeParam::kCombo: + case NodeValue::kCombo: { // Widget is a QComboBox QComboBox* cb = static_cast(widgets_.first()); @@ -412,49 +402,43 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() rational node_time = GetCurrentTimeAsNodeTime(); // We assume the first data type is the "primary" type - switch (input_->data_type()) { + switch (input_->GetDataType()) { // None of these inputs have applicable UI widgets - case NodeParam::kNone: - case NodeParam::kAny: - case NodeParam::kTexture: - case NodeParam::kMatrix: - case NodeParam::kRational: - case NodeParam::kSamples: - case NodeParam::kDecimal: - case NodeParam::kNumber: - case NodeParam::kString: - case NodeParam::kBuffer: - case NodeParam::kShaderJob: - case NodeParam::kSampleJob: - case NodeParam::kGenerateJob: - case NodeParam::kVector: + case NodeValue::kNone: + case NodeValue::kTexture: + case NodeValue::kMatrix: + case NodeValue::kRational: + case NodeValue::kSamples: + case NodeValue::kShaderJob: + case NodeValue::kSampleJob: + case NodeValue::kGenerateJob: break; - case NodeParam::kInt: + case NodeValue::kInt: { int64_t offset = input_->property("offset").toLongLong(); - static_cast(widgets_.first())->SetValue(input_->get_value_at_time(node_time).toLongLong() + offset); + static_cast(widgets_.first())->SetValue(input_->GetValueAtTime(node_time, element_).toLongLong() + offset); break; } - case NodeParam::kFloat: + case NodeValue::kFloat: { double offset = input_->property("offset").toDouble(); - static_cast(widgets_.first())->SetValue(input_->get_value_at_time(node_time).toDouble() + offset); + static_cast(widgets_.first())->SetValue(input_->GetValueAtTime(node_time, element_).toDouble() + offset); break; } - case NodeParam::kVec2: + case NodeValue::kVec2: { - QVector2D vec2 = input_->get_value_at_time(node_time).value(); + QVector2D vec2 = input_->GetValueAtTime(node_time, -1).value(); QVector2D offset = input_->property("offset").value(); static_cast(widgets_.at(0))->SetValue(static_cast(vec2.x() + offset.x())); static_cast(widgets_.at(1))->SetValue(static_cast(vec2.y() + offset.y())); break; } - case NodeParam::kVec3: + case NodeValue::kVec3: { - QVector3D vec3 = input_->get_value_at_time(node_time).value(); + QVector3D vec3 = input_->GetValueAtTime(node_time, element_).value(); QVector3D offset = input_->property("offset").value(); static_cast(widgets_.at(0))->SetValue(static_cast(vec3.x() + offset.x())); @@ -462,9 +446,9 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() static_cast(widgets_.at(2))->SetValue(static_cast(vec3.z() + offset.z())); break; } - case NodeParam::kVec4: + case NodeValue::kVec4: { - QVector4D vec4 = input_->get_value_at_time(node_time).value(); + QVector4D vec4 = input_->GetValueAtTime(node_time, element_).value(); QVector4D offset = input_->property("offset").value(); static_cast(widgets_.at(0))->SetValue(static_cast(vec4.x() + offset.x())); @@ -473,12 +457,12 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() static_cast(widgets_.at(3))->SetValue(static_cast(vec4.w() + offset.w())); break; } - case NodeParam::kFile: + case NodeValue::kFile: // FIXME: File selector break; - case NodeParam::kColor: + case NodeValue::kColor: { - ManagedColor mc = input_->get_value_at_time(node_time).value(); + ManagedColor mc = input_->GetValueAtTime(node_time, element_).value(); mc.set_color_input(input_->property("col_input").toString()); @@ -491,40 +475,40 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() static_cast(widgets_.first())->SetColor(mc); break; } - case NodeParam::kText: + case NodeValue::kText: { NodeParamViewRichText* e = static_cast(widgets_.first()); - e->setTextPreservingCursor(input_->get_value_at_time(node_time).toString()); + e->setTextPreservingCursor(input_->GetValueAtTime(node_time, element_).toString()); break; } - case NodeParam::kBoolean: - static_cast(widgets_.first())->setChecked(input_->get_value_at_time(node_time).toBool()); + case NodeValue::kBoolean: + static_cast(widgets_.first())->setChecked(input_->GetValueAtTime(node_time, element_).toBool()); break; - case NodeParam::kFont: + case NodeValue::kFont: { QFontComboBox* fc = static_cast(widgets_.first()); fc->blockSignals(true); - fc->setCurrentFont(input_->get_value_at_time(node_time).toString()); + fc->setCurrentFont(input_->GetValueAtTime(node_time, element_).toString()); fc->blockSignals(false); break; } - case NodeParam::kCombo: + case NodeValue::kCombo: { QComboBox* cb = static_cast(widgets_.first()); cb->blockSignals(true); - cb->setCurrentIndex(input_->get_value_at_time(node_time).toInt()); + cb->setCurrentIndex(input_->GetValueAtTime(node_time, element_).toInt()); cb->blockSignals(false); break; } - case NodeParam::kFootage: - static_cast(widgets_.first())->SetFootage(Node::ValueToPtr(input_->get_value_at_time(node_time))); + case NodeValue::kFootage: + static_cast(widgets_.first())->SetFootage(Node::ValueToPtr(input_->GetValueAtTime(node_time, element_))); break; } } rational NodeParamViewWidgetBridge::GetCurrentTimeAsNodeTime() const { - return GetAdjustedTime(GetTimeTarget(), input_->parentNode(), time_, NodeParam::kInput); + return GetAdjustedTime(GetTimeTarget(), input_->parent(), time_, true); } void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range) @@ -538,7 +522,7 @@ void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range) void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVariant &value) { // Parameters for vectors only - if (input_->data_type() & NodeParam::kVector) { + if (NodeValue::type_is_vector(input_->GetDataType())) { if (key == QStringLiteral("disablex")) { static_cast(widgets_.at(0))->setEnabled(!value.toBool()); } else if (key == QStringLiteral("disabley")) { @@ -551,26 +535,26 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria } // Parameters for integers, floats, and vectors - if (input_->data_type() & NodeParam::kNumber || input_->data_type() & NodeParam::kVector) { + if (NodeValue::type_is_numeric(input_->GetDataType()) || NodeValue::type_is_vector(input_->GetDataType())) { if (key == QStringLiteral("min")) { - switch (input_->data_type()) { - case NodeParam::kInt: + switch (input_->GetDataType()) { + case NodeValue::kInt: static_cast(widgets_.first())->SetMinimum(value.value()); break; - case NodeParam::kFloat: + case NodeValue::kFloat: static_cast(widgets_.first())->SetMinimum(value.toDouble()); break; - case NodeParam::kRational: + case NodeValue::kRational: // FIXME: Rational doesn't have a UI implementation yet break; - case NodeParam::kVec2: + case NodeValue::kVec2: { QVector2D min = value.value(); static_cast(widgets_.at(0))->SetMinimum(min.x()); static_cast(widgets_.at(1))->SetMinimum(min.y()); break; } - case NodeParam::kVec3: + case NodeValue::kVec3: { QVector3D min = value.value(); static_cast(widgets_.at(0))->SetMinimum(min.x()); @@ -578,7 +562,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria static_cast(widgets_.at(2))->SetMinimum(min.z()); break; } - case NodeParam::kVec4: + case NodeValue::kVec4: { QVector4D min = value.value(); static_cast(widgets_.at(0))->SetMinimum(min.x()); @@ -591,24 +575,24 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria break; } } else if (key == QStringLiteral("max")) { - switch (input_->data_type()) { - case NodeParam::kInt: + switch (input_->GetDataType()) { + case NodeValue::kInt: static_cast(widgets_.first())->SetMaximum(value.value()); break; - case NodeParam::kFloat: + case NodeValue::kFloat: static_cast(widgets_.first())->SetMaximum(value.toDouble()); break; - case NodeParam::kRational: + case NodeValue::kRational: // FIXME: Rational doesn't have a UI implementation yet break; - case NodeParam::kVec2: + case NodeValue::kVec2: { QVector2D max = value.value(); static_cast(widgets_.at(0))->SetMaximum(max.x()); static_cast(widgets_.at(1))->SetMaximum(max.y()); break; } - case NodeParam::kVec3: + case NodeValue::kVec3: { QVector3D max = value.value(); static_cast(widgets_.at(0))->SetMaximum(max.x()); @@ -616,7 +600,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria static_cast(widgets_.at(2))->SetMaximum(max.z()); break; } - case NodeParam::kVec4: + case NodeValue::kVec4: { QVector4D max = value.value(); static_cast(widgets_.at(0))->SetMaximum(max.x()); @@ -634,7 +618,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria } // ComboBox strings changing - if (input_->data_type() & NodeParam::kCombo) { + if (input_->GetDataType() & NodeValue::kCombo) { QComboBox* cb = static_cast(widgets_.first()); int old_index = cb->currentIndex(); @@ -665,7 +649,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria } // Parameters for floats and vectors only - if (input_->data_type() & NodeParam::kFloat || input_->data_type() & NodeParam::kVector) { + if (input_->GetDataType() == NodeValue::kFloat || NodeValue::type_is_vector(input_->GetDataType())) { if (key == QStringLiteral("view")) { FloatSlider::DisplayType display_type; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index 7704229d2..48aad2ccf 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -41,7 +41,7 @@ class NodeParamViewWidgetBridge : public QObject, public TimeTargetObject { Q_OBJECT public: - NodeParamViewWidgetBridge(NodeInput* input, QObject* parent); + NodeParamViewWidgetBridge(NodeInput* input, int element, QObject* parent); void SetTime(const rational& time); @@ -64,6 +64,8 @@ private: NodeInput* input_; + int element_; + QList widgets_; rational time_; diff --git a/app/widget/nodetableview/nodetableview.cpp b/app/widget/nodetableview/nodetableview.cpp index 90afc94af..7b58ad2ec 100644 --- a/app/widget/nodetableview/nodetableview.cpp +++ b/app/widget/nodetableview/nodetableview.cpp @@ -23,7 +23,6 @@ #include #include -#include "node/param.h" #include "nodetabletraverser.h" namespace olive { @@ -130,7 +129,7 @@ void NodeTableView::SetTime(const rational &time) QTreeWidgetItem* sub_item = input_item->child(j); // Set data type name - sub_item->setText(0, NodeParam::GetPrettyDataTypeName(value.type())); + sub_item->setText(0, NodeValue::GetPrettyDataTypeName(value.type())); // Determine source QString source_name; @@ -142,7 +141,7 @@ void NodeTableView::SetTime(const rational &time) sub_item->setText(1, source_name); switch (value.type()) { - case NodeParam::kTexture: + case NodeValue::kTexture: { // NodeTableTraverser puts video params in here for (int k=0;k split_values = input->split_normal_value_into_track_values(value.data()); + QVector split_values = NodeValue::split_normal_value_into_track_values(input->GetDataType(), value.data()); for (int k=0;ksetText(2 + k, NodeInput::ValueToString(value.type(), split_values.at(k), true)); + sub_item->setText(2 + k, NodeValue::ValueToString(value.type(), split_values.at(k), true)); } } } diff --git a/app/widget/nodetreeview/nodetreeview.cpp b/app/widget/nodetreeview/nodetreeview.cpp index 8d4dfa4c3..e1410c369 100644 --- a/app/widget/nodetreeview/nodetreeview.cpp +++ b/app/widget/nodetreeview/nodetreeview.cpp @@ -34,9 +34,8 @@ void NodeTreeView::SetNodes(const QVector &nodes) node_item->setData(0, kItemType, kItemTypeNode); node_item->setData(0, kItemPointer, reinterpret_cast(n)); - QVector inputs = n->GetInputsIncludingArrays(); - foreach (NodeInput* i, inputs) { - if (only_show_keyframable_ && !i->is_keyframable()) { + foreach (NodeInput* i, n->inputs()) { + if (only_show_keyframable_ && !i->IsKeyframable()) { continue; } diff --git a/app/widget/nodeview/CMakeLists.txt b/app/widget/nodeview/CMakeLists.txt index 00aca1075..567cf75b0 100644 --- a/app/widget/nodeview/CMakeLists.txt +++ b/app/widget/nodeview/CMakeLists.txt @@ -16,18 +16,9 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - widget/nodeview/nodeview.h widget/nodeview/nodeview.cpp - widget/nodeview/nodeviewcommon.h - widget/nodeview/nodeviewedge.h - widget/nodeview/nodeviewedge.cpp - widget/nodeview/nodeviewitem.h - widget/nodeview/nodeviewitem.cpp - widget/nodeview/nodeviewitemwidgetproxy.h - widget/nodeview/nodeviewitemwidgetproxy.cpp - widget/nodeview/nodeviewscene.h - widget/nodeview/nodeviewscene.cpp - widget/nodeview/nodeviewundo.h + widget/nodeview/nodeview.h widget/nodeview/nodeviewundo.cpp + widget/nodeview/nodeviewundo.h PARENT_SCOPE ) diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 8948c98ce..318412fc5 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -20,887 +20,14 @@ #include "nodeview.h" -#include -#include - -#include "core.h" -#include "nodeviewundo.h" -#include "node/factory.h" -#include "widget/menu/menushared.h" - -#define super HandMovableView - namespace olive { -NodeView::NodeView(QWidget *parent) : +NodeView::NodeView(QWidget* parent) : HandMovableView(parent), - graph_(nullptr), - drop_edge_(nullptr), - filter_mode_(kFilterShowSelectedBlocks), - scale_(1.0) + graph_(nullptr) { - setScene(&scene_); - SetDefaultDragMode(RubberBandDrag); - setContextMenuPolicy(Qt::CustomContextMenu); - setMouseTracking(true); - setRenderHint(QPainter::Antialiasing); - setViewportUpdateMode(FullViewportUpdate); - connect(&scene_, &QGraphicsScene::changed, this, &NodeView::ItemsChanged); - connect(this, &NodeView::customContextMenuRequested, this, &NodeView::ShowContextMenu); - - ConnectSelectionChangedSignal(); - - SetFlowDirection(NodeViewCommon::kTopToBottom); - - // Set massive scene rect and hide the scrollbars to create an "infinite space" effect - scene_.setSceneRect(-1000000, -1000000, 2000000, 2000000); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - select_blocks_internal_timer_.setSingleShot(true); - select_blocks_internal_timer_.setInterval(100); - connect(&select_blocks_internal_timer_, &QTimer::timeout, this, &NodeView::SelectBlocksInternal); -} - -NodeView::~NodeView() -{ - // Unset the current graph - SetGraph(nullptr); -} - -void NodeView::SetGraph(NodeGraph *graph) -{ - if (graph_ == graph) { - return; - } - - if (graph_ != nullptr) { - disconnect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode); - disconnect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode); - disconnect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::GraphNodeRemoved); - disconnect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::GraphEdgeAdded); - disconnect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::GraphEdgeRemoved); - } - - // Clear the scene of all UI objects - scene_.clear(); - - // Set reference to the graph - graph_ = graph; - scene_.SetGraph(graph_); - - // If the graph is valid, add UI objects for each of its Nodes - if (graph_ != nullptr) { - connect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode); - connect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode); - connect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::GraphNodeRemoved); - connect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::GraphEdgeAdded); - connect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::GraphEdgeRemoved); - - foreach (Node* n, graph_->nodes()) { - scene_.AddNode(n); - } - - ValidateFilter(); - } -} - -void NodeView::DeleteSelected() -{ - if (!graph_) { - return; - } - - QUndoCommand* command = new QUndoCommand(); - - { - QVector selected_edges = scene_.GetSelectedEdges(); - - foreach (NodeEdge* edge, selected_edges) { - new NodeEdgeRemoveCommand(edge->output(), edge->input(), command); - } - - } - - { - QVector selected_nodes = scene_.GetSelectedNodes(); - - // Ensure no nodes are "undeletable" - for (int i=0;iCanBeDeleted()) { - selected_nodes.removeAt(i); - i--; - } - } - - if (!selected_nodes.isEmpty()) { - new NodeRemoveCommand(graph_, selected_nodes, command); - } - } - - Core::instance()->undo_stack()->pushIfHasChildren(command); -} - -void NodeView::SelectAll() -{ - // Optimization: rather than respond to every single item being selected, ignore the signal and - // then handle them all at the end. - DisconnectSelectionChangedSignal(); - - scene_.SelectAll(); - - ConnectSelectionChangedSignal(); - SceneSelectionChangedSlot(); -} - -void NodeView::DeselectAll() -{ - // Optimization: rather than respond to every single item being selected, ignore the signal and - // then handle them all at the end. - DisconnectSelectionChangedSignal(); - - scene_.DeselectAll(); - - ConnectSelectionChangedSignal(); - SceneSelectionChangedSlot(); -} - -void NodeView::Select(const QVector &nodes) -{ - if (!graph_) { - return; - } - - // Optimization: rather than respond to every single item being selected, ignore the signal and - // then handle them all at the end. - DisconnectSelectionChangedSignal(); - - scene_.DeselectAll(); - - foreach (Node* n, nodes) { - NodeViewItem* item = scene_.NodeToUIObject(n); - - item->setSelected(true); - } - - ConnectSelectionChangedSignal(); - SceneSelectionChangedSlot(); -} - -void NodeView::SelectWithDependencies(QVector nodes) -{ - if (!graph_) { - return; - } - - int original_length = nodes.size(); - for (int i=0;iGetDependencies()); - } - - Select(nodes); -} - -void NodeView::SelectBlocks(const QVector &blocks) -{ - if (!graph_) { - return; - } - - selected_blocks_.append(blocks); - - QueueSelectBlocksInternal(); -} - -void NodeView::DeselectBlocks(const QVector &blocks) -{ - if (!graph_) { - return; - } - - // Remove temporary associations - foreach (Block* b, selected_blocks_) { - if (!blocks.contains(b)) { - temporary_association_map_.remove(b); - } - } - - // Remove blocks from selected array - foreach (Block* b, blocks) { - selected_blocks_.removeOne(b); - } - - QueueSelectBlocksInternal(); -} - -void NodeView::CopySelected(bool cut) -{ - if (!graph_) { - return; - } - - QVector selected = scene_.GetSelectedNodes(); - - if (selected.isEmpty()) { - return; - } - - CopyNodesToClipboard(selected); - - if (cut) { - DeleteSelected(); - } -} - -void NodeView::Paste() -{ - if (!graph_) { - return; - } - - QUndoCommand* command = new QUndoCommand(); - - QVector pasted_nodes = PasteNodesFromClipboard(static_cast(graph_), command); - - Core::instance()->undo_stack()->pushIfHasChildren(command); - - if (!pasted_nodes.isEmpty()) { - foreach (Node* n, pasted_nodes) { - AssociateNodeWithSelectedBlocks(n); - } - - AttachNodesToCursor(pasted_nodes); - } -} - -void NodeView::Duplicate() -{ - if (!graph_) { - return; - } - - QVector selected = scene_.GetSelectedNodes(); - - if (selected.isEmpty()) { - return; - } - - QUndoCommand* command = new QUndoCommand(); - - QVector duplicated_nodes = Node::CopyDependencyGraph(selected, command); - - Core::instance()->undo_stack()->pushIfHasChildren(command); - - AttachNodesToCursor(duplicated_nodes); -} - -void NodeView::ItemsChanged() -{ - QHash::const_iterator i; - - for (i=scene_.edge_map().constBegin(); i!=scene_.edge_map().constEnd(); i++) { - i.value()->Adjust(); - } -} - -void NodeView::keyPressEvent(QKeyEvent *event) -{ - super::keyPressEvent(event); - - if (event->key() == Qt::Key_Escape && !attached_items_.isEmpty()) { - DetachItemsFromCursor(); - - // We undo the last action which SHOULD be adding the node - // FIXME: Possible danger of this not being the case? - Core::instance()->undo_stack()->undo(); - } -} - -void NodeView::mousePressEvent(QMouseEvent *event) -{ - if (HandPress(event)) return; - - if (event->button() == Qt::RightButton) { - // Qt doesn't do this by default for some reason - if (!(event->modifiers() & Qt::ShiftModifier)) { - scene_.clearSelection(); - } - - // If there's an item here, select it - QGraphicsItem* item = itemAt(event->pos()); - if (item) { - item->setSelected(true); - } - } - - super::mousePressEvent(event); -} - -void NodeView::mouseMoveEvent(QMouseEvent *event) -{ - if (HandMove(event)) return; - - super::mouseMoveEvent(event); - - if (!attached_items_.isEmpty()) { - MoveAttachedNodesToCursor(event->pos()); - - // See if the user clicked on an edge (only when dropping single nodes) - if (attached_items_.size() == 1) { - Node* attached_node = attached_items_.first().item->GetNode(); - - QRect edge_detect_rect(event->pos(), event->pos()); - - int edge_detect_radius = fontMetrics().height(); - edge_detect_rect.adjust(-edge_detect_radius, -edge_detect_radius, edge_detect_radius, edge_detect_radius); - - QList items = this->items(edge_detect_rect); - - NodeViewEdge* new_drop_edge = nullptr; - - // See if there is an edge here - foreach (QGraphicsItem* item, items) { - new_drop_edge = dynamic_cast(item); - - if (new_drop_edge) { - drop_input_ = nullptr; - - foreach (NodeParam* param, attached_node->parameters()) { - if (param->type() == NodeParam::kInput) { - NodeInput* input = static_cast(param); - - if (input->is_connectable()) { - if (input->data_type() & new_drop_edge->edge()->input()->data_type()) { - drop_input_ = input; - break; - } else if (!drop_input_) { - drop_input_ = input; - } - } - } - } - - if (drop_input_) { - break; - } else { - new_drop_edge = nullptr; - } - } - } - - if (drop_edge_ != new_drop_edge) { - if (drop_edge_) { - drop_edge_->SetHighlighted(false); - } - - drop_edge_ = new_drop_edge; - - if (drop_edge_) { - drop_edge_->SetHighlighted(true); - } - } - } - } -} - -void NodeView::mouseReleaseEvent(QMouseEvent *event) -{ - if (HandRelease(event)) return; - - if (!attached_items_.isEmpty()) { - if (attached_items_.size() == 1) { - Node* dropping_node = attached_items_.first().item->GetNode(); - - if (drop_edge_) { - NodeEdgePtr old_edge = drop_edge_->edge(); - - // We have everything we need to place the node in between - QUndoCommand* command = new QUndoCommand(); - - // Remove old edge - new NodeEdgeRemoveCommand(old_edge, command); - - // Place new edges - new NodeEdgeAddCommand(old_edge->output(), drop_input_, command); - new NodeEdgeAddCommand(dropping_node->output(), old_edge->input(), command); - - Core::instance()->undo_stack()->push(command); - } - - drop_edge_ = nullptr; - } - - DetachItemsFromCursor(); - } - - super::mouseReleaseEvent(event); -} - -void NodeView::wheelEvent(QWheelEvent *event) -{ - if (event->modifiers() & Qt::ControlModifier) { - // FIXME: Hardcoded divider (0.001) - qreal multiplier = 1.0 + (static_cast(event->angleDelta().x() + event->angleDelta().y()) * 0.001); - - double test_scale = scale_ * multiplier; - - if (test_scale > 0.1) { - scale(multiplier, multiplier); - scale_ = test_scale; - } - } else { - QWidget::wheelEvent(event); - } -} - -void NodeView::SceneSelectionChangedSlot() -{ - QVector current_selection = scene_.GetSelectedNodes(); - - QVector selected; - QVector deselected; - - // Determine which nodes are newly selected - if (selected_nodes_.isEmpty()) { - // All nodes in the current selection have just been selected - selected = current_selection; - } else { - foreach (Node* n, current_selection) { - if (!selected_nodes_.contains(n)) { - selected.append(n); - } - } - } - - // Determine which nodes are newly deselected - if (current_selection.isEmpty()) { - // All nodes that were selected have been deselected - deselected = selected_nodes_; - } else { - foreach (Node* n, selected_nodes_) { - if (!current_selection.contains(n)) { - deselected.append(n); - } - } - } - - selected_nodes_ = current_selection; - - if (!selected.isEmpty()) { - emit NodesSelected(selected); - } - - if (!deselected.isEmpty()) { - emit NodesDeselected(deselected); - } -} - -void NodeView::ShowContextMenu(const QPoint &pos) -{ - if (!graph_) { - return; - } - - Menu m; - - MenuShared::instance()->AddItemsForEditMenu(&m, false); - - m.addSeparator(); - - QVector selected = scene_.GetSelectedItems(); - - if (itemAt(pos) && !selected.isEmpty()) { - - // Label node action - QAction* label_action = m.addAction(tr("Label")); - connect(label_action, &QAction::triggered, this, [this](){ - Core::instance()->LabelNodes(scene_.GetSelectedNodes()); - }); - - m.addSeparator(); - - // Auto-position action - QAction* autopos = m.addAction(tr("Auto-Position")); - connect(autopos, &QAction::triggered, this, &NodeView::AutoPositionDescendents); - - } else { - - QAction* curved_action = m.addAction(tr("Smooth Edges")); - curved_action->setCheckable(true); - curved_action->setChecked(scene_.GetEdgesAreCurved()); - connect(curved_action, &QAction::triggered, &scene_, &NodeViewScene::SetEdgesAreCurved); - - m.addSeparator(); - - Menu* filter_menu = new Menu(tr("Filter"), &m); - m.addMenu(filter_menu); - - filter_menu->AddActionWithData(tr("Show All"), - kFilterShowAll, - filter_mode_); - - filter_menu->AddActionWithData(tr("Show Selected Blocks Only"), - kFilterShowSelectedBlocks, - filter_mode_); - - connect(filter_menu, &Menu::triggered, this, &NodeView::ContextMenuFilterChanged); - - - - Menu* direction_menu = new Menu(tr("Direction"), &m); - m.addMenu(direction_menu); - - direction_menu->AddActionWithData(tr("Top to Bottom"), - NodeViewCommon::kTopToBottom, - scene_.GetFlowDirection()); - - direction_menu->AddActionWithData(tr("Bottom to Top"), - NodeViewCommon::kBottomToTop, - scene_.GetFlowDirection()); - - direction_menu->AddActionWithData(tr("Left to Right"), - NodeViewCommon::kLeftToRight, - scene_.GetFlowDirection()); - - direction_menu->AddActionWithData(tr("Right to Left"), - NodeViewCommon::kRightToLeft, - scene_.GetFlowDirection()); - - connect(direction_menu, &Menu::triggered, this, &NodeView::ContextMenuSetDirection); - - m.addSeparator(); - - Menu* add_menu = NodeFactory::CreateMenu(&m); - add_menu->setTitle(tr("Add")); - connect(add_menu, &Menu::triggered, this, &NodeView::CreateNodeSlot); - m.addMenu(add_menu); - - } - - m.exec(mapToGlobal(pos)); -} - -void NodeView::CreateNodeSlot(QAction *action) -{ - Node* new_node = NodeFactory::CreateFromMenuAction(action); - - if (new_node) { - // Associate this new node with these blocks (allows it to be visible with them even while it - // isn't connected to anything) - AssociateNodeWithSelectedBlocks(new_node); - - Core::instance()->undo_stack()->push(new NodeAddCommand(graph_, new_node)); - - NodeViewItem* item = scene_.NodeToUIObject(new_node); - AttachItemsToCursor({item}); - } -} - -void NodeView::ContextMenuSetDirection(QAction *action) -{ - SetFlowDirection(static_cast(action->data().toInt())); -} - -void NodeView::AutoPositionDescendents() -{ - QVector selected = scene_.GetSelectedNodes(); - - foreach (Node* n, selected) { - scene_.ReorganizeFrom(n); - } -} - -void NodeView::ContextMenuFilterChanged(QAction *action) -{ - FilterMode filter = static_cast(action->data().toInt()); - - if (filter_mode_ != filter) { - filter_mode_ = filter; - - if (filter == kFilterShowAll) { - // Un-hide all blocks - foreach (NodeViewItem* item, scene_.item_map()) { - item->setVisible(true); - } - - foreach (NodeViewEdge* edge, scene_.edge_map()) { - edge->setVisible(true); - } - } - - ValidateFilter(); - } -} - -void NodeView::AttachNodesToCursor(const QVector &nodes) -{ - QVector items(nodes.size()); - - for (int i=0; i& items) -{ - DetachItemsFromCursor(); - - if (!items.isEmpty()) { - foreach (NodeViewItem* i, items) { - attached_items_.append({i, i->pos() - items.first()->pos()}); - } - - setMouseTracking(true); - - MoveAttachedNodesToCursor(mapFromGlobal(QCursor::pos())); - } -} - -void NodeView::DetachItemsFromCursor() -{ - attached_items_.clear(); - setMouseTracking(false); -} - -void NodeView::SetFlowDirection(NodeViewCommon::FlowDirection dir) -{ - scene_.SetFlowDirection(dir); -} - -void NodeView::MoveAttachedNodesToCursor(const QPoint& p) -{ - QPointF item_pos = mapToScene(p); - - foreach (const AttachedItem& i, attached_items_) { - i.item->setPos(item_pos + i.original_pos); - } -} - -void NodeView::ConnectSelectionChangedSignal() -{ - connect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::SceneSelectionChangedSlot); -} - -void NodeView::DisconnectSelectionChangedSignal() -{ - disconnect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::SceneSelectionChangedSlot); -} - -void NodeView::UpdateBlockFilter() -{ - // Hide all nodes - foreach (NodeViewItem* item, scene_.item_map()) { - item->setVisible(false); - } - - bool first = true; - QPointF last_bottom_right; - - QVector currently_visible; - - foreach (Block* b, selected_blocks_) { - // Auto-position this node's dependencies - scene_.ReorganizeFrom(b); - - // Start calculating the bounding rect of this node's deps - QPointF node_pos = b->GetPosition(); - QRectF anchor(node_pos, node_pos); - - QVector deps = b->GetDependencies(); - - foreach (Node* d, deps) { - QPointF dep_pos = d->GetPosition(); - - anchor.setLeft(qMin(anchor.left(), dep_pos.x())); - anchor.setRight(qMax(anchor.right(), dep_pos.x())); - anchor.setTop(qMin(anchor.top(), dep_pos.y())); - anchor.setBottom(qMax(anchor.bottom(), dep_pos.y())); - - NodeViewItem* item = scene_.NodeToUIObject(d); - if (item) { - item->setVisible(true); - } - } - - // Shift the bounding rect in relation to the other bounding rects - if (first) { - first = false; - } else { - QPointF desired_anchor_pos = last_bottom_right + QPointF(0, 2); - - QPointF necessary_movement = anchor.topRight() - desired_anchor_pos; - - // Calculate the bottom right from which we'll anchor the next rect - last_bottom_right = anchor.bottomRight() - necessary_movement; - - b->SetPosition(b->GetPosition() - necessary_movement); - foreach (Node* d, deps) { - d->SetPosition(d->GetPosition() - necessary_movement); - } - } - - // Now that we're done calculating the bounding rect, add this block... - deps.append(b); - - // ...then add its associations - deps.append(temporary_association_map_[b]); - for (auto i=association_map_.begin(); i!=association_map_.end(); i++) { - if (i.value().contains(b)) { - deps.append(i.key()); - } - } - - // And make sure all nodes are shown - foreach (Node* n, deps) { - NodeViewItem* item = scene_.NodeToUIObject(n); - if (item) { - item->setVisible(true); - } - } - - // And lastly, add them all to our currently visible list - currently_visible.append(deps); - } - - // Show only edges between those dependencies - foreach (NodeViewEdge* edge, scene_.edge_map()) { - edge->setVisible((currently_visible.contains(edge->edge()->input_node()) - && currently_visible.contains(edge->edge()->output_node()))); - } -} - -void NodeView::AssociateNodeWithSelectedBlocks(Node *n) -{ - association_map_.insert(n, selected_blocks_); - connect(n, &Node::destroyed, this, &NodeView::AssociatedNodeDestroyed, Qt::DirectConnection); -} - -void NodeView::DisassociateNode(Node *n, bool remove_from_map) -{ - if (remove_from_map) { - association_map_.remove(n); - } - - disconnect(n, &Node::destroyed, this, &NodeView::AssociatedNodeDestroyed); -} - -void NodeView::QueueSelectBlocksInternal() -{ - select_blocks_internal_timer_.stop(); - select_blocks_internal_timer_.start(); -} - -void NodeView::SelectBlocksInternal() -{ - // Block scene signals while our selection is changing a lot - DisconnectSelectionChangedSignal(); - - if (filter_mode_ == kFilterShowSelectedBlocks) { - UpdateBlockFilter(); - } - - QVector nodes; - nodes.reserve(selected_blocks_.size()); - - foreach (Block* b, selected_blocks_) { - nodes.append(b); - nodes.append(b->GetDependencies()); - } - - SelectWithDependencies(nodes); - - // Stop blocking signals and send a change signal now that all of our processing is done - ConnectSelectionChangedSignal(); - SceneSelectionChangedSlot(); - - if (!selected_blocks_.isEmpty()) { - NodeViewItem* item = scene_.NodeToUIObject(selected_blocks_.first()); - if (item) { - centerOn(item); - } - } -} - -void NodeView::ValidateFilter() -{ - // Force auto-positioning - switch (filter_mode_) { - case kFilterShowAll: - // NOTE: Assumes Sequence - scene_.ReorganizeFrom(static_cast(graph_)->viewer_output()); - break; - case kFilterShowSelectedBlocks: - UpdateBlockFilter(); - break; - } -} - -void NodeView::AssociatedNodeDestroyed() -{ - DisassociateNode(static_cast(sender()), true); -} - -void NodeView::GraphNodeRemoved(Node *node) -{ - if (selected_blocks_.contains(static_cast(node))) { - DeselectBlocks({static_cast(node)}); - } -} - -void NodeView::GraphEdgeAdded(NodeEdgePtr edge) -{ - Node* input_node = edge->input()->parentNode(); - - if (input_node->OutputsTo(static_cast(graph_)->viewer_output(), true)) { - auto i = association_map_.begin(); - - while (i != association_map_.end()) { - if (input_node->InputsFrom(i.key(), true)) { - i = association_map_.erase(i); - } else { - i++; - } - } - } - - scene_.AddEdge(edge); - - ValidateFilter(); -} - -void NodeView::GraphEdgeRemoved(NodeEdgePtr edge) -{ - scene_.RemoveEdge(edge); - - Node* output_node = edge->output_node(); - - // Check if this disconnected node still connects to a selected block, in which case do nothing - foreach (Block* b, selected_blocks_) { - if (output_node->OutputsTo(b, true)) { - return; - } - } - - QVector disconnected_nodes; - disconnected_nodes.append(output_node); - disconnected_nodes.append(output_node->GetDependencies()); - - if (output_node->OutputsTo(static_cast(graph_)->viewer_output(), true)) { - // Check if this disconnected node still has a path to the viewer somewhere else - foreach (Block* b, selected_blocks_) { - QVector& temp_assocs = temporary_association_map_[b]; - - temp_assocs.append(disconnected_nodes); - } - } else { - // Otherwise, we must associate these nodes - foreach (Node* n, disconnected_nodes) { - AssociateNodeWithSelectedBlocks(n); - } - } } } + diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 9341cac9d..3b1dd85e4 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -22,172 +22,47 @@ #define NODEVIEW_H #include -#include #include "node/graph.h" #include "node/nodecopypaste.h" -#include "nodeviewscene.h" #include "widget/handmovableview/handmovableview.h" namespace olive { -/** - * @brief A widget for viewing and editing node graphs - * - * This widget takes a NodeGraph object and constructs a QGraphicsScene representing its data, viewing and allowing - * the user to make modifications to it. - */ class NodeView : public HandMovableView, public NodeCopyPasteService { Q_OBJECT public: NodeView(QWidget* parent); - virtual ~NodeView() override; - /** * @brief Sets the graph to view */ - void SetGraph(NodeGraph* graph); + void SetGraph(NodeGraph* graph){} /** * @brief Delete selected nodes from graph (user-friendly/undoable) */ - void DeleteSelected(); + void DeleteSelected(){} - void SelectAll(); - void DeselectAll(); + void SelectAll(){} + void DeselectAll(){} - void Select(const QVector& nodes); - void SelectWithDependencies(QVector nodes); + void Select(const QVector& nodes){} + void SelectWithDependencies(QVector nodes){} - void CopySelected(bool cut); - void Paste(); + void CopySelected(bool cut){} + void Paste(){} - void Duplicate(); + void Duplicate(){} - void SelectBlocks(const QVector& blocks); + void SelectBlocks(const QVector& blocks){} - void DeselectBlocks(const QVector& blocks); - -signals: - void NodesSelected(const QVector& nodes); - - void NodesDeselected(const QVector& nodes); - -protected: - virtual void keyPressEvent(QKeyEvent *event) override; - - virtual void mousePressEvent(QMouseEvent *event) override; - virtual void mouseMoveEvent(QMouseEvent *event) override; - virtual void mouseReleaseEvent(QMouseEvent* event) override; - - virtual void wheelEvent(QWheelEvent* event) override; + void DeselectBlocks(const QVector& blocks){} private: - void AttachNodesToCursor(const QVector &nodes); - - void AttachItemsToCursor(const QVector &items); - - void DetachItemsFromCursor(); - - void SetFlowDirection(NodeViewCommon::FlowDirection dir); - - void MoveAttachedNodesToCursor(const QPoint &p); - - void ConnectSelectionChangedSignal(); - void DisconnectSelectionChangedSignal(); - - void UpdateBlockFilter(); - - void AssociateNodeWithSelectedBlocks(Node* n); - void DisassociateNode(Node* n, bool remove_from_map); - - void QueueSelectBlocksInternal(); - - QTimer select_blocks_internal_timer_; - NodeGraph* graph_; - struct AttachedItem { - NodeViewItem* item; - QPointF original_pos; - }; - - QList attached_items_; - - NodeViewEdge* drop_edge_; - NodeInput* drop_input_; - - NodeViewScene scene_; - - QVector selected_nodes_; - - QVector selected_blocks_; - - QHash > association_map_; - - QHash > temporary_association_map_; - - enum FilterMode { - kFilterShowAll, - kFilterShowSelectedBlocks - }; - - FilterMode filter_mode_; - - double scale_; - -private slots: - void ValidateFilter(); - - void AssociatedNodeDestroyed(); - - void GraphNodeRemoved(Node* node); - - void GraphEdgeAdded(NodeEdgePtr edge); - - void GraphEdgeRemoved(NodeEdgePtr edge); - - /** - * @brief Internal function triggered when any change is signalled from the QGraphicsScene - * - * Current primary function is to inform all NodeViewEdges to re-adjust in case any Nodes have moved - */ - void ItemsChanged(); - - /** - * @brief Receiver for when the scene's selected items change - */ - void SceneSelectionChangedSlot(); - - /** - * @brief Receiver for when the user right clicks (or otherwise requests a context menu) - */ - void ShowContextMenu(const QPoint &pos); - - /** - * @brief Receiver for when the user requests a new node from the add menu - */ - void CreateNodeSlot(QAction* action); - - /** - * @brief Receiver for setting the direction from the context menu - */ - void ContextMenuSetDirection(QAction* action); - - /** - * @brief Receiver for auto-position descendents menu action - */ - void AutoPositionDescendents(); - - /** - * @brief Receiver for the user changing the filter - */ - void ContextMenuFilterChanged(QAction* action); - - void SelectBlocksInternal(); - }; } diff --git a/app/widget/nodeview/nodeviewcommon.h b/app/widget/nodeview/nodeviewcommon.h deleted file mode 100644 index 648229b28..000000000 --- a/app/widget/nodeview/nodeviewcommon.h +++ /dev/null @@ -1,58 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 NODEVIEWCOMMON_H -#define NODEVIEWCOMMON_H - -#include - -#include "common/define.h" - -namespace olive { - -class NodeViewCommon { -public: - enum FlowDirection { - kTopToBottom, - kBottomToTop, - kLeftToRight, - kRightToLeft - }; - - static Qt::Orientation GetFlowOrientation(FlowDirection dir) { - if (dir == kTopToBottom || dir == kBottomToTop) { - return Qt::Vertical; - } else { - return Qt::Horizontal; - } - } - - static bool DirectionsAreOpposing(FlowDirection a, FlowDirection b) { - return ((a == NodeViewCommon::kLeftToRight && b == NodeViewCommon::kRightToLeft) - || (a == NodeViewCommon::kRightToLeft && b == NodeViewCommon::kLeftToRight) - || (a == NodeViewCommon::kTopToBottom && b == NodeViewCommon::kBottomToTop) - || (a == NodeViewCommon::kBottomToTop && b == NodeViewCommon::kTopToBottom)); - } - -}; - -} - -#endif // NODEVIEWCOMMON_H diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp deleted file mode 100644 index 15d0bfae2..000000000 --- a/app/widget/nodeview/nodeviewedge.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "nodeviewedge.h" - -#include -#include -#include -#include - -#include "common/bezier.h" -#include "common/lerp.h" -#include "nodeview.h" -#include "nodeviewscene.h" - -namespace olive { - -NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) : - QGraphicsPathItem(parent), - edge_(nullptr), - connected_(false), - highlighted_(false), - flow_dir_(NodeViewCommon::kLeftToRight), - curved_(true) -{ - setFlag(QGraphicsItem::ItemIsSelectable); - - // Ensures this UI object is drawn behind other objects - setZValue(-1); - - // Use font metrics to set edge width for basic high DPI support - edge_width_ = QFontMetrics(QFont()).height() / 12; - arrow_size_ = QFontMetrics(QFont()).height() / 2; -} - -void NodeViewEdge::SetEdge(NodeEdgePtr edge) -{ - SetConnected(true); - - // Set the new edge pointer - edge_ = edge; - - // Re-adjust the line positioning for this new edge - Adjust(); -} - -NodeEdgePtr NodeViewEdge::edge() -{ - return edge_; -} - -void NodeViewEdge::Adjust() -{ - if (!edge_ || !scene()) { - return; - } - - // Get the UI objects of both nodes that this edge connects - NodeViewItem* output = static_cast(scene())->NodeToUIObject(edge_->output()->parentNode()); - NodeViewItem* input = static_cast(scene())->NodeToUIObject(edge_->input()->parentNode()); - - if (!output || !input) { - return; - } - - // Draw a line between the two - SetPoints(output->GetParamPoint(edge_->output(), output->pos()), - input->GetParamPoint(edge_->input(), output->pos()), - input->IsExpanded()); -} - -void NodeViewEdge::SetConnected(bool c) -{ - connected_ = c; - - update(); -} - -void NodeViewEdge::SetHighlighted(bool e) -{ - highlighted_ = e; - - update(); -} - -void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end, bool input_is_expanded) -{ - QPainterPath path; - path.moveTo(start); - - double angle = qAtan2(end.y() - start.y(), end.x() - start.x()); - - if (curved_) { - - double half_x = lerp(start.x(), end.x(), 0.5); - double half_y = lerp(start.y(), end.y(), 0.5); - - QPointF cp1, cp2; - - if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) { - cp1 = QPointF(half_x, start.y()); - } else { - cp1 = QPointF(start.x(), half_y); - } - - if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal || input_is_expanded) { - cp2 = QPointF(half_x, end.y()); - } else { - cp2 = QPointF(end.x(), half_y); - } - - path.cubicTo(cp1, cp2, end); - - if (!qFuzzyCompare(start.x(), end.x())) { - double continue_x = end.x() - qCos(angle)*arrow_size_; - - double x1, x2, x3, x4, y1, y2, y3, y4; - if (start.x() < end.x()) { - x1 = start.x(); - x2 = cp1.x(); - x3 = cp2.x(); - x4 = end.x(); - y1 = start.y(); - y2 = cp1.y(); - y3 = cp2.y(); - y4 = end.y(); - } else { - x1 = end.x(); - x2 = cp2.x(); - x3 = cp1.x(); - x4 = start.x(); - y1 = end.y(); - y2 = cp2.y(); - y3 = cp1.y(); - y4 = start.y(); - } - - double t = Bezier::CubicXtoT(continue_x, x1, x2, x3, x4); - double y = Bezier::CubicTtoY(y1, y2, y3, y4, t); - - angle = qAtan2(end.y() - y, end.x() - continue_x); - } - - } else { - - path.lineTo(end); - - } - - setPath(path); - - const double arrow_angle = 150.0 * 3.141592 / 180.0; - QVector arrow_points(4); - arrow_points[0] = end; - arrow_points[1] = end + QPointF(qCos(angle + arrow_angle) * arrow_size_, qSin(angle + arrow_angle) * arrow_size_); - arrow_points[2] = end + QPointF(qCos(angle - arrow_angle) * arrow_size_, qSin(angle - arrow_angle) * arrow_size_); - arrow_points[3] = end; - - arrow_ = QPolygonF(arrow_points); -} - -void NodeViewEdge::SetFlowDirection(NodeViewCommon::FlowDirection dir) -{ - flow_dir_ = dir; - - Adjust(); -} - -void NodeViewEdge::SetCurved(bool e) -{ - curved_ = e; - - update(); -} - -void NodeViewEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) -{ - QPalette::ColorGroup group; - QPalette::ColorRole role; - - if (connected_) { - group = QPalette::Active; - } else { - group = QPalette::Disabled; - } - - if (highlighted_ != bool(option->state & QStyle::State_Selected)) { - role = QPalette::Highlight; - } else { - role = QPalette::Text; - } - - // Draw main path - QColor edge_color = qApp->palette().color(group, role); - - painter->setPen(QPen(edge_color, edge_width_)); - painter->setBrush(Qt::NoBrush); - painter->drawPath(path()); - - // Draw arrow - painter->setPen(Qt::NoPen); - painter->setBrush(edge_color); - painter->drawPolygon(arrow_); -} - -} diff --git a/app/widget/nodeview/nodeviewedge.h b/app/widget/nodeview/nodeviewedge.h deleted file mode 100644 index 361a3ffb5..000000000 --- a/app/widget/nodeview/nodeviewedge.h +++ /dev/null @@ -1,121 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 NODEEDGEITEM_H -#define NODEEDGEITEM_H - -#include -#include - -#include "node/edge.h" -#include "nodeviewcommon.h" - -namespace olive { - -/** - * @brief A graphical representation of a NodeEdge to be used in NodeView - * - * A fairly simple line widget use to visualize a connection between two node parameters (a NodeEdge). - */ -class NodeViewEdge : public QGraphicsPathItem -{ -public: - NodeViewEdge(QGraphicsItem* parent = nullptr); - - /** - * @brief Set the edge that this item corresponds to - * - * This can be changed at any time (but under most circumstances won't be). Calling this will automatically call - * Adjust() to move this item into the correct position. - */ - void SetEdge(NodeEdgePtr edge); - NodeEdgePtr edge(); - - /** - * @brief Moves/updates this line to visually connect between the two corresponding NodeViewItems - * - * Using the attached edge (see SetEdge()), this function retrieves the NodeViewItems representing the two nodes - * that this edge connects. It uses their positions to determine where the line should visually connect and sets - * it accordingly. - * - * This should be set any time the NodeEdge changes (see SetEdge()), and any time the nodes move in the NodeGraph - * (see NodeView::ItemsChanged()). This will keep the nodes visually connected at all times. - */ - void Adjust(); - - /** - * @brief Set the connected state of this line - * - * When the edge is not connected, it visually depicts this by coloring the line grey. When an edge is connected or - * a potential connection is valid, the line is colored white. This function sets whether the line should be grey - * (false) or white (true). - * - * Using SetEdge() automatically sets this to true. Under most circumstances this should be left alone, and only - * be set when an edge is being created/dragged. - */ - void SetConnected(bool c); - - /** - * @brief Set highlighted state - * - * Changes color of edge. - */ - void SetHighlighted(bool e); - - /** - * @brief Set points to create curve from - */ - void SetPoints(const QPointF& start, const QPointF& end, bool input_is_expanded); - - /** - * @brief Sets the direction nodes are flowing - */ - void SetFlowDirection(NodeViewCommon::FlowDirection dir); - - /** - * @brief Set whether edges should be drawn as curved or as straight lines - */ - void SetCurved(bool e); - -protected: - virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - -private: - NodeEdgePtr edge_; - - int edge_width_; - - bool connected_; - - bool highlighted_; - - NodeViewCommon::FlowDirection flow_dir_; - - bool curved_; - - QPolygonF arrow_; - - int arrow_size_; - -}; - -} - -#endif // NODEEDGEITEM_H diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp deleted file mode 100644 index 596024897..000000000 --- a/app/widget/nodeview/nodeviewitem.cpp +++ /dev/null @@ -1,702 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "nodeviewitem.h" - -#include -#include -#include -#include -#include - -#include "common/flipmodifiers.h" -#include "common/qtutils.h" -#include "config/config.h" -#include "core.h" -#include "nodeview.h" -#include "nodeviewscene.h" -#include "nodeviewundo.h" -#include "ui/icons/icons.h" -#include "window/mainwindow/mainwindow.h" - -namespace olive { - -NodeViewItem::NodeViewItem(QGraphicsItem *parent) : - QGraphicsRectItem(parent), - node_(nullptr), - dragging_edge_(nullptr), - cached_drop_item_(nullptr), - cached_drop_item_expanded_(false), - expanded_(false), - hide_titlebar_(false), - standard_click_(false), - highlighted_index_(-1), - node_edge_change_command_(nullptr), - flow_dir_(NodeViewCommon::kLeftToRight) -{ - // Set flags for this widget - setFlag(QGraphicsItem::ItemIsMovable); - setFlag(QGraphicsItem::ItemIsSelectable); - setFlag(QGraphicsItem::ItemSendsGeometryChanges); - - // - // We use font metrics to set all the UI measurements for DPI-awareness - // - - // Set border width - node_border_width_ = DefaultItemBorder(); - - int widget_width = DefaultItemWidth(); - int widget_height = DefaultItemHeight(); - - title_bar_rect_ = QRectF(-widget_width/2, -widget_height/2, widget_width, widget_height); - setRect(title_bar_rect_); -} - -QPointF NodeViewItem::GetNodePosition() const -{ - QPointF node_pos; - - qreal adjusted_x = pos().x() / DefaultItemHorizontalPadding(); - qreal adjusted_y = pos().y() / DefaultItemVerticalPadding(); - - switch (flow_dir_) { - case NodeViewCommon::kLeftToRight: - node_pos.setX(adjusted_x); - node_pos.setY(adjusted_y); - break; - case NodeViewCommon::kRightToLeft: - node_pos.setX(-adjusted_x); - node_pos.setY(adjusted_y); - break; - case NodeViewCommon::kTopToBottom: - node_pos.setX(adjusted_y); - node_pos.setY(adjusted_x); - break; - case NodeViewCommon::kBottomToTop: - node_pos.setX(-adjusted_y); - node_pos.setY(adjusted_x); - break; - } - - return node_pos; -} - -void NodeViewItem::SetNodePosition(const QPointF &pos) -{ - switch (flow_dir_) { - case NodeViewCommon::kLeftToRight: - setPos(pos.x() * DefaultItemHorizontalPadding(), - pos.y() * DefaultItemVerticalPadding()); - break; - case NodeViewCommon::kRightToLeft: - setPos(-pos.x() * DefaultItemHorizontalPadding(), - pos.y() * DefaultItemVerticalPadding()); - break; - case NodeViewCommon::kTopToBottom: - setPos(pos.y() * DefaultItemHorizontalPadding(), - pos.x() * DefaultItemVerticalPadding()); - break; - case NodeViewCommon::kBottomToTop: - setPos(pos.y() * DefaultItemHorizontalPadding(), - -pos.x() * DefaultItemVerticalPadding()); - break; - } -} - -int NodeViewItem::DefaultTextPadding() -{ - return QFontMetrics(QFont()).height() / 4; -} - -int NodeViewItem::DefaultItemHeight() -{ - return QFontMetrics(QFont()).height() + DefaultTextPadding() * 2; -} - -int NodeViewItem::DefaultItemWidth() -{ - return QtUtils::QFontMetricsWidth(QFontMetrics(QFont()), "HHHHHHHHHHHH");; -} - -int NodeViewItem::DefaultItemBorder() -{ - return QFontMetrics(QFont()).height() / 12; -} - -qreal NodeViewItem::DefaultItemHorizontalPadding() const -{ - if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) { - return DefaultItemWidth() * 1.5; - } else { - return DefaultItemWidth() * 1.25; - } -} - -qreal NodeViewItem::DefaultItemVerticalPadding() const -{ - if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) { - return DefaultItemHeight() * 1.5; - } else { - return DefaultItemHeight() * 2.0; - } -} - -void NodeViewItem::SetNode(Node *n) -{ - node_ = n; - - node_inputs_.clear(); - - if (node_) { - node_->Retranslate(); - - foreach (NodeParam* p, node_->parameters()) { - if (p->type() == NodeParam::kInput) { - NodeInput* input = static_cast(p); - - if (input->is_connectable()) { - node_inputs_.append(input); - } - } - } - - SetNodePosition(node_->GetPosition()); - } - - update(); -} - -Node *NodeViewItem::GetNode() const -{ - return node_; -} - -bool NodeViewItem::IsExpanded() const -{ - return expanded_; -} - -void NodeViewItem::SetExpanded(bool e, bool hide_titlebar) -{ - if (node_inputs_.isEmpty() - || (expanded_ == e && hide_titlebar_ == hide_titlebar)) { - return; - } - - expanded_ = e; - hide_titlebar_ = hide_titlebar; - - if (expanded_ && !node_inputs_.isEmpty()) { - // Create new rect - QRectF new_rect = title_bar_rect_; - - if (hide_titlebar_) { - new_rect.setHeight(new_rect.height() * node_inputs_.size()); - } else { - new_rect.setHeight(new_rect.height() * (node_inputs_.size() + 1)); - } - - setRect(new_rect); - } else { - setRect(title_bar_rect_); - } - - update(); -} - -void NodeViewItem::ToggleExpanded() -{ - SetExpanded(!IsExpanded()); -} - -void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) -{ - // HACK for getting the main QWidget palette color (the `widget`'s palette uses the NodeView color instead which we - // don't want here) - QPalette app_pal = Core::instance()->main_window()->palette(); - - // Draw background rect if expanded - if (IsExpanded()) { - painter->setPen(Qt::NoPen); - painter->setBrush(app_pal.color(QPalette::Window)); - - painter->drawRect(rect()); - - painter->setPen(app_pal.color(QPalette::Text)); - - for (int i=0;ifillRect(input_rect, highlight_col); - } - - painter->drawText(input_rect, Qt::AlignCenter, node_inputs_.at(i)->name()); - } - } - - // Draw the titlebar - if (!hide_titlebar_ && node_) { - - Color node_color = Config::Current()[QStringLiteral("NodeCatColor%1") - .arg(node_->Category().first())].value(); - - QLinearGradient grad; - - grad.setStart(0, rect().top()); - grad.setFinalStop(0, rect().bottom()); - - QColor node_color_as_qcolor = node_color.toQColor(); - - { - // Generate lighter color for gradient - qreal hue, sat, lightness; - node_color_as_qcolor.getHslF(&hue, &sat, &lightness); - lightness = qMin(1.0, lightness + 0.2); - grad.setColorAt(0.0, QColor::fromHslF(hue, sat, lightness)); - } - - grad.setColorAt(1.0, node_color_as_qcolor); - - painter->setPen(Qt::black); - painter->setBrush(grad); - - painter->drawRect(title_bar_rect_); - - painter->setPen(app_pal.color(QPalette::Text)); - - QString node_label; - - if (!node_->GetLabel().isEmpty()) { - // Use label directly if node has one - node_label = node_->GetLabel(); - } else if (node_->IsTrack()) { - // If node is a track, use a special track name to help users identify better - // Exception for tracks - TrackOutput* track = static_cast(node_); - node_label = TrackOutput::GetDefaultTrackName(track->track_type(), track->Index()); - } else { - // Otherwise, just use the node's short name - node_label = node_->ShortName(); - } - - QFont f; - QFontMetrics fm(f); - - // Draw right or down arrow based on expanded state - int icon_size = fm.height() / 2; - int icon_padding = title_bar_rect_.height() / 2 - icon_size / 2; - int icon_full_size = icon_size + icon_padding * 2; - const QIcon& expand_icon = IsExpanded() ? icon::TriDown : icon::TriRight; - expand_icon.paint(painter, QRect(title_bar_rect_.x() + icon_padding, - title_bar_rect_.y() + icon_padding, - icon_size, - icon_size)); - - // Calculate how much space we have for text - int item_width = title_bar_rect_.width(); - int max_text_width = item_width - DefaultTextPadding() * 2 - icon_full_size; - int label_width = QtUtils::QFontMetricsWidth(fm, node_label); - - // Concatenate text if necessary (adds a "..." to the end and removes characters until the - // string fits in the bounds) - if (label_width > max_text_width) { - QString concatenated; - - do { - node_label.chop(1); - concatenated = QCoreApplication::translate("NodeViewItem", "%1...").arg(node_label); - } while ((label_width = QtUtils::QFontMetricsWidth(fm, concatenated)) > max_text_width); - - node_label = concatenated; - } - - // Determine the text color (automatically calculate from node background color) - if (node_color.GetRoughLuminance() > 0.5) { - painter->setPen(Qt::black); - } else { - painter->setPen(Qt::white); - } - - // Determine X position (favors horizontal centering unless it'll overrun the arrow) - QRectF text_rect = title_bar_rect_; - Qt::Alignment text_align = Qt::AlignCenter; - int likely_x = item_width / 2 - label_width / 2; - if (likely_x < icon_full_size) { - text_rect.adjust(icon_full_size, 0, 0, 0); - text_align = Qt::AlignLeft | Qt::AlignVCenter; - } - - // Draw the text in a rect (the rect is sized around text already in the constructor) - painter->drawText(text_rect, - text_align, - node_label); - } - - // Draw final border - QPen border_pen; - border_pen.setWidth(node_border_width_); - - if (option->state & QStyle::State_Selected) { - border_pen.setColor(app_pal.color(QPalette::Highlight)); - } else { - border_pen.setColor(Qt::black); - } - - painter->setPen(border_pen); - painter->setBrush(Qt::NoBrush); - - painter->drawRect(rect()); -} - -void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event) -{ - // We override standard mouse behavior in some cases. In these cases, we don't want the standard "move" and "release" - // events to trigger if we haven't already triggered the "press" event. We use this variable to determine whether - // base class behavior is valid here. - standard_click_ = false; - - // If CTRL is held, we start a new edge - if (node_ && event->modifiers() & Qt::ControlModifier) { - - NodeParam* param = node_->output(); - - // Create draggable object - dragging_edge_ = new NodeViewEdge(); - dragging_edge_->SetFlowDirection(flow_dir_); - - // Set up a QUndoCommand to make this action undoable - node_edge_change_command_ = new QUndoCommand(); - - if (param->type() == NodeParam::kOutput || param->edges().isEmpty()) { - // For an output param (or an input param with no connections), we default to creating a new edge - drag_source_ = this; - drag_src_param_ = param; - - // Set the starting position to the current param's connector - dragging_edge_start_ = GetParamPoint(param, QPointF()); - - } else if (param->type() == NodeParam::kInput) { - // For an input param, we default to moving an existing edge - // (here we use the last one, which will usually also be the first) - NodeEdgePtr edge = param->edges().last(); - - // The starting position will be the OPPOSING parameter's rectangle - - // Get the opposing parameter - drag_src_param_ = edge->output(); - - // Get its Node UI object - drag_source_ = static_cast(scene())->NodeToUIObject(drag_src_param_->parentNode()); - - // Get the opposing parameter's rect center using the line's current coordinates - // (we use the current coordinates because a complex formula is used for the line's coords if - // the opposing node is collapsed, therefore it's easier to just retrieve it from line itself) - NodeViewEdge* existing_edge_ui = static_cast(scene())->EdgeToUIObject(edge); - QPainterPath existing_edge_line = existing_edge_ui->path(); - QPointF edge_start = existing_edge_line.pointAtPercent(0); - QPointF edge_end = existing_edge_line.pointAtPercent(1); - if (existing_edge_ui->contains(edge_start)) { - dragging_edge_start_ = edge_start; - } else { - dragging_edge_start_ = edge_end; - } - - - // Remove old edge - NodeEdgeRemoveCommand* remove_command = new NodeEdgeRemoveCommand(edge->output(), - edge->input(), - node_edge_change_command_); - remove_command->redo(); - - } - - // Add it to the scene - scene()->addItem(dragging_edge_); - - // Trigger initial line setting - mouseMoveEvent(event); - - return; - - } else { - - // We aren't using any override behaviors, switch back to standard click behavior - standard_click_ = true; - - event->setModifiers(FlipControlAndShiftModifiers(event->modifiers())); - - QGraphicsRectItem::mousePressEvent(event); - - } -} - -void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) -{ - // Check if an edge drag was initiated - if (dragging_edge_ != nullptr) { - QPointF end_point = event->scenePos(); - drag_dest_param_ = nullptr; - - if (!cached_drop_item_ - || !cached_drop_item_->contains(cached_drop_item_->mapFromScene(event->scenePos()))) { - - // Cursor has left last entered item, we need to find a new one - if (cached_drop_item_) { - - // If we expanded an item below but are no longer dragging over it, re-collapse it - if (cached_drop_item_expanded_) { - cached_drop_item_->SetExpanded(false); - } - - cached_drop_item_->SetHighlightedIndex(-1); - - cached_drop_item_->setZValue(0); - cached_drop_item_ = nullptr; - } - - NodeViewItem* cursor_item = dynamic_cast(scene()->itemAt(event->scenePos(), sceneTransform())); - - if (cursor_item - && cursor_item != drag_source_ - && !cursor_item->node_inputs_.isEmpty()) { - cached_drop_item_ = cursor_item; - - // If the item we're dragging over is collapsed, expand it - cached_drop_item_expanded_ = !cached_drop_item_->IsExpanded(); - - if (cached_drop_item_expanded_) { - cached_drop_item_->SetExpanded(true, true); - } - - cached_drop_item_->setZValue(1); - } else { - cached_drop_item_ = nullptr; - } - - } - - if (cached_drop_item_) { - - int highlight_their_index = -1; - - // See if the mouse is currently inside a connector rect - for (int i=0;inode_inputs_.size();i++) { - - // Get the parameter we're dragging into - NodeParam* comp_param = cached_drop_item_->node_inputs_.at(i); - - // See if cursor is inside its UI - if (cached_drop_item_->GetInputRect(i).contains(cached_drop_item_->mapFromScene(event->scenePos()))) { // See if we're dragging inside the hitbox - - // Attempt to prevent circular dependency - ensure the receiving node doesn't output to the outputting node - if (!cached_drop_item_->GetNode()->OutputsTo(node_, true)) { - drag_dest_param_ = comp_param; - highlight_their_index = i; - - QPointF end_point_local = cached_drop_item_->GetInputPoint(i, pos()); - end_point = cached_drop_item_->mapToScene(end_point_local); - } - - break; - } - } - - cached_drop_item_->SetHighlightedIndex(highlight_their_index); - } - - dragging_edge_->SetConnected(drag_dest_param_); - - dragging_edge_->SetPoints(dragging_edge_start_, - end_point, - cached_drop_item_ ? cached_drop_item_->IsExpanded() : false); - - return; - } - - if (standard_click_) { - event->setModifiers(FlipControlAndShiftModifiers(event->modifiers())); - - QGraphicsRectItem::mouseMoveEvent(event); - } -} - -void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) -{ - // Check if an edge drag was initiated - if (dragging_edge_ != nullptr) { - - // Remove the drag object - scene()->removeItem(dragging_edge_); - - // If we expanded an item in the drag, re-collapse it now - if (cached_drop_item_) { - if (cached_drop_item_expanded_) { - cached_drop_item_->SetExpanded(false); - } - - cached_drop_item_->SetHighlightedIndex(-1); - cached_drop_item_->setZValue(0); - - cached_drop_item_ = nullptr; - } - - if (drag_dest_param_ != nullptr) { - // We dragged to somewhere, so we'll make a new connection - - NodeEdgePtr new_edge; - - NodeOutput* output; - NodeInput* input; - - // Connecting will automatically add an edge UI object through the signal/slot system - if (drag_dest_param_->type() == NodeParam::kOutput) { - output = static_cast(drag_dest_param_); - input = static_cast(drag_src_param_); - } else { - output = static_cast(drag_src_param_); - input = static_cast(drag_dest_param_); - } - - // Use a command to make Node connecting undoable - new NodeEdgeAddCommand(output, - input, - node_edge_change_command_); - } - - dragging_edge_ = nullptr; - - Core::instance()->undo_stack()->pushIfHasChildren(node_edge_change_command_); - node_edge_change_command_ = nullptr; - return; - } - - if (standard_click_) { - event->setModifiers(FlipControlAndShiftModifiers(event->modifiers())); - - QGraphicsRectItem::mouseReleaseEvent(event); - } -} - -void NodeViewItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) -{ - QGraphicsRectItem::mouseDoubleClickEvent(event); - - if (!(event->modifiers() & Qt::ControlModifier)) { - SetExpanded(!IsExpanded()); - } -} - -QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) -{ - if (change == ItemPositionHasChanged && node_) { - node_->blockSignals(true); - node_->SetPosition(GetNodePosition()); - node_->blockSignals(false); - } - - return QGraphicsItem::itemChange(change, value); -} - -void NodeViewItem::SetHighlightedIndex(int index) -{ - if (highlighted_index_ == index) { - return; - } - - highlighted_index_ = index; - - update(); -} - -QRectF NodeViewItem::GetInputRect(int index) const -{ - QRectF r = title_bar_rect_; - - if (!hide_titlebar_) { - index++; - } - - if (IsExpanded()) { - r.translate(0, r.height() * index); - } - - return r; -} - -QPointF NodeViewItem::GetParamPoint(NodeParam *param, const QPointF& source_pos) const -{ - if (param->type() == NodeParam::kOutput) { - - switch (flow_dir_) { - case NodeViewCommon::kLeftToRight: - default: - return pos() + QPointF(rect().right(), rect().center().y()); - case NodeViewCommon::kRightToLeft: - return pos() + QPointF(rect().left(), rect().center().y()); - case NodeViewCommon::kTopToBottom: - return pos() + QPointF(rect().center().x(), rect().bottom()); - case NodeViewCommon::kBottomToTop: - return pos() + QPointF(rect().center().x(), rect().top()); - } - - } else { - NodeInput* input = static_cast(param); - - // Resolve NodeInputArray elements - while (input->parentNode() != input->parent()) { - input = static_cast(input->parent()); - } - - return pos() + GetInputPoint(node_inputs_.indexOf(input), source_pos); - } -} - -void NodeViewItem::SetFlowDirection(NodeViewCommon::FlowDirection dir) -{ - flow_dir_ = dir; -} - -QPointF NodeViewItem::GetInputPoint(int index, const QPointF& source_pos) const -{ - QRectF input_rect = GetInputRect(index); - - Qt::Orientation flow_orientation = NodeViewCommon::GetFlowOrientation(flow_dir_); - - if (flow_orientation == Qt::Horizontal || IsExpanded()) { - if (flow_dir_ == NodeViewCommon::kLeftToRight - || (flow_orientation == Qt::Vertical && source_pos.x() < pos().x())) { - return QPointF(input_rect.left(), input_rect.center().y()); - } else { - return QPointF(input_rect.right(), input_rect.center().y()); - } - } else { - if (flow_dir_ == NodeViewCommon::kTopToBottom) { - return QPointF(input_rect.center().x(), input_rect.top()); - } else { - return QPointF(input_rect.center().x(), input_rect.bottom()); - } - } -} - -} diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h deleted file mode 100644 index 845f0d652..000000000 --- a/app/widget/nodeview/nodeviewitem.h +++ /dev/null @@ -1,179 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 NODEVIEWITEM_H -#define NODEVIEWITEM_H - -#include -#include -#include -#include -#include - -#include "node/node.h" -#include "nodeviewcommon.h" -#include "nodeviewedge.h" -#include "nodeviewitemwidgetproxy.h" - -namespace olive { - -/** - * @brief A visual widget representation of a Node object to be used in a NodeView - * - * This widget can be collapsed or expanded to show/hide the node's various parameters. - * - * To retrieve the NodeViewItem for a certain Node, use NodeView::NodeToUIObject(). - */ -class NodeViewItem : public QGraphicsRectItem -{ -public: - NodeViewItem(QGraphicsItem* parent = nullptr); - - QPointF GetNodePosition() const; - void SetNodePosition(const QPointF& pos); - - /** - * @brief Set the Node to correspond to this widget - */ - void SetNode(Node* n); - - /** - * @brief Get currently attached node - */ - Node* GetNode() const; - - /** - * @brief Get expanded state - */ - bool IsExpanded() const; - - /** - * @brief Set expanded state - */ - void SetExpanded(bool e, bool hide_titlebar = false); - void ToggleExpanded(); - - /** - * @brief Returns GLOBAL point that edges should connect to for any NodeParam member of this object - */ - QPointF GetParamPoint(NodeParam* param, const QPointF &source_pos) const; - - /** - * @brief Sets the direction nodes are flowing - */ - void SetFlowDirection(NodeViewCommon::FlowDirection dir); - - static int DefaultTextPadding(); - - static int DefaultItemHeight(); - - static int DefaultItemWidth(); - - static int DefaultItemBorder(); - - qreal DefaultItemHorizontalPadding() const; - - qreal DefaultItemVerticalPadding() const; - -protected: - virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - - virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override; - virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; - virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; - virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; - - virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override; - -private: - /** - * @brief Highlight an input (for instance when the user is dragging over it) - */ - void SetHighlightedIndex(int index); - - /** - * @brief Returns local rect of a NodeInput in array node_inputs_[index] - */ - QRectF GetInputRect(int index) const; - - /** - * @brief Returns local point that edges should connect to for a NodeInput in array node_inputs_[index] - */ - QPointF GetInputPoint(int index, const QPointF &source_pos) const; - - /** - * @brief Reference to attached Node - */ - Node* node_; - - /** - * @brief Cached list of node inputs - */ - QList node_inputs_; - - /** - * @brief Rectangle of the Node's title bar (equal to rect() when collapsed) - */ - QRectF title_bar_rect_; - - /// Edge dragging variables - NodeViewEdge* dragging_edge_; - QPointF dragging_edge_start_; - NodeParam* drag_src_param_; - NodeParam* drag_dest_param_; - NodeViewItem* drag_source_; - - NodeViewItem* cached_drop_item_; - bool cached_drop_item_expanded_; - - /// Sizing variables to use when drawing - int node_border_width_; - - /** - * @brief Expanded state - */ - bool expanded_; - - bool hide_titlebar_; - - /** - * @brief Current click mode - * - * \see mousePressEvent() - */ - bool standard_click_; - - int highlighted_index_; - - /** - * @brief QUndoCommand for creating and deleting edges by dragging - * - * \see mousePressEvent() - * \see mouseReleaseEvent() - */ - QUndoCommand* node_edge_change_command_; - - NodeViewCommon::FlowDirection flow_dir_; - -}; - -} - -#endif // NODEVIEWITEM_H diff --git a/app/widget/nodeview/nodeviewitemwidgetproxy.cpp b/app/widget/nodeview/nodeviewitemwidgetproxy.cpp deleted file mode 100644 index b219e8b67..000000000 --- a/app/widget/nodeview/nodeviewitemwidgetproxy.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "nodeviewitemwidgetproxy.h" - -QColor NodeViewItemWidget::TitleBarColor() -{ - return title_bar_color_; -} - -void NodeViewItemWidget::SetTitleBarColor(QColor color) -{ - title_bar_color_ = color; -} - -QColor NodeViewItemWidget::BorderColor() -{ - return border_color_; -} - -void NodeViewItemWidget::SetBorderColor(QColor color) -{ - border_color_ = color; -} diff --git a/app/widget/nodeview/nodeviewitemwidgetproxy.h b/app/widget/nodeview/nodeviewitemwidgetproxy.h deleted file mode 100644 index 91d0312f3..000000000 --- a/app/widget/nodeview/nodeviewitemwidgetproxy.h +++ /dev/null @@ -1,49 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 NODEVIEWITEMWIDGETPROXY_H -#define NODEVIEWITEMWIDGETPROXY_H - -#include - -/** - * @brief A proxy object to allow NodeViewItem access to CSS functions - * - * QGraphicsItems can't take Q_PROPERTYs for CSS stylesheet input, but QWidgets can. This is a hack to allow CSS - * properties to be set from CSS and then read by NodeViewItem. - */ -class NodeViewItemWidget : public QWidget { - Q_OBJECT - Q_PROPERTY(QColor titlebarColor READ TitleBarColor WRITE SetTitleBarColor DESIGNABLE true) - Q_PROPERTY(QColor borderColor READ BorderColor WRITE SetBorderColor DESIGNABLE true) -public: - NodeViewItemWidget() = default; - - QColor TitleBarColor(); - void SetTitleBarColor(QColor color); - - QColor BorderColor(); - void SetBorderColor(QColor color); -private: - QColor title_bar_color_; - QColor border_color_; -}; - -#endif // NODEVIEWITEMWIDGETPROXY_H diff --git a/app/widget/nodeview/nodeviewscene.cpp b/app/widget/nodeview/nodeviewscene.cpp deleted file mode 100644 index f326c100c..000000000 --- a/app/widget/nodeview/nodeviewscene.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 "nodeviewscene.h" - -#include "common/functiontimer.h" -#include "nodeviewedge.h" -#include "nodeviewitem.h" -#include "project/item/sequence/sequence.h" - -namespace olive { - -NodeViewScene::NodeViewScene(QObject *parent) : - QGraphicsScene(parent), - graph_(nullptr), - direction_(NodeViewCommon::kLeftToRight), - curved_edges_(true) -{ -} - -void NodeViewScene::SetFlowDirection(NodeViewCommon::FlowDirection direction) -{ - direction_ = direction; - - { - // Iterate over node items setting direction - QHash::const_iterator i; - for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) { - i.value()->SetFlowDirection(direction_); - - // Update position too - i.value()->SetNodePosition(i.key()->GetPosition()); - } - } - - { - // Iterate over edge items setting direction - QHash::const_iterator i; - for (i=edge_map_.constBegin(); i!=edge_map_.constEnd(); i++) { - i.value()->SetFlowDirection(direction_); - } - } -} - -void NodeViewScene::clear() -{ - // Deselect everything (prevents signals that a selection has changed after deleting an object) - DeselectAll(); - - // HACK: QGraphicsScene contains some sort of internal caching of the selected items which doesn't update unless - // we call a function like this. That means even though we deselect all items above, QGraphicsScene will - // continue to incorrectly signal selectionChanged() when items that were selected (but are now not) get - // deleted. Calling this function appears to update the internal cache and prevent this. - selectedItems(); - - { - QHash::const_iterator i; - for (i=item_map_.begin();i!=item_map_.end();i++) { - delete i.value(); - } - item_map_.clear(); - } - - { - QHash::const_iterator i; - for (i=edge_map_.begin();i!=edge_map_.end();i++) { - delete i.value(); - } - edge_map_.clear(); - } -} - -void NodeViewScene::SelectAll() -{ - QList all_items = this->items(); - - foreach (QGraphicsItem* i, all_items) { - i->setSelected(true); - } -} - -void NodeViewScene::DeselectAll() -{ - QList selected_items = this->selectedItems(); - - foreach (QGraphicsItem* i, selected_items) { - i->setSelected(false); - } -} - -NodeViewItem *NodeViewScene::NodeToUIObject(Node *n) -{ - return item_map_.value(n); -} - -NodeViewEdge *NodeViewScene::EdgeToUIObject(NodeEdgePtr n) -{ - return edge_map_.value(n.get()); -} - -void NodeViewScene::SetGraph(NodeGraph *graph) -{ - graph_ = graph; -} - -QVector NodeViewScene::GetSelectedNodes() const -{ - QHash::const_iterator iterator; - QVector selected; - - for (iterator=item_map_.begin();iterator!=item_map_.end();iterator++) { - if (iterator.value()->isSelected()) { - selected.append(iterator.key()); - } - } - - return selected; -} - -QVector NodeViewScene::GetSelectedItems() const -{ - QHash::const_iterator iterator; - QVector selected; - - for (iterator=item_map_.begin();iterator!=item_map_.end();iterator++) { - if (iterator.value()->isSelected()) { - selected.append(iterator.value()); - } - } - - return selected; -} - -QVector NodeViewScene::GetSelectedEdges() const -{ - QVector edges; - - QHash::const_iterator i; - - for (i=edge_map_.constBegin(); i!=edge_map_.constEnd(); i++) { - if (i.value()->isSelected()) { - edges.append(i.key()); - } - } - - return edges; -} - -const QHash &NodeViewScene::item_map() const -{ - return item_map_; -} - -const QHash &NodeViewScene::edge_map() const -{ - return edge_map_; -} - -void NodeViewScene::AddNode(Node* node) -{ - NodeViewItem* item = new NodeViewItem(); - - item->SetFlowDirection(direction_); - item->SetNode(node); - - addItem(item); - item_map_.insert(node, item); - - // Add a NodeViewEdge for each connection - foreach (NodeParam* param, node->parameters()) { - - // We only bother working with outputs since eventually this will cover all inputs too - // (covering both would lead to duplicates since every edge connects to one input and one output) - if (param->type() == NodeParam::kOutput) { - const QVector& edges = param->edges(); - - foreach(NodeEdgePtr edge, edges) { - AddEdge(edge); - } - } - } - - connect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged); - connect(node, &Node::LabelChanged, this, &NodeViewScene::NodeLabelChanged); -} - -void NodeViewScene::RemoveNode(Node *node) -{ - disconnect(node, &Node::LabelChanged, this, &NodeViewScene::NodeLabelChanged); - disconnect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged); - - delete item_map_.take(node); -} - -void NodeViewScene::AddEdge(NodeEdgePtr edge) -{ - NodeViewEdge* edge_ui = new NodeViewEdge(); - - edge_ui->SetEdge(edge); - edge_ui->SetFlowDirection(direction_); - edge_ui->SetCurved(curved_edges_); - - addItem(edge_ui); - edge_map_.insert(edge.get(), edge_ui); -} - -void NodeViewScene::RemoveEdge(NodeEdgePtr edge) -{ - delete edge_map_.take(edge.get()); -} - -int NodeViewScene::DetermineWeight(Node *n) -{ - QVector inputs = n->GetImmediateDependencies(); - - int weight = 0; - - foreach (Node* i, inputs) { - if (i->GetRoutesTo(n) == 1) { - weight += DetermineWeight(i); - } - } - - return qMax(1, weight); -} - -Qt::Orientation NodeViewScene::GetFlowOrientation() const -{ - return NodeViewCommon::GetFlowOrientation(direction_); -} - -NodeViewCommon::FlowDirection NodeViewScene::GetFlowDirection() const -{ - return direction_; -} - -void NodeViewScene::ReorganizeFrom(Node* n) -{ - QVector immediates = n->GetImmediateDependencies(); - - if (immediates.isEmpty()) { - // Nothing to do - return; - } - - QPointF parent_pos = n->GetPosition(); - - int weight_count = DetermineWeight(n); - - qreal child_x = parent_pos.x() - 1.0; - qreal children_height = weight_count-1; - qreal children_y = parent_pos.y() - children_height * 0.5; - - int weight_counter = 0; - - foreach (Node* i, immediates) { - if (i->GetRoutesTo(n) == 1) { - int weight = DetermineWeight(i); - - i->SetPosition(QPointF(child_x, - children_y + weight_counter + (weight - 1) * 0.5)); - - weight_counter += weight; - - ReorganizeFrom(i); - } - } -} - -bool NodeViewScene::GetEdgesAreCurved() const -{ - return curved_edges_; -} - -void NodeViewScene::SetEdgesAreCurved(bool curved) -{ - if (curved_edges_ != curved) { - curved_edges_ = curved; - - foreach (NodeViewEdge* e, edge_map_) { - e->SetCurved(curved_edges_); - } - } -} - -void NodeViewScene::NodePositionChanged(const QPointF &pos) -{ - item_map_.value(static_cast(sender()))->SetNodePosition(pos); -} - -void NodeViewScene::NodeLabelChanged() -{ - item_map_.value(static_cast(sender()))->update(); -} - -} diff --git a/app/widget/nodeview/nodeviewscene.h b/app/widget/nodeview/nodeviewscene.h deleted file mode 100644 index 51064c965..000000000 --- a/app/widget/nodeview/nodeviewscene.h +++ /dev/null @@ -1,151 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2020 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 NODEVIEWSCENE_H -#define NODEVIEWSCENE_H - -#include -#include - -#include "node/graph.h" -#include "nodeviewedge.h" -#include "nodeviewitem.h" - -namespace olive { - -class NodeViewScene : public QGraphicsScene -{ - Q_OBJECT -public: - NodeViewScene(QObject *parent = nullptr); - - void clear(); - - void SelectAll(); - void DeselectAll(); - - /** - * @brief Retrieve the graphical widget corresponding to a specific Node - * - * In situations where you know what Node you're working with but need the UI object (e.g. for positioning), this - * static function will retrieve the NodeViewItem (Node UI representation) connected to this Node in a certain - * QGraphicsScene. This can be called from any other UI object, since it'll have a reference to the QGraphicsScene - * through QGraphicsItem::scene(). - * - * If the scene does not contain a widget for this node (usually meaning the node's graph is not the active graph - * in this view/scene), this function returns nullptr. - */ - NodeViewItem* NodeToUIObject(Node* n); - - /** - * @brief Retrieve the graphical widget corresponding to a specific NodeEdge - * - * Same as NodeToUIObject() but returns a NodeViewEdge corresponding to a NodeEdgePtr instead. - */ - NodeViewEdge* EdgeToUIObject(NodeEdgePtr n); - - void SetGraph(NodeGraph* graph); - - QVector GetSelectedNodes() const; - QVector GetSelectedItems() const; - QVector GetSelectedEdges() const; - - const QHash& item_map() const; - const QHash& edge_map() const; - - Qt::Orientation GetFlowOrientation() const; - - NodeViewCommon::FlowDirection GetFlowDirection() const; - void SetFlowDirection(NodeViewCommon::FlowDirection direction); - - /** - * @brief Automatically reposition the nodes based on their connections - */ - void ReorganizeFrom(Node* n); - - bool GetEdgesAreCurved() const; - -public slots: - /** - * @brief Slot when a Node is added to a graph (SetGraph() connects this) - * - * This should NEVER be called directly, only connected to a NodeGraph. To add a Node to the NodeGraph - * use NodeGraph::AddNode(). - */ - void AddNode(Node* node); - - /** - * @brief Slot when a Node is removed from a graph (SetGraph() connects this) - * - * This should NEVER be called directly, only connected to a NodeGraph. To remove a Node from the NodeGraph - * use NodeGraph::RemoveNode(). - */ - void RemoveNode(Node* node); - - /** - * @brief Slot when an edge is added to a graph (SetGraph() connects this) - * - * This should NEVER be called directly, only connected to a NodeGraph. To add an edge (i.e. connect two node - * parameters together), use NodeParam::ConnectEdge(). - */ - void AddEdge(NodeEdgePtr edge); - - /** - * @brief Slot when an edge is removed from a graph (SetGraph() connects this) - * - * This should NEVER be called directly, only connected to a NodeGraph. To remove an edge (i.e. disconnect two node - * parameters), use NodeParam::DisconnectEdge(). - */ - void RemoveEdge(NodeEdgePtr edge); - - /** - * @brief Set whether edges in this scene should be curved or not - */ - void SetEdgesAreCurved(bool curved); - -private: - static int DetermineWeight(Node* n); - - QHash item_map_; - - QHash edge_map_; - - NodeGraph* graph_; - - NodeViewCommon::FlowDirection direction_; - - bool curved_edges_; - -private slots: - /** - * @brief Receiver for whenever a node position changes - */ - void NodePositionChanged(const QPointF& pos); - - /** - * @brief Receiver for when a node's label has changed - */ - void NodeLabelChanged(); - -}; - -} - -#endif // NODEVIEWSCENE_H diff --git a/app/widget/nodeview/nodeviewundo.cpp b/app/widget/nodeview/nodeviewundo.cpp index 66416c158..c4129dfdb 100644 --- a/app/widget/nodeview/nodeviewundo.cpp +++ b/app/widget/nodeview/nodeviewundo.cpp @@ -24,87 +24,50 @@ namespace olive { -NodeEdgeAddCommand::NodeEdgeAddCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) : +NodeEdgeAddCommand::NodeEdgeAddCommand(Node *output, NodeInput *input, int element, QUndoCommand *parent) : UndoCommand(parent), output_(output), input_(input), - old_edge_(nullptr), - done_(false) + element_(element) { } void NodeEdgeAddCommand::redo_internal() { - if (done_) { - return; - } - - old_edge_ = NodeParam::DisconnectForNewOutput(input_); - - NodeParam::ConnectEdge(output_, input_); - - done_ = true; + Node::ConnectEdge(output_, input_, element_); } void NodeEdgeAddCommand::undo_internal() { - if (!done_) { - return; - } - - NodeParam::DisconnectEdge(output_, input_); - - if (old_edge_ != nullptr) { - NodeParam::ConnectEdge(old_edge_->output(), old_edge_->input()); - } - - done_ = false; + Node::DisconnectEdge(output_, input_, element_); } Project *NodeEdgeAddCommand::GetRelevantProject() const { - return static_cast(output_->parentNode()->parent())->project(); + return output_->parent()->project(); } -NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) : +NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(Node *output, NodeInput *input, int element, QUndoCommand *parent) : UndoCommand(parent), output_(output), input_(input), - done_(false) -{ -} - -NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(NodeEdgePtr edge, QUndoCommand *parent) : - UndoCommand(parent), - output_(edge->output()), - input_(edge->input()), - done_(false) + element_(element) { } void NodeEdgeRemoveCommand::redo_internal() { - if (done_) { - return; - } - - NodeParam::DisconnectEdge(output_, input_); - done_ = true; + Node::DisconnectEdge(output_, input_, element_); } void NodeEdgeRemoveCommand::undo_internal() { - if (!done_) { - return; - } - - NodeParam::ConnectEdge(output_, input_); - done_ = false; + Node::ConnectEdge(output_, input_, element_); } Project *NodeEdgeRemoveCommand::GetRelevantProject() const { - return static_cast(output_->parentNode()->parent())->project(); + return output_->parent()->project(); } NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node, QUndoCommand *parent) : @@ -118,75 +81,34 @@ NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node, QUndoCommand *paren void NodeAddCommand::redo_internal() { - graph_->AddNode(node_); + node_->setParent(graph_); } void NodeAddCommand::undo_internal() { - graph_->TakeNode(node_, &memory_manager_); + node_->setParent(&memory_manager_); } Project *NodeAddCommand::GetRelevantProject() const { - return static_cast(graph_)->project(); + return graph_->project(); } -NodeRemoveCommand::NodeRemoveCommand(NodeGraph *graph, const QVector &nodes, QUndoCommand *parent) : +NodeRemoveCommand::NodeRemoveCommand(Node *node, QUndoCommand *parent) : UndoCommand(parent), - graph_(graph), - nodes_(nodes) + graph_(node->parent()), + node_(node) { } void NodeRemoveCommand::redo_internal() { - // Cache edges for undoing - foreach (Node* n, nodes_) { - foreach (NodeParam* param, n->parameters()) { - foreach (NodeEdgePtr edge, param->edges()) { - // Ensures the same edge isn't added twice (prevents double connecting when undoing) - if (!edges_.contains(edge)) { - edges_.append(edge); - } - } - } - } - - // Take nodes from graph (TakeNode() will automatically disconnect edges) - foreach (Node* n, nodes_) { - // If the node is a block, unlink any linked blocks before removing - if (n->IsBlock()) { - Block *b = static_cast(n); - if (b->HasLinks()) { - BlockUnlinkAllCommand *unlink_command = new BlockUnlinkAllCommand(b); - unlink_command->redo(); - block_unlink_commands_.append(unlink_command); - } - } - graph_->TakeNode(n, &memory_manager_); - } + node_->setParent(&memory_manager_); } void NodeRemoveCommand::undo_internal() { - // Re-add nodes to graph - foreach (Node* n, nodes_) { - graph_->AddNode(n); - } - - // Relink any blocks that were unlinked - foreach(BlockUnlinkAllCommand* command, block_unlink_commands_) { - command->undo(); - delete command; - } - - // Re-connect edges - foreach (NodeEdgePtr edge, edges_) { - NodeParam::ConnectEdge(edge->output(), edge->input()); - } - - edges_.clear(); - block_unlink_commands_.clear(); + node_->setParent(graph_); } Project *NodeRemoveCommand::GetRelevantProject() const @@ -194,21 +116,6 @@ Project *NodeRemoveCommand::GetRelevantProject() const return static_cast(graph_)->project(); } -NodeRemoveWithExclusiveDeps::NodeRemoveWithExclusiveDeps(NodeGraph *graph, Node *node, QUndoCommand *parent) : - UndoCommand(parent) -{ - QVector node_and_its_deps; - node_and_its_deps.append(node); - node_and_its_deps.append(node->GetExclusiveDependencies()); - - remove_command_ = new NodeRemoveCommand(graph, node_and_its_deps, this); -} - -Project *NodeRemoveWithExclusiveDeps::GetRelevantProject() const -{ - return remove_command_->GetRelevantProject(); -} - NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include_connections, QUndoCommand *parent) : QUndoCommand(parent), src_(src), @@ -217,14 +124,6 @@ NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include { } -NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, QUndoCommand *parent) : - QUndoCommand(parent), - src_(src), - dest_(dest), - include_connections_(true) -{ -} - void NodeCopyInputsCommand::redo() { Node::CopyInputs(src_, dest_, include_connections_); diff --git a/app/widget/nodeview/nodeviewundo.h b/app/widget/nodeview/nodeviewundo.h index 03f9f4bcd..5d7aeba59 100644 --- a/app/widget/nodeview/nodeviewundo.h +++ b/app/widget/nodeview/nodeviewundo.h @@ -25,7 +25,6 @@ #include "node/graph.h" #include "node/node.h" -#include "nodeviewitem.h" #include "undo/undocommand.h" #include "widget/timelinewidget/undo/undo.h" @@ -38,7 +37,7 @@ namespace olive { */ class NodeEdgeAddCommand : public UndoCommand { public: - NodeEdgeAddCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr); + NodeEdgeAddCommand(Node* output, NodeInput* input, int element, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -47,12 +46,10 @@ protected: virtual void undo_internal() override; private: - NodeOutput* output_; + Node* output_; NodeInput* input_; + int element_; - NodeEdgePtr old_edge_; - - bool done_; }; /** @@ -62,8 +59,7 @@ private: */ class NodeEdgeRemoveCommand : public UndoCommand { public: - NodeEdgeRemoveCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr); - NodeEdgeRemoveCommand(NodeEdgePtr edge, QUndoCommand* parent = nullptr); + NodeEdgeRemoveCommand(Node* output, NodeInput* input, int element, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -72,10 +68,10 @@ protected: virtual void undo_internal() override; private: - NodeOutput* output_; + Node* output_; NodeInput* input_; + int element_; - bool done_; }; class NodeAddCommand : public UndoCommand { @@ -97,8 +93,7 @@ private: class NodeRemoveCommand : public UndoCommand { public: - NodeRemoveCommand(NodeGraph* graph, - const QVector& nodes, + NodeRemoveCommand(Node* node, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; @@ -111,32 +106,15 @@ private: QObject memory_manager_; NodeGraph* graph_; - QVector nodes_; - QVector edges_; - QVector block_unlink_commands_; -}; + Node* node_; -class NodeRemoveWithExclusiveDeps : public UndoCommand { -public: - NodeRemoveWithExclusiveDeps(NodeGraph* graph, - Node* node, - QUndoCommand* parent = nullptr); - - virtual Project* GetRelevantProject() const override; - -private: - NodeRemoveCommand* remove_command_; }; class NodeCopyInputsCommand : public QUndoCommand { public: NodeCopyInputsCommand(Node* src, Node* dest, - bool include_connections = true, - QUndoCommand* parent = nullptr); - - NodeCopyInputsCommand(Node* src, - Node* dest, + bool include_connections, QUndoCommand* parent = nullptr); protected: @@ -151,64 +129,6 @@ private: }; -class NodeGraphBeginOperationCommand : public UndoCommand { -public: - NodeGraphBeginOperationCommand(NodeGraph* graph, QUndoCommand* parent = nullptr) : - UndoCommand(parent), - graph_(graph) - { - } - - virtual Project* GetRelevantProject() const override - { - return static_cast(graph_)->project(); - } - -protected: - virtual void redo_internal() override - { - graph_->BeginOperation(); - } - - virtual void undo_internal() override - { - graph_->EndOperation(); - } - -private: - NodeGraph* graph_; - -}; - -class NodeGraphEndOperationCommand : public UndoCommand { -public: - NodeGraphEndOperationCommand(NodeGraph* graph, QUndoCommand* parent = nullptr) : - UndoCommand(parent), - graph_(graph) - { - } - - virtual Project* GetRelevantProject() const override - { - return static_cast(graph_)->project(); - } - -protected: - virtual void redo_internal() override - { - graph_->EndOperation(); - } - - virtual void undo_internal() override - { - graph_->BeginOperation(); - } - -private: - NodeGraph* graph_; - -}; - } #endif // NODEVIEWUNDO_H diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index 8162065e6..2ece5f0fa 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -541,9 +541,9 @@ void ProjectExplorer::DeselectAll() CurrentView()->selectionModel()->clearSelection(); } -QList ProjectExplorer::GetMediaNodesUsingFootage(Footage *item) +QVector ProjectExplorer::GetMediaNodesUsingFootage(Footage *item) { - QList list; + QVector list; // Get all sequences. QVector sequences = model_.project()->get_items_of_type(Item::kSequence); @@ -552,12 +552,10 @@ QList ProjectExplorer::GetMediaNodesUsingFootage(Footage *item) foreach (Item* s, sequences) { const QList& nodes = static_cast(s)->nodes(); foreach (Node* n, nodes) { - if (n->IsMedia()) { - MediaInput* media_node = static_cast(n); + MediaInput* media_node = dynamic_cast(n); - if (media_node->stream()->footage() == item) { - list.append(media_node); - } + if (media_node && media_node->stream()->footage() == item) { + list.append(media_node); } } } @@ -593,7 +591,7 @@ void ProjectExplorer::DeleteSelected() // If this is footage, check if it's used anywhere in any sequence Footage* footage = static_cast(item); - QList footage_nodes = GetMediaNodesUsingFootage(footage); + QVector footage_nodes = GetMediaNodesUsingFootage(footage); if (!footage_nodes.isEmpty()) { // Footage is in use, show messagebox asking what to do about it diff --git a/app/widget/projectexplorer/projectexplorer.h b/app/widget/projectexplorer/projectexplorer.h index e935b9084..b2f8d6ab3 100644 --- a/app/widget/projectexplorer/projectexplorer.h +++ b/app/widget/projectexplorer/projectexplorer.h @@ -105,11 +105,11 @@ private: /** * @brief Check if an item is in use anywhere and return any relevant input nodes */ - QList GetMediaNodesUsingFootage(Footage* item); + QVector GetMediaNodesUsingFootage(Footage* item); /** * @brief Get all the blocks that solely rely on an input node - * + * * Ignores blocks that depend on multiple inputs */ QList GetFootageBlocks(QList nodes); diff --git a/app/widget/projectexplorer/projectexplorerundo.cpp b/app/widget/projectexplorer/projectexplorerundo.cpp index 3bb2753d7..c11950e44 100644 --- a/app/widget/projectexplorer/projectexplorerundo.cpp +++ b/app/widget/projectexplorer/projectexplorerundo.cpp @@ -22,7 +22,7 @@ namespace olive { -OfflineFootageCommand::OfflineFootageCommand(const QList &media, QUndoCommand* parent) : +OfflineFootageCommand::OfflineFootageCommand(const QVector &media, QUndoCommand* parent) : UndoCommand(parent) { foreach (MediaInput* i, media) { diff --git a/app/widget/projectexplorer/projectexplorerundo.h b/app/widget/projectexplorer/projectexplorerundo.h index 2148e4b87..ddbafb924 100644 --- a/app/widget/projectexplorer/projectexplorerundo.h +++ b/app/widget/projectexplorer/projectexplorerundo.h @@ -31,7 +31,7 @@ namespace olive { */ class OfflineFootageCommand : public UndoCommand { public: - OfflineFootageCommand(const QList& media, QUndoCommand* parent = nullptr); + OfflineFootageCommand(const QVector& media, QUndoCommand* parent = nullptr); virtual Project* GetRelevantProject() const override; diff --git a/app/widget/scope/histogram/histogram.cpp b/app/widget/scope/histogram/histogram.cpp index 54211a066..b0aa95daf 100644 --- a/app/widget/scope/histogram/histogram.cpp +++ b/app/widget/scope/histogram/histogram.cpp @@ -73,9 +73,9 @@ void HistogramScope::DrawScope(TexturePtr managed_tex, QVariant pipeline) ShaderJob shader_job; - shader_job.InsertValue(QStringLiteral("viewport"), ShaderValue(QVector2D(width(), height()), NodeParam::kVec2)); - shader_job.InsertValue(QStringLiteral("histogram_scale"), ShaderValue(histogram_scale, NodeParam::kFloat)); - shader_job.InsertValue(QStringLiteral("histogram_power"), ShaderValue(histogram_power, NodeParam::kFloat)); + shader_job.InsertValue(QStringLiteral("viewport"), NodeValue(NodeValue::kVec2, QVector2D(width(), height()))); + shader_job.InsertValue(QStringLiteral("histogram_scale"), NodeValue(NodeValue::kFloat, histogram_scale)); + shader_job.InsertValue(QStringLiteral("histogram_power"), NodeValue(NodeValue::kFloat, histogram_power)); if (!texture_row_sums_ || texture_row_sums_->width() != this->width() @@ -86,11 +86,11 @@ void HistogramScope::DrawScope(TexturePtr managed_tex, QVariant pipeline) } // Draw managed texture to a sums texture - shader_job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(managed_tex), NodeParam::kTexture)); + shader_job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex))); renderer()->BlitToTexture(pipeline, shader_job, texture_row_sums_.get()); // Draw sums into a histogram - shader_job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(texture_row_sums_), NodeParam::kTexture)); + shader_job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_row_sums_))); renderer()->Blit(pipeline_secondary_, shader_job, texture_row_sums_->params()); // Draw line overlays diff --git a/app/widget/scope/scopebase/scopebase.cpp b/app/widget/scope/scopebase/scopebase.cpp index 3f9c4cd68..3ae2ce669 100644 --- a/app/widget/scope/scopebase/scopebase.cpp +++ b/app/widget/scope/scopebase/scopebase.cpp @@ -54,7 +54,7 @@ void ScopeBase::DrawScope(TexturePtr managed_tex, QVariant pipeline) { ShaderJob job; - job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(managed_tex), NodeParam::kTexture)); + job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex))); renderer()->Blit(pipeline, job, VideoParams(width(), height(), static_cast(Config::Current()["OfflinePixelFormat"].toInt()), diff --git a/app/widget/scope/waveform/waveform.cpp b/app/widget/scope/waveform/waveform.cpp index c92a7158a..d4f6e283e 100644 --- a/app/widget/scope/waveform/waveform.cpp +++ b/app/widget/scope/waveform/waveform.cpp @@ -58,22 +58,22 @@ void WaveformScope::DrawScope(TexturePtr managed_tex, QVariant pipeline) // Set viewport size job.InsertValue(QStringLiteral("viewport"), - ShaderValue(QVector2D(width(), height()), NodeParam::kVec2)); + NodeValue(NodeValue::kVec2, QVector2D(width(), height()))); // Set luma coefficients double luma_coeffs[3] = {0.0f, 0.0f, 0.0f}; color_manager()->GetDefaultLumaCoefs(luma_coeffs); job.InsertValue(QStringLiteral("luma_coeffs"), - ShaderValue(QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2]), NodeParam::kVec3)); + NodeValue(NodeValue::kVec3, QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2]))); // Scale of the waveform relative to the viewport surface. job.InsertValue(QStringLiteral("waveform_scale"), - ShaderValue(waveform_scale, NodeParam::kFloat)); + NodeValue(NodeValue::kFloat, waveform_scale)); // Insert source texture job.InsertValue(QStringLiteral("ove_maintex"), - ShaderValue(QVariant::fromValue(managed_tex), NodeParam::kTexture)); + NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex))); renderer()->Blit(pipeline, job, VideoParams(width(), height(), static_cast(Config::Current()["OfflinePixelFormat"].toInt()), diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index de6a66d86..9c3f8ede4 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -469,7 +469,7 @@ void TimelineWidget::ReplaceBlocksWithGaps(const QVector &blocks, new TrackReplaceBlockWithGapCommand(original_track, b, command); if (remove_from_graph) { - new NodeRemoveWithExclusiveDeps(static_cast(b->parent()), b, command); + Node::RemoveNodesAndExclusiveDependencies(b, command); } } } @@ -509,9 +509,7 @@ void TimelineWidget::DeleteSelected(bool ripple) transition, command); - new NodeRemoveWithExclusiveDeps(static_cast(GetConnectedNode()->parent()), - transition, - command); + Node::RemoveNodesAndExclusiveDependencies(transition, command); } // Replace clips with gaps (effectively deleting them) @@ -910,37 +908,28 @@ void TimelineWidget::AddBlock(Block *block, TrackReference track) } } -void TimelineWidget::RemoveBlock(const QList &blocks) +void TimelineWidget::RemoveBlock(Block *b) { - QVector deselect_blocks; + // Disconnect all signals + disconnect(b, &Block::Refreshed, this, &TimelineWidget::BlockRefreshed); + disconnect(b, &Block::LinksChanged, this, &TimelineWidget::BlockUpdated); + disconnect(b, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated); + disconnect(b, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated); - foreach (Block* b, blocks) { - // Disconnect all signals - disconnect(b, &Block::Refreshed, this, &TimelineWidget::BlockRefreshed); - disconnect(b, &Block::LinksChanged, this, &TimelineWidget::BlockUpdated); - disconnect(b, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated); - disconnect(b, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated); + // Take item from map + TimelineViewBlockItem* item = block_items_.take(b); - // Take item from map - TimelineViewBlockItem* item = block_items_.take(b); - - // If selected, deselect it - int select_index = selected_blocks_.indexOf(b); - if (select_index > -1) { - selected_blocks_.removeAt(select_index); - deselect_blocks.append(b); - RemoveSelection(item); - } - - // Finally, delete item - delete item; + // If selected, deselect it + int select_index = selected_blocks_.indexOf(b); + if (select_index > -1) { + selected_blocks_.removeAt(select_index); + RemoveSelection(item); } - if (!deselect_blocks.isEmpty()) { - // We already removed the blocks from selected_blocks_, so we can signal directly rather than - // through - emit BlocksDeselected(deselect_blocks); - } + // Finally, delete item + delete item; + + emit BlocksDeselected({b}); } void TimelineWidget::AddTrack(TrackOutput *track, Timeline::TrackType type) @@ -958,7 +947,9 @@ void TimelineWidget::RemoveTrack(TrackOutput *track) disconnect(track, &TrackOutput::IndexChanged, this, &TimelineWidget::TrackIndexChanged); disconnect(track, &TrackOutput::PreviewChanged, this, &TimelineWidget::TrackPreviewUpdated); - RemoveBlock(track->Blocks()); + foreach (Block* b, track->Blocks()) { + RemoveBlock(b); + } } void TimelineWidget::TrackIndexChanged() diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 5078a5a02..697aa692f 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -281,7 +281,7 @@ private slots: void ViewDragDropped(TimelineViewMouseEvent* event); void AddBlock(Block* block, TrackReference track); - void RemoveBlock(const QList& blocks); + void RemoveBlock(Block *blocks); void AddTrack(TrackOutput* track, Timeline::TrackType type); void RemoveTrack(TrackOutput* track); diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index cf4cea5ad..79b5e7662 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -123,7 +123,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) solid, command); - new NodeEdgeAddCommand(solid->output(), clip->texture_input(), command); + new NodeEdgeAddCommand(solid, clip->texture_input(), -1, command); break; } case olive::Tool::kAddableTitle: @@ -134,7 +134,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) text, command); - new NodeEdgeAddCommand(text->output(), clip->texture_input(), command); + new NodeEdgeAddCommand(text, clip->texture_input(), -1, command); break; } case olive::Tool::kAddableBars: diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index cebd75b18..027a160c0 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -435,8 +435,8 @@ void ImportTool::DropGhosts(bool insert) TransformDistortNode* transform = new TransformDistortNode(); new NodeAddCommand(dst_graph, transform, command); - new NodeEdgeAddCommand(video_input->output(), transform->texture_input(), command); - new NodeEdgeAddCommand(transform->output(), clip->texture_input(), command); + new NodeEdgeAddCommand(video_input, transform->texture_input(), -1, command); + new NodeEdgeAddCommand(transform, clip->texture_input(), -1, command); /* MatrixGenerator* matrix = new MatrixGenerator(); @@ -461,8 +461,8 @@ void ImportTool::DropGhosts(bool insert) VolumeNode* volume_node = new VolumeNode(); new NodeAddCommand(dst_graph, volume_node, command); - new NodeEdgeAddCommand(audio_input->output(), volume_node->samples_input(), command); - new NodeEdgeAddCommand(volume_node->output(), clip->texture_input(), command); + new NodeEdgeAddCommand(audio_input, volume_node->samples_input(), -1, command); + new NodeEdgeAddCommand(volume_node, clip->texture_input(), -1, command); break; } default: diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index d059c21ef..466a1d98a 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -572,8 +572,6 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) QUndoCommand* command = new QUndoCommand(); - new NodeGraphBeginOperationCommand(static_cast(parent()->GetConnectedNode()->parent()), command); - if (!blocks_trimming.isEmpty()) { foreach (const GhostBlockPair& p, blocks_trimming) { TimelineViewGhostItem* ghost = p.ghost; @@ -730,8 +728,6 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) } } - new NodeGraphEndOperationCommand(static_cast(parent()->GetConnectedNode()->parent()), command); - Core::instance()->undo_stack()->pushIfHasChildren(command); } diff --git a/app/widget/timelinewidget/tool/transition.cpp b/app/widget/timelinewidget/tool/transition.cpp index 335d6652f..c78f0e986 100644 --- a/app/widget/timelinewidget/tool/transition.cpp +++ b/app/widget/timelinewidget/tool/transition.cpp @@ -147,12 +147,14 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) Block* in_block = (ghost_->GetMode() == Timeline::kTrimIn) ? active_block : friend_block; // Connect block to transition - new NodeEdgeAddCommand(out_block->output(), + new NodeEdgeAddCommand(out_block, transition->out_block_input(), + -1, command); - new NodeEdgeAddCommand(in_block->output(), + new NodeEdgeAddCommand(in_block, transition->in_block_input(), + -1, command); } else { Block* block_to_transition = Node::ValueToPtr(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock)); @@ -167,8 +169,9 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) } // Connect block to transition - new NodeEdgeAddCommand(block_to_transition->output(), + new NodeEdgeAddCommand(block_to_transition, transition_input_to_connect, + -1, command); } diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index 3195aa709..ce5d66c17 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -30,13 +30,6 @@ namespace olive { -Node* TakeNodeFromParentGraph(Node* n, QObject* new_parent = nullptr) -{ - static_cast(n->parent())->TakeNode(n, new_parent); - - return n; -} - BlockResizeCommand::BlockResizeCommand(Block *block, rational new_length, QUndoCommand* parent) : UndoCommand(parent), block_(block), @@ -266,7 +259,8 @@ void TrackRippleRemoveAreaCommand::redo_internal() foreach (Block* remove_block, removed_blocks_) { track_->RippleRemoveBlock(remove_block); - NodeRemoveWithExclusiveDeps* remove_command = new NodeRemoveWithExclusiveDeps(static_cast(remove_block->parent()), remove_block); + QUndoCommand* remove_command = new QUndoCommand(); + Node::RemoveNodesAndExclusiveDependencies(remove_block, remove_command); remove_command->redo(); remove_block_commands_.append(remove_command); } @@ -295,7 +289,6 @@ void TrackRippleRemoveAreaCommand::redo_internal() track_->EndOperation(); track_->Node::InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX), - track_->block_input(), track_->block_input()); } @@ -334,7 +327,7 @@ void TrackRippleRemoveAreaCommand::undo_internal() // Restore blocks that were removed for (int i=remove_block_commands_.size()-1;i>=0;i--) { - UndoCommand* command = remove_block_commands_.at(i); + QUndoCommand* command = remove_block_commands_.at(i); command->undo(); delete command; } @@ -361,7 +354,7 @@ void TrackRippleRemoveAreaCommand::undo_internal() track_->EndOperation(); track_->Node::InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX), - track_->block_input(), track_->block_input()); + track_->block_input()); if (splice_split_command_) { delete splice_split_command_; @@ -385,13 +378,24 @@ Project *TrackPlaceBlockCommand::GetRelevantProject() const void TrackPlaceBlockCommand::redo_internal() { - added_track_count_ = 0; + // Determine if we need to add tracks + if (track_index_ >= timeline_->GetTracks().size()) { + if (added_tracks_.isEmpty()) { + // First redo, create tracks now + added_tracks_.resize(track_index_ - timeline_->GetTracks().size() + 1); - // Get track (or make it if necessary) - while (track_index_ >= timeline_->GetTracks().size()) { - timeline_->AddTrack(); + for (int i=0; isetParent(timeline_->GetParentGraph()); + + timeline_->track_input()->ArrayAppend(); + Node::ConnectEdge(track, timeline_->track_input(), timeline_->track_input()->ArraySize() - 1); + } } track_ = timeline_->GetTrackAt(track_index_); @@ -404,7 +408,7 @@ void TrackPlaceBlockCommand::redo_internal() // If so, insert a gap here gap_ = new GapBlock(); gap_->set_length_and_media_out(in_ - track_->track_length()); - static_cast(track_->parent())->AddNode(gap_); + gap_->setParent(track_->parent()); track_->AppendBlock(gap_); } @@ -424,14 +428,17 @@ void TrackPlaceBlockCommand::undo_internal() if (gap_ != nullptr) { track_->RippleRemoveBlock(gap_); - TakeNodeFromParentGraph(gap_, &memory_manager_); + gap_->setParent(&memory_manager_); } } else { TrackRippleRemoveAreaCommand::undo_internal(); } - for (;added_track_count_>0;added_track_count_--) { - timeline_->RemoveTrack(&memory_manager_); + for (int i=added_tracks_.size()-1; i>=0; i--) { + TrackOutput* track = added_tracks_.at(i); + Node::DisconnectEdge(track, timeline_->track_input(), timeline_->track_input()->ArraySize() - 1); + track->setParent(&memory_manager_); + timeline_->track_input()->ArrayRemoveLast(); } } @@ -465,13 +472,16 @@ void BlockSplitCommand::redo_internal() { track_->BeginOperation(); - NodeGraph* graph = static_cast(block_->parent()); + NodeGraph* graph = block_->parent(); add_command_ = new QUndoCommand(); new NodeAddCommand(graph, new_block_, add_command_); - new NodeCopyInputsCommand(block_, new_block_, true, add_command_); - if (Config::Current()[QStringLiteral("SplitClipsCopyNodes")].toBool()) { + bool copy_dependencies_too = Config::Current()[QStringLiteral("SplitClipsCopyNodes")].toBool(); + + new NodeCopyInputsCommand(block_, new_block_, !copy_dependencies_too, add_command_); + + if (copy_dependencies_too) { QVector src_nodes; QVector dst_nodes; @@ -502,8 +512,8 @@ void BlockSplitCommand::redo_internal() track_->InsertBlockAfter(new_block_, block_); foreach (NodeInput* transition, transitions_to_move_) { - NodeParam::DisconnectEdge(block_->output(), transition); - NodeParam::ConnectEdge(new_block_->output(), transition); + Node::DisconnectEdge(block_, transition); + Node::ConnectEdge(new_block_, transition); } track_->EndOperation(); @@ -514,8 +524,8 @@ void BlockSplitCommand::undo_internal() track_->BeginOperation(); foreach (NodeInput* transition, transitions_to_move_) { - NodeParam::DisconnectEdge(new_block_->output(), transition); - NodeParam::ConnectEdge(block_->output(), transition); + Node::DisconnectEdge(new_block_, transition); + Node::ConnectEdge(block_, transition); } block_->set_length_and_media_out(old_length_); @@ -929,7 +939,7 @@ void BlockTrimCommand::redo_internal() adjacent_ = new GapBlock(); adjacent_->set_length_and_media_out(trim_diff); - static_cast(track_->parent())->AddNode(adjacent_); + adjacent_->setParent(track_->parent()); if (mode_ == Timeline::kTrimIn) { track_->InsertBlockBefore(adjacent_, block_); @@ -948,7 +958,7 @@ void BlockTrimCommand::redo_internal() if (adjacent_length.isNull()) { // Ripple remove block track_->RippleRemoveBlock(adjacent_); - TakeNodeFromParentGraph(adjacent_, &memory_manager_); + adjacent_->setParent(&memory_manager_); we_deleted_adjacent_ = true; } else if (mode_ == Timeline::kTrimIn) { adjacent_->set_length_and_media_out(adjacent_length); @@ -965,7 +975,7 @@ void BlockTrimCommand::redo_internal() invalidate_range = TimeRange(block_->in(), block_->out()); } - track_->Node::InvalidateCache(invalidate_range, track_->block_input(), track_->block_input()); + track_->Node::InvalidateCache(invalidate_range, track_->block_input()); } void BlockTrimCommand::undo_internal() @@ -987,7 +997,7 @@ void BlockTrimCommand::undo_internal() if (we_created_adjacent_) { // If we created a gap, just remove it straight up track_->RippleRemoveBlock(adjacent_); - TakeNodeFromParentGraph(adjacent_, &memory_manager_); + adjacent_->setParent(&memory_manager_); adjacent_ = nullptr; we_created_adjacent_ = false; } else if (adjacent_) { @@ -999,7 +1009,7 @@ void BlockTrimCommand::undo_internal() // If trimmed LONGER, we adjusted an existing block // (assume if there's no adjacent, we're at the end of the timeline and do nothing) if (we_deleted_adjacent_) { - static_cast(track_->parent())->AddNode(adjacent_); + adjacent_->setParent(track_->parent()); if (mode_ == Timeline::kTrimIn) { track_->InsertBlockBefore(adjacent_, block_); @@ -1032,7 +1042,7 @@ void BlockTrimCommand::undo_internal() track_->EndOperation(); - track_->Node::InvalidateCache(invalidate_range, track_->block_input(), track_->block_input()); + track_->Node::InvalidateCache(invalidate_range, track_->block_input()); } TrackReplaceBlockWithGapCommand::TrackReplaceBlockWithGapCommand(TrackOutput *track, Block *block, QUndoCommand *command) : @@ -1074,7 +1084,7 @@ void TrackReplaceBlockWithGapCommand::redo_internal() existing_merged_gap_ = static_cast(next); new_gap_length += existing_merged_gap_->length(); track_->RippleRemoveBlock(existing_merged_gap_); - TakeNodeFromParentGraph(existing_merged_gap_, &memory_manager_); + existing_merged_gap_->setParent(&memory_manager_); } else if (previous_is_a_gap) { // Extend this gap to fill space left by block existing_gap_ = static_cast(previous); @@ -1094,7 +1104,7 @@ void TrackReplaceBlockWithGapCommand::redo_internal() // No gap exists to fill this space, create a new one and swap it in our_gap_ = new GapBlock(); our_gap_->set_length_and_media_out(new_gap_length); - static_cast(track_->parent())->AddNode(our_gap_); + our_gap_->setParent(track_->parent()); track_->ReplaceBlock(block_, our_gap_); } @@ -1105,7 +1115,7 @@ void TrackReplaceBlockWithGapCommand::redo_internal() Block* preceding = block_->previous(); if (preceding && preceding->type() == Block::kGap) { track_->RippleRemoveBlock(preceding); - TakeNodeFromParentGraph(preceding, &memory_manager_); + preceding->setParent(&memory_manager_); existing_merged_gap_ = static_cast(preceding); } @@ -1116,7 +1126,7 @@ void TrackReplaceBlockWithGapCommand::redo_internal() track_->EndOperation(); - track_->Node::InvalidateCache(invalidate_range, track_->block_input(), track_->block_input()); + track_->Node::InvalidateCache(invalidate_range, track_->block_input()); } void TrackReplaceBlockWithGapCommand::undo_internal() @@ -1127,7 +1137,7 @@ void TrackReplaceBlockWithGapCommand::undo_internal() // We made this gap, simply swap our gap back track_->ReplaceBlock(our_gap_, block_); - TakeNodeFromParentGraph(our_gap_, &memory_manager_); + our_gap_->setParent(&memory_manager_); our_gap_ = nullptr; } else if (existing_gap_) { @@ -1138,7 +1148,7 @@ void TrackReplaceBlockWithGapCommand::undo_internal() // If we merged two gaps together, restore the second one now if (existing_merged_gap_) { original_gap_length -= existing_merged_gap_->length(); - static_cast(track_->parent())->AddNode(existing_merged_gap_); + existing_merged_gap_->setParent(track_->parent()); track_->InsertBlockAfter(existing_merged_gap_, existing_gap_); existing_merged_gap_ = nullptr; } @@ -1162,7 +1172,7 @@ void TrackReplaceBlockWithGapCommand::undo_internal() // However, we may have removed an unnecessary gap that preceded it if (existing_merged_gap_) { - static_cast(track_->parent())->AddNode(existing_merged_gap_); + existing_merged_gap_->setParent(track_->parent()); track_->AppendBlock(existing_merged_gap_); existing_merged_gap_ = nullptr; } @@ -1174,7 +1184,7 @@ void TrackReplaceBlockWithGapCommand::undo_internal() track_->EndOperation(); - track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input(), track_->block_input()); + track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input()); } TrackSlideCommand::TrackSlideCommand(TrackOutput* track, const QList& moving_blocks, Block *in_adjacent, Block *out_adjacent, const rational& movement, QUndoCommand* parent) : @@ -1218,12 +1228,12 @@ void TrackSlideCommand::slide_internal(bool undo) if (we_created_in_adjacent_) { // This is a gap we made, we can just delete it entirely track_->RippleRemoveBlock(in_adjacent_); - TakeNodeFromParentGraph(in_adjacent_, &memory_manager_); + in_adjacent_->setParent(&memory_manager_); we_created_in_adjacent_ = false; in_adjacent_ = nullptr; } else if (in_adjacent_->parent() == &memory_manager_) { // This is a gap we removed, we can re-insert it now - static_cast(track_->parent())->AddNode(in_adjacent_); + in_adjacent_->setParent(track_->parent()); track_->InsertBlockBefore(in_adjacent_, blocks_.first()); } else { // We must have just resized this block @@ -1233,7 +1243,7 @@ void TrackSlideCommand::slide_internal(bool undo) if (we_created_out_adjacent_) { // This is a gap we made, we can just delete it entirely track_->RippleRemoveBlock(out_adjacent_); - TakeNodeFromParentGraph(out_adjacent_, &memory_manager_); + out_adjacent_->setParent(&memory_manager_); we_created_out_adjacent_ = false; out_adjacent_ = nullptr; } else if (out_adjacent_) { @@ -1241,7 +1251,7 @@ void TrackSlideCommand::slide_internal(bool undo) // to check here if (out_adjacent_->parent() == &memory_manager_) { // This is a gap we removed, we can re-insert it now - static_cast(track_->parent())->AddNode(out_adjacent_); + out_adjacent_->setParent(track_->parent()); track_->InsertBlockAfter(out_adjacent_, blocks_.last()); } else { // We must have just resized this block @@ -1257,14 +1267,14 @@ void TrackSlideCommand::slide_internal(bool undo) // be created GapBlock* gap = new GapBlock(); gap->set_length_and_media_out(movement_); - static_cast(track_->parent())->AddNode(gap); + gap->setParent(track_->parent()); track_->InsertBlockBefore(gap, blocks_.first()); we_created_in_adjacent_ = true; in_adjacent_ = gap; } else if (-movement_ == in_adjacent_->length()) { // Remove in adjacent entirely track_->RippleRemoveBlock(in_adjacent_); - TakeNodeFromParentGraph(in_adjacent_, &memory_manager_); + in_adjacent_->setParent(&memory_manager_); } else { // Resize in adjacent in_adjacent_->set_length_and_media_out(in_adjacent_->length() + movement_); @@ -1276,7 +1286,7 @@ void TrackSlideCommand::slide_internal(bool undo) if (blocks_.last()->next()) { GapBlock* gap = new GapBlock(); gap->set_length_and_media_out(-movement_); - static_cast(track_->parent())->AddNode(gap); + gap->setParent(track_->parent()); track_->InsertBlockAfter(gap, blocks_.last()); we_created_out_adjacent_ = true; out_adjacent_ = gap; @@ -1284,7 +1294,7 @@ void TrackSlideCommand::slide_internal(bool undo) } else if (movement_ == out_adjacent_->length()) { // Remove out adjacent entirely track_->RippleRemoveBlock(out_adjacent_); - TakeNodeFromParentGraph(out_adjacent_, &memory_manager_); + out_adjacent_->setParent(&memory_manager_); } else { // Resize out adjacent out_adjacent_->set_length_and_media_in(out_adjacent_->length() - movement_); @@ -1298,7 +1308,7 @@ void TrackSlideCommand::slide_internal(bool undo) invalidate_range.set_range(qMin(invalidate_range.in(), blocks_.first()->in()), qMax(invalidate_range.out(), blocks_.last()->out())); - track_->InvalidateCache(invalidate_range, track_->block_input(), track_->block_input()); + track_->Node::InvalidateCache(invalidate_range, track_->block_input()); } TrackListRippleRemoveAreaCommand::TrackListRippleRemoveAreaCommand(TrackList *list, rational in, rational out, QUndoCommand *parent) : @@ -1465,13 +1475,13 @@ void TrackListRippleToolCommand::redo_internal() // Assume the Block was a Gap and it was reduced to zero length, remove it here working_data_[i].removed_gap_after = b->previous(); info.track->RippleRemoveBlock(b); - TakeNodeFromParentGraph(b, &memory_manager_); + b->setParent(&memory_manager_); } } else if (info.new_length > 0) { // This is a gap we are creating GapBlock* gap = new GapBlock(); gap->set_length_and_media_out(info.new_length); - static_cast(info.ref_block->parent())->AddNode(gap); + gap->setParent(info.ref_block->parent()); working_data_[i].created_gap = gap; info.track->InsertBlockAfter(gap, info.ref_block); @@ -1508,7 +1518,6 @@ void TrackListRippleToolCommand::redo_internal() // FIXME: Untested, is this desirable behavior? if (earliest_pt < new_latest_pt) { info.track->InvalidateCache(TimeRange(earliest_pt, new_latest_pt), - info.track->block_input(), info.track->block_input()); } } @@ -1538,7 +1547,7 @@ void TrackListRippleToolCommand::undo_internal() // Assume the Block was a Gap and it was reduced to zero length, remove it here Block* previous_block = working_data_[i].removed_gap_after; - static_cast(info.track->parent())->AddNode(b); + b->setParent(info.track->parent()); if (previous_block) { info.track->InsertBlockAfter(b, previous_block); @@ -1551,7 +1560,7 @@ void TrackListRippleToolCommand::undo_internal() GapBlock* gap = working_data_.at(i).created_gap; info.track->RippleRemoveBlock(gap); - TakeNodeFromParentGraph(gap, &memory_manager_); + gap->setParent(&memory_manager_); } } } @@ -1627,7 +1636,7 @@ void TrackListInsertGaps::redo_internal() foreach (Block* block, blocks_to_append_gap_to) { GapBlock* gap = new GapBlock(); gap->set_length_and_media_out(length_); - static_cast(block->parent())->AddNode(gap); + gap->setParent(block->parent()); TrackOutput::TrackFromBlock(block)->InsertBlockAfter(gap, block); gaps_added_.append(gap); } @@ -1657,7 +1666,7 @@ void TrackListInsertGaps::undo_internal() // Remove added gaps foreach (GapBlock* gap, gaps_added_) { TrackOutput::TrackFromBlock(gap)->RippleRemoveBlock(gap); - TakeNodeFromParentGraph(gap, &memory_manager_); + gap->setParent(&memory_manager_); } gaps_added_.clear(); @@ -1712,18 +1721,18 @@ void TransitionRemoveCommand::redo_internal() } if (in_block_) { - NodeParam::DisconnectEdge(in_block_->output(), block_->in_block_input()); + Node::DisconnectEdge(in_block_, block_->in_block_input()); } if (out_block_) { - NodeParam::DisconnectEdge(out_block_->output(), block_->out_block_input()); + Node::DisconnectEdge(out_block_, block_->out_block_input()); } track_->RippleRemoveBlock(block_); track_->EndOperation(); - track_->InvalidateCache(invalidate_range, track_->block_input(), track_->block_input()); + track_->Node::InvalidateCache(invalidate_range, track_->block_input()); } void TransitionRemoveCommand::undo_internal() @@ -1737,11 +1746,11 @@ void TransitionRemoveCommand::undo_internal() } if (in_block_) { - NodeParam::ConnectEdge(in_block_->output(), block_->in_block_input()); + Node::ConnectEdge(in_block_, block_->in_block_input()); } if (out_block_) { - NodeParam::ConnectEdge(out_block_->output(), block_->out_block_input()); + Node::ConnectEdge(out_block_, block_->out_block_input()); } // These if statements must be separated because in_offset and out_offset report different things @@ -1757,7 +1766,7 @@ void TransitionRemoveCommand::undo_internal() track_->EndOperation(); - track_->InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input(), track_->block_input()); + track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input()); } TimelineSetSelectionsCommand::TimelineSetSelectionsCommand(TimelineWidget *timeline, const TimelineWidgetSelections &now, const TimelineWidgetSelections &old, QUndoCommand *parent) : diff --git a/app/widget/timelinewidget/undo/undo.h b/app/widget/timelinewidget/undo/undo.h index adb670e99..cbed95f2c 100644 --- a/app/widget/timelinewidget/undo/undo.h +++ b/app/widget/timelinewidget/undo/undo.h @@ -208,7 +208,7 @@ protected: QObject memory_manager_; - QList remove_block_commands_; + QVector remove_block_commands_; }; @@ -312,7 +312,8 @@ private: int track_index_; bool append_; GapBlock* gap_; - int added_track_count_; + QVector added_tracks_; + }; class BlockSplitCommand : public UndoCommand { diff --git a/app/widget/timetarget/timetarget.cpp b/app/widget/timetarget/timetarget.cpp index eb8830bd6..38d2c447a 100644 --- a/app/widget/timetarget/timetarget.cpp +++ b/app/widget/timetarget/timetarget.cpp @@ -45,22 +45,22 @@ void TimeTargetObject::SetPathIndex(int index) path_index_ = index; } -rational TimeTargetObject::GetAdjustedTime(Node* from, Node* to, const rational &r, NodeParam::Type direction) const +rational TimeTargetObject::GetAdjustedTime(Node* from, Node* to, const rational &r, bool input_direction) const { if (!from || !to) { return r; } - return GetAdjustedTime(from, to, TimeRange(r, r), direction).in(); + return GetAdjustedTime(from, to, TimeRange(r, r), input_direction).in(); } -TimeRange TimeTargetObject::GetAdjustedTime(Node* from, Node* to, const TimeRange &r, NodeParam::Type direction) const +TimeRange TimeTargetObject::GetAdjustedTime(Node* from, Node* to, const TimeRange &r, bool input_direction) const { if (!from || !to) { return r; } - QVector adjusted = from->TransformTimeTo(r, to, direction); + QVector adjusted = from->TransformTimeTo(r, to, input_direction); if (adjusted.isEmpty()) { return r; diff --git a/app/widget/timetarget/timetarget.h b/app/widget/timetarget/timetarget.h index 5826bfd61..3c9935094 100644 --- a/app/widget/timetarget/timetarget.h +++ b/app/widget/timetarget/timetarget.h @@ -35,8 +35,8 @@ public: void SetPathIndex(int index); - rational GetAdjustedTime(Node* from, Node* to, const rational& r, NodeParam::Type direction) const; - TimeRange GetAdjustedTime(Node* from, Node* to, const TimeRange& r, NodeParam::Type direction) const; + rational GetAdjustedTime(Node* from, Node* to, const rational& r, bool input_direction) const; + TimeRange GetAdjustedTime(Node* from, Node* to, const TimeRange& r, bool input_direction) const; //int GetNumberOfPathAdjustments(Node* from, NodeParam::Type direction) const; diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index b8328fc78..0ee3818b5 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -33,10 +33,10 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) : footage_(nullptr) { video_node_ = new MediaInput(); - sequence_.AddNode(video_node_); + video_node_->setParent(&sequence_); audio_node_ = new MediaInput(); - sequence_.AddNode(audio_node_); + audio_node_->setParent(&sequence_); connect(display_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag); @@ -60,8 +60,8 @@ void FootageViewerWidget::SetFootage(Footage *footage) video_node_->SetStream(nullptr); audio_node_->SetStream(nullptr); - NodeParam::DisconnectEdge(video_node_->output(), sequence_.viewer_output()->texture_input()); - NodeParam::DisconnectEdge(audio_node_->output(), sequence_.viewer_output()->samples_input()); + Node::DisconnectEdge(video_node_, sequence_.viewer_output()->texture_input()); + Node::DisconnectEdge(audio_node_, sequence_.viewer_output()->samples_input()); } footage_ = footage; @@ -98,12 +98,12 @@ void FootageViewerWidget::SetFootage(Footage *footage) if (video_stream) { video_node_->SetStream(video_stream); - NodeParam::ConnectEdge(video_node_->output(), sequence_.viewer_output()->texture_input()); + Node::ConnectEdge(video_node_, sequence_.viewer_output()->texture_input()); } if (audio_stream) { audio_node_->SetStream(audio_stream); - NodeParam::ConnectEdge(audio_node_->output(), sequence_.viewer_output()->samples_input()); + Node::ConnectEdge(audio_node_, sequence_.viewer_output()->samples_input()); } ConnectViewerNode(sequence_.viewer_output(), footage_->project()->color_manager()); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 722a7d13a..2f18aae2d 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -178,7 +178,8 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) connect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters); connect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange); connect(n->video_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange); - connect(n, &ViewerOutput::GraphChangedFrom, this, &ViewerWidget::UpdateStack); + connect(n->texture_input(), &NodeInput::InputConnected, this, &ViewerWidget::UpdateStack); + connect(n->texture_input(), &NodeInput::InputDisconnected, this, &ViewerWidget::UpdateStack); InterlacingChangedSlot(n->video_params().interlacing()); @@ -232,7 +233,8 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) disconnect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters); disconnect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange); disconnect(n->video_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange); - disconnect(n, &ViewerOutput::GraphChangedFrom, this, &ViewerWidget::UpdateStack); + disconnect(n->texture_input(), &NodeInput::InputConnected, this, &ViewerWidget::UpdateStack); + disconnect(n->texture_input(), &NodeInput::InputDisconnected, this, &ViewerWidget::UpdateStack); ruler()->SetPlaybackCache(nullptr); @@ -396,8 +398,8 @@ void ViewerWidget::DecodeCachedImage(RenderTicketPtr ticket, const QString &fn, bool ViewerWidget::ShouldForceWaveform() const { return GetConnectedNode() - && !GetConnectedNode()->texture_input()->is_connected() - && GetConnectedNode()->samples_input()->is_connected(); + && !GetConnectedNode()->texture_input()->IsConnected() + && GetConnectedNode()->samples_input()->IsConnected(); } void ViewerWidget::UpdateTextureFromNode(const rational& time) diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index f2ca65a63..e246453a8 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -280,8 +280,8 @@ void ViewerDisplayWidget::OnPaint() } ShaderJob job; - job.InsertValue(QStringLiteral("resolution_in"), ShaderValue(QVector2D(texture_to_draw->width(), texture_to_draw->height()), NodeParam::kVec2)); - job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(texture_to_draw), NodeParam::kTexture)); + job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture_to_draw->width(), texture_to_draw->height()))); + job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_to_draw))); renderer()->BlitToTexture(deinterlace_shader_, job, deinterlace_texture_.get()); @@ -375,7 +375,7 @@ QPointF ViewerDisplayWidget::GetTexturePosition(const double &x, const double &y rational ViewerDisplayWidget::GetGizmoTime() { - return GetAdjustedTime(GetTimeTarget(), gizmos_, time_, NodeParam::kInput); + return GetAdjustedTime(GetTimeTarget(), gizmos_, time_, true); } bool ViewerDisplayWidget::IsHandDrag(QMouseEvent *event) const