Files
SDL3CPlusPlus/MaterialX/source/MaterialXCore/Traversal.cpp
2026-01-06 13:25:49 +00:00

237 lines
5.3 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXCore/Traversal.h>
#include <MaterialXCore/Node.h>
MATERIALX_NAMESPACE_BEGIN
const Edge& getNullEdge() {
static const auto ret = new Edge(nullptr, nullptr, nullptr);
return *ret;
}
const TreeIterator& getNullTreeIterator() {
static const auto ret = new TreeIterator(nullptr);
return *ret;
}
const GraphIterator& getNullGraphIterator() {
static const auto ret = new GraphIterator(nullptr);
return *ret;
}
const InheritanceIterator& getNullInheritanceIterator() {
static const auto ret = new InheritanceIterator(nullptr);
return *ret;
}
//
// Edge methods
//
Edge::operator bool() const
{
return *this != getNullEdge();
}
string Edge::getName() const
{
return _elemConnect ? _elemConnect->getName() : EMPTY_STRING;
}
//
// TreeIterator methods
//
const TreeIterator& TreeIterator::end()
{
return getNullTreeIterator();
}
TreeIterator& TreeIterator::operator++()
{
if (_holdCount)
{
_holdCount--;
return *this;
}
if (!_prune && _elem && !_elem->getChildren().empty())
{
// Traverse to the first child of this element.
_stack.emplace_back(_elem, 0);
_elem = _elem->getChildren()[0];
return *this;
}
_prune = false;
while (true)
{
if (_stack.empty())
{
// Traversal is complete.
_elem = ElementPtr();
return *this;
}
// Traverse to our siblings.
StackFrame& parentFrame = _stack.back();
const ElementVec& siblings = parentFrame.first->getChildren();
if (parentFrame.second + 1 < siblings.size())
{
_elem = siblings[++parentFrame.second];
return *this;
}
// Traverse to our parent's siblings.
_stack.pop_back();
}
}
//
// GraphIterator methods
//
size_t GraphIterator::getNodeDepth() const
{
size_t nodeDepth = 0;
for (ElementPtr elem : _pathElems)
{
if (elem->isA<Node>())
{
nodeDepth++;
}
}
return nodeDepth;
}
const GraphIterator& GraphIterator::end()
{
return getNullGraphIterator();
}
GraphIterator& GraphIterator::operator++()
{
if (_holdCount)
{
_holdCount--;
return *this;
}
if (!_prune && _upstreamElem && _upstreamElem->getUpstreamEdgeCount())
{
// Traverse to the first upstream edge of this element.
_stack.emplace_back(_upstreamElem, 0);
Edge nextEdge = _upstreamElem->getUpstreamEdge(0);
if (nextEdge && nextEdge.getUpstreamElement() && !skipOrMarkAsVisited(nextEdge))
{
extendPathUpstream(nextEdge.getUpstreamElement(), nextEdge.getConnectingElement());
return *this;
}
}
_prune = false;
while (true)
{
if (_upstreamElem)
{
returnPathDownstream(_upstreamElem);
}
if (_stack.empty())
{
// Traversal is complete.
*this = GraphIterator::end();
return *this;
}
// Traverse to our siblings.
StackFrame& parentFrame = _stack.back();
if (parentFrame.second + 1 < parentFrame.first->getUpstreamEdgeCount())
{
Edge nextEdge = parentFrame.first->getUpstreamEdge(++parentFrame.second);
if (nextEdge && nextEdge.getUpstreamElement() && !skipOrMarkAsVisited(nextEdge))
{
extendPathUpstream(nextEdge.getUpstreamElement(), nextEdge.getConnectingElement());
return *this;
}
continue;
}
// Traverse to our parent's siblings.
returnPathDownstream(parentFrame.first);
_stack.pop_back();
}
return *this;
}
void GraphIterator::extendPathUpstream(ElementPtr upstreamElem, ElementPtr connectingElem)
{
// Check for cycles.
if (_pathElems.count(upstreamElem))
{
throw ExceptionFoundCycle("Encountered cycle at element: " + upstreamElem->asString());
}
// Extend the current path to the new element.
_pathElems.insert(upstreamElem);
_upstreamElem = upstreamElem;
_connectingElem = connectingElem;
}
void GraphIterator::returnPathDownstream(ElementPtr upstreamElem)
{
_pathElems.erase(upstreamElem);
_upstreamElem = ElementPtr();
_connectingElem = ElementPtr();
}
bool GraphIterator::skipOrMarkAsVisited(const Edge& edge)
{
auto [it, inserted] = _visitedEdges.emplace(edge);
return !inserted;
}
//
// InheritanceIterator methods
//
const InheritanceIterator& InheritanceIterator::end()
{
return getNullInheritanceIterator();
}
InheritanceIterator& InheritanceIterator::operator++()
{
if (_holdCount)
{
_holdCount--;
return *this;
}
if (_elem)
{
ElementPtr super = _elem->getInheritsFrom();
if (super && super->getCategory() != _elem->getCategory())
{
super = nullptr;
}
if (super)
{
// Check for cycles.
if (_pathElems.count(super))
{
throw ExceptionFoundCycle("Encountered cycle at element: " + super->asString());
}
_pathElems.insert(super);
}
_elem = super;
}
return *this;
}
MATERIALX_NAMESPACE_END