Files
Nexus/2023/scripts/animation_tools/studiolibrary/mutils/matchnames.py
2025-11-24 00:15:32 +08:00

175 lines
5.4 KiB
Python

# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. This library 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 Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
import logging
import mutils
from collections import OrderedDict
__all__ = [
"matchNames",
"groupObjects",
]
logger = logging.getLogger(__name__)
def rotateSequence(seq, current):
"""
:type seq:
:type current:
:rtype:
"""
n = len(seq)
for i in range(n):
yield seq[(i + current) % n]
def groupObjects(objects):
"""
:type objects:
:rtype:
"""
results = OrderedDict()
for name in objects:
node = mutils.Node(name)
results.setdefault(node.namespace(), [])
results[node.namespace()].append(name)
results = OrderedDict(sorted(results.items(), key=lambda t: (t[0].count(":"), len(t[0]))))
return results
def indexObjects(objects):
"""
:type objects: list[str]
:rtype: dict
"""
result = {}
if objects:
for name in objects:
node = mutils.Node(name)
result.setdefault(node.shortname(), [])
result[node.shortname()].append(node)
return result
def matchInIndex(node, index):
"""
:type node: mutils.Node
:type index: dict[list[mutils.Node]]
:rtype: Node
"""
result = None
if node.shortname() in index:
nodes = index[node.shortname()]
if nodes:
for n in nodes:
if node.name().endswith(n.name()) or n.name().endswith(node.name()):
result = n
break
if result is not None:
index[node.shortname()].remove(result)
return result
def matchNames(srcObjects, dstObjects=None, dstNamespaces=None, search=None, replace=None):
"""
:type srcObjects: list[str]
:type dstObjects: list[str]
:type dstNamespaces: list[str]
:rtype: list[(mutils.Node, mutils.Node)]
"""
results = []
if dstObjects is None:
dstObjects = []
srcGroup = groupObjects(srcObjects)
srcNamespaces = srcGroup.keys()
if not dstObjects and not dstNamespaces: # and not selection:
dstNamespaces = srcNamespaces
if not dstNamespaces and dstObjects:
dstGroup = groupObjects(dstObjects)
dstNamespaces = dstGroup.keys()
dstIndex = indexObjects(dstObjects)
# DESTINATION NAMESPACES NOT IN SOURCE OBJECTS
dstNamespaces2 = [x for x in dstNamespaces if x not in srcNamespaces]
# DESTINATION NAMESPACES IN SOURCE OBJECTS
dstNamespaces1 = [x for x in dstNamespaces if x not in dstNamespaces2]
# CACHE DESTINATION OBJECTS WITH NAMESPACES IN SOURCE OBJECTS
usedNamespaces = []
notUsedNamespaces = []
# FIRST LOOP THROUGH ALL DESTINATION NAMESPACES IN SOURCE OBJECTS
for srcNamespace in srcNamespaces:
if srcNamespace in dstNamespaces1:
usedNamespaces.append(srcNamespace)
for name in srcGroup[srcNamespace]:
srcNode = mutils.Node(name)
if search is not None and replace is not None:
# Using the mirror table which supports * style replacing
name = mutils.MirrorTable.replace(name, search, replace)
dstNode = mutils.Node(name)
if dstObjects:
dstNode = matchInIndex(dstNode, dstIndex)
if dstNode:
results.append((srcNode, dstNode))
yield (srcNode, dstNode)
else:
logger.debug("Cannot find matching destination object for %s" % srcNode.name())
else:
notUsedNamespaces.append(srcNamespace)
# SECOND LOOP THROUGH ALL OTHER DESTINATION NAMESPACES
srcNamespaces = notUsedNamespaces
srcNamespaces.extend(usedNamespaces)
_index = 0
for dstNamespace in dstNamespaces2:
match = False
i = _index
for srcNamespace in rotateSequence(srcNamespaces, _index):
if match:
_index = i
break
i += 1
for name in srcGroup[srcNamespace]:
srcNode = mutils.Node(name)
dstNode = mutils.Node(name)
dstNode.setNamespace(dstNamespace)
if dstObjects:
dstNode = matchInIndex(dstNode, dstIndex)
elif dstNamespaces:
pass
if dstNode:
match = True
results.append((srcNode, dstNode))
yield (srcNode, dstNode)
else:
logger.debug("Cannot find matching destination object for %s" % srcNode.name())
if logger.parent.level == logging.DEBUG or logger.level == logging.DEBUG:
for dstNodes in dstIndex.values():
for dstNode in dstNodes:
logger.debug("Cannot find matching source object for %s" % dstNode.name())