diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index acb7625c7..ba27072e0 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -220,6 +220,8 @@ QString FFmpegDecoder::id() Footage *FFmpegDecoder::Probe(const QString& filename, const QAtomicInt* cancelled) const { + Q_UNUSED(cancelled) + // Variable for receiving errors from FFmpeg int error_code; diff --git a/app/common/xmlutils.cpp b/app/common/xmlutils.cpp index e243ffcf7..cfc5042ed 100644 --- a/app/common/xmlutils.cpp +++ b/app/common/xmlutils.cpp @@ -29,13 +29,13 @@ namespace olive { void XMLConnectNodes(const XMLNodeData &xml_node_data, MultiUndoCommand *command) { foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { - Node* out = xml_node_data.node_ptrs.value(con.output); + NodeOutput out(xml_node_data.node_ptrs.value(con.output_node), con.output); - if (out) { + if (out.IsValid()) { if (command) { - command->add_child(new NodeEdgeAddCommand(out, con.input, con.element)); + command->add_child(new NodeEdgeAddCommand(out, con.input)); } else { - Node::ConnectEdge(out, con.input, con.element); + Node::ConnectEdge(out, con.input); } } } diff --git a/app/common/xmlutils.h b/app/common/xmlutils.h index ea8f43475..20c1585cc 100644 --- a/app/common/xmlutils.h +++ b/app/common/xmlutils.h @@ -23,6 +23,7 @@ #include +#include "node/param.h" #include "project/item/footage/stream.h" #include "undo/undocommand.h" @@ -38,15 +39,9 @@ class NodeInput; struct XMLNodeData { struct SerializedConnection { - NodeInput* input; - int element; - quintptr output; - }; - - struct FootageConnection { - NodeInput* input; - int element; - quintptr footage; + NodeInput input; + quintptr output_node; + QString output; }; struct BlockLink { @@ -56,8 +51,6 @@ struct XMLNodeData { QHash node_ptrs; QList desired_connections; - QHash footage_ptrs; - QList footage_connections; QList block_links; QHash item_ptrs; diff --git a/app/core.cpp b/app/core.cpp index 293aaaf26..b6bd2009e 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -892,6 +892,8 @@ QString Core::GetProjectFilter(bool include_any_filter) if (include_any_filter) { filters.append(QStringLiteral("All Supported Projects (*.ove *.otio);;")); } +#else + Q_UNUSED(include_any_filter) #endif // Append standard filter diff --git a/app/dialog/export/codec/codecsection.h b/app/dialog/export/codec/codecsection.h index 82f70b69e..807eab9af 100644 --- a/app/dialog/export/codec/codecsection.h +++ b/app/dialog/export/codec/codecsection.h @@ -33,7 +33,7 @@ class CodecSection : public QWidget public: CodecSection(QWidget* parent = nullptr); - virtual void AddOpts(EncodingParams* params){} + virtual void AddOpts(EncodingParams* params){Q_UNUSED(params)} }; diff --git a/app/dialog/sequence/sequencedialogpresettab.cpp b/app/dialog/sequence/sequencedialogpresettab.cpp index a57852b64..a75e35479 100644 --- a/app/dialog/sequence/sequencedialogpresettab.cpp +++ b/app/dialog/sequence/sequencedialogpresettab.cpp @@ -31,7 +31,6 @@ #include "common/filefunctions.h" #include "config/config.h" -#include "node/input.h" #include "render/videoparams.h" #include "ui/icons/icons.h" #include "widget/menu/menu.h" diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 70b19a506..640b4466c 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -25,28 +25,28 @@ add_subdirectory(output) set(OLIVE_SOURCES ${OLIVE_SOURCES} - node/connectable.cpp - node/connectable.h - node/factory.h node/factory.cpp - node/graph.h + node/factory.h node/graph.cpp - node/input.h - node/input.cpp - node/inputdragger.h + node/graph.h node/inputdragger.cpp - node/inputimmediate.h + node/inputdragger.h node/inputimmediate.cpp - node/keyframe.h + node/inputimmediate.h node/keyframe.cpp - node/node.h + node/keyframe.h node/node.cpp - node/nodecopypaste.h + node/node.h node/nodecopypaste.cpp + node/nodecopypaste.h + node/param.cpp + node/param.h node/splitvalue.h - node/traverser.h node/traverser.cpp - node/value.h + node/traverser.h node/value.cpp + node/value.h + node/valuedatabase.cpp + node/valuedatabase.h PARENT_SCOPE ) diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 19ada088b..ab8256df2 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -24,14 +24,17 @@ namespace olive { +const QString PanNode::kSamplesInput = QStringLiteral("samples_in"); +const QString PanNode::kPanningInput = QStringLiteral("panning_in"); + PanNode::PanNode() { - samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples); + AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); - 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", FloatSlider::kPercentage); + AddInput(kPanningInput, NodeValue::kFloat, 0.0); + SetInputProperty(kPanningInput, QStringLiteral("min"), -1.0); + SetInputProperty(kPanningInput, QStringLiteral("max"), 1.0); + SetInputProperty(kPanningInput, QStringLiteral("view"), FloatSlider::kPercentage); } Node *PanNode::copy() const @@ -59,18 +62,20 @@ QString PanNode::Description() const return tr("Adjust the stereo panning of an audio source."); } -NodeValueTable PanNode::Value(NodeValueDatabase &value) const +NodeValueTable PanNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // Create a sample job - SampleJob job(samples_input_, value); - job.InsertValue(panning_input_, value); + SampleJob job(kSamplesInput, value); + job.InsertValue(this, kPanningInput, value); // Push it to our table NodeValueTable table = value.Merge(); if (job.HasSamples()) { - float pan_volume = job.GetValue(panning_input_).data().toFloat(); - if (panning_input_->IsStatic()) { + float pan_volume = job.GetValue(kPanningInput).data().toFloat(); + if (IsInputStatic(kPanningInput)) { 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); @@ -95,7 +100,7 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in return; } - float pan_val = values[panning_input_].Get(NodeValue::kFloat).toFloat(); + float pan_val = values[kPanningInput].Get(NodeValue::kFloat).toFloat(); for (int i=0;iaudio_params().channel_count();i++) { output->data()[i][index] = input->data()[i][index]; @@ -110,8 +115,8 @@ void PanNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr in void PanNode::Retranslate() { - samples_input_->set_name(tr("Samples")); - panning_input_->set_name(tr("Pan")); + SetInputName(kSamplesInput, tr("Samples")); + SetInputName(kPanningInput, tr("Pan")); } } diff --git a/app/node/audio/pan/pan.h b/app/node/audio/pan/pan.h index e6298fc84..f8262c1e8 100644 --- a/app/node/audio/pan/pan.h +++ b/app/node/audio/pan/pan.h @@ -38,12 +38,15 @@ public: virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override; virtual void Retranslate() override; + static const QString kSamplesInput; + static const QString kPanningInput; + private: NodeInput* samples_input_; NodeInput* panning_input_; diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 8172fdfaa..c84e03dc9 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -24,14 +24,16 @@ namespace olive { +const QString VolumeNode::kSamplesInput = QStringLiteral("samples_in"); +const QString VolumeNode::kVolumeInput = QStringLiteral("volume_in"); + VolumeNode::VolumeNode() { - samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples); - samples_input_->SetKeyframable(false); + AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); - volume_input_ = new NodeInput(this, QStringLiteral("volume_in"), NodeValue::kFloat, 1.0); - volume_input_->setProperty("min", 0.0); - volume_input_->setProperty("view", FloatSlider::kDecibel); + AddInput(kVolumeInput, NodeValue::kFloat, 1.0); + SetInputProperty(kVolumeInput, QStringLiteral("min"), 0.0); + SetInputProperty(kVolumeInput, QStringLiteral("view"), FloatSlider::kDecibel); } Node *VolumeNode::copy() const @@ -59,26 +61,28 @@ QString VolumeNode::Description() const return tr("Adjusts the volume of an audio source."); } -NodeValueTable VolumeNode::Value(NodeValueDatabase &value) const +NodeValueTable VolumeNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + return ValueInternal(value, kOpMultiply, kPairSampleNumber, - samples_input_, - value[samples_input_].TakeWithMeta(NodeValue::kSamples), - volume_input_, - value[volume_input_].TakeWithMeta(NodeValue::kFloat)); + kSamplesInput, + value[kSamplesInput].TakeWithMeta(NodeValue::kSamples), + kVolumeInput, + value[kVolumeInput].TakeWithMeta(NodeValue::kFloat)); } void VolumeNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const { - return ProcessSamplesInternal(values, kOpMultiply, samples_input_, volume_input_, input, output, index); + return ProcessSamplesInternal(values, kOpMultiply, kSamplesInput, kVolumeInput, input, output, index); } void VolumeNode::Retranslate() { - samples_input_->set_name(tr("Samples")); - volume_input_->set_name(tr("Volume")); + SetInputName(kSamplesInput, tr("Samples")); + SetInputName(kVolumeInput, tr("Volume")); } } diff --git a/app/node/audio/volume/volume.h b/app/node/audio/volume/volume.h index a45aeb340..368a914b0 100644 --- a/app/node/audio/volume/volume.h +++ b/app/node/audio/volume/volume.h @@ -38,20 +38,14 @@ public: virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override; virtual void Retranslate() override; - NodeInput* samples_input() const - { - return samples_input_; - } - -private: - NodeInput* samples_input_; - NodeInput* volume_input_; + static const QString kSamplesInput; + static const QString kVolumeInput; }; diff --git a/app/node/block/block.cpp b/app/node/block/block.cpp index 2787d0974..c07e5ebba 100644 --- a/app/node/block/block.cpp +++ b/app/node/block/block.cpp @@ -28,6 +28,11 @@ namespace olive { +const QString Block::kLengthInput = QStringLiteral("length_in"); +const QString Block::kMediaInInput = QStringLiteral("media_in_in"); +const QString Block::kEnabledInput = QStringLiteral("enabled_in"); +const QString Block::kSpeedInput = QStringLiteral("speed_in"); + Block::Block() : previous_(nullptr), next_(nullptr), @@ -36,24 +41,18 @@ Block::Block() : in_transition_(nullptr), out_transition_(nullptr) { - length_input_ = new NodeInput(this, QStringLiteral("length_in"), NodeValue::kRational); - length_input_->SetConnectable(false); - length_input_->SetKeyframable(false); - IgnoreInvalidationsFrom(length_input_); - connect(length_input_, &NodeInput::ValueChanged, this, &Block::LengthChanged); + AddInput(kLengthInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + IgnoreInvalidationsFrom(kLengthInput); + IgnoreHashingFrom(kLengthInput); - media_in_input_ = new NodeInput(this, QStringLiteral("media_in_in"), NodeValue::kRational); - media_in_input_->SetConnectable(false); - media_in_input_->SetKeyframable(false); + AddInput(kMediaInInput, NodeValue::kRational, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + IgnoreHashingFrom(kMediaInInput); - enabled_input_ = new NodeInput(this, QStringLiteral("enabled_in"), NodeValue::kBoolean); - enabled_input_->SetConnectable(false); - enabled_input_->SetKeyframable(false); - enabled_input_->SetStandardValue(true); + AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - speed_input_ = new NodeInput(this, QStringLiteral("speed_in"), NodeValue::kFloat); - speed_input_->SetStandardValue(1.0); - speed_input_->setProperty("view", FloatSlider::kPercentage); + AddInput(kSpeedInput, NodeValue::kFloat, 1.0); + SetInputProperty(kSpeedInput, QStringLiteral("view"), FloatSlider::kPercentage); + IgnoreHashingFrom(kSpeedInput); // A block's length must be greater than 0 set_length_and_media_out(1); @@ -66,7 +65,7 @@ QVector Block::Category() const rational Block::length() const { - return length_input_->GetStandardValue().value(); + return GetStandardValue(kLengthInput).value(); } void Block::set_length_and_media_out(const rational &length) @@ -97,22 +96,22 @@ void Block::set_length_and_media_in(const rational &length) rational Block::media_in() const { - return media_in_input_->GetStandardValue().value(); + return GetStandardValue(kMediaInInput).value(); } void Block::set_media_in(const rational &media_in) { - media_in_input_->SetStandardValue(QVariant::fromValue(media_in)); + SetStandardValue(kMediaInInput, QVariant::fromValue(media_in)); } bool Block::is_enabled() const { - return enabled_input_->GetStandardValue().toBool(); + return GetStandardValue(kEnabledInput).toBool(); } void Block::set_enabled(bool e) { - enabled_input_->SetStandardValue(e); + SetStandardValue(kEnabledInput, e); emit EnabledChanged(); } @@ -127,10 +126,8 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const rational local_time = sequence_time; // FIXME: Doesn't handle reversing - if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) { - // FIXME: We'll need to calculate the speed hoo boy - } else { - double speed_value = speed_input_->GetStandardValue().toDouble(); + if (IsInputStatic(kSpeedInput)) { + double speed_value = GetStandardValue(kSpeedInput).toDouble(); if (qIsNull(speed_value)) { // Effectively holds the frame at the in point @@ -139,6 +136,8 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const // Multiply time local_time = rational::fromDouble(local_time.toDouble() * speed_value); } + } else { + // FIXME: We'll need to calculate the speed hoo boy } return local_time + media_in(); @@ -154,10 +153,10 @@ rational Block::MediaToSequenceTime(const rational &media_time) const rational sequence_time = media_time - media_in(); // FIXME: Doesn't handle reversing - if (speed_input_->IsKeyframing() || speed_input_->IsConnected()) { + if (IsInputKeyframing(kSpeedInput) || IsInputConnected(kSpeedInput)) { // FIXME: We'll need to calculate the speed hoo boy } else { - double speed_value = speed_input_->GetStandardValue().toDouble(); + double speed_value = GetStandardValue(kSpeedInput).toDouble(); if (qIsNull(speed_value)) { // Effectively holds the frame at the in point, also prevents divide by zero @@ -171,16 +170,15 @@ rational Block::MediaToSequenceTime(const rational &media_time) const return sequence_time; } -QVector Block::GetInputsToHash() const +void Block::InputValueChangedEvent(const QString &input, int element) { - QVector inputs = Node::GetInputsToHash(); + Q_UNUSED(element) - // Ignore these inputs - inputs.removeOne(media_in_input_); - inputs.removeOne(speed_input_); - inputs.removeOne(length_input_); - - return inputs; + if (input == kLengthInput) { + emit LengthChanged(); + } else if (input == kEnabledInput) { + emit EnabledChanged(); + } } void Block::LinkChangeEvent() @@ -198,17 +196,17 @@ void Block::LinkChangeEvent() void Block::set_length_internal(const rational &length) { - length_input_->SetStandardValue(QVariant::fromValue(length)); + SetStandardValue(kLengthInput, QVariant::fromValue(length)); } void Block::Retranslate() { Node::Retranslate(); - length_input_->set_name(tr("Length")); - media_in_input_->set_name(tr("Media In")); - enabled_input_->set_name(tr("Enabled")); - speed_input_->set_name(tr("Speed")); + SetInputName(kLengthInput, tr("Length")); + SetInputName(kMediaInInput, tr("Media In")); + SetInputName(kEnabledInput, tr("Enabled")); + SetInputName(kSpeedInput, tr("Speed")); } void Block::Hash(QCryptographicHash &, const rational &) const diff --git a/app/node/block/block.h b/app/node/block/block.h index f6866dfd6..8c5e883b9 100644 --- a/app/node/block/block.h +++ b/app/node/block/block.h @@ -114,21 +114,6 @@ public: virtual void Retranslate() override; - NodeInput* length_input() const - { - return length_input_; - } - - NodeInput* media_in_input() const - { - return media_in_input_; - } - - NodeInput* speed_input() const - { - return speed_input_; - } - TransitionBlock* in_transition() { return in_transition_; @@ -166,6 +151,11 @@ public: virtual void Hash(QCryptographicHash &hash, const rational &time) const override; + static const QString kLengthInput; + static const QString kMediaInInput; + static const QString kEnabledInput; + static const QString kSpeedInput; + public slots: signals: @@ -178,7 +168,7 @@ protected: rational MediaToSequenceTime(const rational& media_time) const; - virtual QVector GetInputsToHash() const override; + virtual void InputValueChangedEvent(const QString& input, int element) override; virtual void LinkChangeEvent() override; @@ -188,11 +178,6 @@ protected: private: void set_length_internal(const rational &length); - NodeInput* length_input_; - NodeInput* media_in_input_; - NodeInput* speed_input_; - NodeInput* enabled_input_; - rational in_point_; rational out_point_; Track* track_; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 41d0a81de..efb598988 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -22,10 +22,11 @@ namespace olive { +const QString ClipBlock::kBufferIn = QStringLiteral("buffer_in"); + ClipBlock::ClipBlock() { - texture_input_ = new NodeInput(this, QStringLiteral("buffer_in"), NodeValue::kNone); - texture_input_->SetKeyframable(false); + AddInput(kBufferIn, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); } Node *ClipBlock::copy() const @@ -53,15 +54,12 @@ QString ClipBlock::Description() const return tr("A time-based node that represents a media source."); } -NodeInput *ClipBlock::texture_input() const +void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element) { - return texture_input_; -} + Q_UNUSED(element) -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.input == texture_input_) { + if (from == kBufferIn) { // Adjust range from media time to sequence time rational start = MediaToSequenceTime(range.in()); rational end = MediaToSequenceTime(range.out()); @@ -73,32 +71,34 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const InputConnection& f } } -TimeRange ClipBlock::InputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const +TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { Q_UNUSED(element) - if (input == texture_input_) { + if (input == kBufferIn) { return TimeRange(SequenceToMediaTime(input_time.in()), SequenceToMediaTime(input_time.out())); } return Block::InputTimeAdjustment(input, element, input_time); } -TimeRange ClipBlock::OutputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const +TimeRange ClipBlock::OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { Q_UNUSED(element) - if (input == texture_input_) { + if (input == kBufferIn) { return TimeRange(MediaToSequenceTime(input_time.in()), MediaToSequenceTime(input_time.out())); } return Block::OutputTimeAdjustment(input, element, input_time); } -NodeValueTable ClipBlock::Value(NodeValueDatabase &value) const +NodeValueTable ClipBlock::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // We discard most values here except for the buffer we received - NodeValue data = value[texture_input()].GetWithMeta(NodeValue::kBuffer); + NodeValue data = value[kBufferIn].GetWithMeta(NodeValue::kBuffer); NodeValueTable table; if (data.type() != NodeValue::kNone) { @@ -111,15 +111,15 @@ void ClipBlock::Retranslate() { Block::Retranslate(); - texture_input_->set_name(tr("Buffer")); + SetInputName(kBufferIn, tr("Buffer")); } void ClipBlock::Hash(QCryptographicHash &hash, const rational &time) const { - if (texture_input_->IsConnected()) { - rational t = InputTimeAdjustment(texture_input_, -1, TimeRange(time, time)).in(); + if (IsInputConnected(kBufferIn)) { + rational t = InputTimeAdjustment(kBufferIn, -1, TimeRange(time, time)).in(); - texture_input_->GetConnectedNode()->Hash(hash, t); + GetConnectedNode(kBufferIn)->Hash(hash, t); } } diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index 5ed962e92..16b022c64 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -42,22 +42,19 @@ public: virtual QString id() const override; virtual QString Description() const override; - NodeInput* texture_input() const; + virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override; - virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; - virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override; + virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; - virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override; - - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual void Retranslate() override; virtual void Hash(QCryptographicHash &hash, const rational &time) const override; -private: - NodeInput* texture_input_; + static const QString kBufferIn; }; diff --git a/app/node/block/gap/gap.h b/app/node/block/gap/gap.h index 17856c039..0560dcb37 100644 --- a/app/node/block/gap/gap.h +++ b/app/node/block/gap/gap.h @@ -42,8 +42,6 @@ public: virtual QString id() const override; virtual QString Description() const override; -private: - }; } diff --git a/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp b/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp index c37ac9715..55f823e5b 100644 --- a/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp +++ b/app/node/block/transition/crossdissolve/crossdissolvetransition.cpp @@ -61,6 +61,8 @@ ShaderCode CrossDissolveTransition::GetShaderCode(const QString &shader_id) cons void CrossDissolveTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) const { + Q_UNUSED(value) + job.SetAlphaChannelRequired(true); } diff --git a/app/node/block/transition/diptocolor/diptocolortransition.cpp b/app/node/block/transition/diptocolor/diptocolortransition.cpp index cbad89c2a..36ab23611 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.cpp +++ b/app/node/block/transition/diptocolor/diptocolortransition.cpp @@ -22,9 +22,11 @@ namespace olive { +const QString DipToColorTransition::kColorInput = QStringLiteral("color_in"); + DipToColorTransition::DipToColorTransition() { - color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(0, 0, 0))); } Node *DipToColorTransition::copy() const @@ -61,7 +63,7 @@ ShaderCode DipToColorTransition::GetShaderCode(const QString &shader_id) const void DipToColorTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) const { - job.InsertValue(color_input_, value); + job.InsertValue(this, kColorInput, value); } } diff --git a/app/node/block/transition/diptocolor/diptocolortransition.h b/app/node/block/transition/diptocolor/diptocolortransition.h index 0f54b272b..cac7a528a 100644 --- a/app/node/block/transition/diptocolor/diptocolortransition.h +++ b/app/node/block/transition/diptocolor/diptocolortransition.h @@ -40,12 +40,11 @@ public: virtual ShaderCode GetShaderCode(const QString& shader_id) const override; + static const QString kColorInput; + protected: virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const override; -private: - NodeInput* color_input_; - }; } diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index 4f80f987d..822b84192 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -24,23 +24,19 @@ namespace olive { +const QString TransitionBlock::kOutBlockInput = QStringLiteral("out_block_in"); +const QString TransitionBlock::kInBlockInput = QStringLiteral("in_block_in"); +const QString TransitionBlock::kCurveInput = QStringLiteral("curve_in"); + TransitionBlock::TransitionBlock() : connected_out_block_(nullptr), connected_in_block_(nullptr) { - 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); + AddInput(kOutBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); - 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); + AddInput(kInBlockInput, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable)); - curve_input_ = new NodeInput(this, QStringLiteral("curve_in"), NodeValue::kCombo); - curve_input_->SetKeyframable(false); - curve_input_->SetConnectable(false); + AddInput(kCurveInput, NodeValue::kCombo, InputFlags(kInputFlagNotKeyframable | kInputFlagNotConnectable)); } Block::Type TransitionBlock::type() const @@ -48,26 +44,16 @@ Block::Type TransitionBlock::type() const return kTransition; } -NodeInput *TransitionBlock::out_block_input() const -{ - return out_block_input_; -} - -NodeInput *TransitionBlock::in_block_input() const -{ - return in_block_input_; -} - void TransitionBlock::Retranslate() { Block::Retranslate(); - out_block_input_->set_name(tr("From")); - in_block_input_->set_name(tr("To")); - curve_input_->set_name(tr("Curve")); + SetInputName(kOutBlockInput, tr("From")); + SetInputName(kInBlockInput, tr("To")); + SetInputName(kCurveInput, tr("Curve")); // These must correspond to the CurveType enum - curve_input_->set_combobox_strings({ tr("Linear"), tr("Exponential"), tr("Logarithmic") }); + SetComboBoxStrings(kCurveInput, { tr("Linear"), tr("Exponential"), tr("Logarithmic") }); } rational TransitionBlock::in_offset() const @@ -169,56 +155,16 @@ void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &t NodeValue(NodeValue::kFloat, GetInProgress(time), this)); } -void TransitionBlock::OutBlockConnected(Node *node) +NodeValueTable TransitionBlock::Value(const QString &output, NodeValueDatabase &value) const { - // If node is not a block, this will just be null - if ((connected_out_block_ = dynamic_cast(node))) { + Q_UNUSED(output) - Q_ASSERT(connected_out_block_->type() != Block::kTransition - && !connected_out_block_->out_transition() - && connected_out_block_ == this->previous()); - - connected_out_block_->set_out_transition(this); - } -} - -void TransitionBlock::OutBlockDisconnected() -{ - if (connected_out_block_) { - connected_out_block_->set_in_transition(nullptr); - connected_out_block_ = nullptr; - } -} - -void TransitionBlock::InBlockConnected(Node *node) -{ - // If node is not a block, this will just be null - if ((connected_in_block_ = dynamic_cast(node))) { - - Q_ASSERT(connected_in_block_->type() != Block::kTransition - && !connected_in_block_->in_transition() - && connected_in_block_ == this->next()); - - connected_in_block_->set_in_transition(this); - } -} - -void TransitionBlock::InBlockDisconnected() -{ - if (connected_in_block_) { - connected_in_block_->set_in_transition(nullptr); - connected_in_block_ = nullptr; - } -} - -NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const -{ NodeValue::Type data_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(); + if (IsInputConnected(kOutBlockInput)) { + data_type = value[kOutBlockInput].GetWithMeta(NodeValue::kBuffer).type(); + } else if (IsInputConnected(kInBlockInput)) { + data_type = value[kInBlockInput].GetWithMeta(NodeValue::kBuffer).type(); } else { data_type = NodeValue::kNone; } @@ -230,9 +176,9 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const // This must be a visual transition ShaderJob job; - job.InsertValue(out_block_input(), value); - job.InsertValue(in_block_input(), value); - job.InsertValue(curve_input_, value); + job.InsertValue(this, kOutBlockInput, value); + job.InsertValue(this, kInBlockInput, value); + job.InsertValue(this, kCurveInput, value); double time = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble(); InsertTransitionTimes(&job, time); @@ -243,8 +189,8 @@ NodeValueTable TransitionBlock::Value(NodeValueDatabase &value) const push_job = QVariant::fromValue(job); } else if (data_type == NodeValue::kSamples) { // This must be an audio transition - SampleBufferPtr from_samples = value[out_block_input()].Take(NodeValue::kSamples).value(); - SampleBufferPtr to_samples = value[in_block_input()].Take(NodeValue::kSamples).value(); + SampleBufferPtr from_samples = value[kOutBlockInput].Take(NodeValue::kSamples).value(); + SampleBufferPtr to_samples = value[kInBlockInput].Take(NodeValue::kSamples).value(); if (from_samples || to_samples) { double time_in = value[QStringLiteral("global")].Get(NodeValue::kFloat, QStringLiteral("time_in")).toDouble(); @@ -287,7 +233,7 @@ void TransitionBlock::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferP double TransitionBlock::TransformCurve(double linear) const { - switch (static_cast(curve_input_->GetStandardValue().toInt())) { + switch (static_cast(GetStandardValue(kCurveInput).toInt())) { case kLinear: break; case kExponential: @@ -301,4 +247,49 @@ double TransitionBlock::TransformCurve(double linear) const return linear; } +void TransitionBlock::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + Q_UNUSED(element) + + if (input == kOutBlockInput) { + // If node is not a block, this will just be null + if ((connected_out_block_ = dynamic_cast(output.node()))) { + + Q_ASSERT(connected_out_block_->type() != Block::kTransition + && !connected_out_block_->out_transition() + && connected_out_block_ == this->previous()); + + connected_out_block_->set_out_transition(this); + } + } else if (input == kInBlockInput) { + // If node is not a block, this will just be null + if ((connected_in_block_ = dynamic_cast(output.node()))) { + + Q_ASSERT(connected_in_block_->type() != Block::kTransition + && !connected_in_block_->in_transition() + && connected_in_block_ == this->next()); + + connected_in_block_->set_in_transition(this); + } + } +} + +void TransitionBlock::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + Q_UNUSED(element) + Q_UNUSED(output) + + if (input == kOutBlockInput) { + if (connected_out_block_) { + connected_out_block_->set_in_transition(nullptr); + connected_out_block_ = nullptr; + } + } else if (input == kInBlockInput) { + if (connected_in_block_) { + connected_in_block_->set_in_transition(nullptr); + connected_in_block_ = nullptr; + } + } +} + } diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index af1b230e3..a02b3d7c1 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -33,9 +33,6 @@ public: virtual Type type() const override; - NodeInput* out_block_input() const; - NodeInput* in_block_input() const; - virtual void Retranslate() override; rational in_offset() const; @@ -50,7 +47,11 @@ public: virtual void Hash(QCryptographicHash& hash, const rational &time) const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; + + static const QString kOutBlockInput; + static const QString kInBlockInput; + static const QString kCurveInput; protected: virtual void ShaderJobEvent(NodeValueDatabase &value, ShaderJob& job) const; @@ -59,6 +60,10 @@ protected: double TransformCurve(double linear) const; + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output) override; + + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output) override; + private: enum CurveType { kLinear, @@ -70,25 +75,10 @@ private: void InsertTransitionTimes(AcceleratedJob* job, const double& time) const; - NodeInput* out_block_input_; - - NodeInput* in_block_input_; - - NodeInput* curve_input_; - Block* connected_out_block_; Block* connected_in_block_; -private slots: - void OutBlockConnected(Node* node); - - void OutBlockDisconnected(); - - void InBlockConnected(Node* node); - - void InBlockDisconnected(); - }; } diff --git a/app/node/connectable.cpp b/app/node/connectable.cpp deleted file mode 100644 index 7fed464b1..000000000 --- a/app/node/connectable.cpp +++ /dev/null @@ -1,77 +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 "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 (std::find(output->output_connections_.begin(), output->output_connections_.end(), conn_to_in) != output->output_connections_.end()) { - qDebug() << "Ignored connect that already exists:" << output << input->parent() << input->id() << element; - return; - } - - // Ensure a connection isn't getting overwritten - Q_ASSERT(input->input_connections_.find(element) == input->input_connections_.end()); - - // Insert connections in both sides - output->output_connections_.push_back(conn_to_in); - input->input_connections_[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 (std::find(output->output_connections_.begin(), output->output_connections_.end(), conn_to_in) == output->output_connections_.end()) { - qDebug() << "Ignored disconnect that doesn't exist:" << output << input->parent() << input->id() << element; - return; - } - - // Assertions to ensure connection exists - Q_ASSERT(input->input_connections_.at(element) == output); - - // Remove connections from both sides - output->output_connections_.erase(std::find(output->output_connections_.begin(), output->output_connections_.end(), conn_to_in)); - input->input_connections_.erase(input->input_connections_.find(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 deleted file mode 100644 index cada64d43..000000000 --- a/app/node/connectable.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 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; - }; - -protected: - const std::vector& output_connections() const - { - return output_connections_; - } - - const std::map& input_connections() const - { - return input_connections_; - } - -private: - std::vector output_connections_; - - std::map 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 afc323560..42ccb2fab 100644 --- a/app/node/distort/crop/cropdistortnode.cpp +++ b/app/node/distort/crop/cropdistortnode.cpp @@ -25,67 +25,61 @@ namespace olive { +const QString CropDistortNode::kTextureInput = QStringLiteral("tex_in"); +const QString CropDistortNode::kLeftInput = QStringLiteral("left_in"); +const QString CropDistortNode::kTopInput = QStringLiteral("top_in"); +const QString CropDistortNode::kRightInput = QStringLiteral("right_in"); +const QString CropDistortNode::kBottomInput = QStringLiteral("bottom_in"); +const QString CropDistortNode::kFeatherInput = QStringLiteral("feather_in"); + CropDistortNode::CropDistortNode() { - texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - 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", FloatSlider::kPercentage); + CreateCropSideInput(kLeftInput); + CreateCropSideInput(kTopInput); + CreateCropSideInput(kRightInput); + CreateCropSideInput(kBottomInput); - 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", FloatSlider::kPercentage); - - 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", FloatSlider::kPercentage); - - 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", FloatSlider::kPercentage); - - feather_input_ = new NodeInput(this, QStringLiteral("feather_in"), NodeValue::kFloat, 0.0); - feather_input_->setProperty("min", 0.0); + AddInput(kFeatherInput, NodeValue::kFloat, 0.0); + SetInputProperty(kFeatherInput, QStringLiteral("min"), 0.0); } void CropDistortNode::Retranslate() { - texture_input_->set_name(tr("Texture")); - left_input_->set_name(tr("Left")); - top_input_->set_name(tr("Top")); - right_input_->set_name(tr("Right")); - bottom_input_->set_name(tr("Bottom")); - feather_input_->set_name(tr("Feather")); + SetInputName(kTextureInput, tr("Texture")); + SetInputName(kLeftInput, tr("Left")); + SetInputName(kTopInput, tr("Top")); + SetInputName(kRightInput, tr("Right")); + SetInputName(kBottomInput, tr("Bottom")); + SetInputName(kFeatherInput, tr("Feather")); } -NodeValueTable CropDistortNode::Value(NodeValueDatabase &value) const +NodeValueTable CropDistortNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(texture_input_, value); - job.InsertValue(left_input_, value); - job.InsertValue(top_input_, value); - job.InsertValue(right_input_, value); - job.InsertValue(bottom_input_, value); - job.InsertValue(feather_input_, value); + job.InsertValue(this, kTextureInput, value); + job.InsertValue(this, kLeftInput, value); + job.InsertValue(this, kTopInput, value); + job.InsertValue(this, kRightInput, value); + job.InsertValue(this, kBottomInput, value); + job.InsertValue(this, kFeatherInput, value); job.InsertValue(QStringLiteral("resolution_in"), 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())) { + if (!job.GetValue(kTextureInput).data().isNull()) { + if (!qIsNull(job.GetValue(kLeftInput).data().toDouble()) + || !qIsNull(job.GetValue(kRightInput).data().toDouble()) + || !qIsNull(job.GetValue(kTopInput).data().toDouble()) + || !qIsNull(job.GetValue(kBottomInput).data().toDouble())) { table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { - table.Push(NodeValue::kTexture, job.GetValue(texture_input_).data(), this); + table.Push(NodeValue::kTexture, job.GetValue(kTextureInput).data(), this); } } @@ -106,10 +100,10 @@ void CropDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) p->setPen(QPen(Qt::white, 0)); - 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 left_pt = resolution.x() * db[kLeftInput].Get(NodeValue::kFloat).toDouble(); + double top_pt = resolution.y() * db[kTopInput].Get(NodeValue::kFloat).toDouble(); + double right_pt = resolution.x() * (1.0 - db[kRightInput].Get(NodeValue::kFloat).toDouble()); + double bottom_pt = resolution.y() * (1.0 - db[kBottomInput].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); @@ -156,7 +150,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoLeft; - gizmo_start_.append(db[left_input_].Get(NodeValue::kFloat)); + gizmo_start_.append(db[kLeftInput].Get(NodeValue::kFloat)); } if (gizmo_active[kGizmoScaleTopLeft] @@ -165,7 +159,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoTop; - gizmo_start_.append(db[top_input_].Get(NodeValue::kFloat)); + gizmo_start_.append(db[kTopInput].Get(NodeValue::kFloat)); } if (gizmo_active[kGizmoScaleTopRight] @@ -174,7 +168,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoRight; - gizmo_start_.append(db[right_input_].Get(NodeValue::kFloat)); + gizmo_start_.append(db[kRightInput].Get(NodeValue::kFloat)); } if (gizmo_active[kGizmoScaleBottomLeft] @@ -183,7 +177,7 @@ bool CropDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) || in_rect) { gizmo_drag_ |= kGizmoBottom; - gizmo_start_.append(db[bottom_input_].Get(NodeValue::kFloat)); + gizmo_start_.append(db[kBottomInput].Get(NodeValue::kFloat)); } if (gizmo_drag_ > kGizmoNone) { @@ -204,22 +198,22 @@ void CropDistortNode::GizmoMove(const QPointF &p, const rational &time) int counter = 0; if (gizmo_drag_ & kGizmoLeft) { - gizmo_dragger_[counter].Start(left_input_, time, 0); + gizmo_dragger_[counter].Start(NodeInput(this, kLeftInput), time); counter++; } if (gizmo_drag_ & kGizmoTop) { - gizmo_dragger_[counter].Start(top_input_, time, 0); + gizmo_dragger_[counter].Start(NodeInput(this, kTopInput), time); counter++; } if (gizmo_drag_ & kGizmoRight) { - gizmo_dragger_[counter].Start(right_input_, time, 0); + gizmo_dragger_[counter].Start(NodeInput(this, kRightInput), time); counter++; } if (gizmo_drag_ & kGizmoBottom) { - gizmo_dragger_[counter].Start(bottom_input_, time, 0); + gizmo_dragger_[counter].Start(NodeInput(this, kBottomInput), time); counter++; } } @@ -260,4 +254,12 @@ void CropDistortNode::GizmoRelease() gizmo_start_.clear(); } +void CropDistortNode::CreateCropSideInput(const QString &id) +{ + AddInput(id, NodeValue::kFloat, 0.0); + SetInputProperty(id, QStringLiteral("min"), 0.0); + SetInputProperty(id, QStringLiteral("max"), 1.0); + SetInputProperty(id, QStringLiteral("view"), FloatSlider::kPercentage); +} + } diff --git a/app/node/distort/crop/cropdistortnode.h b/app/node/distort/crop/cropdistortnode.h index 92ece059f..692855981 100644 --- a/app/node/distort/crop/cropdistortnode.h +++ b/app/node/distort/crop/cropdistortnode.h @@ -60,7 +60,7 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; @@ -75,15 +75,15 @@ public: virtual void GizmoMove(const QPointF &p, const rational &time) override; virtual void GizmoRelease() override; + static const QString kTextureInput; + static const QString kLeftInput; + static const QString kTopInput; + static const QString kRightInput; + static const QString kBottomInput; + static const QString kFeatherInput; + private: - NodeInput* texture_input_; - - NodeInput* left_input_; - NodeInput* top_input_; - NodeInput* right_input_; - NodeInput* bottom_input_; - - NodeInput* feather_input_; + void CreateCropSideInput(const QString& id); // Gizmo variables QRectF gizmo_resize_handle_[kGizmoScaleCount]; diff --git a/app/node/distort/transform/transformdistortnode.cpp b/app/node/distort/transform/transformdistortnode.cpp index 9c705899d..b40e850e7 100644 --- a/app/node/distort/transform/transformdistortnode.cpp +++ b/app/node/distort/transform/transformdistortnode.cpp @@ -26,35 +26,40 @@ namespace olive { +const QString TransformDistortNode::kTextureInput = QStringLiteral("tex_in"); +const QString TransformDistortNode::kAutoscaleInput = QStringLiteral("autoscale_in"); +const QString TransformDistortNode::kInterpolationInput = QStringLiteral("interpolation_in"); + TransformDistortNode::TransformDistortNode() { - autoscale_input_ = new NodeInput(this, QStringLiteral("autoscale_in"), NodeValue::kCombo, 0); + AddInput(kAutoscaleInput, NodeValue::kCombo, 0); - interpolation_input_ = new NodeInput(this, QStringLiteral("interpolation_in"), NodeValue::kCombo, 2); + AddInput(kInterpolationInput, NodeValue::kCombo, 2); - texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); - texture_input_->SetKeyframable(false); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); } void TransformDistortNode::Retranslate() { MatrixGenerator::Retranslate(); - autoscale_input_->set_name(tr("Auto-Scale")); - texture_input_->set_name(tr("Texture")); - interpolation_input_->set_name(tr("Interpolation")); + SetInputName(kAutoscaleInput, tr("Auto-Scale")); + SetInputName(kTextureInput, tr("Texture")); + SetInputName(kInterpolationInput, tr("Interpolation")); - autoscale_input_->set_combobox_strings({tr("None"), tr("Fit"), tr("Fill"), tr("Stretch")}); - interpolation_input_->set_combobox_strings({tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")}); + SetComboBoxStrings(kAutoscaleInput, {tr("None"), tr("Fit"), tr("Fill"), tr("Stretch")}); + SetComboBoxStrings(kInterpolationInput, {tr("Nearest Neighbor"), tr("Bilinear"), tr("Mipmapped Bilinear")}); } -NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const +NodeValueTable TransformDistortNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // Generate matrix QMatrix4x4 generated_matrix = GenerateMatrix(value, true, false, false, false); // Pop texture - TexturePtr texture = value[texture_input_].Take(NodeValue::kTexture).value(); + TexturePtr texture = value[kTextureInput].Take(NodeValue::kTexture).value(); // Merge table NodeValueTable table = value.Merge(); @@ -64,7 +69,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const // Adjust our matrix by the resolutions involved 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(NodeValue::kCombo).toInt()); + AutoScaleType autoscale = static_cast(value[kAutoscaleInput].Get(NodeValue::kCombo).toInt()); QMatrix4x4 real_matrix = AdjustMatrixByResolutions(generated_matrix, sequence_res, @@ -79,7 +84,7 @@ NodeValueTable TransformDistortNode::Value(NodeValueDatabase &value) const ShaderJob job; 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())); + job.SetInterpolation(QStringLiteral("ove_maintex"), static_cast(value[kInterpolationInput].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. @@ -121,10 +126,10 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) if (scaling) { // Dragging scale handle - gizmo_start_ = {db[scale_input()].Get(NodeValue::kVec2)}; - gizmo_drag_ = scale_input(); + gizmo_start_ = {db[kScaleInput].Get(NodeValue::kVec2)}; + gizmo_drag_ = kScaleInput; - gizmo_scale_uniform_ = db[uniform_scale_input()].Get(NodeValue::kBoolean).toBool(); + gizmo_scale_uniform_ = db[kUniformScaleInput].Get(NodeValue::kBoolean).toBool(); if (gizmo_scale_active[kGizmoScaleTopLeft] || gizmo_scale_active[kGizmoScaleTopRight] || gizmo_scale_active[kGizmoScaleBottomLeft] || gizmo_scale_active[kGizmoScaleBottomRight]) { @@ -136,8 +141,8 @@ bool TransformDistortNode::GizmoPress(NodeValueDatabase &db, const QPointF &p) } // Store texture size - QVector2D texture_sz = db[texture_input()].Get(NodeValue::kTexture).value(); - gizmo_scale_anchor_ = db[anchor_input()].Get(NodeValue::kVec2).value() + texture_sz/2; + QVector2D texture_sz = db[kTextureInput].Get(NodeValue::kTexture).value(); + gizmo_scale_anchor_ = db[kAnchorInput].Get(NodeValue::kVec2).value() + texture_sz/2; if (gizmo_scale_active[kGizmoScaleTopRight] || gizmo_scale_active[kGizmoScaleBottomRight] @@ -161,9 +166,9 @@ 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(NodeValue::kVec2), - db[position_input()].Get(NodeValue::kVec2)}; - gizmo_drag_ = anchor_input(); + gizmo_start_ = {db[kAnchorInput].Get(NodeValue::kVec2), + db[kPositionInput].Get(NodeValue::kVec2)}; + gizmo_drag_ = kAnchorInput; // Store current matrix gizmo_matrix_ = GenerateMatrix(db, false, true, true, false); @@ -173,16 +178,16 @@ 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(NodeValue::kVec2)}; - gizmo_drag_ = position_input(); + gizmo_start_ = {db[kPositionInput].Get(NodeValue::kVec2)}; + gizmo_drag_ = kPositionInput; return true; } else { // Dragging rotation - gizmo_start_ = {db[rotation_input()].Get(NodeValue::kFloat)}; - gizmo_drag_ = rotation_input(); + gizmo_start_ = {db[kRotationInput].Get(NodeValue::kFloat)}; + gizmo_drag_ = kRotationInput; gizmo_start_angle_ = qAtan2(gizmo_drag_pos_.y() - gizmo_anchor_pt_.center().y(), gizmo_drag_pos_.x() - gizmo_anchor_pt_.center().x()); @@ -198,15 +203,15 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time) QPointF movement = (p - gizmo_drag_pos_); QVector2D vec_movement(movement); - if (gizmo_drag_ == anchor_input()) { + if (gizmo_drag_ == kAnchorInput) { // Dragging the anchor point around if (gizmo_dragger_.isEmpty()) { gizmo_dragger_.resize(4); - gizmo_dragger_[0].Start(anchor_input(), time, 0); - gizmo_dragger_[1].Start(anchor_input(), time, 1); - gizmo_dragger_[2].Start(position_input(), time, 0); - gizmo_dragger_[3].Start(position_input(), time, 1); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 0), time); + gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kAnchorInput), 1), time); + gizmo_dragger_[2].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time); + gizmo_dragger_[3].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time); } QVector2D inverted_movement(gizmo_matrix_.toTransform().inverted().map(movement)); @@ -218,13 +223,13 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time) gizmo_dragger_[2].Drag(position_cont.x()); gizmo_dragger_[3].Drag(position_cont.y()); - } else if (gizmo_drag_ == position_input()) { + } else if (gizmo_drag_ == kPositionInput) { // Dragging the main rectangle around if (gizmo_dragger_.isEmpty()) { gizmo_dragger_.resize(2); - gizmo_dragger_[0].Start(position_input(), time, 0); - gizmo_dragger_[1].Start(position_input(), time, 1); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 0), time); + gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kPositionInput), 1), time); } QVector2D position_cont = gizmo_start_[0].value() + vec_movement; @@ -232,20 +237,20 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time) gizmo_dragger_[0].Drag(position_cont.x()); gizmo_dragger_[1].Drag(position_cont.y()); - } else if (gizmo_drag_ == scale_input()) { + } else if (gizmo_drag_ == kScaleInput) { // Dragging a resize handle if (gizmo_dragger_.isEmpty()) { if (gizmo_scale_uniform_ || gizmo_scale_axes_ == kGizmoScaleXOnly) { gizmo_dragger_.resize(1); - gizmo_dragger_[0].Start(scale_input(), time, 0); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0), time); } else if (gizmo_scale_axes_ == kGizmoScaleYOnly) { gizmo_dragger_.resize(1); - gizmo_dragger_[0].Start(scale_input(), time, 1); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time); } else { gizmo_dragger_.resize(2); - gizmo_dragger_[0].Start(scale_input(), time, 0); - gizmo_dragger_[1].Start(scale_input(), time, 1); + gizmo_dragger_[0].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 0), time); + gizmo_dragger_[1].Start(NodeKeyframeTrackReference(NodeInput(this, kScaleInput), 1), time); } } @@ -274,12 +279,12 @@ void TransformDistortNode::GizmoMove(const QPointF &p, const rational &time) break; } - } else if (gizmo_drag_ == rotation_input()) { + } else if (gizmo_drag_ == kRotationInput) { // Dragging outside the rectangle to rotate if (gizmo_dragger_.isEmpty()) { gizmo_dragger_.resize(1); - gizmo_dragger_[0].Start(rotation_input(), time, 0); + gizmo_dragger_[0].Start(NodeInput(this, kRotationInput), time); } double current_angle = qAtan2(p.y() - gizmo_anchor_pt_.center().y(), @@ -367,10 +372,10 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) 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(NodeValue::kTexture).value(); + QVector2D tex_sz = db[kTextureInput].Get(NodeValue::kTexture).value(); // Retrieve autoscale value - AutoScaleType autoscale = static_cast(db[autoscale_input_].Get(NodeValue::kCombo).toInt()); + AutoScaleType autoscale = static_cast(db[kAutoscaleInput].Get(NodeValue::kCombo).toInt()); // Fold values into a matrix for the rectangle QMatrix4x4 rectangle_matrix; @@ -435,8 +440,8 @@ void TransformDistortNode::DrawGizmos(NodeValueDatabase &db, QPainter *p) // Use offsets to make the appearance of values that start in the top left, even though we // really anchor around the center - position_input()->setProperty("offset", sequence_res * 0.5); - anchor_input()->setProperty("offset", tex_sz * 0.5); + SetInputProperty(kPositionInput, QStringLiteral("offset"), sequence_res * 0.5); + SetInputProperty(kAnchorInput, QStringLiteral("offset"), tex_sz * 0.5); } } diff --git a/app/node/distort/transform/transformdistortnode.h b/app/node/distort/transform/transformdistortnode.h index 5002db06b..ca7606a94 100644 --- a/app/node/distort/transform/transformdistortnode.h +++ b/app/node/distort/transform/transformdistortnode.h @@ -63,7 +63,7 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual ShaderCode GetShaderCode(const QString& shader_id) const override; @@ -78,11 +78,6 @@ public: virtual void GizmoMove(const QPointF &p, const rational &time) override; virtual void GizmoRelease() override; - NodeInput* texture_input() const - { - return texture_input_; - } - enum AutoScaleType { kAutoScaleNone, kAutoScaleFit, @@ -95,15 +90,15 @@ public: const QVector2D& texture_res, AutoScaleType autoscale_type = kAutoScaleNone); + static const QString kTextureInput; + static const QString kAutoscaleInput; + static const QString kInterpolationInput; + private: static QPointF CreateScalePoint(double x, double y, const QPointF& half_res, const QMatrix4x4& mat); - NodeInput* texture_input_; - NodeInput* autoscale_input_; - NodeInput* interpolation_input_; - // Gizmo variables - NodeInput* gizmo_drag_; + QString gizmo_drag_; QVector gizmo_start_; QVector gizmo_dragger_; QPointF gizmo_drag_pos_; diff --git a/app/node/factory.cpp b/app/node/factory.cpp index 21365c2d4..f6c1f8443 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -52,13 +52,18 @@ void NodeFactory::Initialize() // Add internal types for (int i=0;i(i))); - } + Node* created_node = CreateFromFactoryIndex(static_cast(i)); - /* - library_.append(new ExternalTransition(":/shaders/crossdissolve.xml")); - library_.append(new ExternalTransition(":/shaders/diptoblack.xml")); - */ + library_.append(created_node); + + if (created_node->inputs().isEmpty()) { + qWarning() << "Node" << created_node->id() << "has no inputs"; + } + + if (created_node->outputs().isEmpty()) { + qWarning() << "Node" << created_node->id() << "has no outputs"; + } + } } void NodeFactory::Destroy() diff --git a/app/node/filter/blur/blur.cpp b/app/node/filter/blur/blur.cpp index b3f8afd30..33975b68e 100644 --- a/app/node/filter/blur/blur.cpp +++ b/app/node/filter/blur/blur.cpp @@ -22,20 +22,27 @@ namespace olive { +const QString BlurFilterNode::kTextureInput = QStringLiteral("tex_in"); +const QString BlurFilterNode::kMethodInput = QStringLiteral("method_in"); +const QString BlurFilterNode::kRadiusInput = QStringLiteral("radius_in"); +const QString BlurFilterNode::kHorizInput = QStringLiteral("horiz_in"); +const QString BlurFilterNode::kVertInput = QStringLiteral("vert_in"); +const QString BlurFilterNode::kRepeatEdgePixelsInput = QStringLiteral("repeat_edge_pixels_in"); + BlurFilterNode::BlurFilterNode() { - texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - method_input_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo, 0); + AddInput(kMethodInput, NodeValue::kCombo, 0); - radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f); - radius_input_->setProperty("min", 0.0f); + AddInput(kRadiusInput, NodeValue::kFloat, 10.0); + SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0); - horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kBoolean, true); + AddInput(kHorizInput, NodeValue::kBoolean, true); - vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kBoolean, true); + AddInput(kVertInput, NodeValue::kBoolean, true); - repeat_edge_pixels_input_ = new NodeInput(this, QStringLiteral("repeat_edge_pixels_in"), NodeValue::kBoolean, false); + AddInput(kRepeatEdgePixelsInput, NodeValue::kBoolean, false); } Node *BlurFilterNode::copy() const @@ -65,13 +72,13 @@ QString BlurFilterNode::Description() const void BlurFilterNode::Retranslate() { - texture_input_->set_name(tr("Input")); - method_input_->set_name(tr("Method")); - method_input_->set_combobox_strings({ tr("Box"), tr("Gaussian") }); - radius_input_->set_name(tr("Radius")); - horiz_input_->set_name(tr("Horizontal")); - vert_input_->set_name(tr("Vertical")); - repeat_edge_pixels_input_->set_name(tr("Repeat Edge Pixels")); + SetInputName(kTextureInput, tr("Input")); + SetInputName(kMethodInput, tr("Method")); + SetComboBoxStrings(kMethodInput, { tr("Box"), tr("Gaussian") }); + SetInputName(kRadiusInput, tr("Radius")); + SetInputName(kHorizInput, tr("Horizontal")); + SetInputName(kVertInput, tr("Vertical")); + SetInputName(kRepeatEdgePixelsInput, tr("Repeat Edge Pixels")); } ShaderCode BlurFilterNode::GetShaderCode(const QString &shader_id) const @@ -80,35 +87,37 @@ ShaderCode BlurFilterNode::GetShaderCode(const QString &shader_id) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/blur.frag")); } -NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const +NodeValueTable BlurFilterNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(texture_input_, value); - job.InsertValue(method_input_, value); - job.InsertValue(radius_input_, value); - job.InsertValue(horiz_input_, value); - job.InsertValue(vert_input_, value); - job.InsertValue(repeat_edge_pixels_input_, value); + job.InsertValue(this, kTextureInput, value); + job.InsertValue(this, kMethodInput, value); + job.InsertValue(this, kRadiusInput, value); + job.InsertValue(this, kHorizInput, value); + job.InsertValue(this, kVertInput, value); + job.InsertValue(this, kRepeatEdgePixelsInput, value); job.InsertValue(QStringLiteral("resolution_in"), 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(kTextureInput).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(kHorizInput).data().toBool() || job.GetValue(kVertInput).data().toBool()) + && job.GetValue(kRadiusInput).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()) { - job.SetIterations(2, texture_input_); + if (job.GetValue(kHorizInput).data().toBool() && job.GetValue(kVertInput).data().toBool()) { + job.SetIterations(2, kTextureInput); } // If we're not repeating pixels, expect an alpha channel to appear - if (!job.GetValue(repeat_edge_pixels_input_).data().toBool()) { + if (!job.GetValue(kRepeatEdgePixelsInput).data().toBool()) { job.SetAlphaChannelRequired(true); } @@ -116,7 +125,7 @@ NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const } else { // If we're not performing the blur job, just push the texture - table.Push(job.GetValue(texture_input_)); + table.Push(job.GetValue(kTextureInput)); } } diff --git a/app/node/filter/blur/blur.h b/app/node/filter/blur/blur.h index 7a121a34b..6a0ddb366 100644 --- a/app/node/filter/blur/blur.h +++ b/app/node/filter/blur/blur.h @@ -41,20 +41,14 @@ public: virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; -private: - NodeInput* texture_input_; - - NodeInput* method_input_; - - NodeInput* radius_input_; - - NodeInput* horiz_input_; - - NodeInput* vert_input_; - - NodeInput* repeat_edge_pixels_input_; + static const QString kTextureInput; + static const QString kMethodInput; + static const QString kRadiusInput; + static const QString kHorizInput; + static const QString kVertInput; + static const QString kRepeatEdgePixelsInput; }; diff --git a/app/node/filter/mosaic/mosaicfilternode.cpp b/app/node/filter/mosaic/mosaicfilternode.cpp index b03532cdc..6b0afe207 100644 --- a/app/node/filter/mosaic/mosaicfilternode.cpp +++ b/app/node/filter/mosaic/mosaicfilternode.cpp @@ -22,46 +22,52 @@ namespace olive { +const QString MosaicFilterNode::kTextureInput = QStringLiteral("tex_in"); +const QString MosaicFilterNode::kHorizInput = QStringLiteral("horiz_in"); +const QString MosaicFilterNode::kVertInput = QStringLiteral("vert_in"); + MosaicFilterNode::MosaicFilterNode() { - tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - horiz_input_ = new NodeInput(this, QStringLiteral("horiz_in"), NodeValue::kFloat, 32.0); - horiz_input_->setProperty("min", 1.0); + AddInput(kHorizInput, NodeValue::kFloat, 32.0); + SetInputProperty(kHorizInput, QStringLiteral("min"), 1.0); - vert_input_ = new NodeInput(this, QStringLiteral("vert_in"), NodeValue::kFloat, 18.0); - vert_input_->setProperty("min", 1.0); + AddInput(kVertInput, NodeValue::kFloat, 18.0); + SetInputProperty(kVertInput, QStringLiteral("min"), 1.0); } void MosaicFilterNode::Retranslate() { - tex_input_->set_name(tr("Texture")); - horiz_input_->set_name(tr("Horizontal")); - vert_input_->set_name(tr("Vertical")); + SetInputName(kTextureInput, tr("Texture")); + SetInputName(kHorizInput, tr("Horizontal")); + SetInputName(kVertInput, tr("Vertical")); } -NodeValueTable MosaicFilterNode::Value(NodeValueDatabase &value) const +NodeValueTable MosaicFilterNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(tex_input_, value); - job.InsertValue(horiz_input_, value); - job.InsertValue(vert_input_, value); + job.InsertValue(this, kTextureInput, value); + job.InsertValue(this, kHorizInput, value); + job.InsertValue(this, kVertInput, value); // Mipmapping makes this look weird, so we just use bilinear for finding the color of each block - job.SetInterpolation(tex_input_, Texture::kLinear); + job.SetInterpolation(kTextureInput, Texture::kLinear); NodeValueTable table = value.Merge(); - if (!job.GetValue(tex_input_).data().isNull()) { - TexturePtr texture = job.GetValue(tex_input_).data().value(); + if (!job.GetValue(kTextureInput).data().isNull()) { + TexturePtr texture = job.GetValue(kTextureInput).data().value(); if (texture - && job.GetValue(horiz_input_).data().toInt() != texture->width() - && job.GetValue(vert_input_).data().toInt() != texture->height()) { + && job.GetValue(kHorizInput).data().toInt() != texture->width() + && job.GetValue(kVertInput).data().toInt() != texture->height()) { table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { - table.Push(job.GetValue(tex_input_)); + table.Push(job.GetValue(kTextureInput)); } } diff --git a/app/node/filter/mosaic/mosaicfilternode.h b/app/node/filter/mosaic/mosaicfilternode.h index 104e05b31..2ecff1e29 100644 --- a/app/node/filter/mosaic/mosaicfilternode.h +++ b/app/node/filter/mosaic/mosaicfilternode.h @@ -58,15 +58,12 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; -private: - NodeInput* tex_input_; - - NodeInput* horiz_input_; - - NodeInput* vert_input_; + static const QString kTextureInput; + static const QString kHorizInput; + static const QString kVertInput; }; diff --git a/app/node/filter/stroke/stroke.cpp b/app/node/filter/stroke/stroke.cpp index 70cabe396..d611fb606 100644 --- a/app/node/filter/stroke/stroke.cpp +++ b/app/node/filter/stroke/stroke.cpp @@ -25,24 +25,27 @@ namespace olive { +const QString StrokeFilterNode::kTextureInput = QStringLiteral("tex_in"); +const QString StrokeFilterNode::kColorInput = QStringLiteral("color_in"); +const QString StrokeFilterNode::kRadiusInput = QStringLiteral("radius_in"); +const QString StrokeFilterNode::kOpacityInput = QStringLiteral("opacity_in"); +const QString StrokeFilterNode::kInnerInput = QStringLiteral("inner_in"); + StrokeFilterNode::StrokeFilterNode() { - tex_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - color_input_ = new NodeInput(this, - QStringLiteral("color_in"), - NodeValue::kColor, - QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0f, 1.0f))); - radius_input_ = new NodeInput(this, QStringLiteral("radius_in"), NodeValue::kFloat, 10.0f); - radius_input_->setProperty("min", 0.0f); + AddInput(kRadiusInput, NodeValue::kFloat, 10.0); + SetInputProperty(kRadiusInput, QStringLiteral("min"), 0.0); - opacity_input_ = new NodeInput(this, QStringLiteral("opacity_in"), NodeValue::kFloat, 1.0f); - opacity_input_->setProperty("view", FloatSlider::kPercentage); - opacity_input_->setProperty("min", 0.0f); - opacity_input_->setProperty("max", 1.0f); + AddInput(kOpacityInput, NodeValue::kFloat, 1.0f); + SetInputProperty(kOpacityInput, QStringLiteral("view"), FloatSlider::kPercentage); + SetInputProperty(kOpacityInput, QStringLiteral("min"), 0.0f); + SetInputProperty(kOpacityInput, QStringLiteral("max"), 1.0f); - inner_input_ = new NodeInput(this, QStringLiteral("inner_in"), NodeValue::kBoolean, false); + AddInput(kInnerInput, NodeValue::kBoolean, false); } Node *StrokeFilterNode::copy() const @@ -72,33 +75,35 @@ QString StrokeFilterNode::Description() const void StrokeFilterNode::Retranslate() { - tex_input_->set_name(tr("Input")); - color_input_->set_name(tr("Color")); - radius_input_->set_name(tr("Radius")); - opacity_input_->set_name(tr("Opacity")); - inner_input_->set_name(tr("Inner")); + SetInputName(kTextureInput, tr("Input")); + SetInputName(kColorInput, tr("Color")); + SetInputName(kRadiusInput, tr("Radius")); + SetInputName(kOpacityInput, tr("Opacity")); + SetInputName(kInnerInput, tr("Inner")); } -NodeValueTable StrokeFilterNode::Value(NodeValueDatabase &value) const +NodeValueTable StrokeFilterNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(tex_input_, value); - job.InsertValue(color_input_, value); - job.InsertValue(radius_input_, value); - job.InsertValue(opacity_input_, value); - job.InsertValue(inner_input_, value); + job.InsertValue(this, kTextureInput, value); + job.InsertValue(this, kColorInput, value); + job.InsertValue(this, kRadiusInput, value); + job.InsertValue(this, kOpacityInput, value); + job.InsertValue(this, kInnerInput, value); job.InsertValue(QStringLiteral("resolution_in"), 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) { + if (!job.GetValue(kTextureInput).data().isNull()) { + if (job.GetValue(kRadiusInput).data().toDouble() > 0.0 + && job.GetValue(kOpacityInput).data().toDouble() > 0.0) { table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); } else { - table.Push(job.GetValue(tex_input_)); + table.Push(job.GetValue(kTextureInput)); } } diff --git a/app/node/filter/stroke/stroke.h b/app/node/filter/stroke/stroke.h index fb29aea33..338e266ea 100644 --- a/app/node/filter/stroke/stroke.h +++ b/app/node/filter/stroke/stroke.h @@ -40,19 +40,14 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; -private: - NodeInput* tex_input_; - - NodeInput* color_input_; - - NodeInput* radius_input_; - - NodeInput* opacity_input_; - - NodeInput* inner_input_; + static const QString kTextureInput; + static const QString kColorInput; + static const QString kRadiusInput; + static const QString kOpacityInput; + static const QString kInnerInput; }; diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index ac04b173f..d5088ecfc 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -27,23 +27,26 @@ namespace olive { +const QString MatrixGenerator::kPositionInput = QStringLiteral("pos_in"); +const QString MatrixGenerator::kRotationInput = QStringLiteral("rot_in"); +const QString MatrixGenerator::kScaleInput = QStringLiteral("scale_in"); +const QString MatrixGenerator::kUniformScaleInput = QStringLiteral("uniform_scale_in"); +const QString MatrixGenerator::kAnchorInput = QStringLiteral("anchor_in"); + MatrixGenerator::MatrixGenerator() { - position_input_ = new NodeInput(this, QStringLiteral("pos_in"), NodeValue::kVec2, QVector2D()); + AddInput(kPositionInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); - rotation_input_ = new NodeInput(this, QStringLiteral("rot_in"), NodeValue::kFloat, 0.0f); + AddInput(kRotationInput, NodeValue::kFloat, 0.0); - 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", FloatSlider::kPercentage); - scale_input_->setProperty("disabley", true); + AddInput(kScaleInput, NodeValue::kVec2, QVector2D(1.0f, 1.0f)); + SetInputProperty(kScaleInput, QStringLiteral("min"), QVector2D(0, 0)); + SetInputProperty(kScaleInput, QStringLiteral("view"), FloatSlider::kPercentage); + SetInputProperty(kScaleInput, QStringLiteral("disabley"), true); - 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(kUniformScaleInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - anchor_input_ = new NodeInput(this, QStringLiteral("anchor_in"), NodeValue::kVec2, QVector2D()); + AddInput(kAnchorInput, NodeValue::kVec2, QVector2D(0.0, 0.0)); } Node *MatrixGenerator::copy() const @@ -78,20 +81,22 @@ QString MatrixGenerator::Description() const void MatrixGenerator::Retranslate() { - position_input_->set_name(tr("Position")); - rotation_input_->set_name(tr("Rotation")); - scale_input_->set_name(tr("Scale")); - uniform_scale_input_->set_name(tr("Uniform Scale")); - anchor_input_->set_name(tr("Anchor Point")); + SetInputName(kPositionInput, tr("Position")); + SetInputName(kRotationInput, tr("Rotation")); + SetInputName(kScaleInput, tr("Scale")); + SetInputName(kUniformScaleInput, tr("Uniform Scale")); + SetInputName(kAnchorInput, tr("Anchor Point")); } -NodeValueTable MatrixGenerator::Value(NodeValueDatabase &value) const +NodeValueTable MatrixGenerator::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // Push matrix output QMatrix4x4 mat = GenerateMatrix(value, true, false, false, false); - NodeValueTable output = value.Merge(); - output.Push(NodeValue::kMatrix, mat, this); - return output; + NodeValueTable out = value.Merge(); + out.Push(NodeValue::kMatrix, mat, this); + return out; } QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool take, bool ignore_anchor, bool ignore_position, bool ignore_scale) const @@ -103,47 +108,47 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(NodeValueDatabase &value, bool take, if (!ignore_anchor) { if (take) { // Take and store - anchor = value[anchor_input_].Take(NodeValue::kVec2).value(); + anchor = value[kAnchorInput].Take(NodeValue::kVec2).value(); } else { // Get and store - anchor = value[anchor_input_].Get(NodeValue::kVec2).value(); + anchor = value[kAnchorInput].Get(NodeValue::kVec2).value(); } } else if (take) { // Just take - value[anchor_input_].Take(NodeValue::kVec2).value(); + value[kAnchorInput].Take(NodeValue::kVec2).value(); } if (!ignore_scale) { if (take) { - scale = value[scale_input_].Take(NodeValue::kVec2).value(); + scale = value[kScaleInput].Take(NodeValue::kVec2).value(); } else { - scale = value[scale_input_].Get(NodeValue::kVec2).value(); + scale = value[kScaleInput].Get(NodeValue::kVec2).value(); } } else if (take) { - value[scale_input_].Take(NodeValue::kVec2).value(); + value[kScaleInput].Take(NodeValue::kVec2).value(); } if (!ignore_position) { if (take) { - position = value[position_input_].Take(NodeValue::kVec2).value(); + position = value[kPositionInput].Take(NodeValue::kVec2).value(); } else { - position = value[position_input_].Get(NodeValue::kVec2).value(); + position = value[kPositionInput].Get(NodeValue::kVec2).value(); } } else if (take) { - value[position_input_].Take(NodeValue::kVec2).value(); + value[kPositionInput].Take(NodeValue::kVec2).value(); } if (take) { return GenerateMatrix(position, - value[rotation_input_].Take(NodeValue::kFloat).toFloat(), + value[kRotationInput].Take(NodeValue::kFloat).toFloat(), scale, - value[uniform_scale_input_].Take(NodeValue::kBoolean).toBool(), + value[kUniformScaleInput].Take(NodeValue::kBoolean).toBool(), anchor); } else { return GenerateMatrix(position, - value[rotation_input_].Get(NodeValue::kFloat).toFloat(), + value[kRotationInput].Get(NodeValue::kFloat).toFloat(), scale, - value[uniform_scale_input_].Get(NodeValue::kBoolean).toBool(), + value[kUniformScaleInput].Get(NodeValue::kBoolean).toBool(), anchor); } @@ -178,9 +183,13 @@ QMatrix4x4 MatrixGenerator::GenerateMatrix(const QVector2D& pos, return mat; } -void MatrixGenerator::UniformScaleChanged() +void MatrixGenerator::InputValueChangedEvent(const QString &input, int element) { - scale_input_->setProperty("disabley", uniform_scale_input_->GetStandardValue().toBool()); + Q_UNUSED(element) + + if (input == kUniformScaleInput) { + SetInputProperty(kScaleInput, QStringLiteral("disabley"), GetStandardValue(kUniformScaleInput).toBool()); + } } } diff --git a/app/node/generator/matrix/matrix.h b/app/node/generator/matrix/matrix.h index 6c22e2e5a..5401a02b6 100644 --- a/app/node/generator/matrix/matrix.h +++ b/app/node/generator/matrix/matrix.h @@ -44,7 +44,13 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; + + static const QString kPositionInput; + static const QString kRotationInput; + static const QString kScaleInput; + static const QString kUniformScaleInput; + static const QString kAnchorInput; protected: QMatrix4x4 GenerateMatrix(NodeValueDatabase &value, bool take, bool ignore_anchor, bool ignore_position, bool ignore_scale) const; @@ -54,44 +60,7 @@ protected: bool uniform_scale, const QVector2D &anchor); - NodeInput* position_input() const - { - return position_input_; - } - - NodeInput* rotation_input() const - { - return rotation_input_; - } - - NodeInput* scale_input() const - { - return scale_input_; - } - - NodeInput* uniform_scale_input() const - { - return uniform_scale_input_; - } - - NodeInput* anchor_input() const - { - return anchor_input_; - } - -private: - NodeInput* position_input_; - - NodeInput* rotation_input_; - - NodeInput* scale_input_; - - NodeInput* uniform_scale_input_; - - NodeInput* anchor_input_; - -private slots: - void UniformScaleChanged(); + virtual void InputValueChangedEvent(const QString& input, int element) override; }; diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index 1a9d053e4..6456694d9 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -25,25 +25,27 @@ namespace olive { +const QString PolygonGenerator::kPointsInput = QStringLiteral("points_in"); +const QString PolygonGenerator::kColorInput = QStringLiteral("color_in"); + PolygonGenerator::PolygonGenerator() { - points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2, QVector2D(0, 0)); - points_input_->SetIsArray(true); + AddInput(kPointsInput, NodeValue::kVec2, QVector2D(0, 0), InputFlags(kInputFlagArray)); - color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0))); // 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); + InputArrayResize(kPointsInput, 5); + SetSplitStandardValueOnTrack(kPointsInput, 0, 960, 0); + SetSplitStandardValueOnTrack(kPointsInput, 1, 240, 0); + SetSplitStandardValueOnTrack(kPointsInput, 0, 640, 1); + SetSplitStandardValueOnTrack(kPointsInput, 1, 480, 1); + SetSplitStandardValueOnTrack(kPointsInput, 0, 760, 2); + SetSplitStandardValueOnTrack(kPointsInput, 1, 800, 2); + SetSplitStandardValueOnTrack(kPointsInput, 0, 1100, 3); + SetSplitStandardValueOnTrack(kPointsInput, 1, 800, 3); + SetSplitStandardValueOnTrack(kPointsInput, 0, 1200, 4); + SetSplitStandardValueOnTrack(kPointsInput, 1, 480, 4); } Node *PolygonGenerator::copy() const @@ -73,8 +75,8 @@ QString PolygonGenerator::Description() const void PolygonGenerator::Retranslate() { - points_input_->set_name(tr("Points")); - color_input_->set_name(tr("Color")); + SetInputName(kPointsInput, tr("Points")); + SetInputName(kColorInput, tr("Color")); } ShaderCode PolygonGenerator::GetShaderCode(const QString &shader_id) const @@ -84,12 +86,14 @@ ShaderCode PolygonGenerator::GetShaderCode(const QString &shader_id) const return ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/polygon.frag"))); } -NodeValueTable PolygonGenerator::Value(NodeValueDatabase &value) const +NodeValueTable PolygonGenerator::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(points_input_, value); - job.InsertValue(color_input_, value); + job.InsertValue(this, kPointsInput, value); + job.InsertValue(this, kColorInput, value); job.InsertValue(QStringLiteral("resolution_in"), value[QStringLiteral("global")].GetWithMeta(NodeValue::kVec2, QStringLiteral("resolution"))); job.SetAlphaChannelRequired(true); @@ -168,10 +172,10 @@ void PolygonGenerator::GizmoRelease() QVector PolygonGenerator::GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D& scale) const { // FIXME: Should Get() use a `kArray` type instead of a `kVec2` type? - QVector array_tbl = db[points_input_].Get(NodeValue::kVec2).value< QVector >(); + QVector array_tbl = db[kPointsInput].Get(NodeValue::kVec2).value< QVector >(); QVector points(array_tbl.size()); - for (int i=0;iArraySize();i++) { + for (int i=0;i(); v *= scale; diff --git a/app/node/generator/polygon/polygon.h b/app/node/generator/polygon/polygon.h index 6ab1fbc7e..cb0e08d25 100644 --- a/app/node/generator/polygon/polygon.h +++ b/app/node/generator/polygon/polygon.h @@ -42,7 +42,7 @@ public: virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const QString& shader_id) const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual bool HasGizmos() const override; //virtual void DrawGizmos(NodeValueDatabase& db, QPainter *p) const override; @@ -51,15 +51,14 @@ public: //virtual void GizmoMove(const QPointF &p, const QVector2D &scale, const rational &time) override; //virtual void GizmoRelease() override; + static const QString kPointsInput; + static const QString kColorInput; + private: QVector GetGizmoCoordinates(NodeValueDatabase &db, const QVector2D &scale) const; QVector GetGizmoRects(const QVector& points) const; - NodeInput* points_input_; - - NodeInput* color_input_; - NodeInput* gizmo_drag_; QPointF gizmo_drag_start_; diff --git a/app/node/generator/solid/solid.cpp b/app/node/generator/solid/solid.cpp index 11afc2ce2..d6b98a46d 100644 --- a/app/node/generator/solid/solid.cpp +++ b/app/node/generator/solid/solid.cpp @@ -24,13 +24,12 @@ namespace olive { +const QString SolidGenerator::kColorInput = QStringLiteral("color_in"); + SolidGenerator::SolidGenerator() { // Default to a color that isn't black - color_input_ = new NodeInput(this, - QStringLiteral("color_in"), - NodeValue::kColor, - QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 0.0f, 0.0f, 1.0f))); } Node *SolidGenerator::copy() const @@ -60,13 +59,15 @@ QString SolidGenerator::Description() const void SolidGenerator::Retranslate() { - color_input_->set_name(tr("Color")); + SetInputName(kColorInput, tr("Color")); } -NodeValueTable SolidGenerator::Value(NodeValueDatabase &value) const +NodeValueTable SolidGenerator::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(color_input_, value); + job.InsertValue(this, kColorInput, value); NodeValueTable table = value.Merge(); table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); diff --git a/app/node/generator/solid/solid.h b/app/node/generator/solid/solid.h index dbf21cf93..bf4aa6544 100644 --- a/app/node/generator/solid/solid.h +++ b/app/node/generator/solid/solid.h @@ -40,11 +40,10 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; -private: - NodeInput* color_input_; + static const QString kColorInput; }; diff --git a/app/node/generator/text/text.cpp b/app/node/generator/text/text.cpp index 55746b5cb..18cb9532d 100644 --- a/app/node/generator/text/text.cpp +++ b/app/node/generator/text/text.cpp @@ -30,31 +30,23 @@ enum TextVerticalAlign { kVerticalAlignBottom, }; +const QString TextGenerator::kTextInput = QStringLiteral("text_in"); +const QString TextGenerator::kColorInput = QStringLiteral("color_in"); +const QString TextGenerator::kVAlignInput = QStringLiteral("valign_in"); +const QString TextGenerator::kFontInput = QStringLiteral("font_in"); +const QString TextGenerator::kFontSizeInput = QStringLiteral("font_size_in"); + TextGenerator::TextGenerator() { - text_input_ = new NodeInput(this, - QStringLiteral("text_in"), - NodeValue::kText, - tr("Sample Text")); + AddInput(kTextInput, NodeValue::kText, tr("Sample Text")); - color_input_ = new NodeInput(this, - QStringLiteral("color_in"), - NodeValue::kColor, - QVariant::fromValue(Color(1.0f, 1.0f, 1.0))); + AddInput(kColorInput, NodeValue::kColor, QVariant::fromValue(Color(1.0f, 1.0f, 1.0))); - valign_input_ = new NodeInput(this, - QStringLiteral("valign_in"), - NodeValue::kCombo, - 1); + AddInput(kVAlignInput, NodeValue::kCombo, 1); - font_input_ = new NodeInput(this, - QStringLiteral("font_in"), - NodeValue::kFont); + AddInput(kFontInput, NodeValue::kFont); - font_size_input_ = new NodeInput(this, - QStringLiteral("font_size_in"), - NodeValue::kFloat, - 72.0f); + AddInput(kFontSizeInput, NodeValue::kFloat, 72.0f); } Node *TextGenerator::copy() const @@ -84,27 +76,29 @@ QString TextGenerator::Description() const void TextGenerator::Retranslate() { - text_input_->set_name(tr("Text")); - font_input_->set_name(tr("Font")); - font_size_input_->set_name(tr("Font Size")); - color_input_->set_name(tr("Color")); - valign_input_->set_name(tr("Vertical Align")); - valign_input_->set_combobox_strings({tr("Top"), tr("Center"), tr("Bottom")}); + SetInputName(kTextInput, tr("Text")); + SetInputName(kFontInput, tr("Font")); + SetInputName(kFontSizeInput, tr("Font Size")); + SetInputName(kColorInput, tr("Color")); + SetInputName(kVAlignInput, tr("Vertical Align")); + SetComboBoxStrings(kVAlignInput, {tr("Top"), tr("Center"), tr("Bottom")}); } -NodeValueTable TextGenerator::Value(NodeValueDatabase &value) const +NodeValueTable TextGenerator::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + GenerateJob job; - job.InsertValue(text_input_, value); - job.InsertValue(color_input_, value); - job.InsertValue(valign_input_, value); - job.InsertValue(font_input_, value); - job.InsertValue(font_size_input_, value); + job.InsertValue(this, kTextInput, value); + job.InsertValue(this, kColorInput, value); + job.InsertValue(this, kVAlignInput, value); + job.InsertValue(this, kFontInput, value); + job.InsertValue(this, kFontSizeInput, value); job.SetAlphaChannelRequired(true); NodeValueTable table = value.Merge(); - if (!job.GetValue(text_input_).data().toString().isEmpty()) { + if (!job.GetValue(kTextInput).data().toString().isEmpty()) { table.Push(NodeValue::kGenerateJob, QVariant::fromValue(job), this); } @@ -124,14 +118,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(kFontInput).data().toString()); + default_font.setPointSizeF(job.GetValue(kFontSizeInput).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(kTextInput).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 +138,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(kVAlignInput).data().toInt()); int doc_height = text_doc.size().height(); switch (valign) { @@ -165,7 +159,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(kColorInput).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/generator/text/text.h b/app/node/generator/text/text.h index f672459a5..1ea6a69cf 100644 --- a/app/node/generator/text/text.h +++ b/app/node/generator/text/text.h @@ -40,20 +40,15 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const override; -private: - NodeInput* text_input_; - - NodeInput* color_input_; - - NodeInput* valign_input_; - - NodeInput* font_input_; - - NodeInput* font_size_input_; + static const QString kTextInput; + static const QString kColorInput; + static const QString kVAlignInput; + static const QString kFontInput; + static const QString kFontSizeInput; }; diff --git a/app/node/graph.h b/app/node/graph.h index c52eecdfa..016461b0d 100644 --- a/app/node/graph.h +++ b/app/node/graph.h @@ -65,11 +65,11 @@ signals: */ void NodeRemoved(Node* node); - void InputConnected(Node* output, NodeInput* input, int element); + void InputConnected(const NodeOutput& output, const NodeInput& input); - void InputDisconnected(Node* output, NodeInput* input, int element); + void InputDisconnected(const NodeOutput& output, const NodeInput& input); - void ValueChanged(NodeInput* input, int element); + void ValueChanged(const NodeInput& input); protected: virtual void childEvent(QChildEvent* event) override; diff --git a/app/node/input.cpp b/app/node/input.cpp deleted file mode 100644 index d694ac70d..000000000 --- a/app/node/input.cpp +++ /dev/null @@ -1,905 +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 "input.h" - -#include "common/bezier.h" -#include "common/lerp.h" -#include "common/xmlutils.h" -#include "core.h" -#include "node.h" -#include "project/item/footage/stream.h" -#include "widget/nodeview/nodeviewundo.h" - -#define super NodeConnectable - -namespace olive { - -NodeInput::NodeInput(Node* parent, const QString& id, NodeValue::Type type, const SplitValue &default_value) -{ - Init(parent, id, type, default_value); -} - -NodeInput::NodeInput(Node *parent, const QString &id, NodeValue::Type type, const QVariant &default_value) -{ - Init(parent, id, type, NodeValue::split_normal_value_into_track_values(type, default_value)); -} - -NodeInput::NodeInput(Node* parent, const QString &id, NodeValue::Type type) -{ - Init(parent, id, type, SplitValue()); -} - -NodeInput::~NodeInput() -{ - // Disconnect everything - DisconnectAll(); - - // Delete instances - delete primary_; - qDeleteAll(subinputs_); -} - -QString NodeInput::name() const -{ - if (name_.isEmpty()) { - return tr("Input"); - } - - return name_; -} - -void NodeInput::DisconnectAll() -{ - std::map copied_edges = edges(); - - for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { - DisconnectEdge(it->second, this, it->first); - } -} - -void NodeInput::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) -{ - while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } - - 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()); - } - } - - int element_counter = 0; - - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("element")) { - LoadImmediate(reader, element_counter, xml_node_data, cancelled); - - element_counter++; - } else { - reader->skipCurrentElement(); - } - } - } else if (reader->name() == QStringLiteral("connections")) { - // Load connections - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("connection")) { - 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 { - reader->skipCurrentElement(); - } - } -} - -void NodeInput::Save(QXmlStreamWriter *writer) const -{ - writer->writeAttribute(QStringLiteral("id"), id()); - - 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->first)); - - writer->writeCharacters(QString::number(reinterpret_cast(it->second))); - - 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::InvalidateFromKeyframeTimeChange); - connect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged); - connect(key, &NodeKeyframe::ValueChanged, this, &NodeInput::InvalidateFromKeyframeValueChange); - connect(key, &NodeKeyframe::TypeChanged, this, &NodeInput::InvalidateFromKeyframeTypeChanged); - connect(key, &NodeKeyframe::BezierControlInChanged, this, &NodeInput::InvalidateFromKeyframeBezierInChange); - connect(key, &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::InvalidateFromKeyframeBezierOutChange); - - 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::InvalidateFromKeyframeTimeChange); - disconnect(key, &NodeKeyframe::TimeChanged, this, &NodeInput::KeyframeTimeChanged); - disconnect(key, &NodeKeyframe::ValueChanged, this, &NodeInput::InvalidateFromKeyframeValueChange); - disconnect(key, &NodeKeyframe::TypeChanged, this, &NodeInput::InvalidateFromKeyframeTypeChanged); - disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &NodeInput::InvalidateFromKeyframeBezierInChange); - disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &NodeInput::InvalidateFromKeyframeBezierOutChange); - - GetImmediate(key->element())->remove_keyframe(key); - - emit KeyframeRemoved(key); - emit ValueChanged(time_affected, key->element()); - } - } -} - -void NodeInput::ClearElement(int index) -{ - GetImmediate(index)->delete_all_keyframes(); - - if (IsKeyframable()) { - SetIsKeyframing(false, index); - } - - SetSplitStandardValue(default_value_, index); -} - -void NodeInput::Init(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& default_val) -{ - setParent(parent); - - id_ = id; - keyframable_ = true; - connectable_ = true; - default_value_ = default_val; - array_size_ = 0; - data_type_ = type; - is_array_ = false; - - primary_ = CreateImmediate(); -} - -void NodeInput::LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) -{ - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("standard")) { - // Load standard value - int val_index = 0; - - while (XMLReadNextStartElement(reader)) { - if (cancelled && *cancelled) { - return; - } - - if (reader->name() == QStringLiteral("track")) { - 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") && IsKeyframable()) { - 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 -{ - if (IsKeyframable()) { - writer->writeTextElement(QStringLiteral("keyframing"), QString::number(IsKeyframing(element))); - } - - // Write standard value - writer->writeStartElement("standard"); - - foreach (const QVariant& v, GetSplitStandardValue(element)) { - writer->writeTextElement("track", ValueToString(v)); - } - - writer->writeEndElement(); // standard - - // Write keyframes - writer->writeStartElement("keyframes"); - - foreach (const NodeKeyframeTrack& track, GetKeyframeTracks(element)) { - writer->writeStartElement("track"); - - foreach (NodeKeyframe* key, track) { - writer->writeStartElement("key"); - - writer->writeAttribute("time", key->time().toString()); - writer->writeAttribute("type", QString::number(key->type())); - writer->writeAttribute("inhandlex", QString::number(key->bezier_control_in().x())); - writer->writeAttribute("inhandley", QString::number(key->bezier_control_in().y())); - writer->writeAttribute("outhandlex", QString::number(key->bezier_control_out().x())); - writer->writeAttribute("outhandley", QString::number(key->bezier_control_out().y())); - - writer->writeCharacters(ValueToString(key->value())); - - writer->writeEndElement(); // key - } - - writer->writeEndElement(); // track - } - - writer->writeEndElement(); // keyframes - - 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()); - } -} - -void NodeInput::ArrayResizeInternal(int size) -{ - if (array_size_ != size) { - // Update array size - if (array_size_ < size) { - // Size is larger, create any immediates that don't exist - for (int i=subinputs_.size(); i &list, bool traverse, bool exclusive_only) const -{ - for (auto it=input_connections().cbegin(); it!=input_connections().cend(); it++) { - Node* connected = it->second; - - if (connected->edges().size() == 1 || !exclusive_only) { - if (!list.contains(connected)) { - list.append(connected); - - if (traverse) { - foreach (NodeInput* i, connected->parameters()) { - i->GetDependencies(list, traverse, exclusive_only); - } - } - } - } - } -} - -QVariant NodeInput::GetDefaultValueForTrack(int track) const -{ - if (default_value_.isEmpty()) { - return QVariant(); - } - - return default_value_.at(track); -} - -QVector NodeInput::GetDependencies(bool traverse, bool exclusive_only) const -{ - QVector list; - - GetDependencies(list, traverse, exclusive_only); - - return list; -} - -QVector NodeInput::GetExclusiveDependencies() const -{ - return GetDependencies(true, true); -} - -QVector NodeInput::GetImmediateDependencies() const -{ - return GetDependencies(false, false); -} - -void NodeInput::ArrayAppend(bool undoable) -{ - ArrayResize(ArraySize() + 1, undoable); -} - -void NodeInput::ArrayInsert(int index, bool undoable) -{ - if (undoable) { - Core::instance()->undo_stack()->push(new ArrayInsertCommand(this, index)); - } else { - // Add new input - ArrayResizeInternal(ArraySize() + 1); - - // Move connections down - std::map copied_edges = edges(); - for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) { - if (it->first >= index) { - // Disconnect this and reconnect it one element down - DisconnectEdge(it->second, this, it->first); - ConnectEdge(it->second, this, it->first + 1); - } - } - - // Shift values and keyframes up one element - for (int i=ArraySize()-1; i>index; i--) { - CopyValuesOfElement(this, this, i-1, i); - } - - // Reset value of element we just "inserted" - ClearElement(index); - } -} - -void NodeInput::ArrayRemove(int index, bool undoable) -{ - if (undoable) { - Core::instance()->undo_stack()->push(new ArrayRemoveCommand(this, index)); - } else { - // Remove input - ArrayResizeInternal(ArraySize() - 1); - - // Move connections up - std::map copied_edges = edges(); - for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { - if (it->first >= index) { - // Disconnect this and reconnect it one element up if it's not the element being removed - DisconnectEdge(it->second, this, it->first); - - if (it->first > index) { - ConnectEdge(it->second, this, it->first - 1); - } - } - } - - // Shift values and keyframes down one element - for (int i=index; iundo_stack()->push(c); - } else { - c->redo(); - delete c; - } -} - -void NodeInput::ArrayRemoveLast(bool undoable) -{ - ArrayResize(ArraySize() - 1, undoable); -} - -QVariant NodeInput::GetValueAtTime(const rational &time, int element) const -{ - return NodeValue::combine_track_values_into_normal_value(data_type_, GetSplitValuesAtTime(time, element)); -} - -SplitValue NodeInput::GetSplitValuesAtTime(const rational &time, int element) const -{ - SplitValue vals; - - int nb_tracks = GetNumberOfKeyframeTracks(); - - for (int i=0;itime() >= time) { - // This time precedes any keyframe, so we just return the first value - return key_track.first()->value(); - } - - if (key_track.last()->time() <= time) { - // This time is after any keyframes so we return the last value - return key_track.last()->value(); - } - - // If we're here, the time must be somewhere in between the keyframes - for (int i=0;itime() == time - || !NodeValue::type_can_be_interpolated(data_type_) - || (before->time() < time && before->type() == NodeKeyframe::kHold)) { - - // Time == keyframe time, so value is precise - return before->value(); - - } else if (after->time() == time) { - - // Time == keyframe time, so value is precise - return after->value(); - - } else if (before->time() < time && after->time() > time) { - // We must interpolate between these keyframes - - if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { - // Perform a cubic bezier with two control points - - double t = Bezier::CubicXtoT(time.toDouble(), - before->time().toDouble(), - before->time().toDouble() + before->valid_bezier_control_out().x(), - after->time().toDouble() + after->valid_bezier_control_in().x(), - after->time().toDouble()); - - double y = Bezier::CubicTtoY(before->value().toDouble(), - before->value().toDouble() + before->valid_bezier_control_out().y(), - after->value().toDouble() + after->valid_bezier_control_in().y(), - after->value().toDouble(), - t); - - return y; - - } else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) { - // Perform a quadratic bezier with only one control point - - QPointF control_point; - double control_point_time; - double control_point_value; - - if (before->type() == NodeKeyframe::kBezier) { - control_point = before->valid_bezier_control_out(); - control_point_time = before->time().toDouble() + control_point.x(); - control_point_value = before->value().toDouble() + control_point.y(); - } else { - control_point = after->valid_bezier_control_in(); - control_point_time = after->time().toDouble() + control_point.x(); - control_point_value = after->value().toDouble() + control_point.y(); - } - - // Generate T from time values - used to determine bezier progress - double t = Bezier::QuadraticXtoT(time.toDouble(), before->time().toDouble(), control_point_time, after->time().toDouble()); - - // Generate value using T - double y = Bezier::QuadraticTtoY(before->value().toDouble(), control_point_value, after->value().toDouble(), t); - - return y; - - } else { - // To have arrived here, the keyframes must both be linear - qreal period_progress = (time.toDouble() - before->time().toDouble()) / (after->time().toDouble() - before->time().toDouble()); - - return lerp(before->value().toDouble(), after->value().toDouble(), period_progress); - } - } - } - } - - return GetStandardValueOnTrack(track, element); -} - -bool NodeInput::IsKeyframable() const -{ - return keyframable_; -} - -void NodeInput::SetKeyframable(bool k) -{ - keyframable_ = k; -} - -void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_connections, bool traverse_arrays) -{ - Q_ASSERT(source->id() == 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); - } - } - - // 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->second, dest, it->first); - } - } else { - // Just copy the primary connection (at -1) - if (source->IsConnected()) { - ConnectEdge(source->GetConnectedNode(), dest); - } - } - } -} - -void NodeInput::CopyValuesOfElement(NodeInput *src, NodeInput *dst, int src_element, int dst_element) -{ - if (dst_element >= dst->subinputs_.size()) { - qDebug() << "Ignored destination element that was out of array bounds"; - return; - } - - // Copy standard value - dst->SetSplitStandardValue(src->GetSplitStandardValue(src_element), dst_element); - - // Copy keyframes - dst->GetImmediate(dst_element)->delete_all_keyframes(); - foreach (const NodeKeyframeTrack& track, src->GetImmediate(src_element)->keyframe_tracks()) { - foreach (NodeKeyframe* key, track) { - key->copy(dst_element, dst); - } - } - - // Copy keyframing state - if (src->IsKeyframable()) { - dst->SetIsKeyframing(src->IsKeyframing(src_element), dst_element); - } - - // If this is the root of an array, copy the array size - if (src_element == -1 && dst_element == -1) { - dst->ArrayResize(src->ArraySize()); - } -} - -QStringList NodeInput::get_combobox_strings() const -{ - return property("combo_str").toStringList(); -} - -void NodeInput::set_combobox_strings(const QStringList &strings) -{ - setProperty("combo_str", strings); -} - -void NodeInput::InvalidateFromKeyframeBezierInChange() -{ - NodeKeyframe* key = static_cast(sender()); - const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); - int keyframe_index = track.indexOf(key); - - rational start = RATIONAL_MIN; - rational end = key->time(); - - if (keyframe_index > 0) { - start = track.at(keyframe_index - 1)->time(); - } - - emit ValueChanged(TimeRange(start, end), key->element()); -} - -void NodeInput::InvalidateFromKeyframeBezierOutChange() -{ - NodeKeyframe* key = static_cast(sender()); - const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); - int keyframe_index = track.indexOf(key); - - rational start = key->time(); - rational end = RATIONAL_MAX; - - if (keyframe_index < track.size() - 1) { - end = track.at(keyframe_index + 1)->time(); - } - - emit ValueChanged(TimeRange(start, end), key->element()); -} - -void NodeInput::InvalidateFromKeyframeTimeChange() -{ - NodeKeyframe* key = static_cast(sender()); - NodeInputImmediate* immediate = GetImmediate(key->element()); - TimeRange original_range = get_range_affected_by_keyframe(key); - - TimeRangeList invalidate_range; - invalidate_range.insert(original_range); - - if (!(original_range.in() < key->time() && 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)); - } - - // 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::InvalidateFromKeyframeValueChange() -{ - NodeKeyframe* key = static_cast(sender()); - emit ValueChanged(get_range_affected_by_keyframe(key), key->element()); -} - -void NodeInput::InvalidateFromKeyframeTypeChanged() -{ - NodeKeyframe* key = static_cast(sender()); - const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); - - if (track.size() == 1) { - // If there are no other frames, the interpolation won't do anything - return; - } - - // 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 -{ - const NodeKeyframeTrack& key_track = GetTrackFromKeyframe(key); - int keyframe_index = key_track.indexOf(key); - - 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 - && keyframe_index > 0 - && key_track.at(keyframe_index - 1)->type() == NodeKeyframe::kHold) { - range.set_in(key->time()); - } - - return range; -} - -TimeRange NodeInput::get_range_around_index(int index, int track, int element) const -{ - rational range_begin = RATIONAL_MIN; - rational range_end = RATIONAL_MAX; - - const NodeKeyframeTrack& key_track = GetImmediate(element)->keyframe_tracks().at(track); - - if (key_track.size() > 1) { - if (index > 0) { - // If this is not the first key, we'll need to limit it to the key just before - range_begin = key_track.at(index - 1)->time(); - } - if (index < key_track.size() - 1) { - // If this is not the last key, we'll need to limit it to the key just after - range_end = key_track.at(index + 1)->time(); - } - } - - return TimeRange(range_begin, range_end); -} - -QString NodeInput::ValueToString(const QVariant &value) const -{ - return NodeValue::ValueToString(data_type_, value, true); -} - -QVariant NodeInput::StringToValue(const QString &string, QList& footage_connections, int element) -{ - if (data_type_ == NodeValue::kFootage) { - footage_connections.append({this, element, string.toULongLong()}); - } - - return NodeValue::StringToValue(data_type_, string, true); -} - -uint qHash(const NodeInput::KeyframeTrackReference &ref, uint seed) -{ - return qHash(ref.input, seed) ^ qHash(ref.element, seed) ^ qHash(ref.track, seed); -} - -Project *NodeInput::ArrayRemoveCommand::GetRelevantProject() const -{ - return input_->parent()->parent()->project(); -} - -Project *NodeInput::ArrayResizeCommand::GetRelevantProject() const -{ - return input_->parent()->parent()->project(); -} - -Project *NodeInput::ArrayInsertCommand::GetRelevantProject() const -{ - return input_->parent()->parent()->project(); -} - -} diff --git a/app/node/input.h b/app/node/input.h deleted file mode 100644 index 567bbe942..000000000 --- a/app/node/input.h +++ /dev/null @@ -1,629 +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 NODEINPUT_H -#define NODEINPUT_H - -#include "common/timerange.h" -#include "keyframe.h" -#include "node/connectable.h" -#include "node/inputimmediate.h" -#include "node/value.h" -#include "splitvalue.h" -#include "undo/undocommand.h" - -namespace olive { - -class Node; - -/** - * @brief A node parameter designed to take either user input or data from another node - */ -class NodeInput : public NodeConnectable -{ - Q_OBJECT -public: - /** - * @brief NodeInput Constructor - * - * @param id - * - * Unique ID associated with this parameter for this Node. This ID only has to be unique within this Node. Used for - * 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(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& 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 ~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 Deliberately shadow QObject parent, since we only expect NodeInput to have Node as a parent - */ - Node* parent() const; - - /** - * @brief The data type this parameter outputs - * - * This can be used in conjunction with NodeInput::can_accept_type() to determine whether this parameter can be - * connected to it. - */ - NodeValue::Type GetDataType() const - { - return data_type_; - } - - void SetDataType(NodeValue::Type type) - { - data_type_ = type; - - emit DataTypeChanged(type); - } - - const std::map& edges() const - { - return input_connections(); - } - - bool IsConnected(int element = -1) const - { - return input_connections().find(element) != input_connections().end(); - } - - /** - * @brief Returns TRUE if the value is expected to always be the same (i.e. no keyframes and - * not connected to anything) - */ - bool IsStatic(int element = -1) const - { - return !IsConnected(element) && !GetImmediate(element)->is_keyframing(); - } - - Node* GetConnectedNode(int element = -1) const - { - return input_connections().at(element); - } - - bool IsConnectable() const - { - return connectable_; - } - - void SetConnectable(bool e) - { - connectable_ = e; - } - - const QVector &GetKeyframeTracks(int element = -1) const - { - return GetImmediate(element)->keyframe_tracks(); - } - - NodeKeyframe* GetKeyframeAtTimeOnTrack(const rational& time, int track, int element) const - { - return GetImmediate(element)->get_keyframe_at_time_on_track(time, track); - } - - 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 IsKeyframable() const; - - bool IsKeyframing(int element = -1) const - { - return GetImmediate(element)->is_keyframing(); - } - - /** - * @brief Get non-keyframed value - */ - 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 SplitValue& 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 SetStandardValue(const QVariant& value, int element = -1) - { - SetSplitStandardValue(NodeValue::split_normal_value_into_track_values(data_type_, value), element); - } - - void SetSplitStandardValue(const SplitValue& 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 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); - - static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int src_element, int dst_element); - static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int element) - { - CopyValuesOfElement(source, dst, element, element); - } - - QStringList get_combobox_strings() const; - - void set_combobox_strings(const QStringList& strings); - - void GetDependencies(QVector &list, bool traverse, bool exclusive_only) const; - - QVariant GetDefaultValue() const - { - return NodeValue::combine_track_values_into_normal_value(data_type_, default_value_); - } - - const SplitValue& GetSplitDefaultValue() const - { - return default_value_; - } - - QVariant GetDefaultValueForTrack(int track) const; - - QVector GetDependencies(bool traverse = true, bool exclusive_only = false) const; - - QVector GetExclusiveDependencies() const; - - 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) - { - if (!IsKeyframable()) { - qDebug() << "Ignored set keyframing because this input is not keyframable"; - return; - } - - GetImmediate(element)->set_is_keyframing(keyframing); - - emit KeyframeEnableChanged(keyframing, element); - } - - /// Alias for ArrayResize(ArraySize() + 1) - void ArrayAppend(bool undoable = false); - - void ArrayInsert(int index, bool undoable = false); - - void ArrayRemove(int index, bool undoable = false); - - /// Alias for ArrayInsert(0) - void ArrayPrepend(bool undoable = false); - - void ArrayResize(int size, bool undoable = false); - - /// Alias for ArrayResize(ArraySize() - 1) - void ArrayRemoveLast(bool undoable = false); - - int ArraySize() const - { - return array_size_; - } - - 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; - - SplitValue 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); - } - - struct KeyframeTrackReference { - NodeInput* input; - int element; - int track; - - bool operator==(const KeyframeTrackReference& rhs) const - { - return input == rhs.input && element == rhs.element && track == rhs.track; - } - }; - -signals: - void NameChanged(const QString& name); - - void ValueChanged(const olive::TimeRange& range, int element); - - void KeyframeAdded(NodeKeyframe* key); - - void KeyframeRemoved(NodeKeyframe* key); - - void KeyframeTimeChanged(); - - void PropertyChanged(const QString& s, const QVariant& v); - - void ArraySizeChanged(int size); - - void KeyframeEnableChanged(bool enabled, int element); - - void DataTypeChanged(NodeValue::Type type); - - void InputConnected(Node* source, int element); - - void InputDisconnected(Node* source, int element); - -protected: - virtual bool event(QEvent* e) override; - - virtual void childEvent(QChildEvent* e) override; - -private: - class ArrayInsertCommand : public UndoCommand - { - public: - ArrayInsertCommand(NodeInput* input, int index) : - input_(input), - index_(index) - { - } - - virtual Project* GetRelevantProject() const override; - - virtual void redo() override - { - input_->ArrayInsert(index_, false); - } - - virtual void undo() override - { - input_->ArrayRemove(index_, false); - } - - private: - NodeInput* input_; - int index_; - - }; - - class ArrayRemoveCommand : public UndoCommand - { - public: - ArrayRemoveCommand(NodeInput* input, int index) : - input_(input), - index_(index) - { - } - - virtual Project* GetRelevantProject() const override; - - protected: - virtual void redo() override - { - // Save immediate data - if (input_->IsKeyframable()) { - is_keyframing_ = input_->IsKeyframing(index_); - } - standard_value_ = input_->GetSplitStandardValue(index_); - keyframes_ = input_->GetImmediate(index_)->keyframe_tracks(); - input_->GetImmediate(index_)->delete_all_keyframes(&memory_manager_); - - input_->ArrayRemove(index_, false); - } - - virtual void undo() override - { - input_->ArrayInsert(index_, false); - - // Restore keyframes - foreach (const NodeKeyframeTrack& track, keyframes_) { - foreach (NodeKeyframe* key, track) { - key->setParent(input_); - } - } - input_->SetSplitStandardValue(standard_value_, index_); - - if (input_->IsKeyframable()) { - input_->SetIsKeyframing(is_keyframing_, index_); - } - } - - private: - NodeInput* input_; - int index_; - - SplitValue standard_value_; - bool is_keyframing_; - QVector keyframes_; - QObject memory_manager_; - - }; - - class ArrayResizeCommand : public UndoCommand - { - public: - ArrayResizeCommand(NodeInput* input, int size) : - input_(input), - size_(size) - {} - - virtual void redo() override - { - old_size_ = input_->array_size_; - - if (old_size_ > size_) { - // Decreasing in size, disconnect any extraneous edges - for (int i=size_; iedges().at(i); - - removed_connections_.insert(i, output); - - DisconnectEdge(output, input_, i); - } catch (std::out_of_range&) {} - } - } - - input_->ArrayResizeInternal(size_); - } - - virtual void undo() override - { - for (auto it=removed_connections_.cbegin(); it!=removed_connections_.cend(); it++) { - ConnectEdge(it.value(), input_, it.key()); - } - removed_connections_.clear(); - - input_->ArrayResizeInternal(old_size_); - } - - virtual Project* GetRelevantProject() const override; - - private: - NodeInput* input_; - int size_; - int old_size_; - - QHash removed_connections_; - - }; - - void ClearElement(int index); - - void Init(Node *parent, const QString& id, NodeValue::Type type, const SplitValue &default_val); - - void LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled); - - void SaveImmediate(QXmlStreamWriter *writer, int element) const; - - void ArrayResizeInternal(int size); - - NodeInputImmediate* CreateImmediate(); - - 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, int element); - - /** - * @brief Intelligently determine how what time range is affected by a keyframe - */ - TimeRange get_range_affected_by_keyframe(NodeKeyframe *key) const; - - /** - * @brief Gets a time range between the previous and next keyframes of index - */ - TimeRange get_range_around_index(int index, int track, int element) const; - - NodeInputImmediate* primary_; - - QVector subinputs_; - - SplitValue default_value_; - - /** - * @brief Unique identifier of this input within this node - */ - QString id_; - - /** - * @brief User displayable name of input - */ - QString name_; - - /** - * @brief Internal keyframable value - */ - bool keyframable_; - - bool connectable_; - - bool is_array_; - - int array_size_; - - /** - * @brief Default data type - */ - NodeValue::Type data_type_; - -private slots: - /** - * @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time - */ - void InvalidateFromKeyframeTimeChange(); - - /** - * @brief Slot when a keyframe's value changes to signal that the cache needs updating - */ - void InvalidateFromKeyframeValueChange(); - - /** - * @brief Slot when a keyframe's type changes to signal that the cache needs updating - */ - void InvalidateFromKeyframeTypeChanged(); - - /** - * @brief Slot when a keyframe's bezier in value changes to signal that the cache needs updating - */ - void InvalidateFromKeyframeBezierInChange(); - - /** - * @brief Slot when a keyframe's bezier out value changes to signal that the cache needs updating - */ - void InvalidateFromKeyframeBezierOutChange(); - -}; - -uint qHash(const NodeInput::KeyframeTrackReference& ref, uint seed = 0); - -} - -#endif // NODEINPUT_H diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 09211a58e..4c2805c28 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -25,13 +25,12 @@ namespace olive { +const QString MediaInput::kFootageInput = QStringLiteral("footage_in"); + MediaInput::MediaInput() : connected_footage_(nullptr) { - 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(kFootageInput, NodeValue::kFootage, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); } QVector MediaInput::Category() const @@ -41,21 +40,23 @@ QVector MediaInput::Category() const Stream *MediaInput::stream() const { - return Node::ValueToPtr(footage_input_->GetStandardValue()); + return Node::ValueToPtr(GetStandardValue(kFootageInput)); } void MediaInput::SetStream(Stream* s) { - footage_input_->SetStandardValue(Node::PtrToValue(s)); + SetStandardValue(kFootageInput, Node::PtrToValue(s)); } void MediaInput::Retranslate() { - footage_input_->set_name(tr("Media")); + SetInputName(kFootageInput, tr("Media")); } -NodeValueTable MediaInput::Value(NodeValueDatabase &value) const +NodeValueTable MediaInput::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + NodeValueTable table = value.Merge(); if (connected_footage_) { @@ -68,28 +69,32 @@ NodeValueTable MediaInput::Value(NodeValueDatabase &value) const return table; } -void MediaInput::FootageChanged() +void MediaInput::InputValueChangedEvent(const QString &input, int element) { - Stream* new_footage = footage_input_->GetStandardValue().value(); + Q_UNUSED(element) - if (new_footage == connected_footage_) { - return; - } + if (input == kFootageInput) { + Stream* new_footage = stream(); - if (connected_footage_) { - disconnect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); - } + if (new_footage == connected_footage_) { + return; + } - connected_footage_ = new_footage; + if (connected_footage_) { + disconnect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); + } - if (connected_footage_) { - connect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); + connected_footage_ = new_footage; + + if (connected_footage_) { + connect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); + } } } void MediaInput::FootageParametersChanged() { - InvalidateCache(TimeRange(0, RATIONAL_MAX), InputConnection(footage_input_)); + InvalidateCache(TimeRange(0, RATIONAL_MAX), kFootageInput); } } diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index 6dc2b8270..c3c50bf33 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -63,16 +63,16 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; + + static const QString kFootageInput; protected: - NodeInput* footage_input_; + virtual void InputValueChangedEvent(const QString& input, int element); Stream* connected_footage_; private slots: - void FootageChanged(); - void FootageParametersChanged(); }; diff --git a/app/node/input/time/timeinput.cpp b/app/node/input/time/timeinput.cpp index 705f6fc16..5eadf8b83 100644 --- a/app/node/input/time/timeinput.cpp +++ b/app/node/input/time/timeinput.cpp @@ -51,8 +51,10 @@ QString TimeInput::Description() const return tr("Generates the time (in seconds) at this frame"); } -NodeValueTable TimeInput::Value(NodeValueDatabase &value) const +NodeValueTable TimeInput::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + NodeValueTable table = value.Merge(); table.Push(NodeValue::kFloat, diff --git a/app/node/input/time/timeinput.h b/app/node/input/time/timeinput.h index eb2dac8f2..85f5b3d39 100644 --- a/app/node/input/time/timeinput.h +++ b/app/node/input/time/timeinput.h @@ -38,7 +38,7 @@ public: virtual QVector Category() const override; virtual QString Description() const override; - virtual NodeValueTable Value(NodeValueDatabase& value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override; virtual void Hash(QCryptographicHash& hash, const rational& time) const override; diff --git a/app/node/inputdragger.cpp b/app/node/inputdragger.cpp index 2060b37d2..0d835fc77 100644 --- a/app/node/inputdragger.cpp +++ b/app/node/inputdragger.cpp @@ -26,61 +26,64 @@ namespace olive { -NodeInputDragger::NodeInputDragger() : - input_(nullptr) +NodeInputDragger::NodeInputDragger() { } bool NodeInputDragger::IsStarted() const { - return input_; + return input_.IsValid(); } -void NodeInputDragger::Start(NodeInput *input, const rational &time, int track, int element) +void NodeInputDragger::Start(const NodeKeyframeTrackReference &input, const rational &time) { - Q_ASSERT(!input_); + Q_ASSERT(!IsStarted()); // Set up new drag input_ = input; time_ = time; - track_ = track; - element_ = element; + + Node* node = input_.input().node(); // Cache current value - start_value_ = input_->GetValueAtTimeForTrack(time, track, element_); + start_value_ = node->GetSplitValueAtTimeOnTrack(input_, time); // Determine whether we are creating a keyframe or not - if (input_->IsKeyframing(element_)) { - dragging_key_ = input_->GetKeyframeAtTimeOnTrack(time, track, element_); + if (input_.input().IsKeyframing()) { + dragging_key_ = node->GetKeyframeAtTimeOnTrack(input_, time); drag_created_key_ = !dragging_key_; if (drag_created_key_) { dragging_key_ = new NodeKeyframe(time, start_value_, - input_->GetBestKeyframeTypeForTime(time, track, element_), - track, - element_, - input_); + node->GetBestKeyframeTypeForTimeOnTrack(input_, time), + input_.track(), + input_.input().element(), + input_.input().input(), + node); } } } void NodeInputDragger::Drag(QVariant value) { - Q_ASSERT(input_); + Q_ASSERT(IsStarted()); - if (input_->property("min").isValid()) { + Node* node = input_.input().node(); + const QString& input = input_.input().input(); + + if (node->HasInputProperty(input, QStringLiteral("min"))) { // Assumes the value is a double of some kind - double min = input_->property("min").toDouble(); + double min = node->GetInputProperty(input, QStringLiteral("min")).toDouble(); double v = value.toDouble(); if (v < min) { value = min; } } - if (input_->property("max").isValid()) { - double max = input_->property("max").toDouble(); + if (node->HasInputProperty(input, QStringLiteral("max"))) { + double max = node->GetInputProperty(input, QStringLiteral("max")).toDouble(); double v = value.toDouble(); if (v > max) { value = max; @@ -91,10 +94,10 @@ void NodeInputDragger::Drag(QVariant value) //input_->blockSignals(true); - if (input_->IsKeyframing(element_)) { + if (input_.input().IsKeyframing()) { dragging_key_->set_value(value); } else { - input_->SetStandardValueOnTrack(value, track_, element_); + node->SetSplitStandardValueOnTrack(input_, value); } //input_->blockSignals(false); @@ -108,10 +111,10 @@ void NodeInputDragger::End() MultiUndoCommand* command = new MultiUndoCommand(); - if (input_->IsKeyframing(element_)) { + if (input_.input().node()->IsInputKeyframing(input_.input())) { if (drag_created_key_) { // We created a keyframe in this process - command->add_child(new NodeParamInsertKeyframeCommand(input_, dragging_key_)); + command->add_child(new NodeParamInsertKeyframeCommand(input_.input().node(), dragging_key_)); } // We just set a keyframe's value @@ -120,12 +123,12 @@ void NodeInputDragger::End() command->add_child(new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_)); } else { // We just set the standard value - command->add_child(new NodeParamSetStandardValueCommand(input_, track_, element_, end_value_, start_value_)); + command->add_child(new NodeParamSetStandardValueCommand(input_, end_value_, start_value_)); } Core::instance()->undo_stack()->push(command); - input_ = nullptr; + input_.Reset(); } } diff --git a/app/node/inputdragger.h b/app/node/inputdragger.h index fd8efb6e7..8743e7aff 100644 --- a/app/node/inputdragger.h +++ b/app/node/inputdragger.h @@ -21,7 +21,9 @@ #ifndef NODEINPUTDRAGGER_H #define NODEINPUTDRAGGER_H -#include "node/input.h" +#include "common/rational.h" +#include "node/keyframe.h" +#include "node/param.h" namespace olive { @@ -32,21 +34,17 @@ public: bool IsStarted() const; - void Start(NodeInput* input, const rational& time, int track, int element = -1); + void Start(const NodeKeyframeTrackReference& input, const rational& time); void Drag(QVariant value); void End(); private: - NodeInput* input_; - - int track_; + NodeKeyframeTrackReference input_; rational time_; - int element_; - QVariant start_value_; QVariant end_value_; diff --git a/app/node/inputimmediate.cpp b/app/node/inputimmediate.cpp index dd1f083bc..d3fae09ae 100644 --- a/app/node/inputimmediate.cpp +++ b/app/node/inputimmediate.cpp @@ -23,7 +23,6 @@ #include "common/bezier.h" #include "common/lerp.h" #include "common/tohex.h" -#include "input.h" namespace olive { diff --git a/app/node/inputimmediate.h b/app/node/inputimmediate.h index 86694214e..ff6c2794c 100644 --- a/app/node/inputimmediate.h +++ b/app/node/inputimmediate.h @@ -53,7 +53,12 @@ public: return standard_value_; } - void set_standard_value_on_track(const QVariant &value, int track = 0); + const QVariant& get_split_standard_value_on_track(int track) const + { + return standard_value_.at(track); + } + + void set_standard_value_on_track(const QVariant &value, int track); void set_split_standard_value(const SplitValue& value); diff --git a/app/node/keyframe.cpp b/app/node/keyframe.cpp index 0a49c9868..6062f6ad2 100644 --- a/app/node/keyframe.cpp +++ b/app/node/keyframe.cpp @@ -20,18 +20,19 @@ #include "keyframe.h" -#include "input.h" +#include "node.h" namespace olive { const NodeKeyframe::Type NodeKeyframe::kDefaultType = kLinear; -NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, const NodeKeyframe::Type &type, const int &track, int element, QObject *parent) : +NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, Type type, int track, int element, const QString &input, QObject *parent) : time_(time), value_(value), type_(type), bezier_control_in_(QPointF(0.0, 0.0)), bezier_control_out_(QPointF(0.0, 0.0)), + input_(input), track_(track), element_(element), previous_(nullptr), @@ -47,7 +48,7 @@ NodeKeyframe::~NodeKeyframe() NodeKeyframe *NodeKeyframe::copy(int element, QObject *parent) const { - NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element, parent); + NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element, input_, parent); copy->bezier_control_in_ = bezier_control_in_; copy->bezier_control_out_ = bezier_control_out_; return copy; @@ -58,9 +59,9 @@ NodeKeyframe *NodeKeyframe::copy(QObject* parent) const return copy(element_, parent); } -NodeInput *NodeKeyframe::parent() const +Node *NodeKeyframe::parent() const { - return static_cast(QObject::parent()); + return static_cast(QObject::parent()); } const rational &NodeKeyframe::time() const diff --git a/app/node/keyframe.h b/app/node/keyframe.h index 1659b4e48..2eb80d9b9 100644 --- a/app/node/keyframe.h +++ b/app/node/keyframe.h @@ -26,11 +26,11 @@ #include #include "common/rational.h" +#include "node/param.h" namespace olive { -class NodeInput; -class NodeInputImmediate; +class Node; /** * @brief A point of data to be used at a certain time and interpolated with other data @@ -61,14 +61,23 @@ public: /** * @brief NodeKeyframe Constructor */ - NodeKeyframe(const rational& time, const QVariant& value, const Type& type, const int& track, int element, QObject* parent = nullptr); + NodeKeyframe(const rational& time, const QVariant& value, Type type, int track, int element, const QString& input, QObject* parent = nullptr); virtual ~NodeKeyframe() override; NodeKeyframe* copy(int element, QObject* parent = nullptr) const; NodeKeyframe* copy(QObject* parent = nullptr) const; - NodeInput* parent() const; + Node* parent() const; + const QString& input() const + { + return input_; + } + + NodeKeyframeTrackReference key_track_ref() const + { + return NodeKeyframeTrackReference(NodeInput(parent(), input(), element()), track()); + } /** * @brief The time this keyframe is set at @@ -194,6 +203,8 @@ private: QPointF bezier_control_out_; + QString input_; + int track_; int element_; diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 58c9c97fd..1fa074028 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -22,19 +22,22 @@ namespace olive { +const QString MathNode::kMethodIn = QStringLiteral("method_in"); +const QString MathNode::kParamAIn = QStringLiteral("param_a_in"); +const QString MathNode::kParamBIn = QStringLiteral("param_b_in"); +const QString MathNode::kParamCIn = QStringLiteral("param_c_in"); + MathNode::MathNode() { - method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo); - method_in_->SetConnectable(false); - method_in_->SetKeyframable(false); + AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - 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(kParamAIn, NodeValue::kFloat, 0.0); + SetInputProperty(kParamAIn, QStringLiteral("decimalplaces"), 8); + SetInputProperty(kParamAIn, QStringLiteral("autotrim"), true); - 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(kParamBIn, NodeValue::kFloat, 0.0); + SetInputProperty(kParamBIn, QStringLiteral("decimalplaces"), 8); + SetInputProperty(kParamBIn, QStringLiteral("autotrim"), true); } Node *MathNode::copy() const @@ -66,9 +69,9 @@ void MathNode::Retranslate() { Node::Retranslate(); - method_in_->set_name(tr("Method")); - param_a_in_->set_name(tr("Value")); - param_b_in_->set_name(tr("Value")); + SetInputName(kMethodIn, tr("Method")); + SetInputName(kParamAIn, tr("Value")); + SetInputName(kParamBIn, tr("Value")); QStringList operations = {tr("Add"), tr("Subtract"), @@ -77,19 +80,21 @@ void MathNode::Retranslate() QString(), tr("Power")}; - method_in_->set_combobox_strings(operations); + SetComboBoxStrings(kMethodIn, operations); } ShaderCode MathNode::GetShaderCode(const QString &shader_id) const { - return GetShaderCodeInternal(shader_id, param_a_in_, param_b_in_); + return GetShaderCodeInternal(shader_id, kParamAIn, kParamBIn); } -NodeValueTable MathNode::Value(NodeValueDatabase &value) const +NodeValueTable MathNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + // Auto-detect what values to operate with // FIXME: Add manual override for this - PairingCalculator calc(value[param_a_in_], value[param_b_in_]); + PairingCalculator calc(value[kParamAIn], value[kParamBIn]); // Do nothing if no pairing was found if (!calc.FoundMostLikelyPairing()) { @@ -97,23 +102,23 @@ NodeValueTable MathNode::Value(NodeValueDatabase &value) const } NodeValue val_a = calc.GetMostLikelyValueA(); - value[param_a_in_].Remove(val_a); + value[kParamAIn].Remove(val_a); NodeValue val_b = calc.GetMostLikelyValueB(); - value[param_b_in_].Remove(val_b); + value[kParamBIn].Remove(val_b); return ValueInternal(value, GetOperation(), calc.GetMostLikelyPairing(), - param_a_in_, + kParamAIn, val_a, - param_b_in_, + kParamBIn, val_b); } void MathNode::ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const { - return ProcessSamplesInternal(values, GetOperation(), param_a_in_, param_b_in_, input, output, index); + return ProcessSamplesInternal(values, GetOperation(), kParamAIn, kParamBIn, input, output, index); } } diff --git a/app/node/math/math/math.h b/app/node/math/math/math.h index bf78b59c7..b988f3380 100644 --- a/app/node/math/math/math.h +++ b/app/node/math/math/math.h @@ -44,34 +44,22 @@ public: Operation GetOperation() const { - return static_cast(method_in_->GetStandardValue().toInt()); + return static_cast(GetStandardValue(kMethodIn).toInt()); } void SetOperation(Operation o) { - method_in_->SetStandardValue(o); + SetStandardValue(kMethodIn, o); } - NodeInput* param_a_in() const - { - return param_a_in_; - } - - NodeInput* param_b_in() const - { - return param_b_in_; - } - - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; virtual void ProcessSamples(NodeValueDatabase &values, const SampleBufferPtr input, SampleBufferPtr output, int index) const override; -private: - NodeInput* method_in_; - - NodeInput* param_a_in_; - - NodeInput* param_b_in_; + static const QString kMethodIn; + static const QString kParamAIn; + static const QString kParamBIn; + static const QString kParamCIn; }; diff --git a/app/node/math/math/mathbase.cpp b/app/node/math/math/mathbase.cpp index d81e3bcb9..172b17db7 100644 --- a/app/node/math/math/mathbase.cpp +++ b/app/node/math/math/mathbase.cpp @@ -29,7 +29,7 @@ namespace olive { -ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInput *param_a_in, olive::NodeInput *param_b_in) const +ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, const QString& param_a_in, const QString& param_b_in) const { QStringList code_id = shader_id.split('.'); @@ -43,11 +43,11 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp if (pairing == kPairTextureMatrix && op == kOpMultiply) { // Override the operation for this operation since we multiply texture COORDS by the matrix rather than - 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; + const QString& tex_in = (type_a == NodeValue::kTexture) ? param_a_in : param_b_in; + const QString& 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()); + operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in); vert = QStringLiteral("uniform mat4 %1;\n" "\n" @@ -59,7 +59,7 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp "void main() {\n" " gl_Position = %1 * a_position;\n" " ove_texcoord = a_texcoord;\n" - "}\n").arg(mat_in->id()); + "}\n").arg(mat_in); } else { switch (op) { @@ -89,8 +89,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp break; } - operation = operation.arg(GetShaderVariableCall(param_a_in->id(), type_a), - GetShaderVariableCall(param_b_in->id(), type_b)); + operation = operation.arg(GetShaderVariableCall(param_a_in, type_a), + GetShaderVariableCall(param_b_in, type_b)); } frag = QStringLiteral("uniform %1 %3;\n" @@ -104,8 +104,8 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp " fragColor = %5;\n" "}\n").arg(GetShaderUniformType(type_a), GetShaderUniformType(type_b), - param_a_in->id(), - param_b_in->id(), + param_a_in, + param_b_in, operation); return ShaderCode(frag, vert); @@ -165,7 +165,7 @@ void MathNodeBase::PushVector(NodeValueTable *output, olive::NodeValue::Type typ } } -NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, NodeInput *param_a_in, const NodeValue& val_a, NodeInput *param_b_in, const NodeValue& val_b) const +NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue& val_a, const QString& param_b_in, const NodeValue& val_b) const { NodeValueTable output = value.Merge(); @@ -342,7 +342,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o { // Queue a sample job 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; + const QString& number_param = val_a.type() == NodeValue::kSamples ? param_b_in : param_a_in; float number = RetrieveNumber(number_val); @@ -350,7 +350,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o job.InsertValue(number_param, NodeValue(NodeValue::kFloat, number, this)); if (job.HasSamples()) { - if (number_param->IsStatic()) { + if (IsInputStatic(number_param)) { if (!NumberIsNoOp(operation, number)) { for (int i=0;iaudio_params().channel_count();i++) { for (int j=0;jsample_count();j++) { @@ -375,7 +375,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o return output; } -void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBase::Operation operation, NodeInput *param_a_in, NodeInput *param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const +void MathNodeBase::ProcessSamplesInternal(NodeValueDatabase &values, MathNodeBase::Operation operation, const QString ¶m_a_in, const QString ¶m_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(NodeValue::kNumber); diff --git a/app/node/math/math/mathbase.h b/app/node/math/math/mathbase.h index 08be7cae6..857204de5 100644 --- a/app/node/math/math/mathbase.h +++ b/app/node/math/math/mathbase.h @@ -109,13 +109,13 @@ protected: static bool NumberIsNoOp(const Operation& op, const float& number); - ShaderCode GetShaderCodeInternal(const QString &shader_id, NodeInput* param_a_in, NodeInput* param_b_in) const; + ShaderCode GetShaderCodeInternal(const QString &shader_id, const QString ¶m_a_in, const QString ¶m_b_in) 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; + NodeValueTable ValueInternal(NodeValueDatabase &value, Operation operation, Pairing pairing, const QString& param_a_in, const NodeValue &val_a, const QString& param_b_in, const NodeValue& val_b) const; - void ProcessSamplesInternal(NodeValueDatabase &values, Operation operation, NodeInput* param_a_in, NodeInput* param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const; + void ProcessSamplesInternal(NodeValueDatabase &values, Operation operation, const QString& param_a_in, const QString& param_b_in, const SampleBufferPtr input, SampleBufferPtr output, int index) const; }; diff --git a/app/node/math/merge/merge.cpp b/app/node/math/merge/merge.cpp index f4f2c23e2..b6263fe53 100644 --- a/app/node/math/merge/merge.cpp +++ b/app/node/math/merge/merge.cpp @@ -22,11 +22,14 @@ namespace olive { +const QString MergeNode::kBaseIn = QStringLiteral("base_in"); +const QString MergeNode::kBlendIn = QStringLiteral("blend_in"); + MergeNode::MergeNode() { - base_in_ = new NodeInput(this, QStringLiteral("base_in"), NodeValue::kTexture); + AddInput(kBaseIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - blend_in_ = new NodeInput(this, QStringLiteral("blend_in"), NodeValue::kTexture); + AddInput(kBlendIn, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); } Node *MergeNode::copy() const @@ -56,8 +59,9 @@ QString MergeNode::Description() const void MergeNode::Retranslate() { - base_in_->set_name(tr("Base")); - blend_in_->set_name(tr("Blend")); + SetInputName(kBaseIn, tr("Base")); + + SetInputName(kBlendIn, tr("Blend")); } ShaderCode MergeNode::GetShaderCode(const QString &shader_id) const @@ -67,24 +71,26 @@ ShaderCode MergeNode::GetShaderCode(const QString &shader_id) const return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag")); } -NodeValueTable MergeNode::Value(NodeValueDatabase &value) const +NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value) const { + Q_UNUSED(output) + ShaderJob job; - job.InsertValue(base_in_, value); - job.InsertValue(blend_in_, value); + job.InsertValue(this, kBaseIn, value); + job.InsertValue(this, kBlendIn, value); 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(kBaseIn).data().value(); + TexturePtr blend_tex = job.GetValue(kBlendIn).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_)); + table.Push(job.GetValue(kBlendIn)); } else if (!blend_tex) { // We only have a base texture, no need to alpha over - table.Push(job.GetValue(base_in_)); + table.Push(job.GetValue(kBaseIn)); } else { // We have both textures, push the job table.Push(NodeValue::kShaderJob, QVariant::fromValue(job), this); @@ -94,16 +100,6 @@ NodeValueTable MergeNode::Value(NodeValueDatabase &value) const return table; } -NodeInput *MergeNode::base_in() const -{ - return base_in_; -} - -NodeInput *MergeNode::blend_in() const -{ - return blend_in_; -} - void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const { // We do some hash optimization here. If only one of the inputs is connected, this node @@ -116,16 +112,16 @@ void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const bool base_changed_hash = false; bool blend_changed_hash = false; - if (base_in_->IsConnected()) { - base_in_->GetConnectedNode()->Hash(hash, time); + if (IsInputConnected(kBaseIn)) { + GetConnectedNode(kBaseIn)->Hash(hash, time); QByteArray post_base_hash = hash.result(); base_changed_hash = (post_base_hash != current_result); current_result = post_base_hash; } - if (blend_in_->IsConnected()) { - blend_in_->GetConnectedNode()->Hash(hash, time); + if(IsInputConnected(kBlendIn)) { + GetConnectedNode(kBlendIn)->Hash(hash, time); blend_changed_hash = (hash.result() != current_result); } diff --git a/app/node/math/merge/merge.h b/app/node/math/merge/merge.h index 6fd205734..d2a8933d4 100644 --- a/app/node/math/merge/merge.h +++ b/app/node/math/merge/merge.h @@ -41,10 +41,10 @@ public: virtual void Retranslate() override; virtual ShaderCode GetShaderCode(const QString &shader_id) const override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; - NodeInput* base_in() const; - NodeInput* blend_in() const; + static const QString kBaseIn; + static const QString kBlendIn; virtual void Hash(QCryptographicHash &hash, const rational &time) const override; diff --git a/app/node/math/trigonometry/trigonometry.cpp b/app/node/math/trigonometry/trigonometry.cpp index c464d0f0e..b58edbfd3 100644 --- a/app/node/math/trigonometry/trigonometry.cpp +++ b/app/node/math/trigonometry/trigonometry.cpp @@ -22,13 +22,14 @@ namespace olive { +const QString TrigonometryNode::kMethodIn = QStringLiteral("method_in"); +const QString TrigonometryNode::kXIn = QStringLiteral("x_in"); + TrigonometryNode::TrigonometryNode() { - method_in_ = new NodeInput(this, QStringLiteral("method_in"), NodeValue::kCombo); - method_in_->SetConnectable(false); - method_in_->SetKeyframable(false); + AddInput(kMethodIn, NodeValue::kCombo, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); - x_in_ = new NodeInput(this, QStringLiteral("x_in"), NodeValue::kFloat, 0.0); + AddInput(kXIn, NodeValue::kFloat, 0.0); } olive::Node *olive::TrigonometryNode::copy() const @@ -70,18 +71,20 @@ void TrigonometryNode::Retranslate() tr("Hyperbolic Cosine"), tr("Hyperbolic Tangent")}; - method_in_->set_combobox_strings(strings); + SetComboBoxStrings(kMethodIn, strings); - method_in_->set_name(tr("Method")); + SetInputName(kMethodIn, tr("Method")); } -NodeValueTable TrigonometryNode::Value(NodeValueDatabase &value) const +NodeValueTable TrigonometryNode::Value(const QString &output, NodeValueDatabase &value) const { - float x = value[x_in_].Take(NodeValue::kFloat).toFloat(); + Q_UNUSED(output) + + float x = value[kXIn].Take(NodeValue::kFloat).toFloat(); NodeValueTable table = value.Merge(); - switch (static_cast(method_in_->GetStandardValue().toInt())) { + switch (static_cast(GetStandardValue(kMethodIn).toInt())) { case kOpSine: x = qSin(x); break; diff --git a/app/node/math/trigonometry/trigonometry.h b/app/node/math/trigonometry/trigonometry.h index 86c74d235..d59f52161 100644 --- a/app/node/math/trigonometry/trigonometry.h +++ b/app/node/math/trigonometry/trigonometry.h @@ -40,7 +40,10 @@ public: virtual void Retranslate() override; - virtual NodeValueTable Value(NodeValueDatabase &value) const override; + virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override; + + static const QString kMethodIn; + static const QString kXIn; private: enum Operation { @@ -55,10 +58,6 @@ private: kOpHypTangent }; - NodeInput* method_in_; - - NodeInput* x_in_; - }; } diff --git a/app/node/node.cpp b/app/node/node.cpp index 4848f00c7..935357678 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -25,8 +25,11 @@ #include #include +#include "common/bezier.h" +#include "common/lerp.h" #include "common/timecodefunctions.h" #include "common/xmlutils.h" +#include "core.h" #include "config/config.h" #include "project/project.h" #include "project/item/footage/footage.h" @@ -36,19 +39,36 @@ namespace olive { -#define super NodeConnectable +#define super QObject -Node::Node() : +const QString Node::kDefaultOutput = QStringLiteral("output"); + +Node::Node(bool create_default_output) : can_be_deleted_(true), override_color_(-1) { + if (create_default_output) { + AddOutput(); + } } Node::~Node() { + // Disconnect all edges DisconnectAll(); + // Remove self from anything while we're still a node rather than a base QObject setParent(nullptr); + + // Remove all immediates + foreach (NodeInputImmediate* i, standard_immediates_) { + delete i; + } + for (auto it=array_immediates_.cbegin(); it!=array_immediates_.cend(); it++) { + foreach (NodeInputImmediate* i, it.value()) { + delete i; + } + } } NodeGraph *Node::parent() const @@ -64,30 +84,7 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto } if (reader->name() == QStringLiteral("input")) { - QString param_id; - - XMLAttributeLoop(reader, attr) { - if (attr.name() == QStringLiteral("id")) { - param_id = attr.value().toString(); - - break; - } - } - - if (param_id.isEmpty()) { - qDebug() << "Found parameter with no ID"; - continue; - } - - NodeInput* param = GetInputWithID(param_id); - - if (!param) { - qDebug() << "No parameter in" << id() << "with parameter" << param_id; - reader->skipCurrentElement(); - continue; - } - - param->Load(reader, xml_node_data, cancelled); + LoadInput(reader, xml_node_data, cancelled); } else if (reader->name() == QStringLiteral("ptr")) { xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this); } else if (reader->name() == QStringLiteral("pos")) { @@ -118,6 +115,39 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto } } else if (reader->name() == QStringLiteral("custom")) { LoadInternal(reader, xml_node_data); + } else if (reader->name() == QStringLiteral("connections")) { + // Load connections + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("connection")) { + QString param_id; + int ele = -1; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("element")) { + ele = attr.value().toInt(); + } else if (attr.name() == QStringLiteral("input")) { + param_id = attr.value().toString(); + } + } + + QString output_node_id; + QString output_param_id; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("node")) { + output_node_id = reader->readElementText(); + } else if (reader->name() == QStringLiteral("output")) { + output_param_id = reader->readElementText(); + } else { + reader->skipCurrentElement(); + } + } + + xml_node_data.desired_connections.append({NodeInput(this, param_id, ele), output_node_id.toULongLong(), output_param_id}); + } else { + reader->skipCurrentElement(); + } + } } else { reader->skipCurrentElement(); } @@ -136,10 +166,10 @@ void Node::Save(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("label"), GetLabel()); writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_)); - foreach (NodeInput* input, inputs_) { + foreach (const QString& input, input_ids_) { writer->writeStartElement(QStringLiteral("input")); - input->Save(writer); + SaveInput(writer, input); writer->writeEndElement(); // input } @@ -150,6 +180,20 @@ void Node::Save(QXmlStreamWriter *writer) const } writer->writeEndElement(); // links + writer->writeStartElement(QStringLiteral("connections")); + for (auto it=input_connections().cbegin(); it!=input_connections().cend(); it++) { + writer->writeStartElement(QStringLiteral("connection")); + + writer->writeAttribute(QStringLiteral("input"), it->first.input()); + writer->writeAttribute(QStringLiteral("element"), QString::number(it->first.element())); + + writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast(it->second.node()))); + writer->writeTextElement(QStringLiteral("output"), it->second.output()); + + writer->writeEndElement(); // connection + } + writer->writeEndElement(); // connections + writer->writeStartElement(QStringLiteral("custom")); SaveInternal(writer); writer->writeEndElement(); // custom @@ -207,14 +251,753 @@ QBrush Node::brush(qreal top, qreal bottom) const } } -NodeValueTable Node::Value(NodeValueDatabase &value) const +void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input) { + // Ensure parameters exist on the nodes requested + Q_ASSERT(input.node()->HasInputWithID(input.input())); + Q_ASSERT(output.node()->HasOutputWithID(output.output())); + + // Ensure a connection isn't getting overwritten + Q_ASSERT(input.node()->input_connections().find(input) == input.node()->input_connections().end()); + + // Insert connection on both sides + input.node()->input_connections_[input] = output; + output.node()->output_connections_.push_back(std::pair({output, input})); + + // Call internal event + input.node()->InputConnectedEvent(input.input(), input.element(), output); + + // Emit signals + emit input.node()->InputConnected(output, input); + emit output.node()->OutputConnected(output, input); + + // Invalidate all if this node isn't ignoring this input + if (!input.node()->ignore_connections_.contains(input.input())) { + input.node()->InvalidateAll(input.input(), input.element()); + } +} + +void Node::DisconnectEdge(const NodeOutput &output, const NodeInput &input) +{ + // Ensure parameters exist on the nodes requested + Q_ASSERT(input.node()->HasInputWithID(input.input())); + Q_ASSERT(output.node()->HasOutputWithID(output.output())); + + // Ensure connection exists + Q_ASSERT(input.node()->input_connections().at(input) == output); + + // Remove connection from both sides + InputConnections& inputs = input.node()->input_connections_; + inputs.erase(inputs.find(input)); + + OutputConnections& outputs = output.node()->output_connections_; + outputs.erase(std::find(outputs.begin(), outputs.end(), std::pair({output, input}))); + + // Call internal event + input.node()->InputDisconnectedEvent(input.input(), input.element(), output); + + emit input.node()->InputDisconnected(output, input); + emit output.node()->OutputDisconnected(output, input); + + if (!input.node()->ignore_connections_.contains(input.input())) { + input.node()->InvalidateAll(input.input(), input.element()); + } +} + +QString Node::GetInputName(const QString &id) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->human_name; + } else { + ReportInvalidInput("get name of", id); + return QString(); + } +} + +void Node::LoadInput(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) +{ + QString param_id; + + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("id")) { + param_id = attr.value().toString(); + + break; + } + } + + if (param_id.isEmpty()) { + qWarning() << "Failed to load parameter with missing ID"; + return; + } + + if (!HasInputWithID(param_id)) { + qWarning() << "Failed to load parameter that didn't exist"; + return; + } + + while (XMLReadNextStartElement(reader)) { + if (cancelled && *cancelled) { + return; + } + + if (reader->name() == QStringLiteral("primary")) { + // Load primary immediate + LoadImmediate(reader, param_id, -1, xml_node_data, cancelled); + } else if (reader->name() == QStringLiteral("subelements")) { + // Load subelements + XMLAttributeLoop(reader, attr) { + if (attr.name() == QStringLiteral("count")) { + InputArrayResize(param_id, attr.value().toInt()); + } + } + + int element_counter = 0; + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("element")) { + LoadImmediate(reader, param_id, element_counter, xml_node_data, cancelled); + + element_counter++; + } else { + reader->skipCurrentElement(); + } + } + } else { + reader->skipCurrentElement(); + } + } +} + +void Node::SaveInput(QXmlStreamWriter *writer, const QString &id) const +{ + writer->writeAttribute(QStringLiteral("id"), id); + + writer->writeStartElement(QStringLiteral("primary")); + + SaveImmediate(writer, id, -1); + + writer->writeEndElement(); // primary + + writer->writeStartElement(QStringLiteral("subelements")); + + int arr_sz = InputArraySize(id); + + writer->writeAttribute(QStringLiteral("count"), QString::number(arr_sz)); + + for (int i=0; iwriteStartElement(QStringLiteral("element")); + + SaveImmediate(writer, id, i); + + writer->writeEndElement(); // element + } + + writer->writeEndElement(); // subelements +} + +bool Node::IsInputConnectable(const QString &input) const +{ + return !(GetInputFlags(input) & kInputFlagNotConnectable); +} + +bool Node::IsInputKeyframable(const QString &input) const +{ + return !(GetInputFlags(input) & kInputFlagNotKeyframable); +} + +bool Node::IsInputKeyframing(const QString &input, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->is_keyframing(); + } else { + ReportInvalidInput("get keyframing state of", input); + return false; + } +} + +void Node::SetInputIsKeyframing(const QString &input, bool e, int element) +{ + if (!IsInputKeyframable(input)) { + qDebug() << "Ignored set keyframing of" << input << "because this input is not keyframable"; + return; + } + + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + imm->set_is_keyframing(e); + + emit KeyframeEnableChanged(NodeInput(this, input, element), e); + } else { + ReportInvalidInput("set keyframing state of", input); + } +} + +bool Node::IsInputConnected(const QString &input, int element) const +{ + return GetConnectedOutput(input, element).IsValid(); +} + +NodeOutput Node::GetConnectedOutput(const QString &input, int element) const +{ + for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { + if (it->first.input() == input && it->first.element() == element) { + return it->second; + } + } + + return NodeOutput(); +} + +bool Node::IsUsingStandardValue(const QString &input, int track, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->is_using_standard_value(track); + } else { + ReportInvalidInput("determine whether using standard value in", input); + return true; + } +} + +NodeValue::Type Node::GetInputDataType(const QString &id) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->type; + } else { + ReportInvalidInput("get data type of", id); + return NodeValue::kNone; + } +} + +void Node::SetInputDataType(const QString &id, const NodeValue::Type &type) +{ + Input* i = GetInternalInputData(id); + + if (i) { + i->type = type; + + emit InputDataTypeChanged(id, type); + } else { + ReportInvalidInput("set data type of", id); + } +} + +bool Node::HasInputProperty(const QString &id, const QString &name) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->properties.contains(name); + } else { + ReportInvalidInput("get property of", id); + return false; + } +} + +QHash Node::GetInputProperties(const QString &id) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->properties; + } else { + ReportInvalidInput("get property table of", id); + return QHash(); + } +} + +QVariant Node::GetInputProperty(const QString &id, const QString &name) const +{ + const Input* i = GetInternalInputData(id); + + if (i) { + return i->properties.value(name); + } else { + ReportInvalidInput("get property of", id); + return QVariant(); + } +} + +void Node::SetInputProperty(const QString &id, const QString &name, const QVariant &value) +{ + Input* i = GetInternalInputData(id); + + if (i) { + i->properties.insert(name, value); + + emit InputPropertyChanged(id, name, value); + } else { + ReportInvalidInput("set property of", id); + } +} + +SplitValue Node::GetSplitValueAtTime(const QString &input, const rational &time, int element) const +{ + SplitValue vals; + + int nb_tracks = GetNumberOfKeyframeTracks(input); + + for (int i=0;itime() >= time) { + // This time precedes any keyframe, so we just return the first value + return key_track.first()->value(); + } + + if (key_track.last()->time() <= time) { + // This time is after any keyframes so we return the last value + return key_track.last()->value(); + } + + // If we're here, the time must be somewhere in between the keyframes + for (int i=0;itime() == time + || !NodeValue::type_can_be_interpolated(GetInputDataType(input)) + || (before->time() < time && before->type() == NodeKeyframe::kHold)) { + + // Time == keyframe time, so value is precise + return before->value(); + + } else if (after->time() == time) { + + // Time == keyframe time, so value is precise + return after->value(); + + } else if (before->time() < time && after->time() > time) { + // We must interpolate between these keyframes + + if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { + // Perform a cubic bezier with two control points + + double t = Bezier::CubicXtoT(time.toDouble(), + before->time().toDouble(), + before->time().toDouble() + before->valid_bezier_control_out().x(), + after->time().toDouble() + after->valid_bezier_control_in().x(), + after->time().toDouble()); + + double y = Bezier::CubicTtoY(before->value().toDouble(), + before->value().toDouble() + before->valid_bezier_control_out().y(), + after->value().toDouble() + after->valid_bezier_control_in().y(), + after->value().toDouble(), + t); + + return y; + + } else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) { + // Perform a quadratic bezier with only one control point + + QPointF control_point; + double control_point_time; + double control_point_value; + + if (before->type() == NodeKeyframe::kBezier) { + control_point = before->valid_bezier_control_out(); + control_point_time = before->time().toDouble() + control_point.x(); + control_point_value = before->value().toDouble() + control_point.y(); + } else { + control_point = after->valid_bezier_control_in(); + control_point_time = after->time().toDouble() + control_point.x(); + control_point_value = after->value().toDouble() + control_point.y(); + } + + // Generate T from time values - used to determine bezier progress + double t = Bezier::QuadraticXtoT(time.toDouble(), before->time().toDouble(), control_point_time, after->time().toDouble()); + + // Generate value using T + double y = Bezier::QuadraticTtoY(before->value().toDouble(), control_point_value, after->value().toDouble(), t); + + return y; + + } else { + // To have arrived here, the keyframes must both be linear + qreal period_progress = (time.toDouble() - before->time().toDouble()) / (after->time().toDouble() - before->time().toDouble()); + + return lerp(before->value().toDouble(), after->value().toDouble(), period_progress); + } + } + } + } + + return GetSplitStandardValueOnTrack(input, track, element); +} + +QVariant Node::GetDefaultValue(const QString &input) const +{ + NodeValue::Type type = GetInputDataType(input); + + return NodeValue::combine_track_values_into_normal_value(type, GetSplitDefaultValue(input)); +} + +SplitValue Node::GetSplitDefaultValue(const QString &input) const +{ + const Input* i = GetInternalInputData(input); + + if (i) { + return i->default_value; + } else { + ReportInvalidInput("retrieve default value of", input); + return SplitValue(); + } +} + +QVariant Node::GetSplitDefaultValueOnTrack(const QString &input, int track) const +{ + return GetSplitDefaultValue(input).at(track); +} + +const QVector &Node::GetKeyframeTracks(const QString &input, int element) const +{ + return GetImmediate(input, element)->keyframe_tracks(); +} + +QVector Node::GetKeyframesAtTime(const QString &input, const rational &time, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->get_keyframe_at_time(time); + } else { + ReportInvalidInput("get keyframes at time from", input); + return QVector(); + } +} + +NodeKeyframe *Node::GetKeyframeAtTimeOnTrack(const QString &input, const rational &time, int track, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->get_keyframe_at_time_on_track(time, track); + } else { + ReportInvalidInput("get keyframe at time on track from", input); + return nullptr; + } +} + +NodeKeyframe::Type Node::GetBestKeyframeTypeForTimeOnTrack(const QString &input, const rational &time, int track, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->get_best_keyframe_type_for_time(time, track); + } else { + ReportInvalidInput("get closest keyframe before a time from", input); + return NodeKeyframe::kDefaultType; + } +} + +int Node::GetNumberOfKeyframeTracks(const QString &id) const +{ + return NodeValue::get_number_of_keyframe_tracks(GetInputDataType(id)); +} + +NodeKeyframe *Node::GetEarliestKeyframe(const QString &id, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_earliest_keyframe(); + } else { + ReportInvalidInput("get earliest keyframe from", id); + return nullptr; + } +} + +NodeKeyframe *Node::GetLatestKeyframe(const QString &id, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_latest_keyframe(); + } else { + ReportInvalidInput("get latest keyframe from", id); + return nullptr; + } +} + +NodeKeyframe *Node::GetClosestKeyframeBeforeTime(const QString &id, const rational &time, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_closest_keyframe_before_time(time); + } else { + ReportInvalidInput("get closest keyframe before a time from", id); + return nullptr; + } +} + +NodeKeyframe *Node::GetClosestKeyframeAfterTime(const QString &id, const rational &time, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_closest_keyframe_after_time(time); + } else { + ReportInvalidInput("get closest keyframe after a time from", id); + return nullptr; + } +} + +bool Node::HasKeyframeAtTime(const QString &id, const rational &time, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->has_keyframe_at_time(time); + } else { + ReportInvalidInput("determine if it has a keyframe at a time from", id); + return false; + } +} + +QStringList Node::GetComboBoxStrings(const QString &id) const +{ + return GetInputProperty(id, QStringLiteral("combo_str")).toStringList(); +} + +QVariant Node::GetStandardValue(const QString &id, int element) const +{ + NodeValue::Type type = GetInputDataType(id); + + return NodeValue::combine_track_values_into_normal_value(type, GetSplitStandardValue(id, element)); +} + +SplitValue Node::GetSplitStandardValue(const QString &id, int element) const +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + return imm->get_split_standard_value(); + } else { + ReportInvalidInput("get standard value of", id); + return SplitValue(); + } +} + +QVariant Node::GetSplitStandardValueOnTrack(const QString &input, int track, int element) const +{ + NodeInputImmediate* imm = GetImmediate(input, element); + + if (imm) { + return imm->get_split_standard_value_on_track(track); + } else { + ReportInvalidInput("get standard value of", input); + return QVariant(); + } +} + +void Node::SetStandardValue(const QString &id, const QVariant &value, int element) +{ + NodeValue::Type type = GetInputDataType(id); + + SetSplitStandardValue(id, NodeValue::split_normal_value_into_track_values(type, value), element); +} + +void Node::SetSplitStandardValue(const QString &id, const SplitValue &value, int element) +{ + NodeInputImmediate* imm = GetImmediate(id, element); + + if (imm) { + imm->set_split_standard_value(value); + + for (int i=0; iset_standard_value_on_track(value, track); + + if (IsUsingStandardValue(id, track, element)) { + // If this standard value is being used, we need to send a value changed signal + emit ValueChanged(NodeInput(this, id, element), TimeRange(RATIONAL_MIN, RATIONAL_MAX)); + } + } else { + ReportInvalidInput("set standard value of", id); + } +} + +bool Node::InputIsArray(const QString &id) const +{ + return GetInputFlags(id) & kInputFlagArray; +} + +void Node::InputArrayInsert(const QString &id, int index, bool undoable) +{ + if (undoable) { + Core::instance()->undo_stack()->push(new ArrayInsertCommand(this, id, index)); + } else { + // Add new input + ArrayResizeInternal(id, InputArraySize(id) + 1); + + // Move connections down + InputConnections copied_edges = input_connections(); + for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) { + if (it->first.element() >= index) { + // Disconnect this and reconnect it one element down + NodeInput new_edge = it->first; + new_edge.set_element(new_edge.element() + 1); + + DisconnectEdge(it->second, it->first); + ConnectEdge(it->second, new_edge); + } + } + + // Shift values and keyframes up one element + for (int i=InputArraySize(id)-1; i>index; i--) { + CopyValuesOfElement(this, this, id, i-1, i); + } + + // Reset value of element we just "inserted" + ClearElement(id, index); + } +} + +void Node::InputArrayResize(const QString &id, int size, bool undoable) +{ + if (InputArraySize(id) == size) { + return; + } + + ArrayResizeCommand* c = new ArrayResizeCommand(this, id, size); + + if (undoable) { + Core::instance()->undo_stack()->push(c); + } else { + c->redo(); + delete c; + } +} + +void Node::InputArrayRemove(const QString &id, int index, bool undoable) +{ + if (undoable) { + Core::instance()->undo_stack()->push(new ArrayRemoveCommand(this, id, index)); + } else { + // Remove input + ArrayResizeInternal(id, InputArraySize(id) - 1); + + // Move connections up + InputConnections copied_edges = input_connections(); + for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { + if (it->first.element() >= index) { + // Disconnect this and reconnect it one element up if it's not the element being removed + DisconnectEdge(it->second, it->first); + + if (it->first.element() > index) { + NodeInput new_edge = it->first; + new_edge.set_element(new_edge.element() - 1); + + ConnectEdge(it->second, new_edge); + } + } + } + + // Shift values and keyframes down one element + int arr_sz = InputArraySize(id); + for (int i=index; iarray_size; + } else { + ReportInvalidInput("retrieve array size of", id); + return 0; + } +} + +const NodeKeyframeTrack &Node::GetTrackFromKeyframe(NodeKeyframe *key) const +{ + return GetImmediate(key->input(), key->element())->keyframe_tracks().at(key->track()); +} + +NodeInputImmediate *Node::GetImmediate(const QString &input, int element) const +{ + if (element == -1) { + return standard_immediates_.value(input, nullptr); + } else if (array_immediates_.contains(input)) { + const QVector& imm_arr = array_immediates_.value(input); + + if (element >= 0 && element < imm_arr.size()) { + return imm_arr.at(element); + } + } + + return nullptr; +} + +Node::InputFlags Node::GetInputFlags(const QString &input) const +{ + const Input* i = GetInternalInputData(input); + + if (i) { + return i->flags; + } else { + ReportInvalidInput("retrieve flags of", input); + return InputFlags(kInputFlagNormal); + } +} + +NodeValueTable Node::Value(const QString& output, NodeValueDatabase &value) const +{ + Q_UNUSED(output) + return value.Merge(); } -void Node::InvalidateCache(const TimeRange &range, const InputConnection &from) +void Node::InvalidateCache(const TimeRange &range, const QString &from, int element) { Q_UNUSED(from) + Q_UNUSED(element) SendInvalidateCache(range); } @@ -222,26 +1005,26 @@ void Node::InvalidateCache(const TimeRange &range, const InputConnection &from) void Node::BeginOperation() { // Ripple through graph - foreach (const InputConnection& conn, output_connections()) { - conn.input->parent()->BeginOperation(); + for (const std::pair& output : output_connections_) { + output.second.node()->BeginOperation(); } } void Node::EndOperation() { // Ripple through graph - foreach (const InputConnection& conn, output_connections()) { - conn.input->parent()->EndOperation(); + for (const std::pair& output : output_connections_) { + output.second.node()->EndOperation(); } } -TimeRange Node::InputTimeAdjustment(NodeInput *, int, const TimeRange &input_time) const +TimeRange Node::InputTimeAdjustment(const QString &, int, const TimeRange &input_time) const { // Default behavior is no time adjustment at all return input_time; } -TimeRange Node::OutputTimeAdjustment(NodeInput *, int, const TimeRange &input_time) const +TimeRange Node::OutputTimeAdjustment(const QString &, int, const TimeRange &input_time) const { // Default behavior is no time adjustment at all return input_time; @@ -280,20 +1063,24 @@ QVector Node::CopyDependencyGraph(const QVector &nodes, MultiUnd void Node::CopyDependencyGraph(const QVector &src, const QVector &dst, MultiUndoCommand *command) { for (int i=0; iinputs()) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - int connection_index = src.indexOf(it->second); + Node* src_node = src.at(i); + Node* dst_node = dst.at(i); - if (connection_index > -1) { - // Found a connection - Node* dst_output = dst.at(connection_index); - NodeInput* dst_input = dst.at(i)->GetInputWithID(input->id()); + for (auto it=src_node->input_connections_.cbegin(); it!=src_node->input_connections_.cend(); it++) { + // Determine if the connected node is in our src list + int connection_index = src.indexOf(it->second.node()); - if (command) { - command->add_child(new NodeEdgeAddCommand(dst_output, dst_input, it->first)); - } else { - ConnectEdge(dst_output, dst_input, it->first); - } + if (connection_index > -1) { + // Find the equivalent node in the dst list + Node* dst_connection = dst.at(connection_index); + + NodeOutput copied_output = NodeOutput(dst_connection, it->second.output()); + NodeInput copied_input = NodeInput(dst_node, it->first.input(), it->first.element()); + + if (command) { + command->add_child(new NodeEdgeAddCommand(copied_output, copied_input)); + } else { + ConnectEdge(copied_output, copied_input); } } } @@ -302,15 +1089,17 @@ void Node::CopyDependencyGraph(const QVector &src, const QVector void Node::SendInvalidateCache(const TimeRange &range) { - foreach (const InputConnection& conn, output_connections()) { + for (const OutputConnection& conn : output_connections_) { // Send clear cache signal to the Node - conn.input->parent()->InvalidateCache(range, conn); + const NodeInput& in = conn.second; + + in.node()->InvalidateCache(range, in.input(), in.element()); } } -void Node::InvalidateAll(NodeInput* input, int element) +void Node::InvalidateAll(const QString &input, int element) { - InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), {input, element}); + InvalidateCache(TimeRange(RATIONAL_MIN, RATIONAL_MAX), input, element); } bool Node::Link(Node *a, Node *b) @@ -358,9 +1147,149 @@ bool Node::AreLinked(Node *a, Node *b) return a->links_.contains(b); } -void Node::IgnoreInvalidationsFrom(NodeInput *input) +void Node::AddInput(const QString &id, NodeValue::Type type, const QVariant &default_value, Node::InputFlags flags) { - ignore_connections_.append(input); + if (id.isEmpty()) { + qWarning() << "Rejected adding input with an empty ID on node" << this->id(); + return; + } + + if (HasParamWithID(id)) { + qWarning() << "Failed to add input to node" << this->id() << "- param with ID" << id << "already exists"; + return; + } + + Node::Input i; + + i.type = type; + i.default_value = NodeValue::split_normal_value_into_track_values(type, default_value); + i.flags = flags; + i.array_size = 0; + + input_ids_.append(id); + input_data_.append(i); + + if (!standard_immediates_.value(id, nullptr)) { + standard_immediates_.insert(id, CreateImmediate(id)); + } + + emit InputAdded(id); +} + +void Node::RemoveInput(const QString &id) +{ + int index = input_ids_.indexOf(id); + + if (index == -1) { + ReportInvalidInput("remove", id); + return; + } + + input_ids_.removeAt(index); + input_data_.removeAt(index); + + emit InputRemoved(id); +} + +void Node::AddOutput(const QString &id) +{ + if (id.isEmpty()) { + qWarning() << "Rejected adding output with an empty ID on node" << this->id(); + return; + } + + if (HasParamWithID(id)) { + qWarning() << "Failed to add output to node" << this->id() << "- param with ID" << id << "already exists"; + return; + } + + outputs_.append(id); + + emit OutputAdded(id); +} + +void Node::RemoveOutput(const QString &id) +{ + if (outputs_.removeOne(id)) { + emit OutputRemoved(id); + } else { + ReportInvalidInput("remove", id); + } +} + +void Node::ReportInvalidInput(const char *attempted_action, const QString& id) const +{ + qWarning() << "Failed to" << attempted_action << "parameter" << id + << "in node" << this->id() << "- input doesn't exist"; +} + +NodeInputImmediate *Node::CreateImmediate(const QString &input) +{ + const Input* i = GetInternalInputData(input); + + if (i) { + return new NodeInputImmediate(i->type, i->default_value); + } else { + ReportInvalidInput("create immediate", input); + return nullptr; + } +} + +void Node::ArrayResizeInternal(const QString &id, int size) +{ + Input* imm = GetInternalInputData(id); + + if (!imm) { + ReportInvalidInput("set array size", id); + return; + } + + if (imm->array_size != size) { + // Update array size + if (imm->array_size < size) { + // Size is larger, create any immediates that don't exist + QVector& subinputs = array_immediates_[id]; + for (int i=subinputs.size(); iarray_size = size; + emit InputArraySizeChanged(id, size); + emit ValueChanged(NodeInput(this, id, -1), TimeRange(RATIONAL_MIN, RATIONAL_MAX)); + } +} + +int Node::GetInternalInputArraySize(const QString &input) +{ + return array_immediates_.value(input).size(); +} + +void Node::SetInputName(const QString &id, const QString &name) +{ + Input* i = GetInternalInputData(id); + + if (i) { + i->human_name = name; + + emit InputNameChanged(id, name); + } else { + ReportInvalidInput("set name of", id); + } +} + +void Node::IgnoreInvalidationsFrom(const QString& input_id) +{ + ignore_connections_.append(input_id); +} + +void Node::IgnoreHashingFrom(const QString &input_id) +{ + ignore_when_hashing_.append(input_id); } void Node::LoadInternal(QXmlStreamReader *reader, XMLNodeData &) @@ -372,11 +1301,6 @@ void Node::SaveInternal(QXmlStreamWriter *) const { } -QVector Node::GetInputsToHash() const -{ - return inputs_; -} - bool Node::HasGizmos() const { return false; @@ -418,12 +1342,14 @@ void Node::Hash(QCryptographicHash &hash, const rational& time) const // Add this Node's ID hash.addData(id().toUtf8()); - QVector inputs = GetInputsToHash(); - - foreach (NodeInput* input, inputs) { + foreach (const QString& input, input_ids_) { // For each input, try to hash its value - HashInputElement(hash, input, -1, time); - for (int i=0; iArraySize(); i++) { + if (ignore_when_hashing_.contains(input)) { + continue; + } + + int arr_sz = InputArraySize(input); + for (int i=-1; iid() == destination->id()); - 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)); - - NodeInput::CopyValues(src, dst, include_connections); + foreach (const QString& input, source->inputs()) { + CopyInput(source, destination, input, include_connections, true); } destination->SetPosition(source->GetPosition()); destination->SetLabel(source->GetLabel()); } +void Node::CopyInput(Node *src, Node *dst, const QString &input, bool include_connections, bool traverse_arrays) +{ + Q_ASSERT(src->id() == dst->id()); + + CopyValuesOfElement(src, dst, input, -1); + + // Copy array size + if (src->InputIsArray(input) && traverse_arrays) { + int src_array_sz = src->InputArraySize(input); + + for (int i=0; iinput_connections().cbegin(); it!=src->input_connections().cend(); it++) { + ConnectEdge(it->second, NodeInput(dst, input, it->first.element())); + } + } else { + // Just copy the primary connection (at -1) + if (src->IsInputConnected(input)) { + ConnectEdge(src->GetConnectedOutput(input), NodeInput(dst, input)); + } + } + } +} + +void Node::CopyValuesOfElement(Node *src, Node *dst, const QString &input, int src_element, int dst_element) +{ + if (dst_element >= dst->GetInternalInputArraySize(input)) { + qDebug() << "Ignored destination element that was out of array bounds"; + return; + } + + // Copy standard value + dst->SetSplitStandardValue(input, src->GetSplitStandardValue(input, src_element), dst_element); + + // Copy keyframes + dst->GetImmediate(input, dst_element)->delete_all_keyframes(); + foreach (const NodeKeyframeTrack& track, src->GetImmediate(input, src_element)->keyframe_tracks()) { + foreach (NodeKeyframe* key, track) { + key->copy(dst_element, dst); + } + } + + // Copy keyframing state + if (src->IsInputKeyframable(input)) { + dst->SetInputIsKeyframing(input, src->IsInputKeyframing(input, src_element), dst_element); + } + + // If this is the root of an array, copy the array size + if (src_element == -1 && dst_element == -1) { + dst->InputArrayResize(input, dst->InputArraySize(input)); + } +} + bool Node::CanBeDeleted() const { return can_be_deleted_; @@ -457,6 +1437,23 @@ void Node::SetCanBeDeleted(bool s) can_be_deleted_ = s; } +void GetDependenciesRecursively(QVector& list, const Node* node, bool traverse, bool exclusive_only) +{ + for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { + Node* connected_node = it->second.node(); + + if (connected_node->outputs().size() == 1 || !exclusive_only) { + if (!list.contains(connected_node)) { + list.append(connected_node); + + if (traverse) { + GetDependenciesRecursively(list, connected_node, traverse, exclusive_only); + } + } + } + } +} + /** * @brief Recursively collects dependencies of Node `n` and appends them to QList `list` * @@ -469,31 +1466,29 @@ QVector Node::GetDependenciesInternal(bool traverse, bool exclusive_only { QVector list; - foreach (NodeInput* i, inputs_) { - i->GetDependencies(list, traverse, exclusive_only); - } + GetDependenciesRecursively(list, this, traverse, exclusive_only); return list; } -void Node::HashInputElement(QCryptographicHash &hash, NodeInput *input, int element, const rational &time) const +void Node::HashInputElement(QCryptographicHash &hash, const QString& 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, element, TimeRange(time, time)).in(); - if (input->IsConnected(element)) { + if (IsInputConnected(input, element)) { // Traverse down this edge - input->GetConnectedNode(element)->Hash(hash, input_time); + GetConnectedNode(input, 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)); + QVariant value = GetValueAtTime(input, input_time, element); + hash.addData(NodeValue::ValueToBytes(GetInputDataType(input), 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 (GetInputDataType(input) == NodeValue::kFootage) { + Stream* stream = Node::ValueToPtr(GetStandardValue(input, element)); if (stream) { // Add footage details to hash @@ -537,32 +1532,6 @@ void Node::HashInputElement(QCryptographicHash &hash, NodeInput *input, int elem } } -void Node::ParameterConnected(Node *source, int element) -{ - NodeInput* input = static_cast(sender()); - - emit InputConnected(source, input, element); - - if (ignore_connections_.contains(input)) { - return; - } - - InvalidateAll(input, element); -} - -void Node::ParameterDisconnected(Node *source, int element) -{ - NodeInput* input = static_cast(sender()); - - emit InputDisconnected(source, input, element); - - if (ignore_connections_.contains(input)) { - return; - } - - InvalidateAll(input, element); -} - QVector Node::GetDependencies() const { return GetDependenciesInternal(true, false); @@ -595,21 +1564,10 @@ void Node::GenerateFrame(FramePtr frame, const GenerateJob &job) const Q_UNUSED(job) } -NodeInput *Node::GetInputWithID(const QString &id) const -{ - foreach (NodeInput* i, inputs_) { - if (i->id() == id) { - return i; - } - } - - return nullptr; -} - bool Node::OutputsTo(Node *n, bool recursively) const { - foreach (const InputConnection& conn, output_connections()) { - Node* connected = conn.input->parent(); + for (const OutputConnection& conn : output_connections_) { + Node* connected = conn.second.node(); if (connected == n) { return true; @@ -623,8 +1581,8 @@ bool Node::OutputsTo(Node *n, bool recursively) const bool Node::OutputsTo(const QString &id, bool recursively) const { - foreach (const InputConnection& conn, output_connections()) { - Node* connected = conn.input->parent(); + for (const OutputConnection& conn : output_connections_) { + Node* connected = conn.second.node(); if (connected->id() == id) { return true; @@ -636,14 +1594,14 @@ bool Node::OutputsTo(const QString &id, bool recursively) const return false; } -bool Node::OutputsTo(NodeInput *input, bool recursively) const +bool Node::OutputsTo(const NodeInput &input, bool recursively) const { - foreach (const InputConnection& conn, output_connections()) { - NodeInput* connected = conn.input; + for (const OutputConnection& conn : output_connections_) { + const NodeInput& connected = conn.second; if (connected == input) { return true; - } else if (recursively && connected->parent()->OutputsTo(input, recursively)) { + } else if (recursively && connected.node()->OutputsTo(input, recursively)) { return true; } } @@ -653,15 +1611,13 @@ bool Node::OutputsTo(NodeInput *input, bool recursively) const bool Node::InputsFrom(Node *n, bool recursively) const { - foreach (NodeInput* input, inputs_) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - Node* connected = it->second; + for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { + const NodeOutput& connected = it->second; - if (connected == n) { - return true; - } else if (recursively && connected->InputsFrom(n, recursively)) { - return true; - } + if (connected.node() == n) { + return true; + } else if (recursively && connected.node()->InputsFrom(n, recursively)) { + return true; } } @@ -670,15 +1626,13 @@ bool Node::InputsFrom(Node *n, bool recursively) const bool Node::InputsFrom(const QString &id, bool recursively) const { - foreach (NodeInput* input, inputs_) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - Node* connected = it->second; + for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { + const NodeOutput& connected = it->second; - if (connected->id() == id) { - return true; - } else if (recursively && connected->InputsFrom(id, recursively)) { - return true; - } + if (connected.node()->id() == id) { + return true; + } else if (recursively && connected.node()->InputsFrom(id, recursively)) { + return true; } } @@ -690,8 +1644,8 @@ int Node::GetRoutesTo(Node *n) const bool outputs_directly = false; int routes = 0; - foreach (const InputConnection& conn, edges()) { - Node* connected_node = conn.input->parent(); + foreach (const OutputConnection& conn, output_connections_) { + Node* connected_node = conn.second.node(); if (connected_node == n) { outputs_directly = true; @@ -709,9 +1663,15 @@ int Node::GetRoutesTo(Node *n) const void Node::DisconnectAll() { - // Disconnect outputs (inputs will be disconnected in their respective destructors) - while (!edges().empty()) { - DisconnectEdge(this, edges().front().input, edges().front().element); + // Disconnect inputs (copy map since internal map will change as we disconnect) + InputConnections copy = input_connections_; + for (auto it=copy.cbegin(); it!=copy.cend(); it++) { + DisconnectEdge(it->second, it->first); + } + + while (!output_connections_.empty()) { + OutputConnection conn = output_connections_.back(); + DisconnectEdge(conn.first, conn.second); } } @@ -754,33 +1714,31 @@ QVector Node::TransformTimeTo(const TimeRange &time, Node *target, bo if (input_dir) { // If this input is connected, traverse it to see if we stumble across the specified `node` - foreach (NodeInput* input, inputs_) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - TimeRange input_adjustment = InputTimeAdjustment(input, it->first, time); - Node* connected = it->second; + for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { + TimeRange input_adjustment = InputTimeAdjustment(it->first.input(), it->first.element(), time); + Node* connected = it->second.node(); - if (connected == target) { - // We found the target, no need to keep traversing - if (!paths_found.contains(input_adjustment)) { - paths_found.append(input_adjustment); - } - } else { - // We did NOT find the target, traverse this - paths_found.append(connected->TransformTimeTo(input_adjustment, target, input_dir)); + if (connected == target) { + // We found the target, no need to keep traversing + if (!paths_found.contains(input_adjustment)) { + paths_found.append(input_adjustment); } + } else { + // We did NOT find the target, traverse this + paths_found.append(connected->TransformTimeTo(input_adjustment, target, input_dir)); } } } else { // If this input is connected, traverse it to see if we stumble across the specified `node` - foreach (const InputConnection& conn, edges()) { - Node* input_node = conn.input->parent(); + foreach (const OutputConnection& conn, output_connections_) { + Node* connected_node = conn.second.node(); - TimeRange output_adjustment = input_node->OutputTimeAdjustment(conn.input, conn.element, time); + TimeRange output_adjustment = connected_node->OutputTimeAdjustment(conn.second.input(), conn.second.element(), time); - if (input_node == target) { + if (connected_node == target) { paths_found.append(output_adjustment); } else { - paths_found.append(input_node->TransformTimeTo(output_adjustment, target, input_dir)); + paths_found.append(connected_node->TransformTimeTo(output_adjustment, target, input_dir)); } } } @@ -793,17 +1751,6 @@ QVariant Node::PtrToValue(void *ptr) return reinterpret_cast(ptr); } -bool Node::HasParamWithID(const QString &id) const -{ - foreach (NodeInput* i, inputs_) { - if (i->id() == id) { - return true; - } - } - - return false; -} - const QPointF &Node::GetPosition() const { return position_; @@ -816,17 +1763,223 @@ void Node::SetPosition(const QPointF &pos) emit PositionChanged(position_); } -void Node::ParameterValueChanged(const TimeRange& range, int element) +void Node::ParameterValueChanged(const QString& input, int element, const TimeRange& range) { - NodeInput* input = static_cast(sender()); + InputValueChangedEvent(input, element); - emit ValueChanged(input, element); + emit ValueChanged(NodeInput(this, input, element), range); if (ignore_connections_.contains(input)) { return; } - InvalidateCache(range, InputConnection(static_cast(sender()), element)); + InvalidateCache(range, input, element); +} + +void Node::LoadImmediate(QXmlStreamReader *reader, const QString& input, int element, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) +{ + NodeValue::Type data_type = GetInputDataType(input); + + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("standard")) { + // Load standard value + int val_index = 0; + + while (XMLReadNextStartElement(reader)) { + if (cancelled && *cancelled) { + return; + } + + if (reader->name() == QStringLiteral("track")) { + QString value_text = reader->readElementText(); + QVariant value_on_track; + + if (!value_text.isEmpty()) { + value_on_track = NodeValue::StringToValue(data_type, value_text, element); + } + + SetSplitStandardValueOnTrack(input, val_index, value_on_track, element); + + val_index++; + } else { + reader->skipCurrentElement(); + } + } + } else if (reader->name() == QStringLiteral("keyframing") && IsInputKeyframable(input)) { + SetInputIsKeyframing(input, 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")) { + QString key_input; + 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("input")) { + key_input = attr.value().toString(); + } else 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 = NodeValue::StringToValue(data_type, reader->readElementText(), true); + + NodeKeyframe* key = new NodeKeyframe(key_time, key_value, key_type, track, element, key_input, 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")) { + SetInputProperty(input, QStringLiteral("col_input"), reader->readElementText()); + } else if (reader->name() == QStringLiteral("csdisplay")) { + SetInputProperty(input, QStringLiteral("col_display"), reader->readElementText()); + } else if (reader->name() == QStringLiteral("csview")) { + SetInputProperty(input, QStringLiteral("col_view"), reader->readElementText()); + } else if (reader->name() == QStringLiteral("cslook")) { + SetInputProperty(input, QStringLiteral("col_look"), reader->readElementText()); + } else { + reader->skipCurrentElement(); + } + } +} + +void Node::SaveImmediate(QXmlStreamWriter *writer, const QString& input, int element) const +{ + if (IsInputKeyframable(input)) { + writer->writeTextElement(QStringLiteral("keyframing"), QString::number(IsInputKeyframing(input, element))); + } + + NodeValue::Type data_type = GetInputDataType(input); + + // Write standard value + writer->writeStartElement(QStringLiteral("standard")); + + foreach (const QVariant& v, GetSplitStandardValue(input, element)) { + writer->writeTextElement(QStringLiteral("track"), NodeValue::ValueToString(data_type, v, true)); + } + + writer->writeEndElement(); // standard + + // Write keyframes + writer->writeStartElement(QStringLiteral("keyframes")); + + foreach (const NodeKeyframeTrack& track, GetKeyframeTracks(input, element)) { + writer->writeStartElement(QStringLiteral("track")); + + foreach (NodeKeyframe* key, track) { + writer->writeStartElement(QStringLiteral("key")); + + writer->writeAttribute(QStringLiteral("input"), key->input()); + writer->writeAttribute(QStringLiteral("time"), key->time().toString()); + writer->writeAttribute(QStringLiteral("type"), QString::number(key->type())); + writer->writeAttribute(QStringLiteral("inhandlex"), QString::number(key->bezier_control_in().x())); + writer->writeAttribute(QStringLiteral("inhandley"), QString::number(key->bezier_control_in().y())); + writer->writeAttribute(QStringLiteral("outhandlex"), QString::number(key->bezier_control_out().x())); + writer->writeAttribute(QStringLiteral("outhandley"), QString::number(key->bezier_control_out().y())); + + writer->writeCharacters(NodeValue::ValueToString(data_type, key->value(), true)); + + writer->writeEndElement(); // key + } + + writer->writeEndElement(); // track + } + + writer->writeEndElement(); // keyframes + + if (data_type == NodeValue::kColor) { + // Save color management information + writer->writeTextElement(QStringLiteral("csinput"), GetInputProperty(input, QStringLiteral("col_input")).toString()); + writer->writeTextElement(QStringLiteral("csdisplay"), GetInputProperty(input, QStringLiteral("col_display")).toString()); + writer->writeTextElement(QStringLiteral("csview"), GetInputProperty(input, QStringLiteral("col_view")).toString()); + writer->writeTextElement(QStringLiteral("cslook"), GetInputProperty(input, QStringLiteral("col_look")).toString()); + } +} + +TimeRange Node::GetRangeAffectedByKeyframe(NodeKeyframe *key) const +{ + const NodeKeyframeTrack& key_track = GetTrackFromKeyframe(key); + int keyframe_index = key_track.indexOf(key); + + TimeRange range = GetRangeAroundIndex(key->input(), 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 + && keyframe_index > 0 + && key_track.at(keyframe_index - 1)->type() == NodeKeyframe::kHold) { + range.set_in(key->time()); + } + + return range; +} + +TimeRange Node::GetRangeAroundIndex(const QString &input, int index, int track, int element) const +{ + rational range_begin = RATIONAL_MIN; + rational range_end = RATIONAL_MAX; + + const NodeKeyframeTrack& key_track = GetImmediate(input, element)->keyframe_tracks().at(track); + + if (key_track.size() > 1) { + if (index > 0) { + // If this is not the first key, we'll need to limit it to the key just before + range_begin = key_track.at(index - 1)->time(); + } + if (index < key_track.size() - 1) { + // If this is not the last key, we'll need to limit it to the key just after + range_end = key_track.at(index + 1)->time(); + } + } + + return TimeRange(range_begin, range_end); +} + +void Node::ClearElement(const QString& input, int index) +{ + GetImmediate(input, index)->delete_all_keyframes(); + + if (IsInputKeyframable(input)) { + SetInputIsKeyframing(input, false, index); + } + + SetSplitStandardValue(input, GetSplitDefaultValue(input), index); } QRectF Node::CreateGizmoHandleRect(const QPointF &pt, int radius) @@ -859,31 +2012,157 @@ void Node::DrawAndExpandGizmoHandles(QPainter *p, int handle_radius, QRectF *rec } } +void Node::InputValueChangedEvent(const QString &input, int element) +{ + Q_UNUSED(input) + Q_UNUSED(element) +} + +void Node::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + Q_UNUSED(input) + Q_UNUSED(element) + Q_UNUSED(output) +} + +void Node::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + Q_UNUSED(input) + Q_UNUSED(element) + Q_UNUSED(output) +} + void Node::childEvent(QChildEvent *event) { super::childEvent(event); - NodeInput* input = dynamic_cast(event->child()); + NodeKeyframe* key = dynamic_cast(event->child()); + + if (key) { + NodeInput i(this, key->input(), key->element()); - 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())); + GetImmediate(key->input(), key->element())->insert_keyframe(key); - // Keep main output as the last parameter, assume if there are no parameters that this is the output parameter - inputs_.append(input); + connect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange); + connect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged); + connect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange); + connect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged); + connect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange); + connect(key, &NodeKeyframe::BezierControlOutChanged, this, &Node::InvalidateFromKeyframeBezierOutChange); - connect(input, &NodeInput::ValueChanged, this, &Node::ParameterValueChanged); - connect(input, &NodeInput::InputConnected, this, &Node::ParameterConnected); - connect(input, &NodeInput::InputDisconnected, this, &Node::ParameterDisconnected); + emit KeyframeAdded(key); + emit ValueChanged(i, GetRangeAffectedByKeyframe(key)); } else if (event->type() == QEvent::ChildRemoved) { - disconnect(input, &NodeInput::ValueChanged, this, &Node::ParameterValueChanged); - disconnect(input, &NodeInput::InputConnected, this, &Node::ParameterConnected); - disconnect(input, &NodeInput::InputDisconnected, this, &Node::ParameterDisconnected); + TimeRange time_affected = GetRangeAffectedByKeyframe(key); - inputs_.removeOne(input); + disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::InvalidateFromKeyframeTimeChange); + disconnect(key, &NodeKeyframe::TimeChanged, this, &Node::KeyframeTimeChanged); + disconnect(key, &NodeKeyframe::ValueChanged, this, &Node::InvalidateFromKeyframeValueChange); + disconnect(key, &NodeKeyframe::TypeChanged, this, &Node::InvalidateFromKeyframeTypeChanged); + disconnect(key, &NodeKeyframe::BezierControlInChanged, this, &Node::InvalidateFromKeyframeBezierInChange); + disconnect(key, &NodeKeyframe::BezierControlOutChanged, this, &Node::InvalidateFromKeyframeBezierOutChange); + + GetImmediate(key->input(), key->element())->remove_keyframe(key); + + emit KeyframeRemoved(key); + emit ValueChanged(i, time_affected); } } } +void Node::InvalidateFromKeyframeBezierInChange() +{ + NodeKeyframe* key = static_cast(sender()); + const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); + int keyframe_index = track.indexOf(key); + + rational start = RATIONAL_MIN; + rational end = key->time(); + + if (keyframe_index > 0) { + start = track.at(keyframe_index - 1)->time(); + } + + emit ValueChanged(key->key_track_ref().input(), TimeRange(start, end)); +} + +void Node::InvalidateFromKeyframeBezierOutChange() +{ + NodeKeyframe* key = static_cast(sender()); + const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); + int keyframe_index = track.indexOf(key); + + rational start = key->time(); + rational end = RATIONAL_MAX; + + if (keyframe_index < track.size() - 1) { + end = track.at(keyframe_index + 1)->time(); + } + + emit ValueChanged(key->key_track_ref().input(), TimeRange(start, end)); +} + +void Node::InvalidateFromKeyframeTimeChange() +{ + NodeKeyframe* key = static_cast(sender()); + NodeInputImmediate* immediate = GetImmediate(key->input(), key->element()); + TimeRange original_range = GetRangeAffectedByKeyframe(key); + + TimeRangeList invalidate_range; + invalidate_range.insert(original_range); + + if (!(original_range.in() < key->time() && 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(GetRangeAffectedByKeyframe(key)); + } + + // 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(key->key_track_ref().input(), r); + } +} + +void Node::InvalidateFromKeyframeValueChange() +{ + NodeKeyframe* key = static_cast(sender()); + emit ValueChanged(key->key_track_ref().input(), GetRangeAffectedByKeyframe(key)); +} + +void Node::InvalidateFromKeyframeTypeChanged() +{ + NodeKeyframe* key = static_cast(sender()); + const NodeKeyframeTrack& track = GetTrackFromKeyframe(key); + + if (track.size() == 1) { + // If there are no other frames, the interpolation won't do anything + return; + } + + // Invalidate entire range + emit ValueChanged(key->key_track_ref().input(), GetRangeAroundIndex(key->input(), track.indexOf(key), key->track(), key->element())); +} + +Project *Node::ArrayInsertCommand::GetRelevantProject() const +{ + return node_->parent()->project(); +} + +Project *Node::ArrayRemoveCommand::GetRelevantProject() const +{ + return node_->parent()->project(); +} + +Project *Node::ArrayResizeCommand::GetRelevantProject() const +{ + return node_->parent()->project(); +} + } diff --git a/app/node/node.h b/app/node/node.h index 7ae591793..6ecf2b9cb 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -21,6 +21,7 @@ #ifndef NODE_H #define NODE_H +#include #include #include #include @@ -30,15 +31,17 @@ #include "codec/frame.h" #include "codec/samplebuffer.h" #include "common/rational.h" +#include "common/timerange.h" #include "common/xmlutils.h" -#include "node/connectable.h" -#include "node/input.h" -#include "node/value.h" +#include "node/keyframe.h" +#include "node/inputimmediate.h" +#include "node/param.h" #include "render/audioparams.h" #include "render/job/generatejob.h" #include "render/job/samplejob.h" #include "render/job/shaderjob.h" #include "render/shadercode.h" +#include "splitvalue.h" namespace olive { @@ -58,7 +61,7 @@ class NodeGraph; * 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 NodeConnectable +class Node : public QObject { Q_OBJECT public: @@ -80,7 +83,7 @@ public: kCategoryCount }; - Node(); + Node(bool create_default_output = true); virtual ~Node() override; @@ -154,6 +157,31 @@ public: */ virtual void Retranslate(); + const QVector& inputs() const + { + return input_ids_; + } + + const QVector& outputs() const + { + return outputs_; + } + + bool HasInputWithID(const QString& id) const + { + return input_ids_.contains(id); + } + + bool HasOutputWithID(const QString& id) const + { + return outputs_.contains(id); + } + + bool HasParamWithID(const QString& id) const + { + return HasInputWithID(id) || HasOutputWithID(id); + } + /** * @brief Retrieve the color of this node */ @@ -180,26 +208,266 @@ public: } } - /** - * @brief Return a list of NodeParams - */ - const QVector& parameters() const + static void ConnectEdge(const NodeOutput& output, const NodeInput& input); + + static void DisconnectEdge(const NodeOutput& output, const NodeInput& input); + + QString GetInputName(const QString& id) const; + + void LoadInput(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled); + void SaveInput(QXmlStreamWriter* writer, const QString& id) const; + + bool IsInputConnectable(const QString& input) const; + bool IsInputKeyframable(const QString& input) const; + + bool IsInputKeyframing(const QString& input, int element = -1) const; + bool IsInputKeyframing(const NodeInput& input) const { - return inputs_; + return IsInputKeyframing(input.input(), input.element()); } - const QVector& inputs() const + void SetInputIsKeyframing(const QString& input, bool e, int element = -1); + void SetInputIsKeyframing(const NodeInput& input, bool e) { - return inputs_; + SetInputIsKeyframing(input.input(), e, input.element()); } - /** - * @brief Return the index of a parameter - * @return Parameter index or -1 if this parameter is not part of this Node - */ - int IndexOfParameter(NodeInput* param) const + bool IsInputConnected(const QString& input, int element = -1) const; + bool IsInputConnected(const NodeInput& input) const { - return inputs_.indexOf(param); + return IsInputConnected(input.input(), input.element()); + } + + bool IsInputStatic(const QString& input, int element = -1) const + { + return !IsInputConnected(input, element) && !IsInputKeyframing(input, element); + } + + bool IsInputStatic(const NodeInput& input) const + { + return IsInputStatic(input.input(), input.element()); + } + + NodeOutput GetConnectedOutput(const QString& input, int element = -1) const; + + NodeOutput GetConnectedOutput(const NodeInput& input) const + { + return GetConnectedOutput(input.input(), input.element()); + } + + Node* GetConnectedNode(const QString& input, int element = -1) const + { + return GetConnectedOutput(input, element).node(); + } + + Node* GetConnectedNode(const NodeInput& input) const + { + return GetConnectedNode(input.input(), input.element()); + } + + bool IsUsingStandardValue(const QString& input, int track, int element = -1) const; + + NodeValue::Type GetInputDataType(const QString& id) const; + void SetInputDataType(const QString& id, const NodeValue::Type& type); + + bool HasInputProperty(const QString& id, const QString& name) const; + QHash GetInputProperties(const QString& id) const; + QVariant GetInputProperty(const QString& id, const QString& name) const; + void SetInputProperty(const QString& id, const QString& name, const QVariant& value); + + QVariant GetValueAtTime(const QString& input, const rational& time, int element = -1) const + { + NodeValue::Type type = GetInputDataType(input); + + return NodeValue::combine_track_values_into_normal_value(type, GetSplitValueAtTime(input, time, element)); + } + + QVariant GetValueAtTime(const NodeInput& input, const rational& time) + { + return GetValueAtTime(input.input(), time, input.element()); + } + + SplitValue GetSplitValueAtTime(const QString& input, const rational& time, int element = -1) const; + + SplitValue GetSplitValueAtTime(const NodeInput& input, const rational& time) + { + return GetSplitValueAtTime(input.input(), time, input.element()); + } + + QVariant GetSplitValueAtTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const; + QVariant GetSplitValueAtTimeOnTrack(const NodeInput& input, const rational& time, int track) const + { + return GetSplitValueAtTimeOnTrack(input.input(), time, track, input.element()); + } + + QVariant GetSplitValueAtTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const + { + return GetSplitValueAtTimeOnTrack(input.input(), time, input.track()); + } + + QVariant GetDefaultValue(const QString& input) const; + SplitValue GetSplitDefaultValue(const QString& input) const; + QVariant GetSplitDefaultValueOnTrack(const QString& input, int track) const; + + const QVector& GetKeyframeTracks(const QString& input, int element) const; + const QVector& GetKeyframeTracks(const NodeInput& input) const + { + return GetKeyframeTracks(input.input(), input.element()); + } + + QVector GetKeyframesAtTime(const QString& input, const rational& time, int element = -1) const; + QVector GetKeyframesAtTime(const NodeInput& input, const rational& time) const + { + return GetKeyframesAtTime(input.input(), time, input.element()); + } + + NodeKeyframe* GetKeyframeAtTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const; + NodeKeyframe* GetKeyframeAtTimeOnTrack(const NodeInput& input, const rational& time, int track) const + { + return GetKeyframeAtTimeOnTrack(input.input(), time, track, input.element()); + } + + NodeKeyframe* GetKeyframeAtTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const + { + return GetKeyframeAtTimeOnTrack(input.input(), time, input.track()); + } + + NodeKeyframe::Type GetBestKeyframeTypeForTimeOnTrack(const QString& input, const rational& time, int track, int element = -1) const; + + NodeKeyframe::Type GetBestKeyframeTypeForTimeOnTrack(const NodeInput& input, const rational& time, int track) const + { + return GetBestKeyframeTypeForTimeOnTrack(input.input(), time, track, input.element()); + } + + NodeKeyframe::Type GetBestKeyframeTypeForTimeOnTrack(const NodeKeyframeTrackReference& input, const rational& time) const + { + return GetBestKeyframeTypeForTimeOnTrack(input.input(), time, input.track()); + } + + int GetNumberOfKeyframeTracks(const QString& id) const; + int GetNumberOfKeyframeTracks(const NodeInput& id) const + { + return GetNumberOfKeyframeTracks(id.input()); + } + + NodeKeyframe* GetEarliestKeyframe(const QString& id, int element = -1) const; + NodeKeyframe* GetEarliestKeyframe(const NodeInput& id) const + { + return GetEarliestKeyframe(id.input(), id.element()); + } + + NodeKeyframe* GetLatestKeyframe(const QString& id, int element = -1) const; + NodeKeyframe* GetLatestKeyframe(const NodeInput& id) const + { + return GetLatestKeyframe(id.input(), id.element()); + } + + NodeKeyframe* GetClosestKeyframeBeforeTime(const QString& id, const rational& time, int element = -1) const; + NodeKeyframe* GetClosestKeyframeBeforeTime(const NodeInput& id, const rational& time) const + { + return GetClosestKeyframeBeforeTime(id.input(), time, id.element()); + } + + NodeKeyframe* GetClosestKeyframeAfterTime(const QString& id, const rational& time, int element = -1) const; + NodeKeyframe* GetClosestKeyframeAfterTime(const NodeInput& id, const rational& time) const + { + return GetClosestKeyframeAfterTime(id.input(), time, id.element()); + } + + bool HasKeyframeAtTime(const QString& id, const rational& time, int element = -1) const; + bool HasKeyframeAtTime(const NodeInput& id, const rational& time) const + { + return HasKeyframeAtTime(id.input(), time, id.element()); + } + + QStringList GetComboBoxStrings(const QString& id) const; + + QVariant GetStandardValue(const QString& id, int element = -1) const; + QVariant GetStandardValue(const NodeInput& id) const + { + return GetStandardValue(id.input(), id.element()); + } + + SplitValue GetSplitStandardValue(const QString& id, int element = -1) const; + SplitValue GetSplitStandardValue(const NodeInput& id) const + { + return GetSplitStandardValue(id.input(), id.element()); + } + + QVariant GetSplitStandardValueOnTrack(const QString& input, int track, int element = -1) const; + QVariant GetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id) const + { + return GetSplitStandardValueOnTrack(id.input().input(), id.track(), id.input().element()); + } + + void SetStandardValue(const QString& id, const QVariant& value, int element = -1); + void SetStandardValue(const NodeInput& id, const QVariant& value) + { + SetStandardValue(id.input(), value, id.element()); + } + + void SetSplitStandardValue(const QString& id, const SplitValue& value, int element = -1); + void SetSplitStandardValue(const NodeInput& id, const SplitValue& value) + { + SetSplitStandardValue(id.input(), value, id.element()); + } + + void SetSplitStandardValueOnTrack(const QString& id, int track, const QVariant& value, int element = -1); + void SetSplitStandardValueOnTrack(const NodeKeyframeTrackReference& id, const QVariant& value) + { + SetSplitStandardValueOnTrack(id.input().input(), id.track(), value, id.input().element()); + } + + bool InputIsArray(const QString& id) const; + + void InputArrayInsert(const QString& id, int index, bool undoable = false); + void InputArrayResize(const QString& id, int size, bool undoable = false); + void InputArrayRemove(const QString& id, int index, bool undoable = false); + + void InputArrayAppend(const QString& id, bool undoable = false) + { + InputArrayResize(id, InputArraySize(id) + 1, undoable); + } + + void InputArrayPrepend(const QString& id, bool undoable = false) + { + InputArrayInsert(id, 0, undoable); + } + + void InputArrayRemoveLast(const QString& id, bool undoable = false) + { + InputArrayResize(id, InputArraySize(id) - 1, undoable); + } + + int InputArraySize(const QString& id) const; + + const NodeKeyframeTrack& GetTrackFromKeyframe(NodeKeyframe* key) const; + + using InputConnections = std::map; + + /** + * @brief Return map of input connections + * + * Inputs can only have one connection, so the key is the input connected and the value is the + * output that it's connected to. + */ + const InputConnections& input_connections() const + { + return input_connections_; + } + + using OutputConnection = std::pair; + using OutputConnections = std::vector; + + /** + * @brief Return list of output connections + * + * An output can connect an infinite amount of inputs, so in this map, the key is the output and + * the value is a vector of inputs. + */ + const OutputConnections& output_connections() const + { + return output_connections_; } /** @@ -239,11 +507,6 @@ public: */ virtual void GenerateFrame(FramePtr frame, const GenerateJob &job) const; - /** - * @brief Returns the input with the specified ID (or nullptr if it doesn't exist) - */ - NodeInput* GetInputWithID(const QString& id) const; - /** * @brief Returns whether this Node outputs to `n` * @@ -257,7 +520,6 @@ public: * (FALSE). */ bool OutputsTo(Node* n, bool recursively) const; - /** * @brief Same as OutputsTo(Node*), but for a node ID rather than a specific instance. */ @@ -266,7 +528,7 @@ public: /** * @brief Same as OutputsTo(Node*), but for a specific node input rather than just a node. */ - bool OutputsTo(NodeInput* input, bool recursively) const; + bool OutputsTo(const NodeInput &input, bool recursively) const; /** * @brief Returns whether this node ever receives an input from a particular node instance @@ -330,7 +592,11 @@ 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, const InputConnection& from = InputConnection()); + virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1); + void InvalidateCache(const TimeRange& range, const NodeInput& from) + { + InvalidateCache(range, from.input(), from.element()); + } /** * @brief Limits cache invalidation temporarily @@ -351,12 +617,12 @@ public: * If this node modifies the `time` (i.e. a clip converting sequence time to media time), this function should be * overridden to do so. Also make sure to override OutputTimeAdjustment() to provide the inverse function. */ - virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const; /** * @brief The inverse of InputTimeAdjustment() */ - virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const; + virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const; /** * @brief Copies inputs from from Node to another including connections @@ -365,6 +631,14 @@ public: */ static void CopyInputs(Node* source, Node* destination, bool include_connections = true); + static void CopyInput(Node* src, Node* dst, const QString& input, bool include_connections, bool traverse_arrays); + + static void CopyValuesOfElement(Node* src, Node* dst, const QString& input, int src_element, int dst_element); + static void CopyValuesOfElement(Node* src, Node* dst, const QString& input, int element) + { + return CopyValuesOfElement(src, dst, input, element, element); + } + /** * @brief Clones a set of nodes and connects the new ones the way the old ones were */ @@ -394,12 +668,7 @@ public: * corresponding output if it's connected to one. If your node doesn't directly deal with time, the default behavior * of the NodeParam objects will handle everything related to it automatically. */ - virtual NodeValueTable Value(NodeValueDatabase& value) const; - - /** - * @brief Return whether a parameter with ID `id` has already been added to this Node - */ - bool HasParamWithID(const QString& id) const; + virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const; const QPointF& GetPosition() const; @@ -418,12 +687,7 @@ public: virtual void Hash(QCryptographicHash& hash, const rational &time) const; - const std::vector& edges() const - { - return output_connections(); - } - - void InvalidateAll(NodeInput *input, int element); + void InvalidateAll(const QString& input, int element = -1); bool HasLinks() const { @@ -439,7 +703,58 @@ public: static bool Unlink(Node* a, Node* b); static bool AreLinked(Node* a, Node* b); + static const QString kDefaultOutput; + protected: + enum InputFlag { + /// By default, inputs are keyframable, connectable, and NOT arrays + kInputFlagNormal = 0x0, + kInputFlagArray = 0x1, + kInputFlagNotKeyframable = 0x2, + kInputFlagNotConnectable = 0x4 + }; + + class InputFlags { + public: + explicit InputFlags() + { + f_ = kInputFlagNormal; + } + + explicit InputFlags(uint64_t flags) + { + f_ = flags; + } + + bool operator&(const InputFlag& f) const + { + return f_ & f; + } + + private: + uint64_t f_; + + }; + + void AddInput(const QString& id, NodeValue::Type type, const QVariant& default_value, InputFlags flags = InputFlags(kInputFlagNormal)); + void AddInput(const QString& id, NodeValue::Type type, InputFlags flags = InputFlags(kInputFlagNormal)) + { + AddInput(id, type, QVariant(), flags); + } + + void RemoveInput(const QString& id); + + void AddOutput(const QString& id = kDefaultOutput); + + void RemoveOutput(const QString& id); + + void SetInputName(const QString& id, const QString& name); + + void SetComboBoxStrings(const QString& id, const QStringList& strings) + { + SetInputProperty(id, QStringLiteral("combo_str"), strings); + } + void SendInvalidateCache(const TimeRange &range); /** @@ -449,14 +764,14 @@ protected: * 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 IgnoreInvalidationsFrom(NodeInput* input); + void IgnoreInvalidationsFrom(const QString &input_id); + + void IgnoreHashingFrom(const QString& input_id); virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data); virtual void SaveInternal(QXmlStreamWriter* writer) const; - virtual QVector GetInputsToHash() const; - enum GizmoScaleHandles { kGizmoScaleTopLeft, kGizmoScaleTopCenter, @@ -475,10 +790,16 @@ protected: static void DrawAndExpandGizmoHandles(QPainter* p, int handle_radius, QRectF* rects, int count); - virtual void childEvent(QChildEvent* event) override; - virtual void LinkChangeEvent(){} + virtual void InputValueChangedEvent(const QString& input, int element); + + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output); + + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output); + + virtual void childEvent(QChildEvent *event) override; + signals: /** * @brief Signal emitted whenever the position is set through SetPosition() @@ -492,19 +813,231 @@ signals: void ColorChanged(); - void ValueChanged(NodeInput* input, int element); + void ValueChanged(const NodeInput& input, const TimeRange& range); - void InputConnected(Node* output, NodeInput* input, int element); + void InputConnected(const NodeOutput& output, const NodeInput& input); - void InputDisconnected(Node* output, NodeInput* input, int element); + void InputDisconnected(const NodeOutput& output, const NodeInput& input); - void OutputConnected(NodeInput* destination, int element); + void OutputConnected(const NodeOutput& output, const NodeInput& input); - void OutputDisconnected(NodeInput* destination, int element); + void OutputDisconnected(const NodeOutput& output, const NodeInput& input); + + void InputPropertyChanged(const QString& input, const QString& key, const QVariant& value); void LinksChanged(); + void InputArraySizeChanged(const QString& input, int new_size); + + void KeyframeAdded(NodeKeyframe* key); + + void KeyframeRemoved(NodeKeyframe* key); + + void KeyframeTimeChanged(); + + void KeyframeEnableChanged(const NodeInput& input, bool enabled); + + void InputAdded(const QString& id); + + void InputRemoved(const QString& id); + + void OutputAdded(const QString& id); + + void OutputRemoved(const QString& id); + + void InputNameChanged(const QString& id, const QString& name); + + void InputDataTypeChanged(const QString& id, NodeValue::Type type); + private: + class ArrayInsertCommand : public UndoCommand + { + public: + ArrayInsertCommand(Node* node, const QString& input, int index) : + node_(node), + input_(input), + index_(index) + { + } + + virtual Project* GetRelevantProject() const override; + + virtual void redo() override + { + node_->InputArrayInsert(input_, index_, false); + } + + virtual void undo() override + { + node_->InputArrayRemove(input_, index_, false); + } + + private: + Node* node_; + QString input_; + int index_; + + }; + + class ArrayRemoveCommand : public UndoCommand + { + public: + ArrayRemoveCommand(Node* node, const QString& input, int index) : + node_(node), + input_(input), + index_(index) + { + } + + virtual Project* GetRelevantProject() const override; + + protected: + virtual void redo() override + { + // Save immediate data + if (node_->IsInputKeyframable(input_)) { + is_keyframing_ = node_->IsInputKeyframing(input_, index_); + } + standard_value_ = node_->GetSplitStandardValue(input_, index_); + keyframes_ = node_->GetKeyframeTracks(input_, index_); + node_->GetImmediate(input_, index_)->delete_all_keyframes(&memory_manager_); + + node_->InputArrayRemove(input_, index_, false); + } + + virtual void undo() override + { + node_->InputArrayInsert(input_, index_, false); + + // Restore keyframes + foreach (const NodeKeyframeTrack& track, keyframes_) { + foreach (NodeKeyframe* key, track) { + key->setParent(node_); + } + } + node_->SetSplitStandardValue(input_, standard_value_, index_); + + if (node_->IsInputKeyframable(input_)) { + node_->SetInputIsKeyframing(input_, is_keyframing_, index_); + } + } + + private: + Node* node_; + QString input_; + int index_; + + SplitValue standard_value_; + bool is_keyframing_; + QVector keyframes_; + QObject memory_manager_; + + }; + + class ArrayResizeCommand : public UndoCommand + { + public: + ArrayResizeCommand(Node* node, const QString& input, int size) : + node_(node), + input_(input), + size_(size) + {} + + virtual void redo() override + { + old_size_ = node_->GetInternalInputArraySize(input_); + + if (old_size_ > size_) { + // Decreasing in size, disconnect any extraneous edges + for (int i=size_; iinput_connections().at(input); + + removed_connections_[input] = output; + + DisconnectEdge(output, input); + } catch (std::out_of_range&) {} + } + } + + node_->ArrayResizeInternal(input_, size_); + } + + virtual void undo() override + { + for (auto it=removed_connections_.cbegin(); it!=removed_connections_.cend(); it++) { + ConnectEdge(it->second, it->first); + } + removed_connections_.clear(); + + node_->ArrayResizeInternal(input_, old_size_); + } + + virtual Project* GetRelevantProject() const override; + + private: + Node* node_; + QString input_; + int size_; + int old_size_; + + InputConnections removed_connections_; + + }; + + struct Input { + NodeValue::Type type; + InputFlags flags; + SplitValue default_value; + QHash properties; + QString human_name; + int array_size; + }; + + NodeInputImmediate* CreateImmediate(const QString& input); + + NodeInputImmediate* GetImmediate(const QString& input, int element) const; + + int GetInternalInputIndex(const QString& input) const + { + return input_ids_.indexOf(input); + } + + InputFlags GetInputFlags(const QString& input) const; + + Input* GetInternalInputData(const QString& input) + { + int i = GetInternalInputIndex(input); + + if (i == -1) { + return nullptr; + } else { + return &input_data_[i]; + } + } + + const Input* GetInternalInputData(const QString& input) const + { + int i = GetInternalInputIndex(input); + + if (i == -1) { + return nullptr; + } else { + return &input_data_.at(i); + } + } + + void ReportInvalidInput(const char* attempted_action, const QString &id) const; + + void ArrayResizeInternal(const QString& id, int size); + + /** + * @brief Immediates aren't deleted, so the actual array size may be larger than ArraySize() + */ + int GetInternalInputArraySize(const QString& input); + template static void FindInputNodeInternal(const Node* n, QVector& list); @@ -513,11 +1046,29 @@ private: QVector GetDependenciesInternal(bool traverse, bool exclusive_only) const; - void HashInputElement(QCryptographicHash& hash, NodeInput* input, int element, const rational& time) const; + void HashInputElement(QCryptographicHash& hash, const QString &input, int element, const rational& time) const; - QVector inputs_; + void ParameterValueChanged(const QString &input, int element, const olive::TimeRange &range); - QVector ignore_connections_; + void LoadImmediate(QXmlStreamReader *reader, const QString& input, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled); + + void SaveImmediate(QXmlStreamWriter *writer, const QString &input, int element) const; + + /** + * @brief Intelligently determine how what time range is affected by a keyframe + */ + TimeRange GetRangeAffectedByKeyframe(NodeKeyframe *key) const; + + /** + * @brief Gets a time range between the previous and next keyframes of index + */ + TimeRange GetRangeAroundIndex(const QString& input, int index, int track, int element) const; + + void ClearElement(const QString &input, int index); + + QVector ignore_connections_; + + QVector ignore_when_hashing_; /** * @brief Internal variable for whether this Node can be deleted or not @@ -544,29 +1095,59 @@ private: */ QVector links_; + QVector input_ids_; + QVector input_data_; + + QVector outputs_; + + QMap standard_immediates_; + + QMap > array_immediates_; + + InputConnections input_connections_; + + OutputConnections output_connections_; + private slots: - void ParameterValueChanged(const olive::TimeRange &range, int element); + /** + * @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time + */ + void InvalidateFromKeyframeTimeChange(); - void ParameterConnected(Node* source, int element); + /** + * @brief Slot when a keyframe's value changes to signal that the cache needs updating + */ + void InvalidateFromKeyframeValueChange(); - void ParameterDisconnected(Node* source, int element); + /** + * @brief Slot when a keyframe's type changes to signal that the cache needs updating + */ + void InvalidateFromKeyframeTypeChanged(); + + /** + * @brief Slot when a keyframe's bezier in value changes to signal that the cache needs updating + */ + void InvalidateFromKeyframeBezierInChange(); + + /** + * @brief Slot when a keyframe's bezier out value changes to signal that the cache needs updating + */ + void InvalidateFromKeyframeBezierOutChange(); }; template void Node::FindInputNodeInternal(const Node* n, QVector &list) { - foreach (NodeInput* input, n->inputs_) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - Node* edge = it->second; - T* cast_test = dynamic_cast(edge); + for (auto it=n->input_connections_.cbegin(); it!=n->input_connections_.cend(); it++) { + Node* edge = it->second.node(); + T* cast_test = dynamic_cast(edge); - if (cast_test) { - list.append(cast_test); - } - - FindInputNodeInternal(edge, list); + if (cast_test) { + list.append(cast_test); } + + FindInputNodeInternal(edge, list); } } @@ -589,15 +1170,17 @@ T* Node::ValueToPtr(const QVariant &ptr) template 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); + foreach (const std::vector& outputs, n->output_connections_) { + foreach (const NodeInput& output, outputs) { + Node* connected = output.node(); + T* cast_test = dynamic_cast(connected); - if (cast_test) { - list.append(cast_test); + if (cast_test) { + list.append(cast_test); + } + + FindOutputNodeInternal(connected); } - - FindOutputNodeInternal(connected); } } diff --git a/app/node/nodecopypaste.cpp b/app/node/nodecopypaste.cpp index 0a64328a4..f9f000eb0 100644 --- a/app/node/nodecopypaste.cpp +++ b/app/node/nodecopypaste.cpp @@ -132,37 +132,6 @@ QVector NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, // Link blocks XMLLinkBlocks(xml_node_data); - // Connect footage to existing footage if it exists - if (!xml_node_data.footage_connections.isEmpty()) { - // Get list of all footage from project - QVector footage = graph->project()->get_items_of_type(Item::kFootage); - - if (!footage.isEmpty()) { - foreach (const XMLNodeData::FootageConnection& con, xml_node_data.footage_connections) { - if (con.footage) { - // Assume this is a pointer to a Stream* - Stream* loaded_stream = reinterpret_cast(con.footage); - - bool found = false; - - foreach (Item* item, footage) { - foreach (Stream* s, static_cast(item)->streams()) { - if (s == loaded_stream) { - con.input->SetStandardValue(Node::PtrToValue(s), con.element); - found = true; - break; - } - } - - if (found) { - break; - } - } - } - } - } - } - return pasted_nodes; } @@ -172,6 +141,7 @@ void NodeCopyPasteService::CopyNodesToClipboardInternal(QXmlStreamWriter*, void* void NodeCopyPasteService::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, XMLNodeData &xml_node_data, void*) { + Q_UNUSED(xml_node_data) reader->skipCurrentElement(); } diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index c84c67dd7..9726c69f8 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -33,24 +33,21 @@ const double Track::kTrackHeightDefault = 3.0; const double Track::kTrackHeightMinimum = 1.5; const double Track::kTrackHeightInterval = 0.5; +const QString Track::kBlockInput = QStringLiteral("block_in"); +const QString Track::kMutedInput = QStringLiteral("muted_in"); + Track::Track() : track_type_(Track::kNone), index_(-1), locked_(false) { - block_input_ = new NodeInput(this, QStringLiteral("block_in"), NodeValue::kNone); - block_input_->SetKeyframable(false); - block_input_->SetIsArray(true); - connect(block_input_, &NodeInput::InputConnected, this, &Track::BlockConnected); - connect(block_input_, &NodeInput::InputDisconnected, this, &Track::BlockDisconnected); + AddInput(kBlockInput, NodeValue::kNone, InputFlags(kInputFlagArray | kInputFlagNotKeyframable)); // Since blocks are time based, we can handle the invalidate timing a little more intelligently // on our end - IgnoreInvalidationsFrom(block_input_); + IgnoreInvalidationsFrom(kBlockInput); - muted_input_ = new NodeInput(this, QStringLiteral("muted_in"), NodeValue::kBoolean); - muted_input_->SetKeyframable(false); - connect(muted_input_, &NodeInput::ValueChanged, this, &Track::MutedInputValueChanged); + AddInput(kMutedInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); // Set default height track_height_ = kTrackHeightDefault; @@ -97,9 +94,9 @@ QString Track::Description() const "a Sequence."); } -TimeRange Track::InputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const +TimeRange Track::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { - if (input == block_input_ && element >= 0) { + if (input == kBlockInput && element >= 0) { int cache_index = GetCacheIndexFromArrayIndex(element); return TransformRangeForBlock(blocks_.at(cache_index), input_time); @@ -108,9 +105,9 @@ TimeRange Track::InputTimeAdjustment(NodeInput *input, int element, const TimeRa return Node::InputTimeAdjustment(input, element, input_time); } -TimeRange Track::OutputTimeAdjustment(NodeInput *input, int element, const TimeRange &input_time) const +TimeRange Track::OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const { - if (input == block_input_ && element >= 0) { + if (input == kBlockInput && element >= 0) { int cache_index = GetCacheIndexFromArrayIndex(element); const rational& block_in = blocks_.at(cache_index)->in(); @@ -157,12 +154,153 @@ void Track::SaveInternal(QXmlStreamWriter *writer) const writer->writeTextElement(QStringLiteral("height"), QString::number(GetTrackHeight())); } +void Track::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + if (input == kBlockInput) { + if (element == -1) { + // User has replaced the entire array, we will invalidate everything + InvalidateAll(kBlockInput, element); + return; + } + + // Check if a block was connected, if not, ignore + Block* block = dynamic_cast(output.node()); + + if (!block) { + return; + } + + // Determine where in the cache this block will be + int cache_index = -1; + Block *previous = nullptr, *next = nullptr; + + int arr_sz = InputArraySize(kBlockInput); + for (int i=element+1; i= 0) { + next = blocks_.at(cache_index); + break; + } + } + + // If there was no next, this will be inserted at the end + if (cache_index == -1) { + cache_index = blocks_.size(); + } + + // Determine previous block, either by using next's previous or the last block if there was no + // next. If there are neither, they'll both remain null + if (next) { + previous = next->previous(); + } else if (!blocks_.isEmpty()) { + previous = blocks_.last(); + } + + // Insert at index + blocks_.insert(cache_index, block); + block_array_indexes_.insert(cache_index, element); + + // Update previous/next + if (previous) { + previous->set_next(block); + block->set_previous(previous); + } + + if (next) { + block->set_next(next); + next->set_previous(block); + } + + block->set_track(this); + + // Update ins/outs + UpdateInOutFrom(cache_index); + + // Connect to the block + connect(block, &Block::LengthChanged, this, &Track::BlockLengthChanged); + + // Invalidate cache now that block should have an in point + InvalidateCache(TimeRange(block->in(), track_length())); + + // Emit block added signal + emit BlockAdded(block); + } +} + +void Track::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + if (input == kBlockInput) { + if (element == -1) { + // User has replaced the entire array, we will invalidate everything + InvalidateAll(kBlockInput, element); + return; + } + + Block* b = dynamic_cast(output.node()); + + if (!b) { + return; + } + + emit BlockRemoved(b); + + TimeRange invalidate_range(b->in(), track_length()); + + // Get cache index + int cache_index = GetCacheIndexFromArrayIndex(element); + + // Remove block here + blocks_.removeAt(cache_index); + block_array_indexes_.removeAt(cache_index); + + // Update previous/nexts + 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); + b->set_track(nullptr); + + // Update lengths + if (next) { + UpdateInOutFrom(blocks_.indexOf(next)); + } else if (blocks_.isEmpty()) { + SetLengthInternal(rational()); + } else { + SetLengthInternal(blocks_.last()->out()); + } + + disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged); + + InvalidateCache(invalidate_range); + } +} + +void Track::InputValueChangedEvent(const QString &input, int element) +{ + Q_UNUSED(element) + + if (input == kMutedInput) { + emit MutedChanged(IsMuted()); + } +} + void Track::Retranslate() { Node::Retranslate(); - block_input_->set_name(tr("Blocks")); - muted_input_->set_name(tr("Muted")); + SetInputName(kBlockInput, tr("Blocks")); + SetInputName(kMutedInput, tr("Muted")); } void Track::SetIndex(const int &index) @@ -276,15 +414,15 @@ QVector Track::BlocksAtTimeRange(const TimeRange &range) const return list; } -void Track::InvalidateCache(const TimeRange& range, const InputConnection& from) +void Track::InvalidateCache(const TimeRange& range, const QString& from, int element) { TimeRange limited; - Block* b; + const Block* b; - if (from.input == block_input_ - && from.element >= 0 - && (b = dynamic_cast(from.input->GetConnectedNode(from.element)))) { + if (from == kBlockInput + && element >= 0 + && (b = dynamic_cast(GetConnectedOutput(from, element).node()))) { // Limit the range signal to the corresponding block if (range.out() <= b->in() || range.in() >= b->out()) { return; @@ -329,8 +467,8 @@ void Track::PrependBlock(Block *block) { BeginOperation(); - block_input_->ArrayPrepend(); - Node::ConnectEdge(block, block_input_, 0); + InputArrayPrepend(kBlockInput); + Node::ConnectEdge(block, NodeInput(this, kBlockInput, 0)); EndOperation(); @@ -343,8 +481,8 @@ void Track::InsertBlockAtIndex(Block *block, int index) BeginOperation(); int insert_index = GetArrayIndexFromCacheIndex(index); - block_input_->ArrayInsert(insert_index); - Node::ConnectEdge(block, block_input_, insert_index); + InputArrayInsert(kBlockInput, insert_index); + Node::ConnectEdge(block, NodeInput(this, kBlockInput, insert_index)); EndOperation(); @@ -355,8 +493,8 @@ void Track::AppendBlock(Block *block) { BeginOperation(); - block_input_->ArrayAppend(); - Node::ConnectEdge(block, block_input_, block_input_->ArraySize() - 1); + InputArrayAppend(kBlockInput); + Node::ConnectEdge(block, NodeInput(this, kBlockInput, InputArraySize(kBlockInput) - 1)); EndOperation(); @@ -371,7 +509,7 @@ void Track::RippleRemoveBlock(Block *block) rational remove_in = block->in(); rational remove_out = block->out(); - block_input_->ArrayRemove(GetArrayIndexFromBlock(block)); + InputArrayRemove(kBlockInput, GetArrayIndexFromBlock(block)); EndOperation(); @@ -384,9 +522,9 @@ void Track::ReplaceBlock(Block *old, Block *replace) int index_of_old_block = GetArrayIndexFromBlock(old); - DisconnectEdge(old, block_input_, index_of_old_block); + DisconnectEdge(old, NodeInput(this, kBlockInput, index_of_old_block)); - ConnectEdge(replace, block_input_, index_of_old_block); + ConnectEdge(replace, NodeInput(this, kBlockInput, index_of_old_block)); EndOperation(); @@ -421,7 +559,7 @@ QString Track::GetDefaultTrackName(Track::Type type, int index) bool Track::IsMuted() const { - return muted_input_->GetStandardValue().toBool(); + return GetStandardValue(kMutedInput).toBool(); } bool Track::IsLocked() const @@ -429,11 +567,6 @@ bool Track::IsLocked() const return locked_; } -NodeInput *Track::block_input() const -{ - return block_input_; -} - void Track::Hash(QCryptographicHash &hash, const rational &time) const { Block* b = BlockAtTime(time); @@ -446,8 +579,7 @@ void Track::Hash(QCryptographicHash &hash, const rational &time) const void Track::SetMuted(bool e) { - muted_input_->SetStandardValue(e); - InvalidateCache(TimeRange(0, track_length())); + SetStandardValue(kMutedInput, e); } void Track::SetLocked(bool e) @@ -511,133 +643,6 @@ void Track::SetLengthInternal(const rational &r, bool invalidate) } } -void Track::BlockConnected(Node *node, int element) -{ - if (element == -1) { - // User has replaced the entire array, we will invalidate everything - InvalidateAll(block_input_, element); - return; - } - - // Check if a block was connected, if not, ignore - Block* block = dynamic_cast(node); - - if (!block) { - return; - } - - // Determine where in the cache this block will be - int cache_index = -1; - Block *previous = nullptr, *next = nullptr; - - for (int i=element+1; iArraySize(); i++) { - // Find next block because this will be the index that we want to insert at - cache_index = GetCacheIndexFromArrayIndex(i); - - if (cache_index >= 0) { - next = blocks_.at(cache_index); - break; - } - } - - // If there was no next, this will be inserted at the end - if (cache_index == -1) { - cache_index = blocks_.size(); - } - - // Determine previous block, either by using next's previous or the last block if there was no - // next. If there are neither, they'll both remain null - if (next) { - previous = next->previous(); - } else if (!blocks_.isEmpty()) { - previous = blocks_.last(); - } - - // Insert at index - blocks_.insert(cache_index, block); - block_array_indexes_.insert(cache_index, element); - - // Update previous/next - if (previous) { - previous->set_next(block); - block->set_previous(previous); - } - - if (next) { - block->set_next(next); - next->set_previous(block); - } - - block->set_track(this); - - // Update ins/outs - UpdateInOutFrom(cache_index); - - // Connect to the block - connect(block, &Block::LengthChanged, this, &Track::BlockLengthChanged); - - // Invalidate cache now that block should have an in point - InvalidateCache(TimeRange(block->in(), track_length())); - - // Emit block added signal - emit BlockAdded(block); -} - -void Track::BlockDisconnected(Node* node, int element) -{ - if (element == -1) { - // User has replaced the entire array, we will invalidate everything - InvalidateAll(block_input_, element); - return; - } - - Block* b = dynamic_cast(node); - - if (!b) { - return; - } - - emit BlockRemoved(b); - - TimeRange invalidate_range(b->in(), track_length()); - - // Get cache index - int cache_index = GetCacheIndexFromArrayIndex(element); - - // Remove block here - blocks_.removeAt(cache_index); - block_array_indexes_.removeAt(cache_index); - - // Update previous/nexts - 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); - b->set_track(nullptr); - - // Update lengths - if (next) { - UpdateInOutFrom(blocks_.indexOf(next)); - } else if (blocks_.isEmpty()) { - SetLengthInternal(rational()); - } else { - SetLengthInternal(blocks_.last()->out()); - } - - disconnect(b, &Block::LengthChanged, this, &Track::BlockLengthChanged); - - InvalidateCache(invalidate_range); -} - void Track::BlockLengthChanged() { // Assumes sender is a Block @@ -654,11 +659,6 @@ void Track::BlockLengthChanged() InvalidateCache(invalidate_region); } -void Track::MutedInputValueChanged() -{ - emit MutedChanged(IsMuted()); -} - uint qHash(const Track::Reference &r, uint seed) { // Not super efficient, but couldn't think of any better way to ensure a different hash each time diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 32b76f4ac..3beb78ba1 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -56,9 +56,9 @@ public: virtual QVector Category() const override; virtual QString Description() const override; - virtual TimeRange InputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; - virtual TimeRange OutputTimeAdjustment(NodeInput* input, int element, const TimeRange& input_time) const override; + virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; static rational TransformTimeForBlock(Block* block, const rational& time); @@ -222,7 +222,7 @@ public: return blocks_; } - virtual void InvalidateCache(const TimeRange& range, const InputConnection& from = InputConnection()) override; + virtual void InvalidateCache(const TimeRange& range, const QString& from = QString(), int element = -1) override; /** * @brief Adds Block `block` at the very beginning of the Sequence before all other clips @@ -274,8 +274,6 @@ public: bool IsLocked() const; - NodeInput* block_input() const; - virtual void Hash(QCryptographicHash& hash, const rational &time) const override; AudioVisualWaveform& waveform() @@ -287,6 +285,9 @@ public: static const double kTrackHeightMinimum; static const double kTrackHeightInterval; + static const QString kBlockInput; + static const QString kMutedInput; + public slots: void SetMuted(bool e); @@ -338,6 +339,12 @@ protected: virtual void SaveInternal(QXmlStreamWriter* writer) const override; + virtual void InputConnectedEvent(const QString& input, int element, const NodeOutput& output) override; + + virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output) override; + + virtual void InputValueChangedEvent(const QString& input, int element) override; + private: void UpdateInOutFrom(int index); @@ -352,10 +359,6 @@ private: QVector blocks_; QVector block_array_indexes_; - NodeInput* block_input_; - - NodeInput* muted_input_; - Track::Type track_type_; rational track_length_; @@ -371,14 +374,8 @@ private: AudioVisualWaveform waveform_; private slots: - void BlockConnected(Node* node, int element); - - void BlockDisconnected(Node* node, int element); - void BlockLengthChanged(); - void MutedInputValueChanged(); - }; uint qHash(const Track::Reference& r, uint seed = 0); diff --git a/app/node/output/track/tracklist.cpp b/app/node/output/track/tracklist.cpp index dd082966e..06ce79869 100644 --- a/app/node/output/track/tracklist.cpp +++ b/app/node/output/track/tracklist.cpp @@ -27,13 +27,11 @@ namespace olive { -TrackList::TrackList(ViewerOutput *parent, const Track::Type &type, NodeInput *track_input) : +TrackList::TrackList(ViewerOutput *parent, const Track::Type &type, const QString &track_input) : QObject(parent), track_input_(track_input), type_(type) { - connect(track_input_, &NodeInput::InputConnected, this, &TrackList::TrackConnected); - connect(track_input_, &NodeInput::InputDisconnected, this, &TrackList::TrackDisconnected); } Track *TrackList::GetTrackAt(int index) const @@ -48,7 +46,7 @@ Track *TrackList::GetTrackAt(int index) const void TrackList::TrackConnected(Node *node, int element) { if (element == -1) { - parent()->InvalidateAll(track_input_, element); + parent()->InvalidateAll(track_input(), element); return; } @@ -60,7 +58,7 @@ void TrackList::TrackConnected(Node *node, int element) // Determine where in the cache this block will be int cache_index = -1; - for (int i=element+1; iArraySize(); i++) { + for (int i=element+1; iInvalidateAll(track_input_, element); + parent()->InvalidateAll(track_input(), element); return; } @@ -141,11 +139,36 @@ NodeGraph *TrackList::GetParentGraph() const return static_cast(parent()->parent()); } +const QString& TrackList::track_input() const +{ + return track_input_; +} + +NodeInput TrackList::track_input(int element) const +{ + return NodeInput(parent(), track_input(), element); +} + ViewerOutput *TrackList::parent() const { return static_cast(QObject::parent()); } +int TrackList::ArraySize() const +{ + return parent()->InputArraySize(track_input()); +} + +void TrackList::ArrayAppend(bool undoable) +{ + parent()->InputArrayAppend(track_input(), undoable); +} + +void TrackList::ArrayRemoveLast(bool undoable) +{ + parent()->InputArrayRemoveLast(track_input(), undoable); +} + void TrackList::UpdateTotalLength() { total_length_ = 0; diff --git a/app/node/output/track/tracklist.h b/app/node/output/track/tracklist.h index 16cd647a9..042b93558 100644 --- a/app/node/output/track/tracklist.h +++ b/app/node/output/track/tracklist.h @@ -35,7 +35,7 @@ class TrackList : public QObject { Q_OBJECT public: - TrackList(ViewerOutput *parent, const Track::Type& type, NodeInput* track_input); + TrackList(ViewerOutput *parent, const Track::Type& type, const QString& track_input); const Track::Type& type() const { @@ -61,13 +61,27 @@ public: NodeGraph* GetParentGraph() const; - NodeInput* track_input() const - { - return track_input_; - } + const QString &track_input() const; + NodeInput track_input(int element) const; ViewerOutput* parent() const; + int ArraySize() const; + + void ArrayAppend(bool undoable = false); + void ArrayRemoveLast(bool undoable = false); + +public slots: + /** + * @brief Slot for when the track connection is added + */ + void TrackConnected(Node* node, int element); + + /** + * @brief Slot for when the track connection is removed + */ + void TrackDisconnected(Node* node, int element); + signals: void TrackListChanged(); @@ -96,23 +110,13 @@ private: return track_array_indexes_.indexOf(index); } - NodeInput* track_input_; + QString track_input_; rational total_length_; enum Track::Type type_; private slots: - /** - * @brief Slot for when the track connection is added - */ - void TrackConnected(Node* node, int element); - - /** - * @brief Slot for when the track connection is removed - */ - void TrackDisconnected(Node* node, int element); - /** * @brief Slot for when any of the track's length changes so we can update the length of the tracklist */ diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 0d51c1251..52b9c55df 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -24,31 +24,31 @@ namespace olive { +const QString ViewerOutput::kTextureInput = QStringLiteral("tex_in"); +const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in"); +const QString ViewerOutput::kTrackInputFormat = QStringLiteral("track_in_%1"); + ViewerOutput::ViewerOutput() : video_frame_cache_(this), audio_playback_cache_(this), operation_stack_(0) { - texture_input_ = new NodeInput(this, QStringLiteral("tex_in"), NodeValue::kTexture); - texture_input_->SetKeyframable(false); + AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); - samples_input_ = new NodeInput(this, QStringLiteral("samples_in"), NodeValue::kSamples); - samples_input_->SetKeyframable(false); + AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); // Create TrackList instances - track_inputs_.resize(Track::kCount); track_lists_.resize(Track::kCount); for (int i=0;iSetIsArray(true); - track_input->SetKeyframable(false); + QString track_input_id = kTrackInputFormat.arg(i); - IgnoreInvalidationsFrom(track_input); - track_inputs_.replace(i, track_input); + AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray)); - TrackList* list = new TrackList(this, static_cast(i), track_input); + IgnoreInvalidationsFrom(track_input_id); + + TrackList* list = new TrackList(this, static_cast(i), track_input_id); track_lists_.replace(i, list); connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache); connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength); @@ -110,15 +110,17 @@ void ViewerOutput::ShiftCache(const rational &from, const rational &to) ShiftAudioCache(from, to); } -void ViewerOutput::InvalidateCache(const TimeRange& range, const InputConnection& from) +void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, int element) { + Q_UNUSED(element) + if (operation_stack_ == 0) { - if (from.input == texture_input_ || from.input == samples_input_) { + if (from == kTextureInput || from == kSamplesInput) { TimeRange invalidated_range(qMax(rational(), range.in()), qMin(GetLength(), range.out())); if (invalidated_range.in() != invalidated_range.out()) { - if (from.input == texture_input_) { + if (from == kTextureInput) { video_frame_cache_.Invalidate(invalidated_range); } else { audio_playback_cache_.Invalidate(invalidated_range); @@ -216,8 +218,8 @@ void ViewerOutput::VerifyLength() { video_length = track_lists_.at(Track::kVideo)->GetTotalLength(); - if (video_length.isNull() && texture_input_->IsConnected()) { - NodeValueTable t = traverser.GenerateTable(texture_input_->GetConnectedNode(), 0, 0); + if (video_length.isNull() && IsInputConnected(kTextureInput)) { + NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kTextureInput), TimeRange(0, 0)); video_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value(); } @@ -227,8 +229,8 @@ void ViewerOutput::VerifyLength() { audio_length = track_lists_.at(Track::kAudio)->GetTotalLength(); - if (audio_length.isNull() && samples_input_->IsConnected()) { - NodeValueTable t = traverser.GenerateTable(samples_input_->GetConnectedNode(), 0, 0); + if (audio_length.isNull() && IsInputConnected(kSamplesInput)) { + NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kSamplesInput), TimeRange(0, 0)); audio_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value(); } @@ -251,11 +253,11 @@ void ViewerOutput::Retranslate() { Node::Retranslate(); - texture_input_->set_name(tr("Texture")); + SetInputName(kTextureInput, tr("Texture")); - samples_input_->set_name(tr("Samples")); + SetInputName(kSamplesInput, tr("Samples")); - for (int i=0;i(i)) { @@ -274,7 +276,7 @@ void ViewerOutput::Retranslate() } if (!input_name.isEmpty()) { - track_inputs_.at(i)->set_name(input_name); + SetInputName(kTrackInputFormat.arg(i), input_name); } } } @@ -293,4 +295,32 @@ void ViewerOutput::EndOperation() Node::EndOperation(); } +void ViewerOutput::InputConnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + if (input == kTextureInput) { + emit TextureInputChanged(); + } else { + foreach (TrackList* list, track_lists_) { + if (list->track_input() == input) { + list->TrackConnected(output.node(), element); + break; + } + } + } +} + +void ViewerOutput::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) +{ + if (input == kTextureInput) { + emit TextureInputChanged(); + } else { + foreach (TrackList* list, track_lists_) { + if (list->track_input() == input) { + list->TrackDisconnected(output.node(), element); + break; + } + } + } +} + } diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index ed88e671a..80a0a944b 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -59,17 +59,7 @@ public: void ShiftAudioCache(const rational& from, const rational& to); void ShiftCache(const rational& from, const rational& to); - NodeInput* texture_input() const - { - return texture_input_; - } - - NodeInput* samples_input() const - { - return samples_input_; - } - - virtual void InvalidateCache(const TimeRange& range, const InputConnection& from) override; + virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override; const VideoParams& video_params() const { @@ -106,11 +96,6 @@ public: */ QVector GetUnlockedTracks() const; - NodeInput* track_input(Track::Type type) const - { - return track_inputs_.at(type); - } - TrackList* track_list(Track::Type type) const { return track_lists_.at(type); @@ -132,6 +117,10 @@ public: virtual void EndOperation() override; + static const QString kTextureInput; + static const QString kSamplesInput; + static const QString kTrackInputFormat; + signals: void TimebaseChanged(const rational&); @@ -149,19 +138,20 @@ signals: void TrackAdded(Track* track); void TrackRemoved(Track* track); + void TextureInputChanged(); + +protected: + void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override; + + void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override; + private: QUuid uuid_; - NodeInput* texture_input_; - - NodeInput* samples_input_; - VideoParams video_params_; AudioParams audio_params_; - QVector track_inputs_; - QVector track_lists_; QVector track_cache_; diff --git a/app/node/param.cpp b/app/node/param.cpp new file mode 100644 index 000000000..f4e716787 --- /dev/null +++ b/app/node/param.cpp @@ -0,0 +1,147 @@ +/*** + + 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 "node.h" + +namespace olive { + +QString NodeInput::name() const +{ + if (IsValid()) { + return node_->GetInputName(input_); + } else { + return QString(); + } +} + +bool NodeInput::IsConnected() const +{ + if (IsValid()) { + return node_->IsInputConnected(*this); + } else { + return false; + } +} + +bool NodeInput::IsKeyframing() const +{ + if (IsValid()) { + return node_->IsInputKeyframing(*this); + } else { + return false; + } +} + +bool NodeInput::IsArray() const +{ + if (IsValid()) { + return node_->InputIsArray(input_); + } else { + return false; + } +} + +NodeOutput NodeInput::GetConnectedOutput() const +{ + if (IsValid()) { + return node_->GetConnectedOutput(*this); + } else { + return NodeOutput(); + } +} + +NodeValue::Type NodeInput::GetDataType() const +{ + if (IsValid()) { + return node_->GetInputDataType(input_); + } else { + return NodeValue::kNone; + } +} + +QStringList NodeInput::GetComboBoxStrings() const +{ + if (IsValid()) { + return node_->GetComboBoxStrings(input_); + } else { + return QStringList(); + } +} + +QVariant NodeInput::GetProperty(const QString &key) const +{ + if (IsValid()) { + return node_->GetInputProperty(input_, key); + } else { + return QVariant(); + } +} + +QVariant NodeInput::GetValueAtTime(const rational &time) const +{ + if (IsValid()) { + return node_->GetValueAtTime(*this, time); + } else { + return QVariant(); + } +} + +NodeKeyframe* NodeInput::GetKeyframeAtTimeOnTrack(const rational &time, int track) const +{ + if (IsValid()) { + return node_->GetKeyframeAtTimeOnTrack(*this, time, track); + } else { + return nullptr; + } +} + +QVariant NodeInput::GetSplitDefaultValueForTrack(int track) const +{ + if (IsValid()) { + return node_->GetSplitDefaultValueOnTrack(input_, track); + } else { + return QVariant(); + } +} + +uint qHash(const NodeInput &i) +{ + return qHash(i.node()) ^ qHash(i.input()) ^ qHash(i.element()); +} + +NodeOutput::NodeOutput(Node *n) +{ + node_ = n; + output_ = Node::kDefaultOutput; +} + +uint qHash(const NodeKeyframeTrackReference &i) +{ + return qHash(i.input()) & qHash(i.track()); +} + +uint qHash(const NodeInputPair &i) +{ + return qHash(i.node) & qHash(i.input); +} + +} diff --git a/app/node/param.h b/app/node/param.h new file mode 100644 index 000000000..5b7076449 --- /dev/null +++ b/app/node/param.h @@ -0,0 +1,247 @@ +/*** + + 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 "common/rational.h" +#include "value.h" + +namespace olive { + +class Node; +class NodeKeyframe; + +struct NodeInputPair { + bool operator==(const NodeInputPair& rhs) const + { + return node == rhs.node && input == rhs.input; + } + + Node* node; + QString input; +}; + +/** + * @brief Defines a node output + */ +class NodeOutput +{ +public: + NodeOutput(Node* n, const QString& o) + { + node_ = n; + output_ = o; + } + + NodeOutput(Node* n); + + NodeOutput() + { + node_ = nullptr; + } + + bool operator==(const NodeOutput& rhs) const + { + return node_ == rhs.node_ && output_ == rhs.output_; + } + + Node* node() const + { + return node_; + } + + const QString& output() const + { + return output_; + } + + bool IsValid() const + { + return node_; + } + +private: + Node* node_; + QString output_; + +}; + +/** + * @brief Defines a Node input + */ +class NodeInput +{ +public: + NodeInput() + { + node_ = nullptr; + element_ = -1; + } + + NodeInput(Node* n, const QString& i, int e = -1) + { + node_ = n; + input_ = i; + element_ = e; + } + + bool operator==(const NodeInput& rhs) const + { + return node_ == rhs.node_ && input_ == rhs.input_ && element_ == rhs.element_; + } + + bool operator!=(const NodeInput& rhs) const + { + return !(*this == rhs); + } + + bool operator<(const NodeInput& rhs) const + { + if (node_ != rhs.node_) { + return node_ < rhs.node_; + } + + if (input_ != rhs.input_) { + return input_ < rhs.input_; + } + + return element_ < rhs.element_; + } + + Node* node() const + { + return node_; + } + + NodeInputPair input_pair() const + { + return {node_, input_}; + } + + const QString& input() const + { + return input_; + } + + int element() const + { + return element_; + } + + void set_element(int e) + { + element_ = e; + } + + QString name() const; + + bool IsValid() const + { + return node_ && !input_.isEmpty() && element_ >= -1; + } + + bool IsConnected() const; + + bool IsKeyframing() const; + + bool IsArray() const; + + NodeOutput GetConnectedOutput() const; + + NodeValue::Type GetDataType() const; + + QStringList GetComboBoxStrings() const; + + QVariant GetProperty(const QString& key) const; + + QVariant GetValueAtTime(const rational& time) const; + + NodeKeyframe *GetKeyframeAtTimeOnTrack(const rational& time, int track) const; + + QVariant GetSplitDefaultValueForTrack(int track) const; + + void Reset() + { + *this = NodeInput(); + } + +private: + Node* node_; + QString input_; + int element_; + +}; + +class NodeKeyframeTrackReference { +public: + NodeKeyframeTrackReference() + { + track_ = -1; + } + + NodeKeyframeTrackReference(const NodeInput& input, int track = 0) + { + input_ = input; + track_ = track; + } + + bool operator==(const NodeKeyframeTrackReference& rhs) const + { + return input_ == rhs.input_ && track_ == rhs.track_; + } + + const NodeInput& input() const + { + return input_; + } + + int track() const + { + return track_; + } + + bool IsValid() const + { + return input_.IsValid() && track_ >= 0; + } + + void Reset() + { + *this = NodeKeyframeTrackReference(); + } + +private: + NodeInput input_; + int track_; + +}; + +uint qHash(const NodeInputPair& i); +uint qHash(const NodeInput& i); +uint qHash(const NodeKeyframeTrackReference& i); + +} + +Q_DECLARE_METATYPE(olive::NodeKeyframeTrackReference) + +#endif // NODEPARAM_H diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index b7757c415..4724d55f9 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -29,12 +29,12 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa NodeValueDatabase database; // We need to insert tables into the database for each input - foreach (NodeInput* input, node->parameters()) { + foreach (const QString& input, node->inputs()) { if (IsCancelled()) { return NodeValueDatabase(); } - database.Insert(input, ProcessInput(input, range)); + database.Insert(input, ProcessInput(node, input, range)); } AddGlobalsToDatabase(database, range); @@ -42,37 +42,35 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa return database; } -NodeValueTable NodeTraverser::ProcessInput(NodeInput* input, const TimeRange& range) +NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& input, const TimeRange& range) { // If input is connected, retrieve value directly - Node* node = input->parent(); - - if (input->IsConnected()) { + if (node->IsInputConnected(input)) { TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range); // Value will equal something from the connected node, follow it - return GenerateTable(input->GetConnectedNode(), adjusted_range); + return GenerateTable(node->GetConnectedOutput(input), adjusted_range); } else { // Store node QVariant return_val; - if (input->IsArray()) { + if (node->InputIsArray(input)) { // Value is an array, we will return a list of NodeValueTables - QVector array_tbl(input->ArraySize()); + QVector array_tbl(node->InputArraySize(input)); for (int i=0; iInputTimeAdjustment(input, i, range); - if (input->IsConnected(i)) { - sub_tbl = GenerateTable(input->GetConnectedNode(i), adjusted_range); + if (node->IsInputConnected(input, i)) { + sub_tbl = GenerateTable(node->GetConnectedOutput(input, i), adjusted_range); } else { - QVariant input_value = input->GetValueAtTime(adjusted_range.in(), i); - sub_tbl.Push(input->GetDataType(), input_value, node); + QVariant input_value = node->GetValueAtTime(input, adjusted_range.in(), i); + sub_tbl.Push(node->GetInputDataType(input), input_value, node); } } @@ -83,18 +81,18 @@ NodeValueTable NodeTraverser::ProcessInput(NodeInput* input, const TimeRange& ra // Not connected or an array, just pull the immediate TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range); - return_val = input->GetValueAtTime(adjusted_range.in()); + return_val = node->GetValueAtTime(input, adjusted_range.in()); } NodeValueTable return_table; - return_table.Push(input->GetDataType(), return_val, node, true); + return_table.Push(node->GetInputDataType(input), return_val, node, true); return return_table; } } -NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& range) +NodeValueTable NodeTraverser::GenerateTable(const Node *n, const QString& output, const TimeRange& range) { const Track* track = dynamic_cast(n); if (track) { @@ -108,18 +106,13 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const TimeRange& rang NodeValueDatabase database = GenerateDatabase(n, range); // By this point, the node should have all the inputs it needs to render correctly - NodeValueTable table = n->Value(database); + NodeValueTable table = n->Value(output, database); PostProcessTable(n, range, table); return table; } -NodeValueTable NodeTraverser::GenerateTable(const Node *n, const rational &in, const rational &out) -{ - return GenerateTable(n, TimeRange(in, out)); -} - NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeRange &range) { // By default, just follow the in point diff --git a/app/node/traverser.h b/app/node/traverser.h index 3d6158252..eebe6f823 100644 --- a/app/node/traverser.h +++ b/app/node/traverser.h @@ -36,13 +36,16 @@ class NodeTraverser : public CancelableObject public: NodeTraverser() = default; - NodeValueTable GenerateTable(const Node *n, const TimeRange &range); - NodeValueTable GenerateTable(const Node *n, const rational &in, const rational& out); + NodeValueTable GenerateTable(const Node *n, const QString &output, const TimeRange &range); + NodeValueTable GenerateTable(const NodeOutput& output, const TimeRange &range) + { + return GenerateTable(output.node(), output.output(), range); + } NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range); protected: - NodeValueTable ProcessInput(NodeInput *input, const TimeRange &range); + NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range); virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange& range); diff --git a/app/node/value.cpp b/app/node/value.cpp index 93a822e01..7f25b522b 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -20,13 +20,13 @@ #include "value.h" +#include #include #include #include #include #include "common/tohex.h" -#include "node/input.h" #include "render/color.h" namespace olive { @@ -311,26 +311,6 @@ QString NodeValue::GetPrettyDataTypeName(Type type) 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_; - - // Kinda hacky, but we don't need this table to slipstream - copy.remove(QStringLiteral("global")); - - return NodeValueTable::Merge(copy.values()); -} - NodeValue NodeValueTable::GetWithMeta(const QVector &type, const QString &tag) const { int value_index = GetInternal(type, tag); diff --git a/app/node/value.h b/app/node/value.h index 42a7fa8a9..4300aaee7 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -18,16 +18,16 @@ ***/ -#ifndef VALUE_H -#define VALUE_H +#ifndef NODEVALUE_H +#define NODEVALUE_H #include #include +#include namespace olive { class Node; -class NodeInput; class NodeValue { @@ -374,59 +374,13 @@ public: private: int GetInternal(const QVector &type, const QString& tag) const; - QList values_; + QVector values_; }; -class NodeValueDatabase -{ -public: - NodeValueDatabase() = default; - - NodeValueTable& operator[](const QString& input_id) - { - 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); - - NodeValueTable Merge() const; - - using const_iterator = QHash::const_iterator; - - inline QHash::const_iterator begin() const - { - return tables_.cbegin(); - } - - inline QHash::const_iterator end() const - { - return tables_.cend(); - } - - inline bool contains(const QString& s) const - { - return tables_.contains(s); - } - -private: - QHash tables_; - -}; - -using NodeValueMap = QHash; - } Q_DECLARE_METATYPE(olive::NodeValue) Q_DECLARE_METATYPE(olive::NodeValueTable) -Q_DECLARE_METATYPE(olive::NodeValueDatabase) -#endif // VALUE_H +#endif // NODEVALUE_H diff --git a/app/node/valuedatabase.cpp b/app/node/valuedatabase.cpp new file mode 100644 index 000000000..3e207badd --- /dev/null +++ b/app/node/valuedatabase.cpp @@ -0,0 +1,35 @@ +/*** + + 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 "valuedatabase.h" + +namespace olive { + +NodeValueTable NodeValueDatabase::Merge() const +{ + QHash copy = tables_; + + // Kinda hacky, but we don't need this table to slipstream + copy.remove(QStringLiteral("global")); + + return NodeValueTable::Merge(copy.values()); +} + +} diff --git a/app/node/valuedatabase.h b/app/node/valuedatabase.h new file mode 100644 index 000000000..269525a4b --- /dev/null +++ b/app/node/valuedatabase.h @@ -0,0 +1,74 @@ +/*** + + 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 NODEVALUEDATABASE_H +#define NODEVALUEDATABASE_H + +#include "param.h" +#include "value.h" + +namespace olive { + +class NodeValueDatabase +{ +public: + NodeValueDatabase() = default; + + NodeValueTable& operator[](const QString& input_id) + { + return tables_[input_id]; + } + + void Insert(const QString& key, const NodeValueTable &value) + { + tables_.insert(key, value); + } + + NodeValueTable Merge() const; + + using const_iterator = QHash::const_iterator; + + inline QHash::const_iterator begin() const + { + return tables_.cbegin(); + } + + inline QHash::const_iterator end() const + { + return tables_.cend(); + } + + inline bool contains(const QString& s) const + { + return tables_.contains(s); + } + +private: + QHash tables_; + +}; + +using NodeValueMap = QHash; + +} + +Q_DECLARE_METATYPE(olive::NodeValueDatabase) + +#endif // NODEVALUEDATABASE_H diff --git a/app/panel/node/node.h b/app/panel/node/node.h index e8a9e62b6..74cd2e62a 100644 --- a/app/panel/node/node.h +++ b/app/panel/node/node.h @@ -93,12 +93,14 @@ public slots: void SelectBlocks(const QVector& nodes) { + Q_UNUSED(nodes) qDebug() << "Stub"; //node_view_->SelectBlocks(nodes); } void DeselectBlocks(const QVector& nodes) { + Q_UNUSED(nodes) qDebug() << "Stub"; //node_view_->DeselectBlocks(nodes); } diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index 137b94127..5f9501a58 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -44,6 +44,8 @@ Footage::~Footage() void Footage::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled) { + Q_UNUSED(version) + while (XMLReadNextStartElement(reader)) { if (cancelled && *cancelled) { return; diff --git a/app/project/item/footage/stream.cpp b/app/project/item/footage/stream.cpp index 75fb5db94..87843eb52 100644 --- a/app/project/item/footage/stream.cpp +++ b/app/project/item/footage/stream.cpp @@ -67,7 +67,7 @@ Stream *Stream::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const while (XMLReadNextStartElement(reader)) { if (reader->name() == QStringLiteral("ptr")) { - xml_node_data.footage_ptrs.insert(reader->readElementText().toULongLong(), stream); + //xml_node_data.footage_ptrs.insert(reader->readElementText().toULongLong(), stream); } else if (reader->name() == QStringLiteral("index")) { stream->set_index(reader->readElementText().toInt()); } else if (reader->name() == QStringLiteral("timebase")) { diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index d24e741ea..da5e657a1 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -207,17 +207,8 @@ void Sequence::Save(QXmlStreamWriter *writer) const void Sequence::add_default_nodes() { // Create tracks and connect them to the viewer - Track* video_track = new Track(); - video_track->setParent(this); - viewer_output_->track_input(Track::kVideo)->ArrayAppend(); - Node::ConnectEdge(video_track, viewer_output_->track_input(Track::kVideo), 0); - Node::ConnectEdge(video_track, viewer_output_->texture_input()); - - Track* audio_track = new Track(); - audio_track->setParent(this); - viewer_output_->track_input(Track::kAudio)->ArrayAppend(); - Node::ConnectEdge(audio_track, viewer_output_->track_input(Track::kAudio), 0); - Node::ConnectEdge(audio_track, viewer_output_->samples_input()); + TimelineAddTrackCommand(viewer_output_->track_list(Track::kVideo)).redo(); + TimelineAddTrackCommand(viewer_output_->track_list(Track::kAudio)).redo(); } Item::Type Sequence::type() const diff --git a/app/project/project.cpp b/app/project/project.cpp index 00ce92542..eca7d6604 100644 --- a/app/project/project.cpp +++ b/app/project/project.cpp @@ -86,12 +86,6 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint } } - - foreach (const XMLNodeData::FootageConnection& con, xml_node_data.footage_connections) { - if (con.footage) { - con.input->SetStandardValue(Node::PtrToValue(xml_node_data.footage_ptrs.value(con.footage)), con.element); - } - } } void Project::Save(QXmlStreamWriter *writer) const diff --git a/app/render/job/CMakeLists.txt b/app/render/job/CMakeLists.txt index 1a3d982ca..100d7f5e3 100644 --- a/app/render/job/CMakeLists.txt +++ b/app/render/job/CMakeLists.txt @@ -16,6 +16,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} + render/job/acceleratedjob.cpp render/job/acceleratedjob.h render/job/generatejob.h render/job/samplejob.h diff --git a/app/render/job/acceleratedjob.cpp b/app/render/job/acceleratedjob.cpp new file mode 100644 index 000000000..3a3ee3442 --- /dev/null +++ b/app/render/job/acceleratedjob.cpp @@ -0,0 +1,32 @@ +/*** + + 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 "acceleratedjob.h" + +#include "node/node.h" + +namespace olive { + +void AcceleratedJob::InsertValue(const Node* node, const QString& input, NodeValueDatabase& value) +{ + InsertValue(input, value[input].TakeWithMeta(node->GetInputDataType(input))); +} + +} diff --git a/app/render/job/acceleratedjob.h b/app/render/job/acceleratedjob.h index ca52b7c5e..597c05be4 100644 --- a/app/render/job/acceleratedjob.h +++ b/app/render/job/acceleratedjob.h @@ -21,8 +21,8 @@ #ifndef ACCELERATEDJOB_H #define ACCELERATEDJOB_H -#include "node/input.h" -#include "node/value.h" +#include "node/param.h" +#include "node/valuedatabase.h" namespace olive { @@ -30,31 +30,18 @@ class AcceleratedJob { public: AcceleratedJob() = default; - NodeValue GetValue(NodeInput* input) const - { - return value_map_.value(input->id()); - } - NodeValue GetValue(const QString& input) const { return value_map_.value(input); } - void InsertValue(NodeInput* input, NodeValueDatabase& value) - { - InsertValue(input->id(), value[input].TakeWithMeta(input->GetDataType())); - } + void InsertValue(const Node* node, const QString& input, NodeValueDatabase& value); void InsertValue(const QString& input, const NodeValue& value) { value_map_.insert(input, value); } - void InsertValue(NodeInput* input, const NodeValue& value) - { - value_map_.insert(input->id(), value); - } - const NodeValueMap &GetValues() const { return value_map_; diff --git a/app/render/job/samplejob.h b/app/render/job/samplejob.h index c6d5d5eaf..7fec0607b 100644 --- a/app/render/job/samplejob.h +++ b/app/render/job/samplejob.h @@ -38,7 +38,7 @@ public: samples_ = value.data().value(); } - SampleJob(NodeInput* from, NodeValueDatabase& db) + SampleJob(const QString& from, NodeValueDatabase& db) { samples_ = db[from].Take(NodeValue::kSamples).value(); } diff --git a/app/render/job/shaderjob.h b/app/render/job/shaderjob.h index 64848c563..32005a32d 100644 --- a/app/render/job/shaderjob.h +++ b/app/render/job/shaderjob.h @@ -46,9 +46,9 @@ public: shader_id_ = id; } - void SetIterations(int iterations, NodeInput* iterative_input) + void SetIterations(int iterations, const NodeInput& iterative_input) { - SetIterations(iterations, iterative_input->id()); + SetIterations(iterations, iterative_input.input()); } void SetIterations(int iterations, const QString& iterative_input) @@ -72,9 +72,9 @@ public: return interpolation_.value(id, Texture::kDefaultInterpolation); } - void SetInterpolation(NodeInput* input, Texture::Interpolation interp) + void SetInterpolation(const NodeInput& input, Texture::Interpolation interp) { - interpolation_.insert(input->id(), interp); + interpolation_.insert(input.input(), interp); } void SetInterpolation(const QString& id, Texture::Interpolation interp) diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 88ad8a65c..d9548d0ef 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -65,7 +65,7 @@ void PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cac foreach (const rational& time, times) { // See if hash already exists in disk cache - QByteArray hash = RenderManager::Hash(viewer->texture_input()->GetConnectedNode(), viewer->video_params(), time); + QByteArray hash = RenderManager::Hash(viewer->GetConnectedNode(ViewerOutput::kTextureInput), 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()); @@ -256,13 +256,13 @@ void PreviewAutoCacher::ProcessUpdateQueue() RemoveNode(job.node); break; case QueuedJob::kEdgeAdded: - AddEdge(job.node, job.input, job.element); + AddEdge(job.output, job.input); break; case QueuedJob::kEdgeRemoved: - RemoveEdge(job.node, job.input, job.element); + RemoveEdge(job.output, job.input); break; case QueuedJob::kValueChanged: - CopyValue(job.input, job.element); + CopyValue(job.input); break; case QueuedJob::kVideoParamsChanged: UpdateVideoParams(); @@ -305,26 +305,26 @@ void PreviewAutoCacher::RemoveNode(Node *node) delete copy; } -void PreviewAutoCacher::AddEdge(Node *output, NodeInput *input, int element) +void PreviewAutoCacher::AddEdge(const NodeOutput &output, const NodeInput &input) { - Node* our_output = copy_map_.value(output); - NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id()); + Node* our_output = copy_map_.value(output.node()); + Node* our_input = copy_map_.value(input.node()); - Node::ConnectEdge(our_output, our_input, element); + Node::ConnectEdge(NodeOutput(our_output, output.output()), NodeInput(our_input, input.input(), input.element())); } -void PreviewAutoCacher::RemoveEdge(Node *output, NodeInput *input, int element) +void PreviewAutoCacher::RemoveEdge(const NodeOutput &output, const NodeInput &input) { - Node* our_output = copy_map_.value(output); - NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id()); + Node* our_output = copy_map_.value(output.node()); + Node* our_input = copy_map_.value(input.node()); - Node::DisconnectEdge(our_output, our_input, element); + Node::DisconnectEdge(NodeOutput(our_output, output.output()), NodeInput(our_input, input.input(), input.element())); } -void PreviewAutoCacher::CopyValue(NodeInput *input, int element) +void PreviewAutoCacher::CopyValue(const NodeInput &input) { - NodeInput* our_input = copy_map_.value(input->parent())->GetInputWithID(input->id()); - NodeInput::CopyValuesOfElement(input, our_input, element); + Node* our_input = copy_map_.value(input.node()); + Node::CopyValuesOfElement(input.node(), our_input, input.input(), input.element()); } void PreviewAutoCacher::UpdateVideoParams() @@ -423,27 +423,27 @@ void PreviewAutoCacher::ClearVideoDownloadQueue(bool wait) void PreviewAutoCacher::NodeAdded(Node *node) { - graph_update_queue_.append({QueuedJob::kNodeAdded, node, nullptr, -1}); + graph_update_queue_.append({QueuedJob::kNodeAdded, node, NodeInput(), NodeOutput()}); } void PreviewAutoCacher::NodeRemoved(Node *node) { - graph_update_queue_.append({QueuedJob::kNodeRemoved, node, nullptr, -1}); + graph_update_queue_.append({QueuedJob::kNodeRemoved, node, NodeInput(), NodeOutput()}); } -void PreviewAutoCacher::EdgeAdded(Node *output, NodeInput *input, int element) +void PreviewAutoCacher::EdgeAdded(const NodeOutput &output, const NodeInput &input) { - graph_update_queue_.append({QueuedJob::kEdgeAdded, output, input, element}); + graph_update_queue_.append({QueuedJob::kEdgeAdded, nullptr, input, output}); } -void PreviewAutoCacher::EdgeRemoved(Node *output, NodeInput *input, int element) +void PreviewAutoCacher::EdgeRemoved(const NodeOutput &output, const NodeInput &input) { - graph_update_queue_.append({QueuedJob::kEdgeRemoved, output, input, element}); + graph_update_queue_.append({QueuedJob::kEdgeRemoved, nullptr, input, output}); } -void PreviewAutoCacher::ValueChanged(NodeInput *input, int element) +void PreviewAutoCacher::ValueChanged(const NodeInput &input) { - graph_update_queue_.append({QueuedJob::kValueChanged, nullptr, input, element}); + graph_update_queue_.append({QueuedJob::kValueChanged, nullptr, input, NodeOutput()}); } void PreviewAutoCacher::VideoParamsChanged() @@ -451,14 +451,14 @@ void PreviewAutoCacher::VideoParamsChanged() // In case the user is pressing the mouse at this exact moment IgnoreNextMouseButton(); - graph_update_queue_.append({QueuedJob::kVideoParamsChanged, nullptr, nullptr, -1}); + graph_update_queue_.append({QueuedJob::kVideoParamsChanged, nullptr, NodeInput(), NodeOutput()}); ClearVideoQueue(); TryRender(); } void PreviewAutoCacher::AudioParamsChanged() { - graph_update_queue_.append({QueuedJob::kAudioParamsChanged, nullptr, nullptr, -1}); + graph_update_queue_.append({QueuedJob::kAudioParamsChanged, nullptr, NodeInput(), NodeOutput()}); ClearAudioQueue(); TryRender(); } @@ -677,10 +677,8 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) // 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->second, input, it->first); - } + for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { + AddEdge(it->second, it->first); } } diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index c80f7a371..1b28217d2 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -107,9 +107,9 @@ private: 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); + void AddEdge(const NodeOutput& output, const NodeInput& input); + void RemoveEdge(const NodeOutput& output, const NodeInput& input); + void CopyValue(const NodeInput& input); void UpdateVideoParams(); void UpdateAudioParams(); @@ -127,8 +127,8 @@ private: Type type; Node* node; - NodeInput* input; - int element; + NodeInput input; + NodeOutput output; }; ViewerOutput* viewer_node_; @@ -201,11 +201,11 @@ private slots: void NodeRemoved(Node* node); - void EdgeAdded(Node* output, NodeInput* input, int element); + void EdgeAdded(const NodeOutput& output, const NodeInput& input); - void EdgeRemoved(Node* output, NodeInput* input, int element); + void EdgeRemoved(const NodeOutput& output, const NodeInput& input); - void ValueChanged(NodeInput* input, int element); + void ValueChanged(const NodeInput& input); void VideoParamsChanged(); diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index a5bbb5c05..4079ee03a 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -58,7 +58,7 @@ void RenderProcessor::Run() const VideoParams& video_params = ticket_->property("vparam").value(); rational time = ticket_->property("time").value(); - NodeValueTable table = ProcessInput(viewer->texture_input(), + NodeValueTable table = ProcessInput(viewer, ViewerOutput::kTextureInput, TimeRange(time, time + video_params.time_base())); TexturePtr texture = table.Get(NodeValue::kTexture).value(); @@ -133,7 +133,7 @@ void RenderProcessor::Run() ViewerOutput* viewer = Node::ValueToPtr(ticket_->property("viewer")); TimeRange time = ticket_->property("time").value(); - NodeValueTable table = ProcessInput(viewer->samples_input(), time); + NodeValueTable table = ProcessInput(viewer, ViewerOutput::kSamplesInput, time); ticket_->Finish(table.Get(NodeValue::kSamples), IsCancelled()); break; @@ -219,10 +219,10 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim } // FIXME: Doesn't handle reversing - if (b->speed_input()->IsKeyframing() || b->speed_input()->IsConnected()) { + if (b->IsInputKeyframing(Block::kSpeedInput) || b->IsInputConnected(Block::kSpeedInput)) { // FIXME: We'll need to calculate the speed hoo boy } else { - double speed_value = b->speed_input()->GetStandardValue().toDouble(); + double speed_value = b->GetStandardValue(Block::kSpeedInput).toDouble(); if (qIsNull(speed_value)) { // Just silence, don't think there's any other practical application of 0 speed audio @@ -450,16 +450,8 @@ QVariant RenderProcessor::ProcessSamples(const Node *node, const TimeRange &rang rational this_sample_time = rational::fromDouble(range.in().toDouble() + sample_to_second); // Update all non-sample and non-footage inputs - NodeValueMap::const_iterator j; - for (j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) { - NodeValueTable value; - NodeInput* corresponding_input = node->GetInputWithID(j.key()); - - if (corresponding_input) { - value = ProcessInput(corresponding_input, TimeRange(this_sample_time, this_sample_time)); - } else { - value.Push(j.value()); - } + for (auto j=job.GetValues().constBegin(); j!=job.GetValues().constEnd(); j++) { + NodeValueTable value = ProcessInput(node, j.key(), TimeRange(this_sample_time, this_sample_time)); value_db.Insert(j.key(), value); } diff --git a/app/task/precache/precachetask.cpp b/app/task/precache/precachetask.cpp index 374df8cfa..2cc7aaed4 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); - Node::ConnectEdge(video_node_, viewer()->texture_input()); + Node::ConnectEdge(video_node_, NodeInput(viewer(), ViewerOutput::kTextureInput)); SetTitle(tr("Pre-caching %1:%2").arg(footage->footage()->filename(), QString::number(footage->index()))); diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index ede34cb20..bce195ea2 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -63,59 +63,51 @@ void CurveView::Clear() lines_.clear(); } -void CurveView::ConnectInput(NodeInput *input, int element, int track) +void CurveView::ConnectInput(const NodeKeyframeTrackReference& ref) { - NodeInput::KeyframeTrackReference ref = {input, element, track}; - if (connected_inputs_.contains(ref)) { // Input wasn't connected, do nothing return; } // Add keyframes from track - AddKeyframesOfTrack(input, element, track); + AddKeyframesOfTrack(ref); // Append to the list connected_inputs_.append(ref); } -void CurveView::DisconnectInput(NodeInput *input, int element, int track) +void CurveView::DisconnectInput(const NodeKeyframeTrackReference& ref) { - NodeInput::KeyframeTrackReference ref = {input, element, track}; - if (!connected_inputs_.contains(ref)) { // Input wasn't connected, do nothing return; } // Remove keyframes belonging to this element and track - RemoveKeyframesOfTrack(input, element, track); + RemoveKeyframesOfTrack(ref); // Remove from the list connected_inputs_.removeOne(ref); } -void CurveView::SelectKeyframesOfInput(NodeInput *input, int element, int track) +void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference& ref) { DeselectAll(); for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->parent() == input - && it.key()->element() == element - && it.key()->track() == track) { + if (it.key()->key_track_ref() == ref) { it.value()->setSelected(true); } } } -void CurveView::ZoomToFitInput(NodeInput *input, int element, int track) +void CurveView::ZoomToFitInput(const NodeKeyframeTrackReference& ref) { QList keys; for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->parent() == input - && it.key()->element() == element - && it.key()->track() == track) { + if (it.key()->key_track_ref() == ref) { keys.append(it.key()); } } @@ -123,14 +115,14 @@ void CurveView::ZoomToFitInput(NodeInput *input, int element, int track) ZoomToFitInternal(keys); } -void CurveView::SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference &ref, const QColor &color) +void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color) { // Insert color into hashmap keyframe_colors_.insert(ref, color); // Update all keyframes for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->parent() == ref.input && it.key()->element() == ref.element && it.key()->track() == ref.track) { + if (it.key()->key_track_ref() == ref) { it.value()->SetOverrideBrush(color); } } @@ -187,13 +179,14 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) painter->drawLines(lines); // Draw keyframe lines - foreach (const NodeInput::KeyframeTrackReference& ref, connected_inputs_) { - NodeInput* input = ref.input; + foreach (const NodeKeyframeTrackReference& ref, connected_inputs_) { + Node* node = ref.input().node(); + const QString& input = ref.input().input(); - if (input->IsKeyframing(ref.element)) { - const QVector& tracks = input->GetKeyframeTracks(ref.element); + if (node->IsInputKeyframing(input, ref.input().element())) { + const QVector& tracks = node->GetKeyframeTracks(ref.input()); - const NodeKeyframeTrack& track = tracks.at(ref.track); + const NodeKeyframeTrack& track = tracks.at(ref.track()); if (!track.isEmpty()) { painter->setPen(QPen(keyframe_colors_.value(ref), @@ -348,7 +341,7 @@ void CurveView::ZoomToFitInternal(const QList &keys) double max_val = DBL_MIN; foreach (NodeKeyframe* key, keys) { - rational transformed_time = GetAdjustedTime(key->parent()->parent(), + rational transformed_time = GetAdjustedTime(key->parent(), GetTimeTarget(), key->time(), false); @@ -480,7 +473,7 @@ KeyframeViewItem* CurveView::AddKeyframe(NodeKeyframe* key) { KeyframeViewItem* item = super::AddKeyframe(key); SetItemYFromKeyframeValue(key, item); - item->SetOverrideBrush(keyframe_colors_.value({key->parent(), key->element(), key->track()})); + item->SetOverrideBrush(keyframe_colors_.value(key->key_track_ref())); 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 6ae76a0a2..da023e77c 100644 --- a/app/widget/curvewidget/curveview.h +++ b/app/widget/curvewidget/curveview.h @@ -38,15 +38,15 @@ public: virtual void Clear() override; - void ConnectInput(NodeInput* input, int element, int track); + void ConnectInput(const NodeKeyframeTrackReference &ref); - void DisconnectInput(NodeInput* input, int element, int track); + void DisconnectInput(const NodeKeyframeTrackReference &ref); - void SelectKeyframesOfInput(NodeInput* input, int element, int track); + void SelectKeyframesOfInput(const NodeKeyframeTrackReference &ref); - void ZoomToFitInput(NodeInput* input, int element, int track); + void ZoomToFitInput(const NodeKeyframeTrackReference &ref); - void SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference& ref, const QColor& color); + void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color); public slots: virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override; @@ -84,7 +84,7 @@ private: void CreateBezierControlPoints(KeyframeViewItem *item); - QHash keyframe_colors_; + QHash keyframe_colors_; int text_padding_; @@ -94,7 +94,7 @@ private: QVector bezier_control_points_; - QVector connected_inputs_; + QVector connected_inputs_; private slots: void KeyframeValueChanged(); diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index 11ff6ee69..f3f535d1e 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -219,24 +219,37 @@ void CurveWidget::UpdateBridgeTime(const int64_t ×tamp) void CurveWidget::ConnectNode(Node *node, bool connect) { - foreach (NodeInput* input, node->parameters()) { - ConnectInput(input, connect); + foreach (const QString& input, node->inputs()) { + ConnectInput(node, input, connect); + } + + // Connect add/remove signals + if (connect) { + QObject::connect(node, &Node::KeyframeAdded, this, &CurveWidget::AddKeyframe); + QObject::connect(node, &Node::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); + } else { + QObject::disconnect(node, &Node::KeyframeAdded, this, &CurveWidget::AddKeyframe); + QObject::disconnect(node, &Node::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); } } -void CurveWidget::ConnectInput(NodeInput *input, bool connect) +void CurveWidget::ConnectInput(Node *node, const QString &input, bool connect) { - int track_count = NodeValue::get_number_of_keyframe_tracks(input->GetDataType()); + if (!node->IsInputKeyframable(input)) { + qWarning() << "Tried to connect input that isn't keyframable"; + return; + } + + int track_count = NodeValue::get_number_of_keyframe_tracks(node->GetInputDataType(input)); bool multiple_tracks = track_count > 1; - for (int i=-1; iArraySize(); i++) { - if (!input->IsKeyframable()) { - continue; - } - + int arr_sz = node->InputArraySize(input); + for (int i=-1; iGetKeyframeTracks(i).size(); j++) { - NodeInput::KeyframeTrackReference ref = {input, i, j}; + const QVector& tracks = node->GetKeyframeTracks(input, i); + + for (int j=0; jIsInputEnabled(input, i, multiple_tracks ? -1 : 0)) { + if (tree_view_->IsInputEnabled(NodeKeyframeTrackReference(NodeInput(node, input, i), multiple_tracks ? -1 : 0))) { if (multiple_tracks) { for (int j=0; jIsInputEnabled(input, i, j)) { + NodeKeyframeTrackReference ref(NodeInput(node, input, i), j); + if (tree_view_->IsInputEnabled(ref)) { if (connect) { - view_->ConnectInput(input, i, j); + view_->ConnectInput(ref); } else { - view_->DisconnectInput(input, i, j); + view_->DisconnectInput(ref); } } } } else { + NodeKeyframeTrackReference ref(NodeInput(node, input, i), 0); if (connect) { - view_->ConnectInput(input, i, 0); + view_->ConnectInput(ref); } else { - view_->DisconnectInput(input, i, 0); + view_->DisconnectInput(ref); } } } } - - // Connect add/remove signals - if (connect) { - QObject::connect(input, &NodeInput::KeyframeAdded, this, &CurveWidget::AddKeyframe); - QObject::connect(input, &NodeInput::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); - } else { - QObject::disconnect(input, &NodeInput::KeyframeAdded, this, &CurveWidget::AddKeyframe); - QObject::disconnect(input, &NodeInput::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); - } } void CurveWidget::SelectionChanged() @@ -357,12 +363,12 @@ void CurveWidget::NodeEnabledChanged(Node* n, bool e) ConnectNode(n, e); } -void CurveWidget::InputEnabledChanged(NodeInput *i, int element, int track, bool e) +void CurveWidget::InputEnabledChanged(const NodeKeyframeTrackReference& ref, bool e) { if (e) { - view_->ConnectInput(i, element, track); + view_->ConnectInput(ref); } else { - view_->DisconnectInput(i, element, track); + view_->DisconnectInput(ref); } } @@ -376,18 +382,18 @@ void CurveWidget::RemoveKeyframe(NodeKeyframe *key) view_->RemoveKeyframe(key); } -void CurveWidget::InputSelectionChanged(NodeInput *input, int element, int track) +void CurveWidget::InputSelectionChanged(const NodeKeyframeTrackReference& ref) { - key_control_->SetInput(input, element); + key_control_->SetInput(ref.input()); - if (input) { - view_->SelectKeyframesOfInput(input, element, track); + if (ref.IsValid()) { + view_->SelectKeyframesOfInput(ref); } } -void CurveWidget::InputDoubleClicked(NodeInput *input, int element, int track) +void CurveWidget::InputDoubleClicked(const NodeKeyframeTrackReference& ref) { - view_->ZoomToFitInput(input, element, track); + view_->ZoomToFitInput(ref); } void CurveWidget::KeyframeViewDragged(int x, int y) diff --git a/app/widget/curvewidget/curvewidget.h b/app/widget/curvewidget/curvewidget.h index 2fbf4720c..b0a281df7 100644 --- a/app/widget/curvewidget/curvewidget.h +++ b/app/widget/curvewidget/curvewidget.h @@ -27,7 +27,6 @@ #include #include "curveview.h" -#include "node/input.h" #include "widget/nodeparamview/nodeparamviewkeyframecontrol.h" #include "widget/nodeparamview/nodeparamviewwidgetbridge.h" #include "widget/nodetreeview/nodetreeview.h" @@ -81,9 +80,9 @@ private: void ConnectNode(Node* node, bool connect); - void ConnectInput(NodeInput* input, bool connect); + void ConnectInput(Node* node, const QString& input, bool connect); - QHash keyframe_colors_; + QHash keyframe_colors_; NodeTreeView* tree_view_; @@ -108,15 +107,15 @@ private slots: void NodeEnabledChanged(Node* n, bool e); - void InputEnabledChanged(NodeInput* i, int element, int track, bool e); + void InputEnabledChanged(const NodeKeyframeTrackReference &ref, bool e); void AddKeyframe(NodeKeyframe* key); void RemoveKeyframe(NodeKeyframe* key); - void InputSelectionChanged(NodeInput* input, int element, int track); + void InputSelectionChanged(const NodeKeyframeTrackReference& ref); - void InputDoubleClicked(NodeInput* input, int element, int track); + void InputDoubleClicked(const NodeKeyframeTrackReference& ref); void KeyframeViewDragged(int x, int y); diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index 67dfca608..ae3993d63 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -31,14 +31,14 @@ KeyframeView::KeyframeView(QWidget *parent) : setAlignment(Qt::AlignLeft | Qt::AlignTop); } -void KeyframeView::SetElementY(const NodeConnectable::InputConnection &c, int y) +void KeyframeView::SetElementY(const NodeInput &c, int y) { qreal scene_y = mapToScene(mapFromGlobal(QPoint(0, y))).y(); element_y_.insert(c, scene_y); for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->parent() == c.input && it.key()->element() == c.element) { + if (it.key()->key_track_ref().input() == c) { it.value()->SetOverrideY(scene_y); } } @@ -60,7 +60,7 @@ void KeyframeView::SceneRectUpdateEvent(QRectF &rect) KeyframeViewItem* KeyframeView::AddKeyframe(NodeKeyframe* key) { KeyframeViewItem* item = super::AddKeyframe(key); - item->SetOverrideY(element_y_.value({key->parent(), key->element()})); + item->SetOverrideY(element_y_.value(key->key_track_ref().input())); return item; } diff --git a/app/widget/keyframeview/keyframeview.h b/app/widget/keyframeview/keyframeview.h index 37a378244..ec9121b12 100644 --- a/app/widget/keyframeview/keyframeview.h +++ b/app/widget/keyframeview/keyframeview.h @@ -36,7 +36,7 @@ public: max_scroll_ = i; } - void SetElementY(const Node::InputConnection& c, int y); + void SetElementY(const NodeInput& c, int y); protected: virtual void wheelEvent(QWheelEvent* event) override; @@ -47,7 +47,7 @@ public slots: virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override; private: - QHash element_y_; + QHash element_y_; int max_scroll_; diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp index d8e70429a..92e8304d2 100644 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ b/app/widget/keyframeview/keyframeviewbase.cpp @@ -74,35 +74,36 @@ void KeyframeViewBase::DeleteSelected() void KeyframeViewBase::AddKeyframesOfNode(Node *n) { - foreach (NodeInput* i, n->inputs()) { - AddKeyframesOfInput(i); + foreach (const QString& i, n->inputs()) { + AddKeyframesOfInput(n, i); } } -void KeyframeViewBase::AddKeyframesOfInput(NodeInput *input) +void KeyframeViewBase::AddKeyframesOfInput(Node* n, const QString& input) { - if (!input->IsKeyframable()) { + if (!n->IsInputKeyframable(input)) { return; } - for (int i=-1; iArraySize(); i++) { - AddKeyframesOfElement(input, i); + int arr_sz = n->InputArraySize(input); + for (int i=-1; i& tracks = input->GetKeyframeTracks(element); + const QVector& tracks = input.node()->GetKeyframeTracks(input); for (int i=0; i& tracks = input->GetKeyframeTracks(element); - const NodeKeyframeTrack& t = tracks.at(track); + const QVector& tracks = ref.input().node()->GetKeyframeTracks(ref.input()); + const NodeKeyframeTrack& t = tracks.at(ref.track()); foreach (NodeKeyframe* key, t) { AddKeyframe(key); @@ -111,35 +112,36 @@ void KeyframeViewBase::AddKeyframesOfTrack(NodeInput *input, int element, int tr void KeyframeViewBase::RemoveKeyframesOfNode(Node *n) { - foreach (NodeInput* i, n->inputs()) { - RemoveKeyframesOfInput(i); + foreach (const QString& i, n->inputs()) { + RemoveKeyframesOfInput(n, i); } } -void KeyframeViewBase::RemoveKeyframesOfInput(NodeInput *input) +void KeyframeViewBase::RemoveKeyframesOfInput(Node* n, const QString& input) { - if (!input->IsKeyframable()) { + if (!n->IsInputKeyframable(input)) { return; } - for (int i=-1; iArraySize(); i++) { - RemoveKeyframesOfElement(input, i); + int arr_sz = n->InputArraySize(input); + for (int i=-1; i& tracks = input->GetKeyframeTracks(element); + const QVector& tracks = input.node()->GetKeyframeTracks(input); for (int i=0; i& tracks = input->GetKeyframeTracks(element); - const NodeKeyframeTrack& t = tracks.at(track); + const QVector& tracks = ref.input().node()->GetKeyframeTracks(ref.input()); + const NodeKeyframeTrack& t = tracks.at(ref.track()); foreach (NodeKeyframe* key, t) { RemoveKeyframe(key); @@ -222,7 +224,7 @@ void KeyframeViewBase::mousePressEvent(QMouseEvent *event) selected_keys_.replace(i, {key, key->x(), - GetAdjustedTime(key->key()->parent()->parent(), GetTimeTarget(), key->key()->time(), false), + GetAdjustedTime(key->key()->parent(), GetTimeTarget(), key->key()->time(), false), key->key()->value().toDouble()}); } } @@ -286,18 +288,17 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) if (IsYAxisEnabled()) { foreach (const KeyframeItemAndTime& keypair, selected_keys_) { - NodeInput* input = keypair.key->key()->parent(); - QList properties = input->dynamicPropertyNames(); - + Node* node = keypair.key->key()->parent(); + const QString& input = keypair.key->key()->input(); double new_val = keypair.value - mouse_diff_scaled.y(); double limited = new_val; - if (properties.contains("min")) { - limited = qMax(limited, input->property("min").toDouble()); + if (node->HasInputProperty(input, QStringLiteral("min"))) { + limited = qMax(limited, node->GetInputProperty(input, QStringLiteral("min")).toDouble()); } - if (properties.contains("max")) { - limited = qMin(limited, input->property("max").toDouble()); + if (node->HasInputProperty(input, QStringLiteral("max"))) { + limited = qMin(limited, node->GetInputProperty(input, QStringLiteral("max")).toDouble()); } if (limited != new_val) { @@ -305,15 +306,16 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) } } - NodeInput* initial_drag_input = initial_drag_item_->key()->parent(); - if (initial_drag_input->dynamicPropertyNames().contains("view")) { - display_type = static_cast(initial_drag_input->property("view").toInt()); + Node* initial_drag_input = initial_drag_item_->key()->parent(); + const QString& initial_drag_input_id = initial_drag_item_->key()->input(); + if (initial_drag_input->HasInputProperty(initial_drag_input_id, QStringLiteral("view"))) { + display_type = static_cast(initial_drag_input->GetInputProperty(initial_drag_input_id, QStringLiteral("view")).toInt()); } } foreach (const KeyframeItemAndTime& keypair, selected_keys_) { rational node_time = GetAdjustedTime(GetTimeTarget(), - keypair.key->key()->parent()->parent(), + keypair.key->key()->parent(), CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()), true); @@ -590,7 +592,7 @@ void KeyframeViewBase::AutoSelectKeyTimeNeighbors() rational key_time = key_item->key()->time(); - QVector keys = key_item->key()->parent()->GetKeyframesAtTime(key_time, key_item->key()->element()); + QVector keys = key_item->key()->parent()->GetKeyframesAtTime(key_item->key()->input(), key_time, key_item->key()->element()); foreach (NodeKeyframe* k, keys) { if (k == key_item->key()) { diff --git a/app/widget/keyframeview/keyframeviewbase.h b/app/widget/keyframeview/keyframeviewbase.h index b8f4c769a..2c2465589 100644 --- a/app/widget/keyframeview/keyframeviewbase.h +++ b/app/widget/keyframeview/keyframeviewbase.h @@ -42,19 +42,19 @@ public: void AddKeyframesOfNode(Node* n); - void AddKeyframesOfInput(NodeInput* input); + void AddKeyframesOfInput(Node *n, const QString &input); - void AddKeyframesOfElement(NodeInput* input, int element); + void AddKeyframesOfElement(const NodeInput &input); - void AddKeyframesOfTrack(NodeInput* input, int element, int track); + void AddKeyframesOfTrack(const NodeKeyframeTrackReference &ref); void RemoveKeyframesOfNode(Node* n); - void RemoveKeyframesOfInput(NodeInput* input); + void RemoveKeyframesOfInput(Node *n, const QString &input); - void RemoveKeyframesOfElement(NodeInput* input, int element); + void RemoveKeyframesOfElement(const NodeInput &input); - void RemoveKeyframesOfTrack(NodeInput* input, int element, int track); + void RemoveKeyframesOfTrack(const NodeKeyframeTrackReference &ref); void SelectAll(); diff --git a/app/widget/keyframeview/keyframeviewitem.cpp b/app/widget/keyframeview/keyframeviewitem.cpp index a52b815b8..6d1ae9fe1 100644 --- a/app/widget/keyframeview/keyframeviewitem.cpp +++ b/app/widget/keyframeview/keyframeviewitem.cpp @@ -26,7 +26,6 @@ #include #include "common/qtutils.h" -#include "node/input.h" namespace olive { @@ -117,7 +116,7 @@ void KeyframeViewItem::TimeTargetChangedEvent(Node *) void KeyframeViewItem::UpdatePos() { - rational adjusted = GetAdjustedTime(key_->parent()->parent(), GetTimeTarget(), key_->time(), false); + rational adjusted = GetAdjustedTime(key_->parent(), GetTimeTarget(), key_->time(), false); setPos(adjusted.toDouble() * scale_, vert_center_); } diff --git a/app/widget/keyframeview/keyframeviewundo.cpp b/app/widget/keyframeview/keyframeviewundo.cpp index 279186f44..aa6d782d6 100644 --- a/app/widget/keyframeview/keyframeviewundo.cpp +++ b/app/widget/keyframeview/keyframeviewundo.cpp @@ -20,7 +20,6 @@ #include "keyframeviewundo.h" -#include "node/input.h" #include "node/node.h" #include "project/item/sequence/sequence.h" @@ -35,7 +34,7 @@ KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe:: Project *KeyframeSetTypeCommand::GetRelevantProject() const { - return key_->parent()->parent()->parent()->project(); + return key_->parent()->parent()->project(); } void KeyframeSetTypeCommand::redo() @@ -66,7 +65,7 @@ KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, Project *KeyframeSetBezierControlPoint::GetRelevantProject() const { - return key_->parent()->parent()->parent()->project(); + return key_->parent()->parent()->project(); } void KeyframeSetBezierControlPoint::redo() diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index cb7f1c215..d718973e6 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -291,12 +291,8 @@ void NodeParamView::AddNode(Node *n) item->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable); item->SetExpanded(node_expanded_state_.value(n, true)); - foreach (NodeInput* input, n->inputs()) { - if (input->IsKeyframable()) { - connect(input, &NodeInput::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); - connect(input, &NodeInput::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); - } - } + connect(n, &Node::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); + connect(n, &Node::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged); connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode); @@ -326,12 +322,8 @@ void NodeParamView::RemoveNode(Node *n) { keyframe_view_->RemoveKeyframesOfNode(n); - foreach (NodeInput* input, n->inputs()) { - if (input->IsKeyframable()) { - disconnect(input, &NodeInput::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); - disconnect(input, &NodeInput::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); - } - } + disconnect(n, &Node::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); + disconnect(n, &Node::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); delete items_.take(n); @@ -414,9 +406,12 @@ void NodeParamView::KeyframeViewDragged(int x, int y) void NodeParamView::UpdateElementY() { for (auto it=items_.cbegin(); it!=items_.cend(); it++) { - foreach (NodeInput* input, it.key()->inputs()) { - for (int i=-1; iArraySize(); i++) { - Node::InputConnection ic = {input, i}; + foreach (const QString& input, it.key()->inputs()) { + int arr_sz = it.key()->InputArraySize(input); + + for (int i=-1; iGetElementY(ic); keyframe_view_->SetElementY(ic, y); } diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp index 412c54747..87a925405 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp @@ -20,22 +20,26 @@ #include "nodeparamviewarraywidget.h" +#include #include +#include "node/node.h" + namespace olive { -NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInput *array, QWidget* parent) : +NodeParamViewArrayWidget::NodeParamViewArrayWidget(Node *node, const QString &input, QWidget* parent) : QWidget(parent), - array_(array) + node_(node), + input_(input) { QHBoxLayout* layout = new QHBoxLayout(this); count_lbl_ = new QLabel(); layout->addWidget(count_lbl_); - connect(array_, &NodeInput::ArraySizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter); + connect(node_, &Node::InputArraySizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter); - UpdateCounter(); + UpdateCounter(input_, node_->InputArraySize(input_)); } void NodeParamViewArrayWidget::mouseDoubleClickEvent(QMouseEvent *event) @@ -45,9 +49,11 @@ void NodeParamViewArrayWidget::mouseDoubleClickEvent(QMouseEvent *event) emit DoubleClicked(); } -void NodeParamViewArrayWidget::UpdateCounter() +void NodeParamViewArrayWidget::UpdateCounter(const QString& input, int new_size) { - count_lbl_->setText(tr("%1 element(s)").arg(array_->ArraySize())); + if (input == input_) { + count_lbl_->setText(tr("%1 element(s)").arg(new_size)); + } } NodeParamViewArrayButton::NodeParamViewArrayButton(NodeParamViewArrayButton::Type type, QWidget *parent) : diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.h b/app/widget/nodeparamview/nodeparamviewarraywidget.h index 19dae47bd..a61f02235 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.h +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.h @@ -25,7 +25,7 @@ #include #include -#include "node/input.h" +#include "node/param.h" namespace olive { @@ -54,7 +54,7 @@ class NodeParamViewArrayWidget : public QWidget { Q_OBJECT public: - NodeParamViewArrayWidget(NodeInput* array, QWidget* parent = nullptr); + NodeParamViewArrayWidget(Node* node, const QString& input, QWidget* parent = nullptr); signals: void DoubleClicked(); @@ -63,12 +63,14 @@ protected: virtual void mouseDoubleClickEvent(QMouseEvent* event) override; private: - NodeInput* array_; + Node* node_; + + QString input_; QLabel* count_lbl_; private slots: - void UpdateCounter(); + void UpdateCounter(const QString &input, int new_size); }; diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp index a6d680ed9..922f51b48 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.cpp @@ -30,10 +30,10 @@ namespace olive { -NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, int element, QWidget *parent) : +NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(const NodeInput &input, QWidget *parent) : QWidget(parent), input_(input), - element_(element) + connected_node_(nullptr) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setSpacing(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral(" "))); @@ -56,30 +56,38 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, int e connected_to_lbl_->setForegroundRole(QPalette::Link); connected_to_lbl_->setFont(link_font); - UpdateConnected(nullptr, element_); + if (input_.IsConnected()) { + InputConnected(input_.GetConnectedOutput(), input_); + } else { + InputDisconnected(NodeOutput(), input_); + } - connect(input_, &NodeInput::InputConnected, this, &NodeParamViewConnectedLabel::UpdateConnected); - connect(input_, &NodeInput::InputDisconnected, this, &NodeParamViewConnectedLabel::UpdateConnected); + connect(input_.node(), &Node::InputConnected, this, &NodeParamViewConnectedLabel::InputConnected); + connect(input_.node(), &Node::InputDisconnected, this, &NodeParamViewConnectedLabel::InputDisconnected); } -void NodeParamViewConnectedLabel::UpdateConnected(Node *src, int element) +void NodeParamViewConnectedLabel::InputConnected(const NodeOutput& output, const NodeInput& input) { - Q_UNUSED(src) - - if (element_ != element) { - // Do nothing + if (input_ != input) { return; } - QString connection_str; + connected_node_ = output; - if (input_->IsConnected(element_)) { - connection_str = input_->GetConnectedNode(element_)->Name(); - } else { - connection_str = tr("Nothing"); + UpdateLabel(); +} + +void NodeParamViewConnectedLabel::InputDisconnected(const NodeOutput &output, const NodeInput &input) +{ + if (input_ != input) { + return; } - connected_to_lbl_->setText(connection_str); + Q_UNUSED(output) + + connected_node_ = NodeOutput(); + + UpdateLabel(); } void NodeParamViewConnectedLabel::ShowLabelContextMenu() @@ -88,7 +96,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_->GetConnectedNode(element_), input_, element_)); + Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(connected_node_, input_)); }); m.exec(QCursor::pos()); @@ -96,7 +104,22 @@ void NodeParamViewConnectedLabel::ShowLabelContextMenu() void NodeParamViewConnectedLabel::ConnectionClicked() { - emit RequestSelectNode({input_->GetConnectedNode(element_)}); + if (connected_node_.IsValid()) { + emit RequestSelectNode({connected_node_.node()}); + } +} + +void NodeParamViewConnectedLabel::UpdateLabel() +{ + QString s; + + if (connected_node_.IsValid()) { + s = connected_node_.node()->Name(); + } else { + s = tr("Nothing"); + } + + connected_to_lbl_->setText(s); } } diff --git a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h index 685fc4721..047c1f749 100644 --- a/app/widget/nodeparamview/nodeparamviewconnectedlabel.h +++ b/app/widget/nodeparamview/nodeparamviewconnectedlabel.h @@ -21,7 +21,7 @@ #ifndef NODEPARAMVIEWCONNECTEDLABEL_H #define NODEPARAMVIEWCONNECTEDLABEL_H -#include "node/input.h" +#include "node/param.h" #include "widget/clickablelabel/clickablelabel.h" namespace olive { @@ -29,24 +29,28 @@ namespace olive { class NodeParamViewConnectedLabel : public QWidget { Q_OBJECT public: - NodeParamViewConnectedLabel(NodeInput* input, int element, QWidget* parent = nullptr); + NodeParamViewConnectedLabel(const NodeInput& input, QWidget* parent = nullptr); signals: void RequestSelectNode(const QVector& node); private slots: - void UpdateConnected(Node* src, int element); + void InputConnected(const NodeOutput &output, const NodeInput &input); + + void InputDisconnected(const NodeOutput &output, const NodeInput &input); void ShowLabelContextMenu(); void ConnectionClicked(); private: + void UpdateLabel(); + ClickableLabel* connected_to_lbl_; - NodeInput* input_; + NodeInput input_; - int element_; + NodeOutput connected_node_; }; diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 191768fe2..fb1dbd690 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -134,7 +134,7 @@ bool NodeParamViewItem::IsExpanded() const return body_->isVisible(); } -int NodeParamViewItem::GetElementY(const NodeConnectable::InputConnection &c) const +int NodeParamViewItem::GetElementY(const NodeInput &c) const { if (IsExpanded()) { return body_->GetElementY(c); @@ -209,14 +209,12 @@ NodeParamViewItemBody::NodeParamViewItemBody(Node* node, QWidget *parent) : int insert_row = 0; // Create widgets all root level components - for (int i=0; iinputs().size(); i++) { - NodeInput* input = node->inputs().at(i); - - CreateWidgets(root_layout, input, -1, insert_row); + foreach (const QString& input, node->inputs()) { + CreateWidgets(root_layout, node, input, -1, insert_row); insert_row++; - if (input->IsArray()) { + if (node->InputIsArray(input)) { // Insert here QWidget* array_widget = new QWidget(); @@ -225,28 +223,33 @@ NodeParamViewItemBody::NodeParamViewItemBody(Node* node, QWidget *parent) : root_layout->addWidget(array_widget, insert_row, 1, 1, 10); - for (int j=0; jArraySize(); j++) { - CreateWidgets(array_layout, input, j, j); + int arr_sz = node->InputArraySize(input); + for (int j=0; jaddWidget(append_btn, input->ArraySize(), kArrayInsertColumn); + array_layout->addWidget(append_btn, arr_sz, kArrayInsertColumn); array_widget->setVisible(false); - array_ui_.insert(input, {array_widget, input->ArraySize(), append_btn}); + array_ui_.insert({node, input}, {array_widget, arr_sz, append_btn}); insert_row++; - - connect(input, &NodeInput::ArraySizeChanged, this, &NodeParamViewItemBody::InputArraySizeChanged); } } + + connect(node, &Node::InputArraySizeChanged, this, &NodeParamViewItemBody::InputArraySizeChanged); + connect(node, &Node::InputConnected, this, &NodeParamViewItemBody::EdgeChanged); + connect(node, &Node::InputDisconnected, this, &NodeParamViewItemBody::EdgeChanged); } -void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, int element, int row) +void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, Node *node, const QString &input, int element, int row) { + NodeInput input_ref(node, input, element); + InputUI ui_objects; // Add descriptor label @@ -255,7 +258,7 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, // Label always goes into column 1 (array collapse button goes into 0 if applicable) layout->addWidget(ui_objects.main_label, row, 1); - if (input->IsArray()) { + if (node->InputIsArray(input)) { if (element == -1) { // Create a collapse toggle for expanding/collapsing the array @@ -270,7 +273,7 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, // Connect signal to show/hide array params when toggled connect(array_collapse_btn, &CollapseButton::toggled, this, &NodeParamViewItemBody::ArrayCollapseBtnPressed); - array_collapse_buttons_.insert(input, array_collapse_btn); + array_collapse_buttons_.insert({node, input}, array_collapse_btn); } else { @@ -290,7 +293,7 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, } // Create a widget/input bridge for this input - ui_objects.widget_bridge = new NodeParamViewWidgetBridge(input, element, this); + ui_objects.widget_bridge = new NodeParamViewWidgetBridge(NodeInput(node, input, element), this); connect(ui_objects.widget_bridge, &NodeParamViewWidgetBridge::ArrayWidgetDoubleClicked, this, &NodeParamViewItemBody::ToggleArrayExpanded); // 0 is for the array collapse button, 1 is for the main label, widgets start at 2 @@ -303,28 +306,25 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, layout->addWidget(w, row, i+widget_start); } - if (input->IsConnectable()) { + if (node->IsInputConnectable(input)) { // Create clickable label used when an input is connected - ui_objects.connected_label = new NodeParamViewConnectedLabel(input, element); + ui_objects.connected_label = new NodeParamViewConnectedLabel(input_ref); connect(ui_objects.connected_label, &NodeParamViewConnectedLabel::RequestSelectNode, this, &NodeParamViewItemBody::RequestSelectNode); layout->addWidget(ui_objects.connected_label, row, widget_start); - - connect(input, &NodeInput::InputConnected, this, &NodeParamViewItemBody::EdgeChanged); - connect(input, &NodeInput::InputDisconnected, this, &NodeParamViewItemBody::EdgeChanged); } // Add keyframe control to this layout if parameter is keyframable - if (input->IsKeyframable()) { + if (node->IsInputKeyframable(input)) { ui_objects.key_control = new NodeParamViewKeyframeControl(); - ui_objects.key_control->SetInput(input, element); + ui_objects.key_control->SetInput(input_ref); layout->addWidget(ui_objects.key_control, row, kKeyControlColumn); connect(ui_objects.key_control, &NodeParamViewKeyframeControl::RequestSetTime, this, &NodeParamViewItemBody::RequestSetTime); } - input_ui_map_.insert(Node::InputConnection(input, element), ui_objects); + input_ui_map_.insert(input_ref, ui_objects); - if (input->IsConnectable()) { - UpdateUIForEdgeConnection(input, element); + if (node->IsInputConnectable(input)) { + UpdateUIForEdgeConnection(input_ref); } } @@ -355,23 +355,23 @@ void NodeParamViewItemBody::SetTime(const rational &time) void NodeParamViewItemBody::Retranslate() { for (auto i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) { - const Node::InputConnection& ic = i.key(); + const NodeInput& ic = i.key(); - if (ic.input->IsArray() && ic.element >= 0) { + if (ic.IsArray() && ic.element() >= 0) { // Make the label the array index - i.value().main_label->setText(tr("%n:", nullptr, ic.element)); + i.value().main_label->setText(tr("%n:", nullptr, ic.element())); } else { // Set to the input's name - i.value().main_label->setText(tr("%1:").arg(i.key().input->name())); + i.value().main_label->setText(tr("%1:").arg(ic.name())); } } } -int NodeParamViewItemBody::GetElementY(NodeConnectable::InputConnection c) const +int NodeParamViewItemBody::GetElementY(NodeInput c) const { - if (c.input->IsArray() && !array_ui_.value(c.input).widget->isVisible()) { + if (c.IsArray() && !array_ui_.value(c.input_pair()).widget->isVisible()) { // Array is collapsed, so we'll return the Y of its root - c.element = -1; + c.set_element(-1); } // Find its row in the parameters @@ -387,40 +387,40 @@ int NodeParamViewItemBody::GetElementY(NodeConnectable::InputConnection c) const return lbl_center.y(); } -void NodeParamViewItemBody::EdgeChanged(Node* src, int element) +void NodeParamViewItemBody::EdgeChanged(const NodeOutput& output, const NodeInput& input) { - Q_UNUSED(src) + Q_UNUSED(output) - UpdateUIForEdgeConnection(static_cast(sender()), element); + UpdateUIForEdgeConnection(input); } -void NodeParamViewItemBody::UpdateUIForEdgeConnection(NodeInput *input, int element) +void NodeParamViewItemBody::UpdateUIForEdgeConnection(const NodeInput& input) { // Show/hide bridge widgets - const InputUI& ui_objects = input_ui_map_[{input, element}]; + const InputUI& ui_objects = input_ui_map_[input]; foreach (QWidget* w, ui_objects.widget_bridge->widgets()) { - w->setVisible(!input->IsConnected(element)); + w->setVisible(!input.IsConnected()); } // Show/hide connection label - ui_objects.connected_label->setVisible(input->IsConnected(element)); + ui_objects.connected_label->setVisible(input.IsConnected()); } void NodeParamViewItemBody::ArrayCollapseBtnPressed(bool checked) { - NodeInput* input = array_collapse_buttons_.key(static_cast(sender())); + const NodeInputPair& input = array_collapse_buttons_.key(static_cast(sender())); array_ui_.value(input).widget->setVisible(checked); emit ArrayExpandedChanged(checked); } -void NodeParamViewItemBody::InputArraySizeChanged(int size) +void NodeParamViewItemBody::InputArraySizeChanged(const QString& input, int size) { - NodeInput* input = static_cast(sender()); + Node* node = static_cast(sender()); - ArrayUI& array_ui = array_ui_[input]; + ArrayUI& array_ui = array_ui_[{node, input}]; if (size != array_ui.count) { QGridLayout* grid = static_cast(array_ui.widget->layout()); @@ -430,12 +430,12 @@ void NodeParamViewItemBody::InputArraySizeChanged(int size) grid->addWidget(array_ui.append_btn, size, kArrayInsertColumn); for (int i=array_ui.count; i=size; i--) { // Our UI count is larger than the size, delete - InputUI input_ui = input_ui_map_.take({input, i}); + InputUI input_ui = input_ui_map_.take({node, input, i}); delete input_ui.main_label; qDeleteAll(input_ui.widget_bridge->widgets()); delete input_ui.widget_bridge; @@ -458,7 +458,7 @@ void NodeParamViewItemBody::ArrayAppendClicked() { for (auto it=array_ui_.cbegin(); it!=array_ui_.cend(); it++) { if (it.value().append_btn == sender()) { - it.key()->ArrayAppend(true); + it.key().node->InputArrayAppend(it.key().input, true); break; } } @@ -469,8 +469,8 @@ void NodeParamViewItemBody::ArrayInsertClicked() for (auto it=input_ui_map_.cbegin(); it!=input_ui_map_.cend(); it++) { if (it.value().array_insert_btn == sender()) { // Found our input and element - const Node::InputConnection& ic = it.key(); - ic.input->ArrayInsert(ic.element, true); + const NodeInput& ic = it.key(); + ic.node()->InputArrayInsert(ic.input(), ic.element(), true); break; } } @@ -481,8 +481,8 @@ void NodeParamViewItemBody::ArrayRemoveClicked() for (auto it=input_ui_map_.cbegin(); it!=input_ui_map_.cend(); it++) { if (it.value().array_remove_btn == sender()) { // Found our input and element - const Node::InputConnection& ic = it.key(); - ic.input->ArrayRemove(ic.element, true); + const NodeInput& ic = it.key(); + ic.node()->InputArrayRemove(ic.input(), ic.element(), true); break; } } @@ -494,7 +494,7 @@ void NodeParamViewItemBody::ToggleArrayExpanded() for (auto it=input_ui_map_.cbegin(); it!=input_ui_map_.cend(); it++) { if (it.value().widget_bridge == bridge) { - CollapseButton* b = array_collapse_buttons_.value(it.key().input); + CollapseButton* b = array_collapse_buttons_.value(it.key().input_pair()); b->setChecked(!b->isChecked()); return; } diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 4e07e2df4..a45fbc661 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -81,7 +81,7 @@ public: void Retranslate(); - int GetElementY(NodeConnectable::InputConnection c) const; + int GetElementY(NodeInput c) const; signals: void RequestSetTime(const rational& time); @@ -91,9 +91,9 @@ signals: void ArrayExpandedChanged(bool e); private: - void CreateWidgets(QGridLayout *layout, NodeInput* input, int element, int row_index); + void CreateWidgets(QGridLayout *layout, Node* node, const QString& input, int element, int row_index); - void UpdateUIForEdgeConnection(NodeInput* input, int element); + void UpdateUIForEdgeConnection(const NodeInput &input); struct InputUI { InputUI(); @@ -107,7 +107,7 @@ private: NodeParamViewArrayButton* array_remove_btn; }; - QHash input_ui_map_; + QHash input_ui_map_; struct ArrayUI { QWidget* widget; @@ -115,9 +115,9 @@ private: NodeParamViewArrayButton* append_btn; }; - QHash array_ui_; + QHash array_ui_; - QHash array_collapse_buttons_; + QHash array_collapse_buttons_; /** * @brief The column to place the keyframe controls in @@ -131,11 +131,11 @@ private: static const int kArrayRemoveColumn; private slots: - void EdgeChanged(Node *src, int element); + void EdgeChanged(const NodeOutput &output, const NodeInput &input); void ArrayCollapseBtnPressed(bool checked); - void InputArraySizeChanged(int size); + void InputArraySizeChanged(const QString &input, int size); void ArrayAppendClicked(); @@ -168,7 +168,7 @@ public: update(); } - int GetElementY(const Node::InputConnection& c) const; + int GetElementY(const NodeInput& c) const; public slots: void SetExpanded(bool e); diff --git a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp index bfa218d0c..61b3134c7 100644 --- a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp +++ b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp @@ -30,8 +30,7 @@ namespace olive { NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(bool right_align, QWidget *parent) : - QWidget(parent), - input_(nullptr) + QWidget(parent) { QHBoxLayout* layout = new QHBoxLayout(this); layout->setMargin(0); @@ -64,37 +63,36 @@ NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(bool right_align, QWi connect(next_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::GoToNextKey); connect(toggle_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::ToggleKeyframe); connect(enable_key_btn_, &QPushButton::toggled, this, &NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable); - connect(enable_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged); + connect(enable_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::KeyframeEnableBtnClicked); // Set defaults - SetInput(nullptr, -1); + SetInput(NodeInput()); ShowButtonsFromKeyframeEnable(false); } -void NodeParamViewKeyframeControl::SetInput(NodeInput *input, int element) +void NodeParamViewKeyframeControl::SetInput(const NodeInput& input) { - if (input_ != nullptr) { - disconnect(input_, &NodeInput::KeyframeEnableChanged, enable_key_btn_, &QPushButton::setChecked); - disconnect(input_, &NodeInput::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState); - disconnect(input_, &NodeInput::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState); - disconnect(input_, &NodeInput::KeyframeTimeChanged, this, &NodeParamViewKeyframeControl::UpdateState); + if (input_.IsValid()) { + disconnect(input_.node(), &Node::KeyframeEnableChanged, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged); + disconnect(input_.node(), &Node::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState); + disconnect(input_.node(), &Node::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState); + disconnect(input_.node(), &Node::KeyframeTimeChanged, this, &NodeParamViewKeyframeControl::UpdateState); } input_ = input; - element_ = element; - SetButtonsEnabled(input_); + SetButtonsEnabled(input_.IsValid()); // Pick up keyframing value - enable_key_btn_->setChecked(input_ && input_->IsKeyframing(element_)); + enable_key_btn_->setChecked(input_.IsValid() && input_.IsKeyframing()); // Update buttons UpdateState(); - if (input_ != nullptr) { - connect(input_, &NodeInput::KeyframeEnableChanged, enable_key_btn_, &QPushButton::setChecked); - connect(input_, &NodeInput::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState); - connect(input_, &NodeInput::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState); - connect(input_, &NodeInput::KeyframeTimeChanged, this, &NodeParamViewKeyframeControl::UpdateState); + if (input_.IsValid()) { + connect(input_.node(), &Node::KeyframeEnableChanged, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged); + connect(input_.node(), &Node::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState); + connect(input_.node(), &Node::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState); + connect(input_.node(), &Node::KeyframeTimeChanged, this, &NodeParamViewKeyframeControl::UpdateState); } } @@ -124,12 +122,12 @@ void NodeParamViewKeyframeControl::SetButtonsEnabled(bool e) rational NodeParamViewKeyframeControl::GetCurrentTimeAsNodeTime() const { - return GetAdjustedTime(GetTimeTarget(), input_->parent(), time_, true); + return GetAdjustedTime(GetTimeTarget(), input_.node(), time_, true); } rational NodeParamViewKeyframeControl::ConvertToViewerTime(const rational &r) const { - return GetAdjustedTime(input_->parent(), GetTimeTarget(), r, false); + return GetAdjustedTime(input_.node(), GetTimeTarget(), r, false); } void NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable(bool e) @@ -143,34 +141,33 @@ void NodeParamViewKeyframeControl::ToggleKeyframe(bool e) { rational node_time = GetCurrentTimeAsNodeTime(); - QVector keys = input_->GetKeyframesAtTime(node_time, element_); + QVector keys = input_.node()->GetKeyframesAtTime(input_, node_time); MultiUndoCommand* command = new MultiUndoCommand(); - int nb_tracks = input_->GetNumberOfKeyframeTracks(); + int nb_tracks = input_.node()->GetNumberOfKeyframeTracks(input_); if (e && keys.isEmpty()) { // Add a keyframe here (one for each track) for (int i=0;iGetValueAtTimeForTrack(node_time, i, element_), - input_->GetBestKeyframeTypeForTime(node_time, i, element_), + input_.node()->GetSplitValueAtTimeOnTrack(input_, node_time, i), + input_.node()->GetBestKeyframeTypeForTimeOnTrack(input_, node_time, i), i, - element_); + input_.element(), + input_.input()); - command->add_child(new NodeParamInsertKeyframeCommand(input_, key)); + command->add_child(new NodeParamInsertKeyframeCommand(input_.node(), key)); } } else if (!e && !keys.isEmpty()) { // Remove all keyframes at this time foreach (NodeKeyframe* key, keys) { command->add_child(new NodeParamRemoveKeyframeCommand(key)); - if (input_->GetKeyframeTracks(key->track()).size() == 1) { + if (input_.node()->GetKeyframeTracks(input_).size() == 1) { // If this was the last keyframe on this track, set the standard value to the value at this time too - command->add_child(new NodeParamSetStandardValueCommand(input_, - key->track(), - element_, - input_->GetValueAtTimeForTrack(node_time, key->track(), element_))); + command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(input_, key->track()), + input_.node()->GetSplitValueAtTimeOnTrack(input_, node_time, key->track()))); } } } @@ -180,25 +177,25 @@ void NodeParamViewKeyframeControl::ToggleKeyframe(bool e) void NodeParamViewKeyframeControl::UpdateState() { - if (!input_) { + if (!input_.IsValid()) { return; } - NodeKeyframe* earliest_key = input_->GetEarliestKeyframe(element_); - NodeKeyframe* latest_key = input_->GetLatestKeyframe(element_); + NodeKeyframe* earliest_key = input_.node()->GetEarliestKeyframe(input_); + NodeKeyframe* latest_key = input_.node()->GetLatestKeyframe(input_); 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_->HasKeyframeAtTime(node_time, element_)); + toggle_key_btn_->setChecked(input_.node()->HasKeyframeAtTime(input_, node_time)); } void NodeParamViewKeyframeControl::GoToPreviousKey() { rational node_time = GetCurrentTimeAsNodeTime(); - NodeKeyframe* previous_key = input_->GetClosestKeyframeBeforeTime(node_time, element_); + NodeKeyframe* previous_key = input_.node()->GetClosestKeyframeBeforeTime(input_, node_time); if (previous_key) { rational key_time = ConvertToViewerTime(previous_key->time()); @@ -211,7 +208,7 @@ void NodeParamViewKeyframeControl::GoToNextKey() { rational node_time = GetCurrentTimeAsNodeTime(); - NodeKeyframe* next_key = input_->GetClosestKeyframeAfterTime(node_time, element_); + NodeKeyframe* next_key = input_.node()->GetClosestKeyframeAfterTime(input_, node_time); if (next_key) { rational key_time = ConvertToViewerTime(next_key->time()); @@ -220,9 +217,9 @@ void NodeParamViewKeyframeControl::GoToNextKey() } } -void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) +void NodeParamViewKeyframeControl::KeyframeEnableBtnClicked(bool e) { - if (e == input_->IsKeyframing(element_)) { + if (e == input_.IsKeyframing()) { // No-op return; } @@ -231,19 +228,20 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) if (e) { // Enable keyframing - command->add_child(new NodeParamSetKeyframingCommand(input_, element_, true)); + command->add_child(new NodeParamSetKeyframingCommand(input_, true)); // Create one keyframe across all tracks here - const QVector& key_vals = input_->GetSplitStandardValue(element_); + const QVector& key_vals = input_.node()->GetSplitStandardValue(input_); for (int i=0;iadd_child(new NodeParamInsertKeyframeCommand(input_, key)); + command->add_child(new NodeParamInsertKeyframeCommand(input_.node(), key)); } } else { // Confirm the user wants to clear all keyframes @@ -253,10 +251,10 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { // Store value at this time, we'll set this as the persistent value later - const QVector& stored_vals = input_->GetSplitValuesAtTime(GetCurrentTimeAsNodeTime(), element_); + const QVector& stored_vals = input_.node()->GetSplitValueAtTime(input_, GetCurrentTimeAsNodeTime()); // Delete all keyframes - foreach (const NodeKeyframeTrack& track, input_->GetKeyframeTracks(element_)) { + foreach (const NodeKeyframeTrack& track, input_.node()->GetKeyframeTracks(input_)) { for (int i=track.size()-1;i>=0;i--) { command->add_child(new NodeParamRemoveKeyframeCommand(track.at(i))); } @@ -264,11 +262,11 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) // Update standard value for (int i=0;iadd_child(new NodeParamSetStandardValueCommand(input_, i, element_, stored_vals.at(i))); + command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(input_, i), stored_vals.at(i))); } // Disable keyframing - command->add_child(new NodeParamSetKeyframingCommand(input_, element_, false)); + command->add_child(new NodeParamSetKeyframingCommand(input_, false)); } else { // Disable action has effectively been ignored @@ -279,4 +277,11 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) Core::instance()->undo_stack()->pushIfHasChildren(command); } +void NodeParamViewKeyframeControl::KeyframeEnableChanged(const NodeInput &input, bool e) +{ + if (input_ == input) { + enable_key_btn_->setChecked(e); + } +} + } diff --git a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h index 581bb865c..93a963b84 100644 --- a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h +++ b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.h @@ -24,7 +24,7 @@ #include #include -#include "node/input.h" +#include "node/param.h" #include "widget/timetarget/timetarget.h" namespace olive { @@ -35,17 +35,12 @@ class NodeParamViewKeyframeControl : public QWidget, public TimeTargetObject public: NodeParamViewKeyframeControl(bool right_align = true, QWidget* parent = nullptr); - NodeInput* GetConnectedInput() const + const NodeInput& GetConnectedInput() const { return input_; } - int GetConnectedElement() const - { - return element_; - } - - void SetInput(NodeInput* input, int element); + void SetInput(const NodeInput& input); void SetTime(const rational& time); @@ -66,8 +61,7 @@ private: QPushButton* next_key_btn_; QPushButton* enable_key_btn_; - NodeInput* input_; - int element_; + NodeInput input_; rational time_; @@ -82,7 +76,9 @@ private slots: void GoToNextKey(); - void KeyframeEnableChanged(bool e); + void KeyframeEnableBtnClicked(bool e); + + void KeyframeEnableChanged(const NodeInput& input, bool e); }; diff --git a/app/widget/nodeparamview/nodeparamviewundo.cpp b/app/widget/nodeparamview/nodeparamviewundo.cpp index 89ca47444..99d45fd07 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.cpp +++ b/app/widget/nodeparamview/nodeparamviewundo.cpp @@ -25,27 +25,26 @@ namespace olive { -NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(NodeInput *input, int element, bool setting) : +NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(const NodeInput &input, bool setting) : input_(input), - setting_(setting), - element_(element) + setting_(setting) { - Q_ASSERT(setting != input_->IsKeyframing()); + Q_ASSERT(setting != input_.IsKeyframing()); } Project *NodeParamSetKeyframingCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return input_.node()->parent()->project(); } void NodeParamSetKeyframingCommand::redo() { - input_->SetIsKeyframing(setting_, element_); + input_.node()->SetInputIsKeyframing(input_, setting_); } void NodeParamSetKeyframingCommand::undo() { - input_->SetIsKeyframing(!setting_, element_); + input_.node()->SetInputIsKeyframing(input_, !setting_); } NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value) : @@ -65,7 +64,7 @@ NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* Project *NodeParamSetKeyframeValueCommand::GetRelevantProject() const { - return key_->parent()->parent()->parent()->project(); + return key_->parent()->parent()->project(); } void NodeParamSetKeyframeValueCommand::redo() @@ -78,16 +77,17 @@ void NodeParamSetKeyframeValueCommand::undo() key_->set_value(old_value_); } -NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframe* keyframe) : - input_(input), +NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(Node* node, NodeKeyframe* keyframe) : + input_(node), keyframe_(keyframe) { - keyframe_->setParent(&memory_manager_); + // Take ownership of the keyframe + undo(); } Project *NodeParamInsertKeyframeCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return input_->parent()->project(); } void NodeParamInsertKeyframeCommand::redo() @@ -108,7 +108,7 @@ NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeKeyframe* key Project *NodeParamRemoveKeyframeCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return input_->parent()->project(); } void NodeParamRemoveKeyframeCommand::redo() @@ -138,7 +138,7 @@ NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* k Project *NodeParamSetKeyframeTimeCommand::GetRelevantProject() const { - return key_->parent()->parent()->parent()->project(); + return key_->parent()->parent()->project(); } void NodeParamSetKeyframeTimeCommand::redo() @@ -151,19 +151,15 @@ void NodeParamSetKeyframeTimeCommand::undo() key_->set_time(old_time_); } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &value) : - input_(input), - element_(element), - track_(track), - old_value_(input_->GetStandardValue(element_)), +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant &value) : + ref_(input), + old_value_(ref_.input().node()->GetStandardValue(ref_.input())), new_value_(value) { } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &new_value, const QVariant &old_value) : - input_(input), - element_(element), - track_(track), +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant &new_value, const QVariant &old_value) : + ref_(input), old_value_(old_value), new_value_(new_value) { @@ -171,22 +167,22 @@ NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *in Project *NodeParamSetStandardValueCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return ref_.input().node()->parent()->project(); } void NodeParamSetStandardValueCommand::redo() { - input_->SetStandardValueOnTrack(new_value_, track_, element_); + ref_.input().node()->SetSplitStandardValueOnTrack(ref_, new_value_); } void NodeParamSetStandardValueCommand::undo() { - input_->SetStandardValueOnTrack(old_value_, track_, element_); + ref_.input().node()->SetSplitStandardValueOnTrack(ref_, old_value_); } Project *NodeParamArrayInsertCommand::GetRelevantProject() const { - return input_->parent()->parent()->project(); + return input_.node()->parent()->project(); } } diff --git a/app/widget/nodeparamview/nodeparamviewundo.h b/app/widget/nodeparamview/nodeparamviewundo.h index 8f28600eb..7191369f8 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.h +++ b/app/widget/nodeparamview/nodeparamviewundo.h @@ -21,7 +21,9 @@ #ifndef NODEPARAMVIEWUNDO_H #define NODEPARAMVIEWUNDO_H -#include "node/input.h" +#include "node/keyframe.h" +#include "node/node.h" +#include "node/param.h" #include "undo/undocommand.h" namespace olive { @@ -29,7 +31,7 @@ namespace olive { class NodeParamSetKeyframingCommand : public UndoCommand { public: - NodeParamSetKeyframingCommand(NodeInput* input, int element, bool setting); + NodeParamSetKeyframingCommand(const NodeInput& input, bool setting); virtual Project* GetRelevantProject() const override; @@ -37,16 +39,15 @@ public: virtual void undo() override; private: - NodeInput* input_; + NodeInput input_; bool setting_; - int element_; }; class NodeParamInsertKeyframeCommand : public UndoCommand { public: - NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframe* keyframe); + NodeParamInsertKeyframeCommand(Node *node, NodeKeyframe* keyframe); virtual Project* GetRelevantProject() const override; @@ -54,7 +55,7 @@ public: virtual void undo() override; private: - NodeInput* input_; + Node* input_; NodeKeyframe* keyframe_; @@ -73,7 +74,7 @@ public: virtual void undo() override; private: - NodeInput* input_; + Node* input_; NodeKeyframe* keyframe_; @@ -122,8 +123,8 @@ private: class NodeParamSetStandardValueCommand : public UndoCommand { public: - NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& value); - NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& new_value, const QVariant& old_value); + NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant& value); + NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant& new_value, const QVariant& old_value); virtual Project* GetRelevantProject() const override; @@ -131,9 +132,7 @@ public: virtual void undo() override; private: - NodeInput* input_; - int element_; - int track_; + NodeKeyframeTrackReference ref_; QVariant old_value_; QVariant new_value_; @@ -143,7 +142,7 @@ private: class NodeParamArrayInsertCommand : public UndoCommand { public: - NodeParamArrayInsertCommand(NodeInput* input, int index) : + NodeParamArrayInsertCommand(const NodeInput& input, int index) : input_(input), index_(index) { @@ -153,16 +152,16 @@ public: virtual void redo() override { - input_->ArrayInsert(index_); + input_.node()->InputArrayInsert(input_.input(), index_); } virtual void undo() override { - input_->ArrayRemove(index_); + input_.node()->InputArrayRemove(input_.input(), index_); } private: - NodeInput* input_; + NodeInput input_; int index_; }; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index f8e70ded4..f402c67d6 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -40,43 +40,37 @@ namespace olive { -NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput *input, int element, QObject *parent) : +NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(const NodeInput &input, QObject *parent) : QObject(parent), - input_(input), - element_(element) + input_(input) { CreateWidgets(); - connect(input_, &NodeInput::ValueChanged, this, &NodeParamViewWidgetBridge::InputValueChanged); - connect(input_, &NodeInput::PropertyChanged, this, &NodeParamViewWidgetBridge::PropertyChanged); + connect(input_.node(), &Node::ValueChanged, this, &NodeParamViewWidgetBridge::InputValueChanged); + connect(input_.node(), &Node::InputPropertyChanged, this, &NodeParamViewWidgetBridge::PropertyChanged); } void NodeParamViewWidgetBridge::SetTime(const rational &time) { time_ = time; - if (input_) { + if (input_.IsValid()) { UpdateWidgetValues(); } } -const QList &NodeParamViewWidgetBridge::widgets() const -{ - return widgets_; -} - void NodeParamViewWidgetBridge::CreateWidgets() { - if (input_->IsArray() && element_ == -1) { + if (input_.IsArray() && input_.element() == -1) { - NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(input_); + NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(input_.node(), input_.input()); connect(w, &NodeParamViewArrayWidget::DoubleClicked, this, &NodeParamViewWidgetBridge::ArrayWidgetDoubleClicked); widgets_.append(w); } else { // We assume the first data type is the "primary" type - switch (input_->GetDataType()) { + switch (input_.GetDataType()) { // None of these inputs have applicable UI widgets case NodeValue::kNone: case NodeValue::kTexture: @@ -89,38 +83,34 @@ void NodeParamViewWidgetBridge::CreateWidgets() break; case NodeValue::kInt: { - IntegerSlider* slider = new IntegerSlider(); - slider->SetDefaultValue(input_->GetDefaultValue()); - slider->SetLadderElementCount(2); - widgets_.append(slider); - connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); + CreateSliders(1); break; } case NodeValue::kFloat: { - CreateSliders(1); + CreateSliders(1); break; } case NodeValue::kVec2: { - CreateSliders(2); + CreateSliders(2); break; } case NodeValue::kVec3: { - CreateSliders(3); + CreateSliders(3); break; } case NodeValue::kVec4: { - CreateSliders(4); + CreateSliders(4); break; } case NodeValue::kCombo: { QComboBox* combobox = new QComboBox(); - QStringList items = input_->get_combobox_strings(); + QStringList items = input_.GetComboBoxStrings(); foreach (const QString& s, items) { combobox->addItem(s); } @@ -135,7 +125,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() case NodeValue::kColor: { // NOTE: Very convoluted way to get back to the project's color manager - ColorButton* color_button = new ColorButton(input_->parent()->parent()->project()->color_manager()); + ColorButton* color_button = new ColorButton(input_.node()->parent()->project()->color_manager()); widgets_.append(color_button); connect(color_button, &ColorButton::ColorChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); break; @@ -164,7 +154,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() case NodeValue::kFootage: { FootageComboBox* footage_combobox = new FootageComboBox(); - footage_combobox->SetRoot(input_->parent()->parent()->project()->root()); + footage_combobox->SetRoot(input_.node()->parent()->project()->root()); connect(footage_combobox, &FootageComboBox::FootageChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); @@ -175,8 +165,9 @@ void NodeParamViewWidgetBridge::CreateWidgets() } // Check all properties - foreach (const QByteArray& key, input_->dynamicPropertyNames()) { - PropertyChanged(key, input_->property(key)); + auto input_properties = input_.node()->GetInputProperties(input_.input()); + for (auto it=input_properties.cbegin(); it!=input_properties.cend(); it++) { + PropertyChanged(input_.input(), it.key(), it.value()); } UpdateWidgetValues(); @@ -202,8 +193,8 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int { rational node_time = GetCurrentTimeAsNodeTime(); - if (input_->IsKeyframing(element_)) { - NodeKeyframe* existing_key = input_->GetKeyframeAtTimeOnTrack(node_time, track, element_); + if (input_.IsKeyframing()) { + NodeKeyframe* existing_key = input_.GetKeyframeAtTimeOnTrack(node_time, track); if (existing_key) { command->add_child(new NodeParamSetKeyframeValueCommand(existing_key, value)); @@ -211,14 +202,15 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int // No existing key, create a new one NodeKeyframe* new_key = new NodeKeyframe(node_time, value, - input_->GetBestKeyframeTypeForTime(node_time, track, element_), + input_.node()->GetBestKeyframeTypeForTimeOnTrack(NodeKeyframeTrackReference(input_, track), node_time), track, - element_); + input_.element(), + input_.input()); - command->add_child(new NodeParamInsertKeyframeCommand(input_, new_key)); + command->add_child(new NodeParamInsertKeyframeCommand(input_.node(), new_key)); } } else { - command->add_child(new NodeParamSetStandardValueCommand(input_, track, element_, value)); + command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(input_, track), value)); } } @@ -232,7 +224,7 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant // While we're dragging, we block the input's normal signalling and create our own if (!dragger_.IsStarted()) { - dragger_.Start(input_, node_time, slider_track, element_); + dragger_.Start(NodeKeyframeTrackReference(input_, slider_track), node_time); } dragger_.Drag(value); @@ -253,7 +245,7 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant void NodeParamViewWidgetBridge::WidgetCallback() { - switch (input_->GetDataType()) { + switch (input_.GetDataType()) { // None of these inputs have applicable UI widgets case NodeValue::kNone: case NodeValue::kTexture: @@ -269,7 +261,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a IntegerSlider IntegerSlider* slider = static_cast(sender()); - int64_t offset = input_->property("offset").toLongLong(); + int64_t offset = input_.GetProperty("offset").toLongLong(); ProcessSlider(slider, QVariant::fromValue(slider->GetValue() - offset)); break; @@ -279,7 +271,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); - double offset = input_->property("offset").toDouble(); + double offset = input_.GetProperty("offset").toDouble(); ProcessSlider(slider, slider->GetValue() - offset); break; @@ -289,7 +281,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); - QVector2D offset = input_->property("offset").value(); + QVector2D offset = input_.GetProperty("offset").value(); ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]); break; @@ -299,7 +291,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); - QVector3D offset = input_->property("offset").value(); + QVector3D offset = input_.GetProperty("offset").value(); ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]); break; @@ -309,7 +301,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Widget is a FloatSlider FloatSlider* slider = static_cast(sender()); - QVector4D offset = input_->property("offset").value(); + QVector4D offset = input_.GetProperty("offset").value(); ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]); break; @@ -329,12 +321,13 @@ void NodeParamViewWidgetBridge::WidgetCallback() SetInputValueInternal(c.blue(), 2, command); SetInputValueInternal(c.alpha(), 3, command); - input_->blockSignals(true); - input_->setProperty("col_input", c.color_input()); - input_->setProperty("col_display", c.color_output().display()); - input_->setProperty("col_view", c.color_output().view()); - input_->setProperty("col_look", c.color_output().look()); - input_->blockSignals(false); + Node* n = input_.node(); + n->blockSignals(true); + n->SetInputProperty(input_.input(), QStringLiteral("col_input"), c.color_input()); + n->SetInputProperty(input_.input(), QStringLiteral("col_display"), c.color_output().display()); + n->SetInputProperty(input_.input(), QStringLiteral("col_view"), c.color_output().view()); + n->SetInputProperty(input_.input(), QStringLiteral("col_look"), c.color_output().look()); + n->blockSignals(false); Core::instance()->undo_stack()->pushIfHasChildren(command); break; @@ -382,27 +375,28 @@ void NodeParamViewWidgetBridge::WidgetCallback() } } +template void NodeParamViewWidgetBridge::CreateSliders(int count) { for (int i=0;iSetDefaultValue(input_->GetDefaultValueForTrack(i)); + T* fs = new T(); + fs->SetDefaultValue(input_.GetSplitDefaultValueForTrack(i)); fs->SetLadderElementCount(2); widgets_.append(fs); - connect(fs, &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); + connect(fs, &T::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); } } void NodeParamViewWidgetBridge::UpdateWidgetValues() { - if (input_->IsArray() && element_ == -1) { + if (input_.IsArray() && input_.element() == -1) { return; } rational node_time = GetCurrentTimeAsNodeTime(); // We assume the first data type is the "primary" type - switch (input_->GetDataType()) { + switch (input_.GetDataType()) { // None of these inputs have applicable UI widgets case NodeValue::kNone: case NodeValue::kTexture: @@ -415,22 +409,22 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() break; case NodeValue::kInt: { - int64_t offset = input_->property("offset").toLongLong(); + int64_t offset = input_.GetProperty("offset").toLongLong(); - static_cast(widgets_.first())->SetValue(input_->GetValueAtTime(node_time, element_).toLongLong() + offset); + static_cast(widgets_.first())->SetValue(input_.GetValueAtTime(node_time).toLongLong() + offset); break; } case NodeValue::kFloat: { - double offset = input_->property("offset").toDouble(); + double offset = input_.GetProperty("offset").toDouble(); - static_cast(widgets_.first())->SetValue(input_->GetValueAtTime(node_time, element_).toDouble() + offset); + static_cast(widgets_.first())->SetValue(input_.GetValueAtTime(node_time).toDouble() + offset); break; } case NodeValue::kVec2: { - QVector2D vec2 = input_->GetValueAtTime(node_time, element_).value(); - QVector2D offset = input_->property("offset").value(); + QVector2D vec2 = input_.GetValueAtTime(node_time).value(); + QVector2D offset = input_.GetProperty("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())); @@ -438,8 +432,8 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() } case NodeValue::kVec3: { - QVector3D vec3 = input_->GetValueAtTime(node_time, element_).value(); - QVector3D offset = input_->property("offset").value(); + QVector3D vec3 = input_.GetValueAtTime(node_time).value(); + QVector3D offset = input_.GetProperty("offset").value(); static_cast(widgets_.at(0))->SetValue(static_cast(vec3.x() + offset.x())); static_cast(widgets_.at(1))->SetValue(static_cast(vec3.y() + offset.y())); @@ -448,8 +442,8 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() } case NodeValue::kVec4: { - QVector4D vec4 = input_->GetValueAtTime(node_time, element_).value(); - QVector4D offset = input_->property("offset").value(); + QVector4D vec4 = input_.GetValueAtTime(node_time).value(); + QVector4D offset = input_.GetProperty("offset").value(); static_cast(widgets_.at(0))->SetValue(static_cast(vec4.x() + offset.x())); static_cast(widgets_.at(1))->SetValue(static_cast(vec4.y() + offset.y())); @@ -462,13 +456,13 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() break; case NodeValue::kColor: { - ManagedColor mc = input_->GetValueAtTime(node_time, element_).value(); + ManagedColor mc = input_.GetValueAtTime(node_time).value(); - mc.set_color_input(input_->property("col_input").toString()); + mc.set_color_input(input_.GetProperty("col_input").toString()); - QString d = input_->property("col_display").toString(); - QString v = input_->property("col_view").toString(); - QString l = input_->property("col_look").toString(); + QString d = input_.GetProperty("col_display").toString(); + QString v = input_.GetProperty("col_view").toString(); + QString l = input_.GetProperty("col_look").toString(); mc.set_color_output(ColorTransform(d, v, l)); @@ -478,17 +472,17 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() case NodeValue::kText: { NodeParamViewRichText* e = static_cast(widgets_.first()); - e->setTextPreservingCursor(input_->GetValueAtTime(node_time, element_).toString()); + e->setTextPreservingCursor(input_.GetValueAtTime(node_time).toString()); break; } case NodeValue::kBoolean: - static_cast(widgets_.first())->setChecked(input_->GetValueAtTime(node_time, element_).toBool()); + static_cast(widgets_.first())->setChecked(input_.GetValueAtTime(node_time).toBool()); break; case NodeValue::kFont: { QFontComboBox* fc = static_cast(widgets_.first()); fc->blockSignals(true); - fc->setCurrentFont(input_->GetValueAtTime(node_time, element_).toString()); + fc->setCurrentFont(input_.GetValueAtTime(node_time).toString()); fc->blockSignals(false); break; } @@ -496,24 +490,24 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() { QComboBox* cb = static_cast(widgets_.first()); cb->blockSignals(true); - cb->setCurrentIndex(input_->GetValueAtTime(node_time, element_).toInt()); + cb->setCurrentIndex(input_.GetValueAtTime(node_time).toInt()); cb->blockSignals(false); break; } case NodeValue::kFootage: - static_cast(widgets_.first())->SetFootage(Node::ValueToPtr(input_->GetValueAtTime(node_time, element_))); + static_cast(widgets_.first())->SetFootage(Node::ValueToPtr(input_.GetValueAtTime(node_time))); break; } } rational NodeParamViewWidgetBridge::GetCurrentTimeAsNodeTime() const { - return GetAdjustedTime(GetTimeTarget(), input_->parent(), time_, true); + return GetAdjustedTime(GetTimeTarget(), input_.node(), time_, true); } -void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range, int element) +void NodeParamViewWidgetBridge::InputValueChanged(const NodeInput &input, const TimeRange &range) { - if (element == element_ + if (input_ == input && !dragger_.IsStarted() && range.in() <= time_ && range.out() >= time_) { // We'll need to update the widgets because the values have changed on our current time @@ -521,10 +515,16 @@ void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range, int el } } -void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVariant &value) +void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QString &key, const QVariant &value) { + if (input != input_.input()) { + return; + } + + NodeValue::Type data_type = input_.GetDataType(); + // Parameters for vectors only - if (NodeValue::type_is_vector(input_->GetDataType())) { + if (NodeValue::type_is_vector(data_type)) { if (key == QStringLiteral("disablex")) { static_cast(widgets_.at(0))->setEnabled(!value.toBool()); } else if (key == QStringLiteral("disabley")) { @@ -537,9 +537,9 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria } // Parameters for integers, floats, and vectors - if (NodeValue::type_is_numeric(input_->GetDataType()) || NodeValue::type_is_vector(input_->GetDataType())) { + if (NodeValue::type_is_numeric(data_type) || NodeValue::type_is_vector(data_type)) { if (key == QStringLiteral("min")) { - switch (input_->GetDataType()) { + switch (data_type) { case NodeValue::kInt: static_cast(widgets_.first())->SetMinimum(value.value()); break; @@ -577,7 +577,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria break; } } else if (key == QStringLiteral("max")) { - switch (input_->GetDataType()) { + switch (data_type) { case NodeValue::kInt: static_cast(widgets_.first())->SetMaximum(value.value()); break; @@ -620,7 +620,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria } // ComboBox strings changing - if (input_->GetDataType() & NodeValue::kCombo) { + if (data_type & NodeValue::kCombo) { QComboBox* cb = static_cast(widgets_.first()); int old_index = cb->currentIndex(); @@ -630,7 +630,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria cb->clear(); - QStringList items = input_->get_combobox_strings(); + QStringList items = input_.GetComboBoxStrings(); foreach (const QString& s, items) { if (s.isEmpty()) { cb->insertSeparator(cb->count()); @@ -651,7 +651,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria } // Parameters for floats and vectors only - if (input_->GetDataType() == NodeValue::kFloat || NodeValue::type_is_vector(input_->GetDataType())) { + if (data_type == NodeValue::kFloat || NodeValue::type_is_vector(data_type)) { if (key == QStringLiteral("view")) { FloatSlider::DisplayType display_type = static_cast(value.toInt()); diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index b3a1c0736..7b191f143 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -23,7 +23,6 @@ #include -#include "node/input.h" #include "node/inputdragger.h" #include "widget/slider/sliderbase.h" #include "widget/timetarget/timetarget.h" @@ -41,11 +40,14 @@ class NodeParamViewWidgetBridge : public QObject, public TimeTargetObject { Q_OBJECT public: - NodeParamViewWidgetBridge(NodeInput* input, int element, QObject* parent); + NodeParamViewWidgetBridge(const NodeInput& input, QObject* parent); void SetTime(const rational& time); - const QList& widgets() const; + const QVector& widgets() const + { + return widgets_; + } signals: void ArrayWidgetDoubleClicked(); @@ -59,17 +61,16 @@ private: void ProcessSlider(SliderBase* slider, const QVariant& value); + template void CreateSliders(int count); void UpdateWidgetValues(); rational GetCurrentTimeAsNodeTime() const; - NodeInput* input_; + NodeInput input_; - int element_; - - QList widgets_; + QVector widgets_; rational time_; @@ -80,9 +81,9 @@ private: private slots: void WidgetCallback(); - void InputValueChanged(const TimeRange& range, int element); + void InputValueChanged(const NodeInput& input, const TimeRange& range); - void PropertyChanged(const QString& key, const QVariant& value); + void PropertyChanged(const QString &input, const QString& key, const QVariant& value); }; diff --git a/app/widget/nodetableview/nodetableview.cpp b/app/widget/nodetableview/nodetableview.cpp index 7b58ad2ec..d67f23f8c 100644 --- a/app/widget/nodetableview/nodetableview.cpp +++ b/app/widget/nodetableview/nodetableview.cpp @@ -87,8 +87,7 @@ void NodeTableView::SetTime(const rational &time) for (l=db.begin(); l!=db.end(); l++) { const NodeValueTable& table = l.value(); - NodeInput* input = node->GetInputWithID(l.key()); - if (!input) { + if (!node->HasInputWithID(l.key())) { // Filters out table entries that aren't inputs (like "global") continue; } @@ -98,7 +97,7 @@ void NodeTableView::SetTime(const rational &time) for (int j=0; jchildCount(); j++) { QTreeWidgetItem* compare = item->child(j); - if (compare->data(0, Qt::UserRole).toString() == input->id()) { + if (compare->data(0, Qt::UserRole).toString() == l.key()) { input_item = compare; break; } @@ -106,8 +105,8 @@ void NodeTableView::SetTime(const rational &time) if (!input_item) { input_item = new QTreeWidgetItem(); - input_item->setText(0, input->name()); - input_item->setData(0, Qt::UserRole, input->id()); + input_item->setText(0, node->GetInputName(l.key())); + input_item->setData(0, Qt::UserRole, l.key()); input_item->setFirstColumnSpanned(true); item->addChild(input_item); } @@ -151,7 +150,7 @@ void NodeTableView::SetTime(const rational &time) } default: { - QVector split_values = NodeValue::split_normal_value_into_track_values(input->GetDataType(), value.data()); + QVector split_values = NodeValue::split_normal_value_into_track_values(node->GetInputDataType(l.key()), value.data()); for (int k=0;ksetText(2 + k, NodeValue::ValueToString(value.type(), split_values.at(k), true)); } diff --git a/app/widget/nodetableview/nodetablewidget.h b/app/widget/nodetableview/nodetablewidget.h index a68db7371..59e11d5af 100644 --- a/app/widget/nodetableview/nodetablewidget.h +++ b/app/widget/nodetableview/nodetablewidget.h @@ -42,7 +42,7 @@ public: } protected: - virtual void TimeChangedEvent(const int64_t& ts) override + virtual void TimeChangedEvent(const int64_t&) override { UpdateView(); } diff --git a/app/widget/nodetreeview/nodetreeview.cpp b/app/widget/nodetreeview/nodetreeview.cpp index 5772150f8..33a881ae1 100644 --- a/app/widget/nodetreeview/nodetreeview.cpp +++ b/app/widget/nodetreeview/nodetreeview.cpp @@ -18,12 +18,12 @@ bool NodeTreeView::IsNodeEnabled(Node *n) const return !disabled_nodes_.contains(n); } -bool NodeTreeView::IsInputEnabled(NodeInput *i, int element, int track) const +bool NodeTreeView::IsInputEnabled(const NodeKeyframeTrackReference &ref) const { - return !disabled_inputs_.contains({i, element, track}); + return !disabled_inputs_.contains(ref); } -void NodeTreeView::SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference &ref, const QColor &color) +void NodeTreeView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color) { // Insert into hashmap keyframe_colors_.insert(ref, color); @@ -47,21 +47,24 @@ void NodeTreeView::SetNodes(const QVector &nodes) node_item->setText(0, n->Name()); node_item->setCheckState(0, disabled_nodes_.contains(n) ? Qt::Unchecked : Qt::Checked); node_item->setData(0, kItemType, kItemTypeNode); - node_item->setData(0, kItemPointer, reinterpret_cast(n)); + node_item->setData(0, kItemNodePointer, Node::PtrToValue(n)); - foreach (NodeInput* input, n->inputs()) { - if (only_show_keyframable_ && !input->IsKeyframable()) { + foreach (const QString& input, n->inputs()) { + if (only_show_keyframable_ && !n->IsInputKeyframable(input)) { continue; } QTreeWidgetItem* input_item = nullptr; - for (int i=-1; iArraySize(); i++) { - const QVector& key_tracks = input->GetKeyframeTracks(i); + int arr_sz = n->InputArraySize(input); + for (int i=-1; i& key_tracks = n->GetKeyframeTracks(input_ref); int this_element_track; - if (show_keyframe_tracks_as_rows_ && (key_tracks.size() == 1 || (i == -1 && input->IsArray()))) { + if (show_keyframe_tracks_as_rows_ + && (key_tracks.size() == 1 || (i == -1 && n->InputIsArray(input)))) { this_element_track = 0; } else { this_element_track = -1; @@ -70,14 +73,14 @@ void NodeTreeView::SetNodes(const QVector &nodes) QTreeWidgetItem* element_item; if (input_item) { - element_item = CreateItem(input_item, input, i, this_element_track); + element_item = CreateItem(input_item, NodeKeyframeTrackReference(input_ref, this_element_track)); } else { - input_item = CreateItem(node_item, input, i, this_element_track); + input_item = CreateItem(node_item, NodeKeyframeTrackReference(input_ref, this_element_track)); element_item = input_item; } - if (show_keyframe_tracks_as_rows_ && key_tracks.size() > 1 && (!input->IsArray() || i >= 0)) { - CreateItemsForTracks(element_item, input, i, key_tracks.size()); + if (show_keyframe_tracks_as_rows_ && key_tracks.size() > 1 && (!n->InputIsArray(input) || i >= 0)) { + CreateItemsForTracks(element_item, input_ref, key_tracks.size()); } } @@ -105,10 +108,10 @@ void NodeTreeView::mouseDoubleClickEvent(QMouseEvent *e) { QTreeWidget::mouseDoubleClickEvent(e); - NodeInput::KeyframeTrackReference ref = GetSelectedInput(); + NodeKeyframeTrackReference ref = GetSelectedInput(); - if (ref.input) { - emit InputDoubleClicked(ref.input, ref.element, ref.track); + if (ref.input().IsValid()) { + emit InputDoubleClicked(ref); } } @@ -117,36 +120,32 @@ void NodeTreeView::Retranslate() setHeaderLabel(tr("Nodes")); } -NodeInput::KeyframeTrackReference NodeTreeView::GetSelectedInput() +NodeKeyframeTrackReference NodeTreeView::GetSelectedInput() { QList sel = selectedItems(); - NodeInput* selected_input = nullptr; - int selected_element = -1; - int selected_track = -1; + NodeKeyframeTrackReference selected_ref; if (!sel.isEmpty()) { QTreeWidgetItem* item = sel.first(); if (item->data(0, kItemType).toInt() == kItemTypeInput) { - selected_input = reinterpret_cast(item->data(0, kItemPointer).value()); - selected_element = item->data(0, kItemElement).toInt(); - selected_track = item->data(0, kItemTrack).toInt(); + selected_ref = item->data(0, kItemInputReference).value(); } } - return {selected_input, selected_element, selected_track}; + return selected_ref; } -QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, NodeInput *input, int element, int track) +QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, const NodeKeyframeTrackReference& ref) { QTreeWidgetItem* input_item = new QTreeWidgetItem(parent); QString item_name; - if (track == -1 || NodeValue::get_number_of_keyframe_tracks(input->GetDataType()) == 1) { - item_name = input->name(); + if (ref.track() == -1 || NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType()) == 1) { + item_name = ref.input().name(); } else { - switch (track) { + switch (ref.track()) { case 0: item_name = tr("X"); break; @@ -160,18 +159,14 @@ QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, NodeInput *in item_name = tr("W"); break; default: - item_name = QString::number(track); + item_name = QString::number(ref.track()); } } input_item->setText(0, item_name); - input_item->setCheckState(0, disabled_inputs_.contains({input, element, track}) ? Qt::Unchecked : Qt::Checked); + input_item->setCheckState(0, disabled_inputs_.contains(ref) ? Qt::Unchecked : Qt::Checked); input_item->setData(0, kItemType, kItemTypeInput); - input_item->setData(0, kItemPointer, reinterpret_cast(input)); - input_item->setData(0, kItemElement, element); - input_item->setData(0, kItemTrack, track); - - NodeInput::KeyframeTrackReference ref = {input, element, track}; + input_item->setData(0, kItemInputReference, QVariant::fromValue(ref)); if (keyframe_colors_.contains(ref)) { input_item->setForeground(0, keyframe_colors_.value(ref)); @@ -182,10 +177,10 @@ QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, NodeInput *in return input_item; } -void NodeTreeView::CreateItemsForTracks(QTreeWidgetItem *parent, NodeInput *input, int element, int track_count) +void NodeTreeView::CreateItemsForTracks(QTreeWidgetItem *parent, const NodeInput& input, int track_count) { for (int j=0; jdata(0, kItemType).toInt()) { case kItemTypeNode: { - Node* n = reinterpret_cast(item->data(0, kItemPointer).value()); + Node* n = Node::ValueToPtr(item->data(0, kItemNodePointer)); if (item->checkState(0) == Qt::Checked) { if (disabled_nodes_.contains(n)) { @@ -211,19 +206,16 @@ void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column) } case kItemTypeInput: { - NodeInput* input = reinterpret_cast(item->data(0, kItemPointer).value()); - int element = item->data(0, kItemElement).toInt(); - int track = item->data(0, kItemTrack).toInt(); - NodeInput::KeyframeTrackReference i = {input, element, track}; + NodeKeyframeTrackReference i = item->data(0, kItemInputReference).value(); if (item->checkState(0) == Qt::Checked) { if (disabled_inputs_.contains(i)) { disabled_inputs_.removeOne(i); - emit InputEnableChanged(input, element, track, true); + emit InputEnableChanged(i, true); } } else if (!disabled_inputs_.contains(i)) { disabled_inputs_.append(i); - emit InputEnableChanged(input, element, track, false); + emit InputEnableChanged(i, false); } break; } @@ -232,9 +224,7 @@ void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column) void NodeTreeView::SelectionChanged() { - NodeInput::KeyframeTrackReference ref = GetSelectedInput(); - - emit InputSelectionChanged(ref.input, ref.element, ref.track); + emit InputSelectionChanged(GetSelectedInput()); } } diff --git a/app/widget/nodetreeview/nodetreeview.h b/app/widget/nodetreeview/nodetreeview.h index 32feb058f..6b66d15d4 100644 --- a/app/widget/nodetreeview/nodetreeview.h +++ b/app/widget/nodetreeview/nodetreeview.h @@ -15,9 +15,9 @@ public: bool IsNodeEnabled(Node* n) const; - bool IsInputEnabled(NodeInput* i, int element, int track) const; + bool IsInputEnabled(const NodeKeyframeTrackReference& ref) const; - void SetKeyframeTrackColor(const NodeInput::KeyframeTrackReference& ref, const QColor& color); + void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color); void SetOnlyShowKeyframable(bool e) { @@ -35,11 +35,11 @@ public slots: signals: void NodeEnableChanged(Node* n, bool e); - void InputEnableChanged(NodeInput* i, int element, int track, bool e); + void InputEnableChanged(const NodeKeyframeTrackReference& ref, bool e); - void InputSelectionChanged(NodeInput* input, int element, int track); + void InputSelectionChanged(const NodeKeyframeTrackReference& ref); - void InputDoubleClicked(NodeInput* input, int element, int track); + void InputDoubleClicked(const NodeKeyframeTrackReference& ref); protected: virtual void changeEvent(QEvent* e) override; @@ -49,11 +49,11 @@ protected: private: void Retranslate(); - NodeInput::KeyframeTrackReference GetSelectedInput(); + NodeKeyframeTrackReference GetSelectedInput(); - QTreeWidgetItem *CreateItem(QTreeWidgetItem* parent, NodeInput* input, int element, int track); + QTreeWidgetItem *CreateItem(QTreeWidgetItem* parent, const NodeKeyframeTrackReference& ref); - void CreateItemsForTracks(QTreeWidgetItem* parent, NodeInput* input, int element, int track_count); + void CreateItemsForTracks(QTreeWidgetItem* parent, const NodeInput& input, int track_count); enum ItemType { kItemTypeNode, @@ -61,23 +61,22 @@ private: }; static const int kItemType = Qt::UserRole; - static const int kItemPointer = Qt::UserRole + 1; - static const int kItemElement = Qt::UserRole + 2; - static const int kItemTrack = Qt::UserRole + 3; + static const int kItemInputReference = Qt::UserRole + 1; + static const int kItemNodePointer = Qt::UserRole + 1; QVector nodes_; QVector disabled_nodes_; - QVector disabled_inputs_; + QVector disabled_inputs_; - QHash item_map_; + QHash item_map_; bool only_show_keyframable_; bool show_keyframe_tracks_as_rows_; - QHash keyframe_colors_; + QHash keyframe_colors_; private slots: void ItemCheckStateChanged(QTreeWidgetItem* item, int column); diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 46ad9fe91..0124e563a 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -40,7 +40,6 @@ NodeView::NodeView(QWidget *parent) : drop_edge_(nullptr), create_edge_(nullptr), create_edge_dst_(nullptr), - create_edge_dst_input_(nullptr), create_edge_dst_temp_expanded_(false), filter_mode_(kFilterShowSelectedBlocks), scale_(1.0) @@ -103,10 +102,8 @@ void NodeView::SetGraph(NodeGraph *graph) } foreach (Node* n, graph_->nodes()) { - foreach (NodeInput* input, n->parameters()) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - scene_.AddEdge(it->second, input, it->first); - } + for (auto it=n->input_connections().cbegin(); it!=n->input_connections().cend(); it++) { + scene_.AddEdge(it->second, it->first); } } } @@ -124,7 +121,7 @@ void NodeView::DeleteSelected() QVector selected_edges = scene_.GetSelectedEdges(); foreach (NodeViewEdge* edge, selected_edges) { - command->add_child(new NodeEdgeRemoveCommand(edge->output(), edge->input(), edge->element())); + command->add_child(new NodeEdgeRemoveCommand(edge->output(), edge->input())); } } @@ -426,18 +423,18 @@ void NodeView::mouseMoveEvent(QMouseEvent *event) if (highlight_index >= 0) { create_edge_dst_input_ = create_edge_dst_->GetInputAtIndex(highlight_index); - create_edge_->SetPoints(create_edge_src_->GetOutputPoint(), - create_edge_dst_->GetInputPoint(highlight_index, create_edge_src_->pos()), + create_edge_->SetPoints(create_edge_src_->GetOutputPoint(Node::kDefaultOutput), + create_edge_dst_->GetInputPoint(create_edge_dst_input_.input(), create_edge_dst_input_.element(), create_edge_src_->pos()), true); } else { - create_edge_dst_input_ = nullptr; - create_edge_->SetPoints(create_edge_src_->GetOutputPoint(), + create_edge_dst_input_.Reset(); + create_edge_->SetPoints(create_edge_src_->GetOutputPoint(Node::kDefaultOutput), scene_pt, false); } // Set connected to whether we have a valid input destination - create_edge_->SetConnected(create_edge_dst_input_); + create_edge_->SetConnected(create_edge_dst_input_.IsValid()); return; } @@ -466,20 +463,22 @@ void NodeView::mouseMoveEvent(QMouseEvent *event) new_drop_edge = dynamic_cast(item); if (new_drop_edge) { - drop_input_ = nullptr; + drop_input_.Reset(); - foreach (NodeInput* input, attached_node->parameters()) { - if (input->IsConnectable()) { - if (input->GetDataType() == new_drop_edge->input()->GetDataType()) { - drop_input_ = input; + foreach (const QString& input, attached_node->inputs()) { + NodeInput i(attached_node, input); + + if (attached_node->IsInputConnectable(input)) { + if (attached_node->GetInputDataType(input) == new_drop_edge->input().GetDataType()) { + drop_input_ = i; break; - } else if (!drop_input_) { - drop_input_ = input; + } else if (!drop_input_.IsValid()) { + drop_input_ = i; } } } - if (drop_input_) { + if (drop_input_.IsValid()) { break; } else { new_drop_edge = nullptr; @@ -519,10 +518,10 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) create_edge_dst_->SetExpanded(false); } - if (create_edge_dst_input_) { + if (create_edge_dst_input_.IsValid()) { // Make connection - Core::instance()->undo_stack()->push(new NodeEdgeAddCommand(create_edge_src_->GetNode(), create_edge_dst_input_, -1)); - create_edge_dst_input_ = nullptr; + Core::instance()->undo_stack()->push(new NodeEdgeAddCommand(create_edge_src_->GetNode(), create_edge_dst_input_)); + create_edge_dst_input_.Reset(); } create_edge_dst_ = nullptr; @@ -539,11 +538,11 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) MultiUndoCommand* command = new MultiUndoCommand(); // Remove old edge - command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input(), drop_edge_->element())); + command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input())); // Place new edges - command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_, -1)); - command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input(), drop_edge_->element())); + command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_)); + command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input())); Core::instance()->undo_stack()->push(command); } diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index a6247450f..7b3982d41 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -107,12 +107,12 @@ private: QList attached_items_; NodeViewEdge* drop_edge_; - NodeInput* drop_input_; + NodeInput drop_input_; NodeViewEdge* create_edge_; NodeViewItem* create_edge_src_; NodeViewItem* create_edge_dst_; - NodeInput* create_edge_dst_input_; + NodeInput create_edge_dst_input_; bool create_edge_dst_temp_expanded_; NodeViewScene scene_; diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp index a52cb762b..34a5dc443 100644 --- a/app/widget/nodeview/nodeviewedge.cpp +++ b/app/widget/nodeview/nodeviewedge.cpp @@ -33,13 +33,12 @@ namespace olive { -NodeViewEdge::NodeViewEdge(Node* output, NodeInput *input, int element, +NodeViewEdge::NodeViewEdge(const NodeOutput &output, const NodeInput &input, NodeViewItem* from_item, NodeViewItem* to_item, QGraphicsItem* parent) : QGraphicsPathItem(parent), output_(output), input_(input), - element_(element), from_item_(from_item), to_item_(to_item) { @@ -56,8 +55,8 @@ NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) : void NodeViewEdge::Adjust() { // Draw a line between the two - SetPoints(from_item()->GetOutputPoint(), - to_item()->GetInputPoint(input_, from_item()->pos()), + SetPoints(from_item()->GetOutputPoint(output_.output()), + to_item()->GetInputPoint(input_.input(), input_.element(), from_item()->pos()), to_item()->IsExpanded()); } diff --git a/app/widget/nodeview/nodeviewedge.h b/app/widget/nodeview/nodeviewedge.h index 43dc1fdc6..9e19be610 100644 --- a/app/widget/nodeview/nodeviewedge.h +++ b/app/widget/nodeview/nodeviewedge.h @@ -39,18 +39,18 @@ class NodeViewItem; class NodeViewEdge : public QGraphicsPathItem { public: - NodeViewEdge(Node* output, NodeInput *input, int element, + NodeViewEdge(const NodeOutput& output, const NodeInput& input, NodeViewItem* from_item, NodeViewItem* to_item, QGraphicsItem* parent = nullptr); NodeViewEdge(QGraphicsItem* parent = nullptr); - Node* output() const + const NodeOutput& output() const { return output_; } - NodeInput* input() const + const NodeInput& input() const { return input_; } @@ -112,9 +112,9 @@ protected: private: void Init(); - Node* output_; + NodeOutput output_; - NodeInput* input_; + NodeInput input_; int element_; diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index cf6b8617e..9d47445a0 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -187,8 +187,8 @@ void NodeViewItem::SetNode(Node *n) if (node_) { node_->Retranslate(); - foreach (NodeInput* input, node_->parameters()) { - if (input->IsConnectable()) { + foreach (const QString& input, node_->inputs()) { + if (node_->IsInputConnectable(input)) { node_inputs_.append(input); } } @@ -258,7 +258,7 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti painter->fillRect(input_rect, highlight_col); } - painter->drawText(input_rect, Qt::AlignCenter, node_inputs_.at(i)->name()); + painter->drawText(input_rect, Qt::AlignCenter, node_->GetInputName(node_inputs_.at(i))); } } @@ -430,17 +430,12 @@ QRectF NodeViewItem::GetInputRect(int index) const return r; } -QPointF NodeViewItem::GetInputPoint(NodeInput *input, const QPointF& source_pos) const +QPointF NodeViewItem::GetInputPoint(const QString &input, int element, const QPointF& source_pos) const { - return GetInputPoint(node_inputs_.indexOf(input), source_pos); + return pos() + GetInputPointInternal(node_inputs_.indexOf(input), source_pos); } -QPointF NodeViewItem::GetInputPoint(int input, const QPointF &source_pos) const -{ - return pos() + GetInputPointInternal(input, source_pos); -} - -QPointF NodeViewItem::GetOutputPoint() const +QPointF NodeViewItem::GetOutputPoint(const QString& output) const { switch (flow_dir_) { case NodeViewCommon::kLeftToRight: diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h index 349dfdff9..3e219cf78 100644 --- a/app/widget/nodeview/nodeviewitem.h +++ b/app/widget/nodeview/nodeviewitem.h @@ -78,10 +78,9 @@ public: /** * @brief Returns GLOBAL point that edges should connect to for any NodeParam member of this object */ - QPointF GetInputPoint(NodeInput* input, const QPointF &source_pos) const; - QPointF GetInputPoint(int input, const QPointF &source_pos) const; + QPointF GetInputPoint(const QString& input, int element, const QPointF &source_pos) const; - QPointF GetOutputPoint() const; + QPointF GetOutputPoint(const QString &output) const; /** * @brief Sets the direction nodes are flowing @@ -105,9 +104,9 @@ public: int GetIndexAt(QPointF pt) const; - NodeInput* GetInputAtIndex(int index) const + NodeInput GetInputAtIndex(int index) const { - return node_inputs_.at(index); + return NodeInput(node_, node_inputs_.at(index)); } void SetHighlightedIndex(int index); @@ -143,7 +142,7 @@ private: /** * @brief Cached list of node inputs */ - QList node_inputs_; + QVector node_inputs_; /** * @brief Rectangle of the Node's title bar (equal to rect() when collapsed) diff --git a/app/widget/nodeview/nodeviewscene.cpp b/app/widget/nodeview/nodeviewscene.cpp index 67dc1d73c..c71309c1c 100644 --- a/app/widget/nodeview/nodeviewscene.cpp +++ b/app/widget/nodeview/nodeviewscene.cpp @@ -98,10 +98,10 @@ NodeViewItem *NodeViewScene::NodeToUIObject(Node *n) return item_map_.value(n); } -NodeViewEdge *NodeViewScene::EdgeToUIObject(Node* output, NodeInput* input, int element) +NodeViewEdge *NodeViewScene::EdgeToUIObject(const NodeOutput& output, const NodeInput& input) { foreach (NodeViewEdge* edge, edges_) { - if (edge->output() == output && edge->input() == input && edge->element() == element) { + if (edge->output() == output && edge->input() == input) { return edge; } } @@ -174,14 +174,14 @@ void NodeViewScene::RemoveNode(Node *node) delete item_map_.take(node); } -void NodeViewScene::AddEdge(Node* output, NodeInput* input, int element) +void NodeViewScene::AddEdge(const NodeOutput &output, const NodeInput &input) { - AddEdgeInternal(output, input, element, NodeToUIObject(output), NodeToUIObject(input->parent())); + AddEdgeInternal(output, input, NodeToUIObject(output.node()), NodeToUIObject(input.node())); } -void NodeViewScene::RemoveEdge(Node* output, NodeInput* input, int element) +void NodeViewScene::RemoveEdge(const NodeOutput &output, const NodeInput &input) { - NodeViewEdge* edge = EdgeToUIObject(output, input, element); + NodeViewEdge* edge = EdgeToUIObject(output, input); edge->from_item()->RemoveEdge(edge); edge->to_item()->RemoveEdge(edge); edges_.removeOne(edge); @@ -203,9 +203,9 @@ int NodeViewScene::DetermineWeight(Node *n) return qMax(1, weight); } -void NodeViewScene::AddEdgeInternal(Node *output, NodeInput *input, int element, NodeViewItem *from, NodeViewItem *to) +void NodeViewScene::AddEdgeInternal(const NodeOutput& output, const NodeInput& input, NodeViewItem *from, NodeViewItem *to) { - NodeViewEdge* edge_ui = new NodeViewEdge(output, input, element, from, to); + NodeViewEdge* edge_ui = new NodeViewEdge(output, input, from, to); edge_ui->SetFlowDirection(direction_); edge_ui->SetCurved(curved_edges_); diff --git a/app/widget/nodeview/nodeviewscene.h b/app/widget/nodeview/nodeviewscene.h index d8362d4f2..d1b886507 100644 --- a/app/widget/nodeview/nodeviewscene.h +++ b/app/widget/nodeview/nodeviewscene.h @@ -53,7 +53,7 @@ public: * in this view/scene), this function returns nullptr. */ NodeViewItem* NodeToUIObject(Node* n); - NodeViewEdge *EdgeToUIObject(Node* output, NodeInput *input, int element); + NodeViewEdge *EdgeToUIObject(const NodeOutput &output, const NodeInput &input); QVector GetSelectedNodes() const; QVector GetSelectedItems() const; @@ -98,8 +98,8 @@ public slots: */ void RemoveNode(Node* node); - void AddEdge(Node* output, NodeInput* input, int element); - void RemoveEdge(Node* output, NodeInput* input, int element); + void AddEdge(const NodeOutput& output, const NodeInput& input); + void RemoveEdge(const NodeOutput& output, const NodeInput& input); /** * @brief Set whether edges in this scene should be curved or not @@ -109,7 +109,7 @@ public slots: private: static int DetermineWeight(Node* n); - void AddEdgeInternal(Node* output, NodeInput* input, int element, NodeViewItem* from, NodeViewItem* to); + void AddEdgeInternal(const NodeOutput &output, const NodeInput &input, NodeViewItem* from, NodeViewItem* to); QHash item_map_; diff --git a/app/widget/nodeview/nodeviewundo.cpp b/app/widget/nodeview/nodeviewundo.cpp index 9e57694e9..1ac1b234c 100644 --- a/app/widget/nodeview/nodeviewundo.cpp +++ b/app/widget/nodeview/nodeviewundo.cpp @@ -25,10 +25,9 @@ namespace olive { -NodeEdgeAddCommand::NodeEdgeAddCommand(Node *output, NodeInput *input, int element) : +NodeEdgeAddCommand::NodeEdgeAddCommand(const NodeOutput &output, const NodeInput &input) : output_(output), input_(input), - element_(element), remove_command_(nullptr) { } @@ -40,22 +39,20 @@ NodeEdgeAddCommand::~NodeEdgeAddCommand() void NodeEdgeAddCommand::redo() { - if (input_->IsConnected(element_)) { + if (input_.IsConnected()) { if (!remove_command_) { - remove_command_ = new NodeEdgeRemoveCommand(input_->GetConnectedNode(element_), - input_, - element_); + remove_command_ = new NodeEdgeRemoveCommand(input_.GetConnectedOutput(), input_); } remove_command_->redo(); } - Node::ConnectEdge(output_, input_, element_); + Node::ConnectEdge(output_, input_); } void NodeEdgeAddCommand::undo() { - Node::DisconnectEdge(output_, input_, element_); + Node::DisconnectEdge(output_, input_); if (remove_command_) { remove_command_->undo(); @@ -64,29 +61,28 @@ void NodeEdgeAddCommand::undo() Project *NodeEdgeAddCommand::GetRelevantProject() const { - return output_->parent()->project(); + return output_.node()->parent()->project(); } -NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(Node *output, NodeInput *input, int element) : +NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(const NodeOutput &output, const NodeInput &input) : output_(output), - input_(input), - element_(element) + input_(input) { } void NodeEdgeRemoveCommand::redo() { - Node::DisconnectEdge(output_, input_, element_); + Node::DisconnectEdge(output_, input_); } void NodeEdgeRemoveCommand::undo() { - Node::ConnectEdge(output_, input_, element_); + Node::ConnectEdge(output_, input_); } Project *NodeEdgeRemoveCommand::GetRelevantProject() const { - return output_->parent()->project(); + return output_.node()->parent()->project(); } NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node) : @@ -134,14 +130,12 @@ void NodeRemoveAndDisconnectCommand::prep() } // Disconnect everything - foreach (const Node::InputConnection& conn, node_->edges()) { - command_->add_child(new NodeEdgeRemoveCommand(node_, conn.input, conn.element)); + for (auto it=node_->input_connections().cbegin(); it!=node_->input_connections().cend(); it++) { + command_->add_child(new NodeEdgeRemoveCommand(it->second, it->first)); } - foreach (NodeInput* input, node_->inputs()) { - for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - command_->add_child(new NodeEdgeRemoveCommand(it->second, input, it->first)); - } + for (const Node::OutputConnection& conn : node_->output_connections()) { + command_->add_child(new NodeEdgeRemoveCommand(conn.first, conn.second)); } } diff --git a/app/widget/nodeview/nodeviewundo.h b/app/widget/nodeview/nodeviewundo.h index 19c117654..90ce7eb43 100644 --- a/app/widget/nodeview/nodeviewundo.h +++ b/app/widget/nodeview/nodeviewundo.h @@ -34,7 +34,7 @@ namespace olive { */ class NodeEdgeRemoveCommand : public UndoCommand { public: - NodeEdgeRemoveCommand(Node* output, NodeInput* input, int element); + NodeEdgeRemoveCommand(const NodeOutput& output, const NodeInput& input); virtual Project* GetRelevantProject() const override; @@ -42,9 +42,8 @@ public: virtual void undo() override; private: - Node* output_; - NodeInput* input_; - int element_; + NodeOutput output_; + NodeInput input_; }; @@ -55,7 +54,7 @@ private: */ class NodeEdgeAddCommand : public UndoCommand { public: - NodeEdgeAddCommand(Node* output, NodeInput* input, int element); + NodeEdgeAddCommand(const NodeOutput& output, const NodeInput& input); virtual ~NodeEdgeAddCommand() override; @@ -65,9 +64,8 @@ public: virtual void undo() override; private: - Node* output_; - NodeInput* input_; - int element_; + NodeOutput output_; + NodeInput input_; NodeEdgeRemoveCommand* remove_command_; diff --git a/app/widget/timelinewidget/timelineundo.h b/app/widget/timelinewidget/timelineundo.h index cf2d328c0..ad0921c79 100644 --- a/app/widget/timelinewidget/timelineundo.h +++ b/app/widget/timelinewidget/timelineundo.h @@ -41,7 +41,7 @@ namespace olive { inline bool NodeCanBeRemoved(Node* n) { //return n->edges().isEmpty() && n->GetExclusiveDependencies().isEmpty(); - return n->edges().empty(); + return n->output_connections().empty(); } inline UndoCommand* CreateRemoveCommand(Node* n) @@ -234,7 +234,7 @@ public: invalidate_range = block_->range(); } - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } virtual void undo() override @@ -294,7 +294,7 @@ public: track_->EndOperation(); - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } private: @@ -578,13 +578,13 @@ public: track->InsertBlockAfter(new_block(), block_); // If the block had an out transition, we move it to the new block - moved_transition_ = nullptr; + moved_transition_ = NodeInput(); TransitionBlock* potential_transition = dynamic_cast(new_block()->next()); if (potential_transition) { - foreach (const Node::InputConnection& conn, block_->edges()) { - if (conn.input->parent() == potential_transition) { - moved_transition_ = potential_transition->out_block_input(); + for (const Node::OutputConnection& output : block_->output_connections()) { + if (output.second.node() == potential_transition) { + moved_transition_ = NodeInput(potential_transition, TransitionBlock::kOutBlockInput); Node::DisconnectEdge(block_, moved_transition_); Node::ConnectEdge(new_block(), moved_transition_); break; @@ -601,9 +601,9 @@ public: track->BeginOperation(); - if (moved_transition_) { + if (moved_transition_.IsValid()) { Node::DisconnectEdge(new_block(), moved_transition_); - Node::DisconnectEdge(block_, moved_transition_); + Node::ConnectEdge(block_, moved_transition_); } block_->set_length_and_media_out(old_length_); @@ -630,7 +630,7 @@ private: UndoCommand* reconnect_tree_command_; - NodeInput* moved_transition_; + NodeInput moved_transition_; QVector src_nodes_; QVector added_nodes_; @@ -1144,8 +1144,7 @@ public: TrackListRippleToolCommand(TrackList* track_list, const QHash& info, const rational& ripple_movement, - const Timeline::MovementMode& movement_mode, - UndoCommand* parent = nullptr) : + const Timeline::MovementMode& movement_mode) : track_list_(track_list), info_(info), ripple_movement_(ripple_movement), @@ -1356,23 +1355,20 @@ private: class TimelineAddTrackCommand : public UndoCommand { public: TimelineAddTrackCommand(TrackList *timeline) : - timeline_(timeline), - direct_(nullptr) + timeline_(timeline) { track_ = new Track(); track_->setParent(&memory_manager_); if (timeline->GetTrackCount() > 0 && Config::Current()[QStringLiteral("AutoMergeTracks")].toBool()) { if (timeline_->type() == Track::kVideo) { - MergeNode* merge = new MergeNode(); - base_ = merge->base_in(); - blend_ = merge->blend_in(); - merge_ = merge; + merge_ = new MergeNode(); + base_ = NodeInput(merge_, MergeNode::kBaseIn); + blend_ = NodeInput(merge_, MergeNode::kBlendIn); } else { - MathNode* math = new MathNode(); - base_ = math->param_a_in(); - blend_ = math->param_b_in(); - merge_ = math; + merge_ = new MathNode(); + base_ = NodeInput(merge_, MathNode::kParamAIn); + blend_ = NodeInput(merge_, MathNode::kParamBIn); } merge_->setParent(&memory_manager_); } else { @@ -1394,8 +1390,8 @@ public: { // Add track track_->setParent(timeline_->GetParentGraph()); - timeline_->track_input()->ArrayAppend(); - Node::ConnectEdge(track_, timeline_->track_input(), timeline_->track_input()->ArraySize() - 1); + timeline_->ArrayAppend(); + Node::ConnectEdge(track_, timeline_->track_input(timeline_->ArraySize() - 1)); // Add merge if applicable if (merge_) { @@ -1404,11 +1400,14 @@ public: Track* last_track = timeline_->GetTrackAt(timeline_->GetTrackCount()-2); // Whatever this track used to be connected to, connect the merge instead - std::vector edges = last_track->edges(); - foreach (const Node::InputConnection& ic, edges) { - if (ic.input != timeline_->track_input()) { - Node::DisconnectEdge(last_track, ic.input, ic.element); - Node::ConnectEdge(merge_, ic.input, ic.element); + const Node::OutputConnections edges = last_track->output_connections(); + for (const Node::OutputConnection& ic : edges) { + const NodeInput& i = ic.second; + + // Ignore the track input, but funnel everything else through our merge + if (i.node() != timeline_->parent() && i.input() != timeline_->track_input()) { + Node::DisconnectEdge(last_track, i); + Node::ConnectEdge(merge_, i); } } @@ -1417,18 +1416,20 @@ public: Node::ConnectEdge(last_track, base_); } else if (timeline_->GetTrackCount() == 1) { // If this was the first track we added, - NodeInput* relevant_input; + QString relevant_input; if (timeline_->type() == Track::kVideo) { - relevant_input = timeline_->parent()->texture_input(); + relevant_input = ViewerOutput::kTextureInput; } else { - relevant_input = timeline_->parent()->samples_input(); + relevant_input = ViewerOutput::kSamplesInput; } - if (!relevant_input->IsConnected()) { - Node::ConnectEdge(track_, relevant_input); + if (!timeline_->parent()->IsInputConnected(relevant_input)) { + direct_ = NodeInput(timeline_->parent(), relevant_input); - direct_ = relevant_input; + Node::ConnectEdge(track_, direct_); + } else { + direct_ = NodeInput(); } } } @@ -1443,20 +1444,23 @@ public: Node::DisconnectEdge(track_, blend_); Node::DisconnectEdge(last_track, base_); - std::vector edges = merge_->edges(); - foreach (const Node::InputConnection& ic, edges) { - Node::DisconnectEdge(merge_, ic.input, ic.element); - Node::ConnectEdge(last_track, ic.input, ic.element); + // Make copy of edges since the node's internal array will change as we disconnect things + const Node::OutputConnections edges = merge_->output_connections(); + for (const Node::OutputConnection& ic : edges) { + const NodeInput& i = ic.second; + + Node::DisconnectEdge(merge_, i); + Node::ConnectEdge(last_track, i); } merge_->setParent(&memory_manager_); - } else if (direct_) { + } else if (direct_.IsValid()) { Node::DisconnectEdge(track_, direct_); } // Remove track - Node::DisconnectEdge(track_, timeline_->track_input(), timeline_->track_input()->ArraySize() - 1); - timeline_->track_input()->ArrayRemoveLast(); + Node::DisconnectEdge(track_, timeline_->track_input(timeline_->ArraySize() - 1)); + timeline_->ArrayRemoveLast(); track_->setParent(&memory_manager_); } @@ -1465,10 +1469,10 @@ private: Track* track_; Node* merge_; - NodeInput* base_; - NodeInput* blend_; + NodeInput base_; + NodeInput blend_; - NodeInput* direct_; + NodeInput direct_; QObject memory_manager_; @@ -1705,7 +1709,7 @@ public: track_->EndOperation(); - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } virtual void undo() override @@ -1762,7 +1766,7 @@ public: track_->EndOperation(); - track_->InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input()); + track_->InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput); } private: @@ -2047,7 +2051,7 @@ public: 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_->InvalidateCache(invalidate_range, Track::kBlockInput); } virtual void undo() override @@ -2087,7 +2091,7 @@ public: 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_->InvalidateCache(invalidate_range, Track::kBlockInput); } private: @@ -2315,7 +2319,7 @@ private: class TransitionRemoveCommand : public UndoCommand { public: - TransitionRemoveCommand(TransitionBlock* block, UndoCommand *parent = nullptr) : + TransitionRemoveCommand(TransitionBlock* block) : block_(block) { } @@ -2346,18 +2350,18 @@ public: } if (in_block_) { - Node::DisconnectEdge(in_block_, block_->in_block_input()); + Node::DisconnectEdge(in_block_, NodeInput(block_, TransitionBlock::kInBlockInput)); } if (out_block_) { - Node::DisconnectEdge(out_block_, block_->out_block_input()); + Node::DisconnectEdge(out_block_, NodeInput(block_, TransitionBlock::kOutBlockInput)); } track_->RippleRemoveBlock(block_); track_->EndOperation(); - track_->InvalidateCache(invalidate_range, track_->block_input()); + track_->InvalidateCache(invalidate_range, Track::kBlockInput); } virtual void undo() override @@ -2371,11 +2375,11 @@ public: } if (in_block_) { - Node::ConnectEdge(in_block_, block_->in_block_input()); + Node::ConnectEdge(in_block_, NodeInput(block_, TransitionBlock::kInBlockInput)); } if (out_block_) { - Node::ConnectEdge(out_block_, block_->out_block_input()); + Node::ConnectEdge(out_block_, NodeInput(block_, TransitionBlock::kOutBlockInput)); } // These if statements must be separated because in_offset and out_offset report different things @@ -2391,7 +2395,7 @@ public: track_->EndOperation(); - track_->InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input()); + track_->InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput); } private: diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index 89b2992e7..44776322e 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -118,7 +118,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) command->add_child(new NodeAddCommand(graph, solid)); - command->add_child(new NodeEdgeAddCommand(solid, clip->texture_input(), -1)); + command->add_child(new NodeEdgeAddCommand(solid, NodeInput(clip, ClipBlock::kBufferIn))); break; } case olive::Tool::kAddableTitle: @@ -128,7 +128,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) command->add_child(new NodeAddCommand(graph, text)); - command->add_child(new NodeEdgeAddCommand(text, clip->texture_input(), -1)); + command->add_child(new NodeEdgeAddCommand(text, NodeInput(clip, ClipBlock::kBufferIn))); break; } case olive::Tool::kAddableBars: diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 9a1c4b483..a3783568c 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -434,8 +434,8 @@ void ImportTool::DropGhosts(bool insert) TransformDistortNode* transform = new TransformDistortNode(); command->add_child(new NodeAddCommand(dst_graph, transform)); - command->add_child(new NodeEdgeAddCommand(video_input, transform->texture_input(), -1)); - command->add_child(new NodeEdgeAddCommand(transform, clip->texture_input(), -1)); + command->add_child(new NodeEdgeAddCommand(video_input, NodeInput(transform, TransformDistortNode::kTextureInput))); + command->add_child(new NodeEdgeAddCommand(transform, NodeInput(clip, ClipBlock::kBufferIn))); break; } case Stream::kAudio: @@ -447,8 +447,8 @@ void ImportTool::DropGhosts(bool insert) VolumeNode* volume_node = new VolumeNode(); command->add_child(new NodeAddCommand(dst_graph, volume_node)); - command->add_child(new NodeEdgeAddCommand(audio_input, volume_node->samples_input(), -1)); - command->add_child(new NodeEdgeAddCommand(volume_node, clip->texture_input(), -1)); + command->add_child(new NodeEdgeAddCommand(audio_input, NodeInput(volume_node, VolumeNode::kSamplesInput))); + command->add_child(new NodeEdgeAddCommand(volume_node, NodeInput(clip, ClipBlock::kBufferIn))); break; } default: diff --git a/app/widget/timelinewidget/tool/import.h b/app/widget/timelinewidget/tool/import.h index 40c653cf9..38f22448d 100644 --- a/app/widget/timelinewidget/tool/import.h +++ b/app/widget/timelinewidget/tool/import.h @@ -37,6 +37,12 @@ public: class DraggedFootage { public: + DraggedFootage() : + footage_(nullptr), + streams_(0) + { + } + DraggedFootage(Footage* f, quint64 streams) : footage_(f), streams_(streams) diff --git a/app/widget/timelinewidget/tool/transition.cpp b/app/widget/timelinewidget/tool/transition.cpp index 541f2707a..0c8bb52bf 100644 --- a/app/widget/timelinewidget/tool/transition.cpp +++ b/app/widget/timelinewidget/tool/transition.cpp @@ -147,28 +147,25 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) // Connect block to transition command->add_child(new NodeEdgeAddCommand(out_block, - transition->out_block_input(), - -1)); + NodeInput(transition, TransitionBlock::kOutBlockInput))); command->add_child(new NodeEdgeAddCommand(in_block, - transition->in_block_input(), - -1)); + NodeInput(transition, TransitionBlock::kInBlockInput))); } else { Block* block_to_transition = Node::ValueToPtr(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock)); - NodeInput* transition_input_to_connect; + QString transition_input_to_connect; if (ghost_->GetMode() == Timeline::kTrimIn) { transition->set_length_and_media_out(ghost_->GetAdjustedLength()); - transition_input_to_connect = transition->in_block_input(); + transition_input_to_connect = TransitionBlock::kInBlockInput; } else { transition->set_length_and_media_out(ghost_->GetAdjustedLength()); - transition_input_to_connect = transition->out_block_input(); + transition_input_to_connect = TransitionBlock::kOutBlockInput; } // Connect block to transition command->add_child(new NodeEdgeAddCommand(block_to_transition, - transition_input_to_connect, - -1)); + NodeInput(transition, transition_input_to_connect))); } Core::instance()->undo_stack()->push(command); diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 563d079e4..cea4a66e4 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -60,8 +60,8 @@ void FootageViewerWidget::SetFootage(Footage *footage) video_node_->SetStream(nullptr); audio_node_->SetStream(nullptr); - Node::DisconnectEdge(video_node_, sequence_.viewer_output()->texture_input()); - Node::DisconnectEdge(audio_node_, sequence_.viewer_output()->samples_input()); + Node::DisconnectEdge(video_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kTextureInput)); + Node::DisconnectEdge(audio_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kSamplesInput)); } footage_ = footage; @@ -98,12 +98,12 @@ void FootageViewerWidget::SetFootage(Footage *footage) if (video_stream) { video_node_->SetStream(video_stream); - Node::ConnectEdge(video_node_, sequence_.viewer_output()->texture_input()); + Node::ConnectEdge(video_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kTextureInput)); } if (audio_stream) { audio_node_->SetStream(audio_stream); - Node::ConnectEdge(audio_node_, sequence_.viewer_output()->samples_input()); + Node::ConnectEdge(audio_node_, NodeInput(sequence_.viewer_output(), ViewerOutput::kSamplesInput)); } ConnectViewerNode(sequence_.viewer_output(), footage_->project()->color_manager()); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 2f18aae2d..4d20618fc 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -178,8 +178,7 @@ 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->texture_input(), &NodeInput::InputConnected, this, &ViewerWidget::UpdateStack); - connect(n->texture_input(), &NodeInput::InputDisconnected, this, &ViewerWidget::UpdateStack); + connect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack); InterlacingChangedSlot(n->video_params().interlacing()); @@ -233,8 +232,7 @@ 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->texture_input(), &NodeInput::InputConnected, this, &ViewerWidget::UpdateStack); - disconnect(n->texture_input(), &NodeInput::InputDisconnected, this, &ViewerWidget::UpdateStack); + disconnect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack); ruler()->SetPlaybackCache(nullptr); @@ -398,8 +396,8 @@ void ViewerWidget::DecodeCachedImage(RenderTicketPtr ticket, const QString &fn, bool ViewerWidget::ShouldForceWaveform() const { return GetConnectedNode() - && !GetConnectedNode()->texture_input()->IsConnected() - && GetConnectedNode()->samples_input()->IsConnected(); + && !GetConnectedNode()->IsInputConnected(ViewerOutput::kTextureInput) + && GetConnectedNode()->IsInputConnected(ViewerOutput::kSamplesInput); } void ViewerWidget::UpdateTextureFromNode(const rational& time)