groups: started revision

- Removed UUIDs since we already have ptrs
- Changed input passthroughs from map to list so they'll always be added in the same order
- Fixed group copy on duplication
- Fixed group copy on clip split
This commit is contained in:
itsmattkc
2022-04-03 19:18:39 -07:00
parent 49e622ac0f
commit afaf6bcf92
12 changed files with 174 additions and 82 deletions
+16 -24
View File
@@ -63,18 +63,23 @@ QString NodeGroup::AddInputPassthrough(const NodeInput &input, const InputFlags
Q_ASSERT(ContextContainsNode(input.node()));
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
if (it->second == input) {
// Already passing this input through
return it.key();
return it->first;
}
}
// Add input
QString id = GetGroupInputIDFromInput(input);
QString id = input.input();
int i = 2;
while (HasInputWithID(id)) {
id = QStringLiteral("%1_%2").arg(input.name(), QString::number(i));
i++;
}
AddInput(id, input.GetDataType(), input.GetDefaultValue(), input.GetFlags() | flags);
input_passthroughs_.insert(id, input);
input_passthroughs_.append({id, input});
emit InputPassthroughAdded(this, input);
@@ -83,11 +88,11 @@ QString NodeGroup::AddInputPassthrough(const NodeInput &input, const InputFlags
void NodeGroup::RemoveInputPassthrough(const NodeInput &input)
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
RemoveInput(it.key());
for (auto it=input_passthroughs_.begin(); it!=input_passthroughs_.end(); it++) {
if (it->second == input) {
RemoveInput(it->first);
emit InputPassthroughRemoved(this, it->second);
input_passthroughs_.erase(it);
emit InputPassthroughRemoved(this, it.value());
break;
}
}
@@ -102,23 +107,10 @@ void NodeGroup::SetOutputPassthrough(Node *node)
emit OutputPassthroughChanged(this, output_passthrough_);
}
QString NodeGroup::GetGroupInputIDFromInput(const NodeInput &input)
{
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(input.node()->GetUUID().toByteArray());
hash.addData(input.input().toUtf8());
hash.addData((const char*) &input.element(), sizeof(input.element()));
return QString::fromLatin1(hash.result().toHex());
}
bool NodeGroup::ContainsInputPassthrough(const NodeInput &input) const
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it.value() == input) {
if (it->second == input) {
return true;
}
}
@@ -135,7 +127,7 @@ QString NodeGroup::GetInputName(const QString &id) const
}
// Call GetInputName of passed through node, which may be another group
NodeInput pass = input_passthroughs_.value(id);
NodeInput pass = GetInputFromID(id);
return pass.node()->GetInputName(pass.input());
}
@@ -149,7 +141,7 @@ NodeInput NodeGroup::ResolveInput(NodeInput input)
bool NodeGroup::GetInner(NodeInput *input)
{
if (NodeGroup *g = dynamic_cast<NodeGroup*>(input->node())) {
const NodeInput &passthrough = g->GetInputPassthroughs().value(input->input());
const NodeInput &passthrough = g->GetInputFromID(input->input());
input->set_node(passthrough.node());
input->set_input(passthrough.input());
return true;
+27 -7
View File
@@ -52,9 +52,9 @@ public:
void SetOutputPassthrough(Node *node);
static QString GetGroupInputIDFromInput(const NodeInput &input);
const QHash<QString, NodeInput> &GetInputPassthroughs() const
using InputPassthrough = QPair<QString, NodeInput>;
using InputPassthroughs = QVector<InputPassthrough>;
const InputPassthroughs &GetInputPassthroughs() const
{
return input_passthroughs_;
}
@@ -66,15 +66,35 @@ public:
static NodeInput ResolveInput(NodeInput input);
static bool GetInner(NodeInput *input);
QString GetIDOfPassthrough(const NodeInput &input) const
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it->second == input) {
return it->first;
}
}
return QString();
}
NodeInput GetInputFromID(const QString &id) const
{
for (auto it=input_passthroughs_.cbegin(); it!=input_passthroughs_.cend(); it++) {
if (it->first == id) {
return it->second;
}
}
return NodeInput();
}
signals:
void InputPassthroughAdded(NodeGroup *group, const NodeInput &input);
void InputPassthroughAdded(olive::NodeGroup *group, const olive::NodeInput &input);
void InputPassthroughRemoved(NodeGroup *group, const NodeInput &input);
void InputPassthroughRemoved(olive::NodeGroup *group, const olive::NodeInput &input);
void OutputPassthroughChanged(NodeGroup *group, Node *output);
void OutputPassthroughChanged(olive::NodeGroup *group, olive::Node *output);
private:
QHash<QString, NodeInput> input_passthroughs_;
InputPassthroughs input_passthroughs_;
Node *output_passthrough_;
+43 -14
View File
@@ -50,7 +50,6 @@ Node::Node() :
cache_result_(false),
flags_(kNone)
{
uuid_ = QUuid::createUuid();
}
Node::~Node()
@@ -991,12 +990,44 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& cre
// Add to map
created.insert(node, copy);
// Copy values to the clone
CopyInputs(node, copy, false);
// Add it to the same graph
command->add_child(new NodeAddCommand(node->parent(), copy));
// Copy context children
const PositionMap &map = node->GetContextPositions();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add either the copy (if it exists) or the original node to the context
Node *child;
if (it.key()->IsItem()) {
child = it.key();
} else {
child = created.value(it.key());
if (!child) {
child = CopyNodeAndDependencyGraphMinusItemsInternal(created, it.key(), command);
}
}
command->add_child(new NodeSetPositionCommand(child, copy, it.value()));
}
// If this is a group, copy input and output passthroughs
if (NodeGroup *src_group = dynamic_cast<NodeGroup*>(node)) {
NodeGroup *dst_group = static_cast<NodeGroup*>(copy);
for (auto it=src_group->GetInputPassthroughs().cbegin(); it!=src_group->GetInputPassthroughs().cend(); it++) {
// This node should have been created by the context loop above
NodeInput input = it->second;
input.set_node(created.value(input.node()));
command->add_child(new NodeGroupAddInputPassthrough(dst_group, input));
}
command->add_child(new NodeGroupSetOutputPassthrough(dst_group, created.value(src_group->GetOutputPassthrough())));
}
// Copy values to the clone
command->add_child(new NodeCopyInputsCommand(node, copy, false));
// Go through input connections and copy if non-item and connect if item
for (auto it=node->input_connections_.cbegin(); it!=node->input_connections_.cend(); it++) {
NodeInput input = it->first;
@@ -1009,23 +1040,17 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& cre
} else {
// Non-item, we want to clone this too
connected_copy = created.value(connected, nullptr);
if (!connected_copy) {
connected_copy = CopyNodeAndDependencyGraphMinusItemsInternal(created, connected, command);
}
}
NodeInput copied_input(copy, input.input(), input.element());
NodeInput copied_input = input;
copied_input.set_node(copy);
command->add_child(new NodeEdgeAddCommand(connected_copy, copied_input));
command->add_child(new NodeSetValueHintCommand(copied_input, node->GetValueHintForInput(input.input(), input.element())));
}
const PositionMap &map = node->GetContextPositions();
for (auto it=map.cbegin(); it!=map.cend(); it++) {
// Add either the copy (if it exists) or the original node to the context
command->add_child(new NodeSetPositionCommand(created.value(it.key(), it.key()), copy, it.value()));
}
return copy;
}
@@ -1045,8 +1070,7 @@ Node *Node::CopyNodeInGraph(Node *node, MultiUndoCommand *command)
} else {
copy = node->copy();
command->add_child(new NodeAddCommand(static_cast<NodeGraph*>(node->parent()),
copy));
command->add_child(new NodeAddCommand(static_cast<NodeGraph*>(node->parent()), copy));
command->add_child(new NodeCopyInputsCommand(node, copy, true));
@@ -1302,6 +1326,11 @@ void Node::CopyInputs(const Node *source, Node *destination, bool include_connec
Q_ASSERT(source->id() == destination->id());
foreach (const QString& input, source->inputs()) {
// NOTE: This assert is to ensure that inputs in the source also exist in the destination, which
// they should. If they don't and you hit this assert, check if you're handling group
// passthroughs correctly.
Q_ASSERT(destination->HasInputWithID(input));
CopyInput(source, destination, input, include_connections, true);
}
-6
View File
@@ -27,7 +27,6 @@
#include <QObject>
#include <QPainter>
#include <QPointF>
#include <QUuid>
#include <QXmlStreamWriter>
#include "codec/frame.h"
@@ -121,9 +120,6 @@ public:
Project* project() const;
const QUuid &GetUUID() const {return uuid_;}
void SetUUID(const QUuid &uuid) {uuid_ = uuid;}
const uint64_t &GetFlags() const
{
return flags_;
@@ -1340,8 +1336,6 @@ private:
PositionMap context_positions_;
QUuid uuid_;
uint64_t flags_;
QVector<NodeGizmo*> gizmos_;
@@ -178,8 +178,6 @@ void ProjectSerializer210528::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), node);
} else if (reader->name() == QStringLiteral("label")) {
node->SetLabel(reader->readElementText());
} else if (reader->name() == QStringLiteral("uuid")) {
node->SetUUID(QUuid::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("color")) {
node->SetOverrideColor(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("links")) {
@@ -178,8 +178,6 @@ void ProjectSerializer210907::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), node);
} else if (reader->name() == QStringLiteral("label")) {
node->SetLabel(reader->readElementText());
} else if (reader->name() == QStringLiteral("uuid")) {
node->SetUUID(QUuid::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("color")) {
node->SetOverrideColor(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("links")) {
@@ -303,7 +303,7 @@ void ProjectSerializer211228::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
} else if (reader->name() == QStringLiteral("label")) {
node->SetLabel(reader->readElementText());
} else if (reader->name() == QStringLiteral("uuid")) {
node->SetUUID(QUuid::fromString(reader->readElementText()));
xml_node_data.node_uuids.insert(node, QUuid::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("color")) {
node->SetOverrideColor(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("links")) {
@@ -377,7 +377,6 @@ void ProjectSerializer211228::SaveNode(Node *node, QXmlStreamWriter *writer) con
{
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(node)));
writer->writeTextElement(QStringLiteral("uuid"), node->GetUUID().toString());
writer->writeTextElement(QStringLiteral("label"), node->GetLabel());
writer->writeTextElement(QStringLiteral("color"), QString::number(node->GetOverrideColor()));
@@ -844,11 +843,11 @@ void ProjectSerializer211228::SaveNodeCustom(QXmlStreamWriter *writer, Node *nod
} else if (NodeGroup *group = dynamic_cast<NodeGroup*>(node)) {
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
foreach (const NodeInput &ip, group->GetInputPassthroughs()) {
foreach (const NodeGroup::InputPassthrough &ip, group->GetInputPassthroughs()) {
writer->writeStartElement(QStringLiteral("inputpassthrough"));
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.node())));
writer->writeTextElement(QStringLiteral("input"), ip.input());
writer->writeTextElement(QStringLiteral("element"), QString::number(ip.element()));
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.second.node())));
writer->writeTextElement(QStringLiteral("input"), ip.second.input());
writer->writeTextElement(QStringLiteral("element"), QString::number(ip.second.element()));
writer->writeEndElement(); // input
}
@@ -64,6 +64,7 @@ private:
QList<BlockLink> block_links;
QVector<GroupLink> group_input_links;
QHash<NodeGroup*, quintptr> group_output_links;
QHash<Node*, QUuid> node_uuids;
};
+10 -3
View File
@@ -378,9 +378,6 @@ void PreviewAutoCacher::AddNode(Node *node)
// Add to project
copy->setParent(&copied_project_);
// Copy UUID
copy->SetUUID(node->GetUUID());
// Insert into map
InsertIntoCopyMap(node, copy);
@@ -420,6 +417,11 @@ void PreviewAutoCacher::RemoveEdge(Node *output, const NodeInput &input)
void PreviewAutoCacher::CopyValue(const NodeInput &input)
{
if (dynamic_cast<NodeGroup*>(input.node())) {
// Group nodes are just dummy nodes, no need to copy them
return;
}
// Copy all values to our graph
Node* our_input = copy_map_.value(input.node());
Node::CopyValuesOfElement(input.node(), our_input, input.input(), input.element());
@@ -427,6 +429,11 @@ void PreviewAutoCacher::CopyValue(const NodeInput &input)
void PreviewAutoCacher::CopyValueHint(const NodeInput &input)
{
if (dynamic_cast<NodeGroup*>(input.node())) {
// Group nodes are just dummy nodes, no need to copy them
return;
}
// Copy value hint to our graph
Node* our_input = copy_map_.value(input.node());
Node::ValueHint hint = input.node()->GetValueHintForInput(input.input(), input.element());
+61 -13
View File
@@ -213,20 +213,67 @@ void NodeView::Paste()
void NodeView::Duplicate()
{
if (!selected_nodes_.isEmpty()) {
Node::PositionMap map;
QVector<Node*> selected = selected_nodes_;
QVector<Node*> new_nodes;
new_nodes.resize(selected_nodes_.size());
Node::PositionMap map;
for (int i=0; i<new_nodes.size(); i++) {
Node *og = selected_nodes_.at(i);
Node *copy = og->copy();
Node::CopyInputs(og, copy, false);
map.insert(copy, GetAssumedPositionForSelectedNode(og));
new_nodes[i] = copy;
new_nodes.resize(selected.size());
// Create copies of each selected node, checking for groups and adding children if necessary
for (int i=0; i<selected.size(); i++) {
new_nodes[i] = selected.at(i)->copy();
if (NodeGroup *g = dynamic_cast<NodeGroup*>(selected.at(i))) {
for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) {
if (!selected.contains(it.key())) {
// This should automatically recurse if this is a group inside a group
selected.append(it.key());
}
}
new_nodes.resize(selected.size());
}
}
Node::CopyDependencyGraph(selected_nodes_, new_nodes, nullptr);
// Get positions in contexts, add input passthroughs, and copy input values/keyframes
for (int i=0; i<new_nodes.size(); i++) {
Node *og = selected.at(i);
Node *copy = new_nodes.at(i);
Node::Position pos;
if (GetAssumedPositionForSelectedNode(og, &pos)) {
map.insert(copy, pos);
}
for (auto it=og->GetContextPositions().cbegin(); it!=og->GetContextPositions().cend(); it++) {
Node *child_og = it.key();
int child_index = selected.indexOf(child_og);
if (child_index != -1) {
Node *child_copy = new_nodes.at(child_index);
copy->SetNodePositionInContext(child_copy, it.value());
}
}
if (NodeGroup *src_group = dynamic_cast<NodeGroup*>(og)) {
NodeGroup *dst_group = static_cast<NodeGroup*>(copy);
for (auto it=src_group->GetInputPassthroughs().cbegin(); it!=src_group->GetInputPassthroughs().cend(); it++) {
NodeInput input = it->second;
input.set_node(new_nodes.at(selected.indexOf(input.node())));
dst_group->AddInputPassthrough(input);
}
dst_group->SetOutputPassthrough(new_nodes.at(selected.indexOf(src_group->GetOutputPassthrough())));
}
Node::CopyInputs(selected.at(i), new_nodes.at(i), false);
}
// Copy connections
Node::CopyDependencyGraph(selected, new_nodes, nullptr);
// Set root level context positions and attach to
PostPaste(new_nodes, map);
}
}
@@ -1020,12 +1067,13 @@ NodeViewItem *NodeView::GetAssumedItemForSelectedNode(Node *node)
return nullptr;
}
Node::Position NodeView::GetAssumedPositionForSelectedNode(Node *node)
bool NodeView::GetAssumedPositionForSelectedNode(Node *node, Node::Position *pos)
{
if (NodeViewItem *item = GetAssumedItemForSelectedNode(node)) {
return item->GetNodePositionData();
*pos = item->GetNodePositionData();
return true;
} else {
return Node::Position();
return false;
}
}
@@ -1353,7 +1401,7 @@ void NodeView::EndEdgeDrag(bool cancel)
}
while (NodeGroup *input_group = dynamic_cast<NodeGroup*>(creating_input.node())) {
creating_input = input_group->GetInputPassthroughs().value(creating_input.input());
creating_input = input_group->GetInputFromID(creating_input.input());
}
if (creating_input.IsConnected()) {
+1 -1
View File
@@ -149,7 +149,7 @@ private:
QPointF GetEstimatedPositionForContext(NodeViewItem *item, Node *context) const;
NodeViewItem *GetAssumedItemForSelectedNode(Node *node);
Node::Position GetAssumedPositionForSelectedNode(Node *node);
bool GetAssumedPositionForSelectedNode(Node *node, Node::Position *pos);
Menu *CreateAddMenu(Menu *parent);
+10 -4
View File
@@ -464,10 +464,16 @@ void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionHasChanged && node_) {
ReadjustAllEdges();
if (node_) {
if (change == ItemPositionHasChanged) {
ReadjustAllEdges();
UpdateContextRect();
UpdateContextRect();
} else if (change == ItemSelectedHasChanged) {
if (value.toBool()) {
qDebug() << "Selected node:" << node_;
}
}
}
return QGraphicsItem::itemChange(change, value);
@@ -786,7 +792,7 @@ NodeViewItem *NodeViewItem::GetItemForInput(NodeInput input)
if (NodeGroup *group = dynamic_cast<NodeGroup*>(node_)) {
if (input.node() != group) {
// Translate input to group input
QString id = NodeGroup::GetGroupInputIDFromInput(input);
QString id = group->GetIDOfPassthrough(input);
input.set_node(group);
input.set_input(id);
}