#include "pxr/exec/vdf/isolatedSubnetwork.h"
#include "pxr/exec/vdf/connection.h"
#include "pxr/exec/vdf/network.h"
#include "pxr/base/trace/trace.h"
#include <stack>
PXR_NAMESPACE_OPEN_SCOPE
std::unique_ptr<VdfIsolatedSubnetwork>
VdfIsolatedSubnetwork::IsolateBranch(
VdfConnection *const connection,
EditFilter canDelete)
{
if (!connection) {
TF_CODING_ERROR("Null connection");
return nullptr;
}
VdfIsolatedSubnetwork *const isolated = new VdfIsolatedSubnetwork(
&connection->GetTargetNode().GetNetwork());
if (!isolated->AddIsolatedBranch(connection, canDelete)) {
delete isolated;
return nullptr;
}
isolated->RemoveIsolatedObjectsFromNetwork();
return std::unique_ptr<VdfIsolatedSubnetwork>(isolated);
}
std::unique_ptr<VdfIsolatedSubnetwork>
VdfIsolatedSubnetwork::IsolateBranch(
VdfNode *const node,
EditFilter canDelete)
{
if (!node) {
TF_CODING_ERROR("Null node");
return nullptr;
}
if (node->HasOutputConnections()) {
TF_CODING_ERROR("Root node has output connections.");
return nullptr;
}
if (!canDelete(node)) {
return nullptr;
}
VdfIsolatedSubnetwork *const isolated =
new VdfIsolatedSubnetwork(&node->GetNetwork());
if (!isolated->AddIsolatedBranch(node, canDelete)) {
delete isolated;
return nullptr;
}
isolated->RemoveIsolatedObjectsFromNetwork();
return std::unique_ptr<VdfIsolatedSubnetwork>(isolated);
}
std::unique_ptr<VdfIsolatedSubnetwork>
VdfIsolatedSubnetwork::New(VdfNetwork *const network)
{
if (!network) {
TF_CODING_ERROR("Null network");
return nullptr;
}
return std::unique_ptr<VdfIsolatedSubnetwork>(
new VdfIsolatedSubnetwork(network));
}
bool
VdfIsolatedSubnetwork::AddIsolatedBranch(
VdfConnection *const connection,
EditFilter canDelete)
{
if (!connection) {
TF_CODING_ERROR("Null connection");
return false;
}
if (&connection->GetTargetNode().GetNetwork() != _network) {
TF_CODING_ERROR(
"Attempt to call AddIsolatedBranch with a connection from a "
"different network.");
return false;
}
if (_removedIsolatedObjects) {
TF_CODING_ERROR(
"Attempt to call AddIsolatedBranch after calling "
"RemoveIsolatedObjectsFromNetwork");
return false;
}
_TraverseBranch(connection, canDelete);
return true;
}
bool
VdfIsolatedSubnetwork::AddIsolatedBranch(
VdfNode *const node,
EditFilter canDelete)
{
if (!node) {
TF_CODING_ERROR("Null node");
return false;
}
if (&node->GetNetwork() != _network) {
TF_CODING_ERROR(
"Attempt to call AddIsolatedBranch with a node from a different "
"network.");
return false;
}
if (_removedIsolatedObjects) {
TF_CODING_ERROR(
"Attempt to call AddIsolatedBranch after calling "
"RemoveIsolatedObjectsFromNetwork");
return false;
}
if (node->HasOutputConnections() || !canDelete(node)) {
return false;
}
for (VdfConnection *const c : node->GetInputConnections()) {
_TraverseBranch(c, canDelete);
}
_nodes.push_back(node);
return true;
}
VdfIsolatedSubnetwork::VdfIsolatedSubnetwork(VdfNetwork *network)
: _network(network)
{
TF_VERIFY(_network);
}
VdfIsolatedSubnetwork::~VdfIsolatedSubnetwork()
{
TRACE_FUNCTION();
if (!_removedIsolatedObjects) {
RemoveIsolatedObjectsFromNetwork();
}
for (VdfConnection *const c : _connections) {
_network->_DeleteConnection(c);
}
for (VdfNode *const n : _nodes) {
_network->_DeleteNode(n);
}
}
bool
VdfIsolatedSubnetwork::_CanTraverse(
const VdfNode &sourceNode,
EditFilter canDelete)
{
if (!canDelete(&sourceNode)) {
return false;
}
const auto [it, emplaced] =
_unisolatedOutputConnections.try_emplace(
VdfNode::GetIndexFromId(sourceNode.GetId()));
int& count = it.value();
if (emplaced) {
for (const auto &[_, output] : sourceNode.GetOutputsIterator()) {
count += output->GetNumConnections();
}
}
--count;
TF_VERIFY(count >= 0);
return count == 0;
}
void
VdfIsolatedSubnetwork::_TraverseBranch(
VdfConnection *const connection,
EditFilter canDelete)
{
TRACE_FUNCTION();
std::stack<VdfConnection*> stack;
stack.push(connection);
while (!stack.empty()) {
VdfConnection *const currentConnection = stack.top();
stack.pop();
const bool connectionInserted =
_connections.insert(currentConnection).second;
if (!connectionInserted) {
continue;
}
VdfNode &sourceNode = currentConnection->GetSourceNode();
if (!_CanTraverse(sourceNode, canDelete)) {
continue;
}
_nodes.push_back(&sourceNode);
const VdfConnectionVector inputConnections =
sourceNode.GetInputConnections();
auto rit = inputConnections.crbegin();
const auto rend = inputConnections.crend();
for (; rit != rend; ++rit) {
stack.push(*rit);
}
}
}
void
VdfIsolatedSubnetwork::RemoveIsolatedObjectsFromNetwork()
{
TRACE_FUNCTION();
for (VdfConnection *const c : _connections) {
_network->_RemoveConnection(c);
}
for (VdfNode *const n : _nodes) {
_network->_RemoveNode(n);
}
_removedIsolatedObjects = true;
}
PXR_NAMESPACE_CLOSE_SCOPE