Python Lists - Maintain Dynamo List Structure

Hi - I’m very new to programming so don’t really understand what I’m doing!

In the following script, I’m taking input from a bunch of rooms (as shown in screenshot). I’d like to extract the room boundaries of each room and maintain the inputted list structure as I perform various other operations (essentially finding the overall boundary of a unit group in Revit by using the rooms in the group). I can perform everything on a single group but as soon as I am inputting multiple groups (so nested lists of rooms), I can’t get the function to work.

I found Konrad’s post about defining a function to work through lists but can’t get it to work in this instance (perhaps because the each room then gets its own nested list once the room boundaries are found?).

Any help appreciated!

rooms = IN[0]
rBounds = []

def ProcessList(_func, _list):
	return map(lambda x: ProcessList(_func, x) if type(x)==list else func(x), _list)

def roomBounds(room):
	return room.FinishBoundary

# get room boundaries as curves

if isinstance (rooms, list):
	for r in rooms:
			s = ProcessList(roomBounds(r), rooms)
			rBounds.append(s)


OUT = rBounds

This is the desired outcome:

And this is the python that currently works for a single list of rooms (the above is working towards getting the below function working with nested lists)

# The inputs to this node will be stored as a list in the IN variables.
rooms = IN[0]
offAmount = IN[1]
solids = []
plane = Plane.XY()
rBounds = []

# get room boundaries as curves
t = []
for r in rooms:
	s = r.FinishBoundary
	t.append(s)

rBounds = List.Flatten(t, 1)

#for each list of curves in input list
for a in rBounds:
	b = PolyCurve.ByJoinedCurves(a, 0.001)	#create polycurves
	c = Curve.Offset(b, offAmount)	#offset curves
	d = Surface.ByPatch(c)	#create surface
	e = Surface.Thicken(d, 10000, True)	#extrude surface (create solid)
	solids.append(e)
	
solid = Solid.ByUnion(solids) #union solids
surfaceInt = Geometry.Intersect(solid,plane) #find intersection of a plane with this solid
perimCurves = Surface.PerimeterCurves(surfaceInt[0]) #get the perimeter curves of the intersection


# Assign your output to the OUT variable.
OUT = perimCurves

@Michael_Dunn1 have you tried creating a custom node with the above Python code that works with single list of rooms ?

Hi Salvatore - yes, the bottom piece of code in the post (I edited after posting) contains the code from a custom node that works with a single list.

@Michael_Dunn1 so can you use list List@level with multiple lists?

Using List@level as the input to my node only works half way and I’m not quite sure why. As in, I should be generating 7 lists of curves but it returns null for the last 3 lists (there shouldn’t be a difference between them).

@Michael_Dunn1 can you provide your dyn and a dummy revit file to test?

Sure - thanks!

PerimeterCurvesFromListOfRooms.dyf (8.0 KB) UnitTypeKeyPlanFromGroup_Python.dyn (212.3 KB)

Revit file here:
https://1drv.ms/u/s!AplXcxKV8foBgodTWmU5sNGW0dO2yg?e=QQYGoT

Thanks. I can’t open your dyn! it crashed Revit 2019. What version of Dynamo are you using?

Can anyone open the dyn?

Running Dynamo 2.0.3 daily build but that graph has that effect on me too (I was hoping it was just me!) Usually if I open another graph first and run it on the model (generally anything that changes a parameter value) and then open that dyn it works fine.

Hi @Michael_Dunn1

I fixed your python Script it will work now with any list structure:

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
import Autodesk

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)


ProcessLists = lambda function, lists: [ProcessLists(function, item) if isinstance(item, list) else function(item) for item in lists]
ApplyFunction = lambda func, objs: ProcessLists(func, objs) if isinstance(objs, list) else [func(objs)]

def Unwrap(item):
    return UnwrapElement(item)
    
if isinstance(IN[0], list):
	room = ProcessLists(Unwrap, IN[0])
else:
	room = Unwrap(IN[0])

def roomFinish(rooms):
	options = Autodesk.Revit.DB.SpatialElementBoundaryOptions()
	options.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish
	segments = rooms.GetBoundarySegments(options) 
	lines = [i.GetCurve().ToProtoType() for i in segments[0]] 
	return lines
OUT = ApplyFunction(roomFinish,room)
6 Likes

Hi @salvatoredragotta

Yes i can open without any issues. See above solution :point_up:

1 Like

Thank you @Kulkul! I’ll give that a go and hopefully understand the solution. :slight_smile:

@Michael_Dunn1 Don’t forget to mark the post as solved.