Dividing parts with dynamo

@jostein_olsen Thank you, I do not have a nice blog like your self, and I thought I might share a little something while trying to learn for my self.

##How to make this work for multiple walls?
A limitation with the above python script is that it will only work for a single wall element, and that is not very efficient. We have two options to make it work for a list of walls:

  1. Wrap the python script in a custom node, be sure to define the input correctly as explained by @Dimitar_Venkov in this post. Dimitar have also improved the python re-execution performance in Dynamo with this PR. (Thank you very much for that!)

  2. Handle the list in the python node. This time I want to try the @Konrad_K_Sobon - way, and I’m using this post as a template.

##Handling list in python:
One of the first lines in Konrads code after the references and imports is this:

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

The above function uses a python Built-in function called map() which does pretty much the same as the List.Map node in Dynamo. It takes a function and a list and calls the function for each element in the list.
The clever thing here is that the function calls it self if a sublist is a list, and so on so this will handle nested lists as well. The lambda is just a function that does not have a name and can be defined in one line. A more “basic” way to do the same as above would be something like this:

def ProcessList(_func, _list)
    ret=[]
    for x in _list:
        if type(x) == list:
            ret.append(ProcessList(_func, x))
        else:
            ret.append(_func(x))
    return ret

The next function in Konrads code is this:

def ProcessParallelLists(_func, *lists):
	return map( lambda *xs: ProcessParallelLists(_func, *xs) if all(type(x) is list for x in xs) else _func(*xs), *lists )

It does pretty much the same as ProcessList(), but the asterix (*) allows an arbitrary amount of lists as argument, if you know you have three lists, you can also make the function like this ProcessThreeLists(func_, list1, list2, list3). This works similar to the List.Combine node in Dynamo, it takes the function with a subelement in each list as an argument, and moves to the next subelements. See docs.python.org for more.

The next step is to convert the data from the input ports as before, but with the new functions:

#Preparing input from dynamo to revit
def Unwrap(item):
	return UnwrapElement(item)
	
def ToRevit(item):
	return item.ToRevitType(True)

if isinstance(IN[0], list):
	wall = ProcessList(Unwrap, IN[0])
else:
	wall = list(Unwrap(IN[0]))
	
if isinstance(IN[1], list):
	divisionLines = ProcessList(ToRevit, IN[1])
else:
	divisionLines = list(ToRevit(IN[1]))

if isinstance(IN[2], list):
	sketchPlane = ProcessList(Unwrap, IN[2])
else:
	sketchPlane = list(Unwrap(IN[2]))

Then to use ProcessParallelLists we have to create a function for our API-calls, just add a def and a colon, indent the code and add a return, and the job is done:

#Define function for API method:

def dividePart(wall,divisionLines,sketchPlane):
	wallList = List[ElementId]()
	wallList.Add(wall.Id)
	intersectionElementsIds = List[ElementId]()
	curveArray = List[Curve](divisionLines)
	
	if PartUtils.AreElementsValidForCreateParts(doc, wallList):
		createParts = PartUtils.CreateParts(doc, wallList)
		doc.Regenerate()
		
	parts = PartUtils.GetAssociatedParts(doc, wall.Id, 0, 0)
	
	return PartUtils.DivideParts(doc, parts, intersectionElementsIds, curveArray, sketchPlane.Id)

The last part is the transaction and the call to the ProcessParallelList with the api-function and the argument lists:

TransactionManager.Instance.EnsureInTransaction(doc)

partDivide = ProcessParallelLists(dividePart, wall, divisionLines, sketchPlane)

TransactionManager.Instance.TransactionTaskDone()

OUT = partDivide

##Complete code:

import clr

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

clr.AddReference('System')
from System.Collections.Generic import List

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

#Functions for list handling
def ProcessList(_func, _list):
    return map( lambda x: ProcessList(_func, x) if type(x)==list else _func(x), _list )

def ProcessParallelLists(_func, *lists):
	return map( lambda *xs: ProcessParallelLists(_func, *xs) if all(type(x) is list for x in xs) else _func(*xs), *lists )

#Preparing input from dynamo to revit
def Unwrap(item):
	return UnwrapElement(item)
	
def ToRevit(item):
	return item.ToRevitType(True)

if isinstance(IN[0], list):
	wall = ProcessList(Unwrap, IN[0])
else:
	wall = list(Unwrap(IN[0]))
	
if isinstance(IN[1], list):
	divisionLines = ProcessList(ToRevit, IN[1])
else:
	divisionLines = list(ToRevit(IN[1]))

if isinstance(IN[2], list):
	sketchPlane = ProcessList(Unwrap, IN[2])
else:
	sketchPlane = list(Unwrap(IN[2]))

#Define function for API method:

def dividePart(wall,divisionLines,sketchPlane):
	wallList = List[ElementId]()
	wallList.Add(wall.Id)
	intersectionElementsIds = List[ElementId]()
	curveArray = List[Curve](divisionLines)
	
	if PartUtils.AreElementsValidForCreateParts(doc, wallList):
		createParts = PartUtils.CreateParts(doc, wallList)
		doc.Regenerate()
		
	parts = PartUtils.GetAssociatedParts(doc, wall.Id, 0, 0)
	
	return PartUtils.DivideParts(doc, parts, intersectionElementsIds, curveArray, sketchPlane.Id)

TransactionManager.Instance.EnsureInTransaction(doc)

partDivide = ProcessParallelLists(dividePart, wall, divisionLines, sketchPlane)

TransactionManager.Instance.TransactionTaskDone()

OUT = partDivide

##Code for getting location line

import clr

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

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

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

def UnwrapAndGetLocation(item):
	return UnwrapElement(item).Location.Curve.ToProtoType(True)

OUT = ProcessList(UnwrapAndGetLocation, IN[0])

##Dynamo file:
partDivisionLinesMultiple.dyn (26.5 KB)

##Screenshot:

28 Likes