# --------------------------------------------------------------------------------------------
# --- Private methods
def cloneCopy(self, oldnode, newnode):
"""
Clone copy a node.
Copy sub node data like tags and tracks.
:param oldnode : The old node to dopy from.
:param newnode : The new node to copy to.
"""
newnode = newnode.GetClone(c4d.COPYFLAGS_NO_HIERARCHY | c4d.COPYFLAGS_NO_BRANCHES)
newnode = self.copyData(oldnode, newnode)
newnode = self.copyNodeData(oldnode, newnode)
return newnode
def copyData(self, oldnode, newnode):
"""
Copy all lowlevel data. (name, global matrix, bits, basecontainer and userdata)
:param oldnode : The old node to dopy from.
:param newnode : The new node to copy to.
"""
# copy matrix
newnode.SetMg(oldnode.GetMg())
# copy nbits
for i in xrange(84):
if oldnode.GetNBit(i):
newnode.ChangeNBit(i, c4d.NBITCONTROL_SET)
else:
newnode.ChangeNBit(i, c4d.NBITCONTROL_CLEAR)
# copy bits
newnode.SetAllBits(oldnode.GetAllBits())
# copy & convert ids
newnode[c4d.PRIM_VECTORNULL_OPTIONS_COLOR_MODE] = c4d.PRIM_VECTORNULL_OPTIONS_COLOR_MODE_STATIC
newnode[c4d.ID_BASELIST_NAME] = oldnode[c4d.ID_BASELIST_NAME]
newnode[c4d.ID_LAYER_LINK] = oldnode[c4d.ID_LAYER_LINK]
newnode[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = oldnode[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR]
newnode[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = oldnode[c4d.ID_BASEOBJECT_VISIBILITY_RENDER]
newnode[c4d.PRIM_VECTORNULL_OPTIONS_ID_COLOR_BASE] = oldnode[c4d.ID_BASEOBJECT_COLOR]
if oldnode[c4d.NULLOBJECT_DISPLAY] != c4d.NULLOBJECT_DISPLAY_NONE:
r = oldnode[c4d.NULLOBJECT_RADIUS] * 2
newnode[c4d.PRIM_VECTORNULL_DRAW_ORIGIN] = True
newnode[c4d.PRIM_VECTORNULL_ORIGIN_SIZE] = c4d.Vector(r)
newnode[c4d.PRIM_VECTORNULL_ORIGIN_TYPE] = c4d.PRIM_VECTORNULL_ORIGIN_TYPE_DOT
if oldnode[c4d.NULLOBJECT_DISPLAY] == c4d.NULLOBJECT_DISPLAY_POINT:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_TYPE] = c4d.PRIM_VECTORNULL_ORIGIN_TYPE_AXIS
elif oldnode[c4d.NULLOBJECT_DISPLAY] == c4d.NULLOBJECT_DISPLAY_CIRCLE:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_TYPE] = c4d.PRIM_VECTORNULL_ORIGIN_TYPE_CIRCLE
elif oldnode[c4d.NULLOBJECT_DISPLAY] == c4d.NULLOBJECT_DISPLAY_RECTANGLE:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_TYPE] = c4d.PRIM_VECTORNULL_ORIGIN_TYPE_BOX
elif oldnode[c4d.NULLOBJECT_DISPLAY] == c4d.NULLOBJECT_DISPLAY_CUBE:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_TYPE] = c4d.PRIM_VECTORNULL_ORIGIN_TYPE_CUBE
elif oldnode[c4d.NULLOBJECT_DISPLAY] == c4d.NULLOBJECT_DISPLAY_PYRAMID:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_TYPE] = c4d.PRIM_VECTORNULL_ORIGIN_TYPE_PYRAMID
elif oldnode[c4d.NULLOBJECT_DISPLAY] == c4d.NULLOBJECT_DISPLAY_SPHERE:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_TYPE] = c4d.PRIM_VECTORNULL_ORIGIN_TYPE_SPHERE
elif oldnode[c4d.NULLOBJECT_DISPLAY] == c4d.NULLOBJECT_DISPLAY_AXIS:
newnode[c4d.PRIM_VECTORNULL_DRAW_ORIGIN] = False
newnode[c4d.PRIM_VECTORNULL_DRAW_MATRIX] = True
newnode[c4d.PRIM_VECTORNULL_MATRIX_AXIS] = True
newnode[c4d.PRIM_VECTORNULL_MATRIX_X] = True
newnode[c4d.PRIM_VECTORNULL_MATRIX_Y] = True
newnode[c4d.PRIM_VECTORNULL_MATRIX_Z] = True
if oldnode[c4d.NULLOBJECT_ORIENTATION] == c4d.NULLOBJECT_ORIENTATION_SCREEN:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_PLANE] = c4d.PRIM_VECTORNULL_ORIGIN_PLANE_CAM
if oldnode[c4d.NULLOBJECT_ORIENTATION] == c4d.NULLOBJECT_ORIENTATION_XY:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_PLANE] = c4d.PRIM_VECTORNULL_ORIGIN_PLANE_XY
if oldnode[c4d.NULLOBJECT_ORIENTATION] == c4d.NULLOBJECT_ORIENTATION_ZY:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_PLANE] = c4d.PRIM_VECTORNULL_ORIGIN_PLANE_ZY
if oldnode[c4d.NULLOBJECT_ORIENTATION] == c4d.NULLOBJECT_ORIENTATION_XZ:
newnode[c4d.PRIM_VECTORNULL_ORIGIN_PLANE] = c4d.PRIM_VECTORNULL_ORIGIN_PLANE_XZ
# copy user data
for descid, container in oldnode.GetUserDataContainer():
newnode.SetUserDataContainer(descid, container)
newnode[descid] = oldnode[descid]
return newnode
def copyNodeData(self, oldnode, newnode):
"""
Copy sub node data like tags and tracks.
:param oldnode : The old node to dopy from.
:param newnode : The new node to copy to.
"""
for tag in reversed(oldnode.GetTags()):
newnode.InsertTag(tag.GetClone(c4d.COPYFLAGS_0))
for track in reversed(oldnode.GetCTracks()):
newnode.InsertTrackSorted(track.GetClone(c4d.COPYFLAGS_0))
return newnode
def copyReferences(self, doc, nodelist, clonelist):
"""
Replace node references in all document nodes.
:param doc : The active document.
:param nodelist : The list of old nodes to be replaced (in the same order as clonelist).
:param clonelist : The list of new nodes as the replacment (in the same order as nodelist).
"""
node = doc.GetFirstObject()
while (node is not None):
if node not in nodelist:
# objects
data = node.GetDataInstance()
self.replaceContainerReference(doc, data, node, nodelist, clonelist)
# tags
for tag in node.GetTags():
data = tag.GetDataInstance()
self.replaceContainerReference(doc, data, tag, nodelist, clonelist)
# if tag.CheckType(c4d.Texpresso):
# master = tag.GetNodeMaster()
# if isinstance(master, graphview.GvNodeMaster):
# gvnode = master.GetRoot()
# while (gvnode is not None):
# print gvnode
# data = gvnode.GetDataInstance()
# self.replaceContainerReference(doc, data, gvnode, nodelist, clonelist)
# gvnode = self.getNextObject(gvnode)
node = self.getNextObject(node)
def getData(self, doc):
"""
Returns all null objects in the active selection and the first fhVectorNull as the template
obejct. If there is no selection the whole document is being searched. If there is no fhVectorNull
found a new instance is returned.
:param doc : The active document.
:return : c4d.Onull[], fhVectorNull
"""
selection = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
template = None
nulls = []
if selection != []:
for node in selection:
if node.CheckType(ID_VECTORNULL) and template is None:
template = node
elif node.CheckType(c4d.Onull):
nulls.append(node)
else:
node = doc.GetFirstObject()
while (node is not None):
if node.CheckType(ID_VECTORNULL) and template is None:
template = node
elif node.CheckType(c4d.Onull):
nulls.append(node)
node = self.getNextObject(node)
if template is None:
template = c4d.BaseObject(ID_VECTORNULL)
return nulls, template
def getNextObject(self, node):
"""
Get the next object.
param node : The GeListNode to walk on.
return : c4d.GeListNode | None
"""
if node.GetDown() is not None:
return node.GetDown()
while not node.GetNext() and node.GetUp():
node = node.GetUp()
return node.GetNext()
def replaceContainerReference(self, doc, container, node, nodelist, clonelist):
"""
Method to replace LinkBoxGui and InExcludeData links in basecontainer. Fail-safe
iteration approach to step over unsopported data-types.
:param doc : The active document.
:param node : The BaseList2D to take the data from.
:param nodelist : The list of old nodes to be replaced (in the same order as clonelist).
:param clonelist : The list of new nodes as the replacment (in the same order as nodelist).
"""
# little helper method to be super safe, actually retrieving the id should also work
# for unknown datatypes.
def nextid(index):
try:
return (index + 1, container.GetIndexId(index + 1))
except:
print 'Skipping unsupported datatype for {0}, index: {1}'.format(node, index + 1)
return nextid(index + 2)
# step through the container
index, cid = nextid(-1)
while (cid != c4d.NOTOK):
try:
data = container[cid]
# baselinks
if container.GetType(cid) == c4d.DTYPE_BASELISTLINK and data in nodelist:
doc.AddUndo(c4d.UNDOTYPE_CHANGE, node)
container[cid] = clonelist[nodelist.index(data)]
print node, cid, data
# inexclude
elif isinstance(data, c4d.InExcludeData):
print node, cid, data
i, maxid = 0, data.GetObjectCount() - 1
while (i < maxid):
lstobj = data.ObjectFromIndex(doc, i)
print i, lstobj
if node is not None and lstobj in nodelist:
doc.AddUndo(c4d.UNDOTYPE_CHANGE, node)
data.DeleteObject(i)
doc.AddUndo(c4d.UNDOTYPE_CHANGE, node)
data.InsertObject(clonelist[nodelist.index(data)], 0)
i -= 1
i += 1
elif isinstance(data, c4d.BaseContainer):
self.replaceContainerReference(doc, data, node, nodelist, clonelist)
except:
print 'Skipping unsupported datatype for {0}, container id: {1}'.format(node, cid)
index, cid = nextid(index)
def replaceNode(self, doc, oldnode, newnode):
"""
Replaces the nodes. Returns the new node and deletes the old node.
Also places places undo steps.
:param doc : The active document.
:param oldnode : The old node to be replaced.
:param newnode : The new replacment node.
:return : c4d.BaseObject
"""
mg = newnode.GetMg()
children = oldnode.GetChildren()
parent = oldnode.GetUp()
previous = oldnode.GetPred()
for child in children:
doc.AddUndo(c4d.UNDOTYPE_CHANGE, child)
child.InsertUnderLast(newnode)
doc.AddUndo(c4d.UNDOTYPE_DELETE, oldnode)
oldnode.Remove()
if previous is None and parent is not None:
doc.AddUndo(c4d.UNDOTYPE_CHANGE, newnode)
newnode.InsertUnder(parent)
elif previous is not None:
doc.AddUndo(c4d.UNDOTYPE_CHANGE, newnode)
newnode.InsertAfter(previous)
else:
doc.AddUndo(c4d.UNDOTYPE_CHANGE, newnode)
doc.InsertObject(newnode)
newnode.SetMg(mg)
return newnode
def run(self, doc):
"""
Runs the replacement command and palces undo points.
:param doc : The active docment.
:return : bool
"""
# get data
nodelist, template = self.getData(doc)
clonelist = []
temp = c4d.BaseObject(c4d.Onull)
doc.StartUndo()
doc.InsertObject(temp)
doc.AddUndo(c4d.UNDOTYPE_NEW, temp)
# copy old node qualities to the new nodes
# and insert the nodes into the docoment at a temp location
for node in nodelist:
clone = self.cloneCopy(node, template)
clonelist.append(clone)
clone.InsertUnderLast(temp)
doc.AddUndo(c4d.UNDOTYPE_NEW, clone)
c4d.EventAdd()
# overwrite all references the old nodes in the document.
self.copyReferences(doc, nodelist, clonelist)
# replace nodes.
for i, oldnode in enumerate(nodelist):
self.replaceNode(doc, oldnode, clonelist[i])
# remove temp
doc.AddUndo(c4d.UNDOTYPE_DELETE, temp)
temp.Remove()
doc.EndUndo()
c4d.EventAdd()
return True