Hierarchy structure with subnets ( python )

   2086   5   1
User Avatar
Member
25 posts
Joined: Jan. 2014
Offline
Hey there guys.

I am trying to explode a pointcloud into separate objects. This pointcloud has an attribute that contains unique paths per object.
Each point represents 1 object instance.

It is not too different from what alembic does with its build subnet network from the path attribute.

It has been proving tricky at least for me, and I have made several code sketches trying to solve this abstracted enough because in the future I want to be able to use this on any pointcloud, where the paths could be entirely different.

So far what I am doing is running over all the points, and for each I am getting the point attribute, then splitting the paths, and then I run a for loop in a range of len(splitPath) so that when it is creating the path structure later it will only loop for as long as there are indexes. The name is derived from the split path list and I use the index to drive which name to use for which group.
def names(nodes):
    return [node.name() for node in nodes]

if FullPath is not None:
    for point in geo.points():
        #set Variable to point attrib FullPathStorage at specific point 

        FirstNodeNew = hou.node('/obj')
       
        FullPathAttrib = point.attribValue(FullPath)
        splitPathName = FullPathAttrib.split("/")

        indexToRemove = [0,1]
        FirstNode = hou.node('/obj').children()
        
        # after splitting the path there was an empty entry and an obj entry which
        # which I will not need later when I create the groups.

        for index in sorted(indexToRemove, reverse=True):
            del splitPathName[index]
        
        # this is where the main iteration over the path list happens.
        
        for index in range (len(splitPathName)):

            if index >= 1:
                print index    
          
        FindNode = names(FirstNodeNew.glob(splitPathName[index]))
        if not FindNode:
                CurrentNode = hou.node('/obj').createNode("subnet", splitPathName[index])



This is obviously not the complete script but it gets the point across I think.. It does create nodes at the obj level, and won't create duplicate nodes at the same level, but the hard part for me is trying to structure the loop so that for each loop it would select the last created node, make a node within. and keep going until the loop is done.

Any help/ nudge in the right direction would be appreciated. Cheers.

PS: don't mind the variable names. I was originally going about the script differently, where I was hard coding the creation of the first node and going from there. But I feel that probably 1 loop that iterates over itself would be better.
Edited by Martin Krol - April 2, 2021 11:22:51
User Avatar
Member
191 posts
Joined: Oct. 2018
Offline
If I'm following what you're talking about, you already have a list of nodes and just trying to create a hierarchy of subnets? See if this is what you mean:

nodes = ['/obj/geo1', '/obj/geo1/someNode1', '/obj/geo1/someNode1/subnet1', '/obj/geo1/someNode1/subnet2',
        '/obj/geo1/subnet2', '/obj/geo1/subnet2/null1', '/obj/geo1/subnet2/subnet1',
        '/obj/geo1/subnet2/textHere2', '/obj/geo1/subnet2/subnet3',
        '/obj/geo2', '/obj/geo2/subnet1', '/obj/geo2/subnet1/anotherNode1', '/obj/geo2/subnet1/sub_net2',
        '/obj/geo2/subnet1/subnet3']

# Sort nodes by path length if they're out of order
# You can probably ignore sorting, but just in case
nodes.sort( key=lambda v: len( v.split("/") ) )

def createNodes( nodes ):
    for node in nodes :
        parent, name = node.rsplit("/", 1)
        parentNode = hou.node( parent )
        
        # Set the default nodeType for creation to subnet
        nodeType = "subnet"
        
        # Change nodeType to geo for /obj level nodes
        # Ignore if just using subnets everywhere
        if len( node.split("/") ) == 3 : nodeType = "geo"
        
        if parentNode :
            if not hou.node( node ) :
                newNode = parentNode.createNode( nodeType, name )
                newNode.moveToGoodPosition()
            else :
                print ( "Node already exists: %s" % ( node ) )
        else :
            print ( "Can't create node because the parent doesn't exist: %s" % ( node ) )
            

createNodes( nodes )
Edited by krueger - April 2, 2021 15:24:02
User Avatar
Member
25 posts
Joined: Jan. 2014
Offline
Hey there mkps.

This works great as an example how to go about an ordered list. as in, the order of the nodes clearly have parent paths predefined as well as children paths.

In my case, all I have is paths on points.

So for example.

pt0 PathAttrib = /obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2/unreal
pt1 PathAttrib = /obj/env_city_layout/instances/largeTower/prp_largeTower_mdl6/unreal

I mean I guess I could do something where I go through these paths, and create an ordered list like you had, and then use that def you made above.

so for pt0 i would need to use the full path: /obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2/unreal

to create..

/obj/env_city_layout
/obj/env_city_layout/instances/
/obj/env_city_layout/instances/flatSlabs/
etc...

I did manage to create a script that gets close but it creates double objs/ (/obj/obj)

import hou

FullPath = ['/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2/unreal', '/obj/env_city_layout/instances/largeTower/prp_largeTower_mdl6/unreal',
            '/obj/env_city_layout/instances/largeTower/prp_TwoTowersYAAA_md24/unreal','/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl4/unreal',
            '/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl5/unreal']



for path in FullPath:
    #print path
    
    
    
    parent, name = path.split("/", 1)
    
    for part in path:
        
        pathSplit = path.split ("/")
        #print pathSplit
        indexToRemove = [0]
        
    for index in sorted(indexToRemove, reverse=True):
        del pathSplit[index]
        #print pathSplit
            
    for index in range (len(pathSplit)):
        #print pathSplit[index]
            
        # if index == 0:
            #pathNew = str(pathSplit[0]) + "/" + str(pathSplit[1])
            #indexToRemove = [0, 1]
            #for index in sorted(indexToRemove, reverse=True):
                #del pathSplit[index]
            
            #pathSplit.insert(0, pathNew)
            
        
            
        pathNew = str(pathSplit[0]) + "/" + str(pathSplit[index])
        del pathSplit[0]
       
        pathSplit.insert(0, pathNew)
            
        print pathNew
        
    
            
    

this spits out:

>>> obj/obj
obj/obj/env_city_layout
obj/obj/env_city_layout/instances
obj/obj/env_city_layout/instances/flatSlabs
obj/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2
obj/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2/unreal
obj/obj
obj/obj/env_city_layout
obj/obj/env_city_layout/instances
obj/obj/env_city_layout/instances/largeTower
obj/obj/env_city_layout/instances/largeTower/prp_largeTower_mdl6
obj/obj/env_city_layout/instances/largeTower/prp_largeTower_mdl6/unreal
obj/obj
obj/obj/env_city_layout
obj/obj/env_city_layout/instances
obj/obj/env_city_layout/instances/largeTower
obj/obj/env_city_layout/instances/largeTower/prp_TwoTowersYAAA_md24
obj/obj/env_city_layout/instances/largeTower/prp_TwoTowersYAAA_md24/unreal
obj/obj
obj/obj/env_city_layout
obj/obj/env_city_layout/instances
obj/obj/env_city_layout/instances/flatSlabs
obj/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl4
obj/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl4/unreal
obj/obj
obj/obj/env_city_layout
obj/obj/env_city_layout/instances
obj/obj/env_city_layout/instances/flatSlabs
obj/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl5
obj/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl5/unreal



So I did the next thing that would make sense which is us if statements. treat the first index differently from the next.. but this seems to delete all indexes of the rest of the paths as well..


FullPath = ['/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2/unreal', '/obj/env_city_layout/instances/largeTower/prp_largeTower_mdl6/unreal',
            '/obj/env_city_layout/instances/largeTower/prp_TwoTowersYAAA_md24/unreal','/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl4/unreal',
            '/obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl5/unreal']



for path in FullPath:
    #print path
    
    
    
    parent, name = path.split("/", 1)
    
    for part in path:
        
        pathSplit = path.split ("/")
        #print pathSplit
        indexToRemove = [0]
        
    for index in sorted(indexToRemove, reverse=True):
        del pathSplit[index]
        #print pathSplit
            
    for index in range (len(pathSplit)):
        #print pathSplit[index]
            
        if index == 0:
            pathNew = str(pathSplit[0]) + "/" + str(pathSplit[1])
            indexToRemove = [0, 1]
            for index in sorted(indexToRemove, reverse=True):
                del pathSplit[index]
            
            pathSplit.insert(0, pathNew)
            
        
        if index > 0:
        
            pathNew = str(pathSplit[0]) + "/" + str(pathSplit[index])
            del pathSplit[0]
       
            pathSplit.insert(0, pathNew)
            
        print pathNew


unfortunately this spits out only 1 of the paths. it's exactly what I want to see in terms of structure however, It also gives me a "list index out of range" error.

>>> obj/env_city_layout
obj/env_city_layout/instances
obj/env_city_layout/instances/flatSlabs
obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2
obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2/unreal


What am I missing? ( maybe having to take care of a kid while coding this is clouding me. But yeah I just can't see it at the moment.

The second result is exactly what I want to see, but for all the paths provided... and then the last thing is for it to create the accumulated list of all of these paths that would then work with your previous script.


so in the end I am hoping for this script to create something like..

FullPath = ['obj/env_city_layout',
'obj/env_city_layout/instances',
'obj/env_city_layout/instances/flatSlabs',
'obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2',
'obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl2/unreal',
'obj/obj/env_city_layout',
'obj/env_city_layout/instances',
'obj/env_city_layout/instances/largeTower',
'obj/env_city_layout/instances/largeTower/prp_largeTower_mdl6',
'obj/env_city_layout/instances/largeTower/prp_largeTower_mdl6/unreal',
'obj/obj/env_city_layout',
'obj/env_city_layout/instances',
'obj/env_city_layout/instances/largeTower',
'obj/env_city_layout/instances/largeTower/prp_TwoTowersYAAA_md24',
'obj/env_city_layout/instances/largeTower/prp_TwoTowersYAAA_md24/unreal',
'obj/env_city_layout',
'obj/env_city_layout/instances',
'obj/env_city_layout/instances/flatSlabs',
'obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl4',
'obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl4/unreal',
'obj/env_city_layout',
'obj/env_city_layout/instances',
'obj/env_city_layout/instances/flatSlabs',
'obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl5',
'obj/env_city_layout/instances/flatSlabs/prp_flatSlab_mdl5/unreal']


Cheers. and thanks again for helping

-mk
Edited by Martin Krol - April 7, 2021 13:18:42
User Avatar
Member
8595 posts
Joined: July 2007
Offline
Martin Krol
It is not too different from what alembic does with its build subnet network from the path attribute.
have you looked at the code in the Alembic Archive Object's Python module?
not sure if helpful, but since you drew that analogy there may be some useful things in there
Tomas Slancik
FX Supervisor
Method Studios, NY
User Avatar
Member
25 posts
Joined: Jan. 2014
Offline
Hey there Tomas.

I didn't realize that it was written in python. I opened up type parameters and went into the code, will check it out now.

But for learning's sake, outside of that. In the example above. If you have time, how would you go about building a list out of what I have above? since it's multiple for loops. so out of 1 path, I break it apart into a mini list, then take another path, break it apart into a mini list of paths, and keep building a larger list as it goes instead of overwriting as soon as a new mini list starts.

In any case. I will look into the alembic archive reader now. cheers
User Avatar
Member
191 posts
Joined: Oct. 2018
Offline
If you just want to have the end node path and build from there, this function can work for you:

nodePath = '/obj/geo2/subnet1/subnet3'
nodes = ['/obj/geo1/someNode1/subnet1', '/obj/geo2/subnet1/sub_net2', '/obj/geo2/subnet1/some_other_subnet']

def createNodeHierarchy( path ):
    tokens = path.split("/")
    
    for i in range( 2, len(tokens) ) : # Start after the root obj node
        parentPath = "/".join( tokens[ 0 : i ] )
        parentNode = hou.node( parentPath )
        newName = tokens[i]
        newNode = hou.node( "%s/%s" % (parentPath, newName) )
        
        if not newNode :
            if parentNode :
                n = parentNode.createNode( "subnet", newName )
                n.moveToGoodPosition()
            else :
                print ( "Parent node doesn't exist: %s" % ( parentPath ) )
        else :
            print ( "Node already exists: %s" % ( newNode.path() ) )
        

createNodeHierarchy( nodePath )

for n in nodes : createNodeHierarchy( n )
  • Quick Links