don't police data type connections between nodes

Nodes were previously written to be "strongly typed" in that a parameter's
"type" enforced whether it could be connected to another. All code related
to that has now been removed since not only is it hard to maintain and
likely unnecessary, it's possible the nodes will work differently later on
anyway.
This commit is contained in:
itsmattkc
2019-10-25 00:44:14 +11:00
parent 076f2699c5
commit 4d96ac9e39
24 changed files with 83 additions and 249 deletions
+2 -3
View File
@@ -23,15 +23,14 @@
BlendNode::BlendNode()
{
base_input_ = new NodeInput("base_in");
base_input_->add_data_input(NodeParam::kTexture);
base_input_->set_data_type(NodeParam::kTexture);
AddParameter(base_input_);
blend_input_ = new NodeInput("blend_in");
blend_input_->add_data_input(NodeParam::kTexture);
blend_input_->set_data_type(NodeParam::kTexture);
AddParameter(blend_input_);
texture_output_ = new NodeOutput("tex_out");
texture_output_->set_data_type(NodeParam::kTexture);
AddParameter(texture_output_);
}
+5 -16
View File
@@ -26,21 +26,15 @@ Block::Block() :
next_(nullptr)
{
previous_input_ = new NodeInput("prev_block");
previous_input_->add_data_input(NodeParam::kBlock);
previous_input_->set_data_type(NodeParam::kBlock);
previous_input_->set_dependent(false);
AddParameter(previous_input_);
block_output_ = new NodeOutput("block_out");
block_output_->set_data_type(NodeParam::kBlock);
AddParameter(block_output_);
texture_output_ = new NodeOutput("tex_out");
texture_output_->set_data_type(NodeParam::kTexture);
AddParameter(texture_output_);
samples_output_ = new NodeOutput("samples_out");
samples_output_->set_data_type(NodeParam::kSamples);
AddParameter(samples_output_);
buffer_output_ = new NodeOutput("buffer_out");
AddParameter(buffer_output_);
connect(this, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(EdgeAddedSlot(NodeEdgePtr)), Qt::DirectConnection);
connect(this, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(EdgeRemovedSlot(NodeEdgePtr)), Qt::DirectConnection);
@@ -158,14 +152,9 @@ void Block::Refresh()
}
}
NodeOutput *Block::texture_output()
NodeOutput *Block::buffer_output()
{
return texture_output_;
}
NodeOutput *Block::samples_output()
{
return samples_output_;
return buffer_output_;
}
NodeOutput *Block::block_output()
+2 -4
View File
@@ -59,8 +59,7 @@ public:
NodeInput* previous_input();
NodeOutput* texture_output();
NodeOutput* samples_output();
NodeOutput* buffer_output();
NodeOutput* block_output();
static void ConnectBlocks(Block* previous, Block* next);
@@ -116,8 +115,7 @@ private:
NodeInput* previous_input_;
NodeOutput* block_output_;
NodeOutput* texture_output_;
NodeOutput* samples_output_;
NodeOutput* buffer_output_;
rational in_point_;
rational out_point_;
+3 -3
View File
@@ -23,7 +23,7 @@
ClipBlock::ClipBlock()
{
texture_input_ = new NodeInput("tex_in");
texture_input_->add_data_input(NodeInput::kTexture);
texture_input_->set_data_type(NodeInput::kTexture);
AddParameter(texture_input_);
}
@@ -63,7 +63,7 @@ NodeInput *ClipBlock::texture_input()
QVariant ClipBlock::Value(NodeOutput* param, const rational& v_in, const rational &v_out)
{
if (param == texture_output()) {
if (param == buffer_output()) {
// If the time retrieved is within this block, get texture information
if (texture_input()->IsConnected() && v_in >= in() && v_out < out()) {
// Retrieve texture
@@ -98,7 +98,7 @@ QList<NodeDependency> ClipBlock::RunDependencies(NodeOutput *output, const ratio
{
QList<NodeDependency> deps;
if (output == texture_output() && texture_input_->IsConnected()) {
if (output == buffer_output() && texture_input_->IsConnected()) {
deps.append(NodeDependency(texture_input_->get_connected_output(), SequenceToMediaTime(time), SequenceToMediaTime(time)));
}
+2 -3
View File
@@ -27,18 +27,17 @@
OpacityNode::OpacityNode()
{
opacity_input_ = new NodeInput("opacity_in");
opacity_input_->add_data_input(NodeParam::kFloat);
opacity_input_->set_data_type(NodeParam::kFloat);
opacity_input_->set_value(100);
opacity_input_->set_minimum(0);
opacity_input_->set_maximum(100);
AddParameter(opacity_input_);
texture_input_ = new NodeInput("tex_in");
texture_input_->add_data_input(NodeParam::kTexture);
texture_input_->set_data_type(NodeParam::kTexture);
AddParameter(texture_input_);
texture_output_ = new NodeOutput("tex_out");
texture_output_->set_data_type(NodeParam::kTexture);
AddParameter(texture_output_);
}
+4 -5
View File
@@ -26,24 +26,23 @@
TransformDistort::TransformDistort()
{
position_input_ = new NodeInput("pos_in");
position_input_->add_data_input(NodeParam::kVec2);
position_input_->set_data_type(NodeParam::kVec2);
AddParameter(position_input_);
rotation_input_ = new NodeInput("rot_in");
rotation_input_->add_data_input(NodeParam::kFloat);
rotation_input_->set_data_type(NodeParam::kFloat);
AddParameter(rotation_input_);
scale_input_ = new NodeInput("scale_in");
scale_input_->add_data_input(NodeParam::kVec2);
scale_input_->set_data_type(NodeParam::kVec2);
scale_input_->set_value(QVector2D(100.0f, 100.0f));
AddParameter(scale_input_);
anchor_input_ = new NodeInput("anchor_in");
anchor_input_->add_data_input(NodeParam::kVec2);
anchor_input_->set_data_type(NodeParam::kVec2);
AddParameter(anchor_input_);
matrix_output_ = new NodeOutput("matrix_out");
matrix_output_->set_data_type(NodeParam::kMatrix);
AddParameter(matrix_output_);
}
+1 -2
View File
@@ -24,11 +24,10 @@ SolidGenerator::SolidGenerator() :
texture_(nullptr)
{
color_input_ = new NodeInput("color_in");
color_input_->add_data_input(NodeParam::kColor);
color_input_->set_data_type(NodeParam::kColor);
AddParameter(color_input_);
texture_output_ = new NodeOutput("tex_out");
texture_output_->set_data_type(NodeOutput::kTexture);
AddParameter(texture_output_);
}
+13 -25
View File
@@ -39,14 +39,23 @@ NodeParam::Type NodeInput::type()
return kInput;
}
void NodeInput::add_data_input(const NodeParam::DataType &data_type)
QString NodeInput::name()
{
inputs_.append(data_type);
if (name_.isEmpty()) {
return GetDefaultDataTypeName(data_type());
}
return NodeParam::name();
}
bool NodeInput::can_accept_type(const NodeParam::DataType &data_type)
const NodeParam::DataType &NodeInput::data_type()
{
return AreDataTypesCompatible(data_type, inputs_);
return data_type_;
}
void NodeInput::set_data_type(const NodeParam::DataType &type)
{
data_type_ = type;
}
NodeOutput *NodeInput::get_connected_output()
@@ -163,27 +172,6 @@ void NodeInput::set_maximum(const QVariant &max)
has_maximum_ = true;
}
NodeParam::DataType NodeInput::data_type()
{
if (IsConnected()) {
// Return the connected output's data type
return edges_.first()->output()->data_type();
}
if (inputs_.isEmpty()) {
// Safety if no inputs have been added
return kNone;
}
// Return first
return inputs_.first();
}
const QList<NodeParam::DataType> &NodeInput::inputs()
{
return inputs_;
}
void NodeInput::CopyValues(NodeInput *source, NodeInput *dest)
{
// Copy values
+7 -19
View File
@@ -47,21 +47,16 @@ public:
*/
virtual Type type() override;
/**
* @brief Add a data type that this input accepts
*
* While an input will usually only accept one data type, NodeInput supports several. Use this to add a data type that
* this input can accept.
*/
void add_data_input(const DataType& data_type);
virtual QString name() override;
/**
* @brief Return whether an input can accept a certain type based on its list of data types
* @brief The data type this parameter outputs
*
* The input checks its list of acceptable data types (added by add_data_input()) to determine whether a certain
* data type can be connected to this input.
* This can be used in conjunction with NodeInput::can_accept_type() to determine whether this parameter can be
* connected to it.
*/
bool can_accept_type(const DataType& data_type);
const DataType& data_type();
void set_data_type(const DataType& type);
/**
* @brief If this input is connected to an output, retrieve the output parameter
@@ -132,13 +127,6 @@ public:
bool has_maximum();
void set_maximum(const QVariant& max);
virtual DataType data_type() override;
/**
* @brief A list of input data types accepted by this parameter
*/
const QList<DataType>& inputs();
/**
* @brief Copy all values including keyframe information and connections from another NodeInput
*/
@@ -153,7 +141,7 @@ private:
*
* Use can_accept_type() to check if a type is in this list
*/
QList<DataType> inputs_;
DataType data_type_;
/**
* @brief Internal keyframe array
-1
View File
@@ -3,7 +3,6 @@
AudioInput::AudioInput()
{
samples_output_ = new NodeOutput("samples_out");
samples_output_->set_data_type(NodeInput::kSamples);
AddParameter(samples_output_);
}
+1 -1
View File
@@ -25,7 +25,7 @@ MediaInput::MediaInput() :
frame_(nullptr)
{
footage_input_ = new NodeInput("footage_in");
footage_input_->add_data_input(NodeInput::kFootage);
footage_input_->set_data_type(NodeInput::kFootage);
AddParameter(footage_input_);
}
+1 -2
View File
@@ -17,11 +17,10 @@ VideoInput::VideoInput() :
ocio_texture_(0)
{
matrix_input_ = new NodeInput("matrix_in");
matrix_input_->add_data_input(NodeInput::kMatrix);
matrix_input_->set_data_type(NodeInput::kMatrix);
AddParameter(matrix_input_);
texture_output_ = new NodeOutput("tex_out");
texture_output_->set_data_type(NodeOutput::kTexture);
texture_output_->SetValueCachingEnabled(false);
AddParameter(texture_output_);
}
+4 -2
View File
@@ -357,9 +357,11 @@ void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time
&& !param->IsConnected()
&& static_cast<NodeInput*>(param)->dependent()) {
// Get the value at this time
QVariant v = static_cast<NodeInput*>(param)->get_value(time);
NodeInput* input = static_cast<NodeInput*>(param);
hash->addData(NodeParam::ValueToBytes(param->data_type(), v));
QVariant v = input->get_value(time);
hash->addData(NodeParam::ValueToBytes(input->data_type(), v));
}
}
+10 -27
View File
@@ -24,7 +24,7 @@
NodeOutput::NodeOutput(const QString &id) :
NodeParam(id),
data_type_(kNone)
linked_input_(nullptr)
{
}
@@ -33,32 +33,6 @@ NodeParam::Type NodeOutput::type()
return kOutput;
}
NodeParam::DataType NodeOutput::data_type()
{
return data_type_;
}
void NodeOutput::set_data_type(const NodeParam::DataType &type)
{
if (data_type_ == type) {
return;
}
data_type_ = type;
// If this output is connected to other inputs, check if they're compatible with this new data type
if (IsConnected()) {
for (int i=0;i<edges_.size();i++) {
NodeEdgePtr edge = edges_.at(i);
if (!AreDataTypesCompatible(this, edge->input())) {
DisconnectEdge(edge);
i--;
}
}
}
}
QVariant NodeOutput::get_value(const rational& in, const rational& out)
{
mutex_.lock();
@@ -87,3 +61,12 @@ void NodeOutput::push_value(const QVariant &v, const rational &in, const rationa
out_ = out;
}
NodeInput *NodeOutput::linked_input()
{
return linked_input_;
}
void NodeOutput::set_linked_input(NodeInput *link)
{
linked_input_ = link;
}
+5 -11
View File
@@ -39,15 +39,6 @@ public:
*/
virtual Type type() override;
/**
* @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.
*/
virtual DataType data_type() override;
void set_data_type(const DataType& type);
/**
* @brief Get the value of this output at a given tie
*
@@ -65,11 +56,14 @@ public:
void push_value(const QVariant& v, const rational& in, const rational &out);
private:
DataType data_type_;
NodeInput* linked_input();
void set_linked_input(NodeInput* link);
private:
QMutex mutex_;
NodeInput* linked_input_;
};
#endif // NODEOUTPUT_H
+1 -2
View File
@@ -35,7 +35,7 @@ TimelineOutput::TimelineOutput()
for (int i=0;i<kTrackTypeCount;i++) {
// Create track input
NodeInput* track_input = new NodeInput(QString("track_in_%1").arg(i));
track_input->add_data_input(NodeParam::kTrack);
track_input->set_data_type(NodeParam::kTrack);
AddParameter(track_input);
track_inputs_.replace(i, track_input);
@@ -50,7 +50,6 @@ TimelineOutput::TimelineOutput()
}
length_output_ = new NodeOutput("length_out");
length_output_->set_data_type(NodeParam::kRational);
AddParameter(length_output_);
}
+4 -4
View File
@@ -136,13 +136,13 @@ void TrackList::AddTrack()
NodeParam::ConnectEdge(track->track_output(), current_last_track->track_input());
// FIXME: Test code only
if (current_last_track->texture_output()->IsConnected()) {
if (current_last_track->buffer_output()->IsConnected()) {
AlphaOverBlend* blend = new AlphaOverBlend();
GetParentGraph()->AddNode(blend);
NodeParam::ConnectEdge(track->texture_output(), blend->blend_input());
NodeParam::ConnectEdge(current_last_track->texture_output(), blend->base_input());
NodeParam::ConnectEdge(blend->texture_output(), current_last_track->texture_output()->edges().first()->input());
NodeParam::ConnectEdge(track->buffer_output(), blend->blend_input());
NodeParam::ConnectEdge(current_last_track->buffer_output(), blend->base_input());
NodeParam::ConnectEdge(blend->texture_output(), current_last_track->buffer_output()->edges().first()->input());
}
// End test code
}
+5 -13
View File
@@ -32,12 +32,11 @@ TrackOutput::TrackOutput() :
index_(-1)
{
track_input_ = new NodeInput("track_in");
track_input_->add_data_input(NodeParam::kTrack);
track_input_->set_data_type(NodeParam::kTrack);
track_input_->set_dependent(false);
AddParameter(track_input_);
track_output_ = new NodeOutput("track_out");
track_output_->set_data_type(NodeParam::kTrack);
AddParameter(track_output_);
}
@@ -127,11 +126,11 @@ QList<NodeDependency> TrackOutput::RunDependencies(NodeOutput* output, const rat
{
QList<NodeDependency> deps;
if (output == texture_output()) {
if (output == buffer_output()) {
ValidateCurrentBlock(time);
if (current_block_ != this) {
deps.append(NodeDependency(current_block_->texture_output(), time, time));
deps.append(NodeDependency(current_block_->buffer_output(), time, time));
}
}
@@ -197,23 +196,16 @@ QVariant TrackOutput::Value(NodeOutput *output, const rational &in, const ration
if (output == track_output_) {
// Set track output correctly
return PtrToValue(this);
} else if (output == texture_output()) {
} else if (output == buffer_output()) {
ValidateCurrentBlock(in);
if (current_block_ != this) {
// At this point, we must have found the correct block so we use its texture output to produce the image
return current_block_->texture_output()->get_value(in, out);
return current_block_->buffer_output()->get_value(in, out);
}
// No texture is valid
return 0;
} else if (output == samples_output()) {
ValidateCurrentBlock(in);
if (current_block_ != this) {
// FIXME: String together samples from blocks in this range
qDebug() << "FIXME: Implement this";
}
}
// Run default node processing
+3 -3
View File
@@ -23,15 +23,15 @@
ViewerOutput::ViewerOutput()
{
texture_input_ = new NodeInput("tex_in");
texture_input_->add_data_input(NodeInput::kTexture);
texture_input_->set_data_type(NodeInput::kTexture);
AddParameter(texture_input_);
samples_input_ = new NodeInput("samples_in");
samples_input_->add_data_input(NodeInput::kSamples);
samples_input_->set_data_type(NodeInput::kSamples);
AddParameter(samples_input_);
length_input_ = new NodeInput("length_in");
length_input_->add_data_input(NodeInput::kRational);
length_input_->set_data_type(NodeInput::kRational);
AddParameter(length_input_);
}
+1 -62
View File
@@ -48,7 +48,7 @@ const QString NodeParam::id()
QString NodeParam::name()
{
if (name_.isEmpty()) {
return GetDefaultDataTypeName(data_type());
return tr("Value");
}
return name_;
@@ -86,61 +86,6 @@ void NodeParam::DisconnectAll()
}
}
bool NodeParam::AreDataTypesCompatible(NodeParam *a, NodeParam *b)
{
// Make sure one is an input and one is an output
if (a->type() == b->type()) {
return false;
}
NodeInput* input;
NodeOutput* output;
// Work out which parameter is which
if (a->type() == NodeParam::kInput) {
input = static_cast<NodeInput*>(a);
output = static_cast<NodeOutput*>(b);
} else {
input = static_cast<NodeInput*>(b);
output = static_cast<NodeOutput*>(a);
}
return AreDataTypesCompatible(output->data_type(), input->inputs());
}
bool NodeParam::AreDataTypesCompatible(const NodeParam::DataType &output_type, const NodeParam::DataType &input_type)
{
if (input_type == output_type) {
return true;
}
if (input_type == kNone) {
return false;
}
if (input_type == kAny) {
return true;
}
// Allow for up-converting integers to floats (but not the other way around)
if (output_type == kInt && input_type == kFloat) {
return true;
}
return false;
}
bool NodeParam::AreDataTypesCompatible(const DataType &output_type, const QList<DataType>& input_types)
{
for (int i=0;i<input_types.size();i++) {
if (AreDataTypesCompatible(output_type, input_types.at(i))) {
return true;
}
}
return false;
}
NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
{
// If the input can only accept one input (the default) and has one already, disconnect it
@@ -153,12 +98,6 @@ NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
}
}
// Refuse to make a connection that is incompatible
if (!input->can_accept_type(output->data_type())) {
qWarning() << tr("Tried to make an invalid Node connection");
return nullptr;
}
// Ensure both nodes are in the same graph
if (output->parent()->parent() != input->parent()->parent()) {
qWarning() << tr("Tried to connect two nodes that aren't part of the same graph");
+6 -32
View File
@@ -132,7 +132,7 @@ public:
/**
* @brief Name of this parameter to be shown to the user
*/
QString name();
virtual QString name();
void set_name(const QString& name);
/**
@@ -165,30 +165,6 @@ public:
*/
void DisconnectAll();
/**
* @brief Determine whether two DataTypes are compatible and therefore whether two NodeParams can be connected
*
* Obviously a data type is compatible with itself, but sometimes fundamentally separate data types may still be
* allowed to connect (e.g. an integer output to a float input). This static function should be used to determine
* whether a data type is compatible with another.
*/
static bool AreDataTypesCompatible(const DataType& output_type, const DataType& input_type);
/**
* @brief Overload of AreDataTypesCompatible(const DataType& output_type, const DataType& input_type)
*
* Use this for a list of input data types (which NodeInput uses as it's possible for it to accept multiple types).
*/
static bool AreDataTypesCompatible(const DataType& output_type, const QList<DataType>& input_types);
/**
* @brief Overload of AreDataTypesCompatible(const DataType& output_type, const DataType& input_type)
*
* Convenience function for two NodeParams. Determines which is the input/output and determines whether their types
* are compatible.
*/
static bool AreDataTypesCompatible(NodeParam* a, NodeParam* b);
/**
* @brief Connect an output parameter to an input parameter
*
@@ -266,8 +242,6 @@ public:
bool ValueCachingEnabled();
void SetValueCachingEnabled(bool enabled);
virtual DataType data_type() = 0;
signals:
/**
* @brief Signal emitted when an edge is added to this parameter
@@ -307,6 +281,11 @@ protected:
*/
bool value_caching_;
/**
* @brief Internal name string
*/
QString name_;
private:
/**
* @brief Internal function for returning a value in the form of bytes
@@ -314,11 +293,6 @@ private:
template<typename T>
static QByteArray ValueToBytesInternal(const QVariant& v);
/**
* @brief Internal name string
*/
QString name_;
/**
* @brief Internal ID string
*/
+2 -2
View File
@@ -73,8 +73,8 @@ void Sequence::add_default_nodes()
AddNode(audio_track_output_);
// Connect tracks to viewer
NodeParam::ConnectEdge(video_track_output_->texture_output(), viewer_output_->texture_input());
NodeParam::ConnectEdge(audio_track_output_->samples_output(), viewer_output_->samples_input());
NodeParam::ConnectEdge(video_track_output_->buffer_output(), viewer_output_->texture_input());
NodeParam::ConnectEdge(audio_track_output_->buffer_output(), viewer_output_->samples_input());
// Connect timeline length to viewer
NodeParam::ConnectEdge(timeline_output_->length_output(), viewer_output_->length_input());
@@ -38,11 +38,6 @@ void NodeParamViewWidgetBridge::CreateWidgets()
{
NodeInput* base_input = inputs_.first();
// Return empty list if the NodeInput has no actual input data types
if (base_input->inputs().isEmpty()) {
return;
}
// We assume the first data type is the "primary" type
switch (base_input->data_type()) {
// None of these inputs have applicable UI widgets
+1 -2
View File
@@ -379,8 +379,7 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
// Get the parameter we're dragging into
NodeParam* comp_param = drop_item->node()->parameters().at(i);
if (param_hitbox.contains(drop_item->mapFromScene(event->scenePos())) // See if we're dragging inside the hitbox
&& NodeParam::AreDataTypesCompatible(drag_src_param_, comp_param)) { // Make sure the types are compatible
if (param_hitbox.contains(drop_item->mapFromScene(event->scenePos()))) { // See if we're dragging inside the hitbox
// Prevent circular dependency - check if the Node we'll be outputting to already outputs to this Node
Node* outputting_node;