Cut sloped floors intersecting with a plane

Hi there,
@Nick_Boyts

After reading the following post.

Cut all elements intersecting with a plane - Revit - Dynamo (dynamobim.com)

I made this script to cut floors by a project-wide grid.

split test 02.rvt (512 KB)
vloeren splitsen 03.dyn (102.6 KB)

I have two questions.

  1. Is there a way to manipulate planes by reference planes rather than to define them by Coordinates?
    2.As mentioned in the post, angled or non planar floors are not accepted as the curves do not define a horizontal loop. Any ideas on how to take this further?

I understand System-family-Floors have some restrictions, but any quantifiable copy will do.

The purpose is quantification and phasing on a dedicated model.

Kind regards,

Willem

Do you really want to overlap floors on top of floors like this? Seems like creating parts and splitting those would be better here as they’d keep the original geometry and not insert new warnings.

1 Like

Hi @jacob.small

very generous to give the solution to 2 questions by making only 1 suggestion!

This covers already 50 % of the needs.

…

And as it appears, the first solutions covers non-planar floors too.

The fact that we have two Floors in the same place in the same place is not really an issues, I would consider a separate model for quantification.

1.If there is a way to use the parts-strategy for non-planar Floors, do not hesitate to share.
2.If no, is there a way to tie geometry to a reference plane, so reference planes can serve both strategies?

Kind regards,

Willem

So the advantage with parts is that it’s already pretty automated in the user interface -

  1. Draw and name your reference planes
  2. Select your floors to make parts from
  3. Make the floor into parts
  4. Select the parts to split the parts by the reference plans by selecting the reference planes by name.

This covers pretty much any shape which can make parts, so most floors, and in the sample you provided is a total of 10 clicks, and likely runs faster than it’ll take to open Dynamo. That said you might want to automate it all the same, so…

The API and therefore Dynamo also enable the same sequence; I recall there are packages to do this but I couldn’t remember what they were and didn’t have them in my library so I built some Python.

The graph:

I used filters for the comment parameter of the floor and the name of the reference planes; you might want another method (select model elements might work better here instead). Then the first bit of Python creates parts from the floors.

Create or Get Parts From Element
########################################
############## Properties ##############
########################################
__author__ = 'Jacob Small'
__version__ = '0.1.0'
__description__ = "Generate or get the parts from an element."
__RevitBuilds__ = "2023.1"
__DynamoBuilds__ = "2.16"
__ReleaseNotes__ = "POC only - review and test closely before using in production."
__Dependancies__ = "None"
__Copyright__ = "2024, Autodesk Inc."
__License__ = "Apache 3"



########################################
### Configure the Python environment ###
########################################
### standard imports ###
import sys #add the sys class to the Python environment so we can work with the sys objects
import clr #add the CLR (common language runtime) class to the Python environment so we can work with .net libraries

### Dynamo Revit imports ###
clr.AddReference("RevitNodes") #add Dynamo's Revit nodes library to the clr
import Revit #import Dynamo's Revit node class
clr.ImportExtensions(Revit.Elements) #add the element conversion methods to the CLR
clr.AddReference("RevitServices") #add the Revit services library to the CLR
import RevitServices #import the Revit services class to the Python environment
from RevitServices.Persistence import DocumentManager #import the document manager class to the Python environment 
from RevitServices.Transactions import TransactionManager #import the transaction manager class to the Python environment 

### Revit API imports ###
clr.AddReference("RevitAPI") #add the Revit API to the CLR
import Autodesk #add the Autodesk class to the Python environment 
from Autodesk.Revit.DB import * #import every class of the Revit API to the Python environment

### .net imports ###
clr.AddReference("System") #add the .net System library to the CLR
from System.Collections.Generic import List as NetList #imports the .net list module to the Python environment as the alias NetList



#########################################
###### Global variables and inputs ######
#########################################
### documents and standard variables ###
doc = DocumentManager.Instance.CurrentDBDocument #the current Revit document
### imports and unwrapping ###
floors = UnwrapElement(IN[0]) #import the floors from IN[0] of the Dynamo environment and convert to native Revit elements
if not floors.__class__ == [].__class__ : floors = [floors] #ensure that floors is a list, not an individual object, so that we can always prepare for a loop

OUT = [] #set the OUTput to an empty list

TransactionManager.Instance.EnsureInTransaction(doc) #start transaction
for floor in floors: #for eachf loor
    hasParts = PartUtils.HasAssociatedParts(doc,floor.Id) #check if the floor has aprts
    if not hasParts: #if not
        makeParts = PartUtils.CreateParts(doc, NetList[ElementId]([floor.Id])) #make parts from the element
        doc.Regenerate() #regenerate the document
    partIds = PartUtils.GetAssociatedParts(doc,floor.Id,True,True) #get the part ids from the element
    parts = [doc.GetElement(p) for p in partIds] #for each part from the part Id
    OUT.append(parts) #append the list of parts to the OUT list
TransactionManager.Instance.TransactionTaskDone() #end transaction

From there I used another bit of python to split the parts - you may want to filter some stuff out - perhaps only split parts which are finish floors, or which are a particular material, or something else.

Split Parts by Elements
########################################
############## Properties ##############
########################################
__author__ = 'Jacob Small'
__version__ = '0.1.0'
__description__ = "Splits each part in a list of parts by a list of datum elements."
__RevitBuilds__ = "2023.1"
__DynamoBuilds__ = "2.16"
__ReleaseNotes__ = "POC only - review and test closely before using in production."
__Dependancies__ = "None"
__Copyright__ = "2024, Autodesk Inc."
__License__ = "Apache 3"



########################################
### Configure the Python environment ###
########################################
### standard imports ###
import sys #add the sys class to the Python environment so we can work with the sys objects
import clr #add the CLR (common language runtime) class to the Python environment so we can work with .net libraries

### Dynamo Revit imports ###
clr.AddReference("RevitNodes") #add Dynamo's Revit nodes library to the clr
import Revit #import Dynamo's Revit node class
clr.ImportExtensions(Revit.Elements) #add the element conversion methods to the CLR
clr.AddReference("RevitServices") #add the Revit services library to the CLR
import RevitServices #import the Revit services class to the Python environment
from RevitServices.Persistence import DocumentManager #import the document manager class to the Python environment 
from RevitServices.Transactions import TransactionManager #import the transaction manager class to the Python environment 

### Revit API imports ###
clr.AddReference("RevitAPI") #add the Revit API to the CLR
import Autodesk #add the Autodesk class to the Python environment 
from Autodesk.Revit.DB import * #import every class of the Revit API to the Python environment

### .net imports ###
clr.AddReference("System") #add the .net System library to the CLR
from System.Collections.Generic import List as NetList #imports the .net list module to the Python environment as the alias NetList



#########################################
###### Global variables and inputs ######
#########################################
doc = DocumentManager.Instance.CurrentDBDocument #the current Revit document

parts = UnwrapElement(IN[0]) #import the parts from IN[0] of the Dynamo environment and convert to native Revit elements
if not parts.__class__ == list : parts = [parts] #ensure the parts variable is a list

splitters = UnwrapElement(IN[1]) #import the splitter elements from IN[1] of the Dynamo environment anc onvert to native Revit elements
if not splitters.__class__ == list : splitters = [splitters] #ensure the splitters variable is a list
splitters = [e.Id for e in splitters] #convert the splitter elements to a list of element IDs
splitters = NetList[ElementId] (splitters) #convert the splitters list to a .net list

defaultSketchPlane = FilteredElementCollector(doc).OfClass(SketchPlane).FirstElementId() #get a sketch plane to use when dividing parts

OUT = [] #set the OUTput to an empty list

TransactionManager.Instance.EnsureInTransaction(doc) #start transaction
for part in parts: #for each part
    partId = NetList[ElementId] ([part.Id]) #get the aprt ID as a .net list
    dividing = PartUtils.DivideParts(doc, partId, splitters, NetList[Curve](), defaultSketchPlane) #divide the part by the splitter elements
    OUT.append(dividing) #append the dividing result to the OUT list
TransactionManager.Instance.TransactionTaskDone() #end transaction

Thank you @jacob.small , this is really helpfull and gives an insight in how to tweak the script.

You are right that the user interface is straightforward and fast, the reason I look at Dynamo is that;
-I want to assign metadata both from the original element as from the reference planes.
-Both the original element and the data tend to change through iterations in the design process.

Therefore I would like to adress the non-coplanar copy accordingly, following the same reference planes.

What is the shortest way to get reference planes recognized as geometry?

Kind regards,

Willem

Split floors by reference planes.dyn (65.1 KB)
split test 03.rvt (468 KB)

Well that node shouldn’t be used - it is a stopgap at best as lunchbox hasn’t been supported for Dynamo in a half decade now as the owners abandoned the code base way back when.

You can pull the geometry from a reference plan using a property of the reference plane. In your library go to:
Revit > Elements > Reference Planes > ?

One of the items in the properties (the ? section) will have a ‘Plane’ or ‘Dynamo Plane’ or similar.

OK, there is a "ReferencePlane.Plane " Node indeed…I’ll try to build the habit of looking there first

Although it seems better practice to manage these routines separately, I post them together if anybody is interested.


Split floors by Floors and parts reference planes.dyn (92.9 KB)

1 Like

Hi @jacob.small

I try to run the same graph, but now on a linked model.

Creating floors goes well, but there is an issue with the parts.

Dividing Parts is not available in the user interface when I select a part on a linked model…so is it doable through script, or can they only be made in the original model?

Kind regards,

Willem
Split floors by Floors and parts reference planes gelinkte elementen 03.dyn (69.3 KB)

split test 03.rvt (508 KB)
copie voor P & F met eigen refplanes.rvt (492 KB)

Are you creating the floor in the link somehow? Or in your model?

If you’re trying to make parts in your model from an element in a link you need to make quite a few changes. The method for that requires a LinkElementId instead of a ElementId, which requires a different constructor.

The specific method to create parts form a linked element is shown here: CreateParts Method (Document, ICollection(LinkElementId))

And the constructor for a LinkedElementId which is required in that method is here: LinkElementId Constructor (ElementId, ElementId).

I don’t have time to rework the code for this added complexity, but if you give it a shot I can try to guide you along.

Hi @jacob.small

Can you explain me the method on where to apply the code you show me?

Is this in the OODB “Parts.DivideParts” node?

Kind regards,

Willem

In the previous Python code I built an element ID and used that to create the parts. This was done any time I used this in the code: floor.Id, always inside of the for floor in floors: loop. If you want to use a linked element instead of using a floor.Id you’ll need to generate a linked element Id before the hasParts line. This is done using the method above, but the implementation will vary depending on what your object is. If you’re feeding a LinkedElement you may be able to just pull the linked element id directly, and if not you’ll need to construct one using the method above. This is a short week here in Sweden (2 days off) so I can’t build an example, but if you give it a shot on your own I can try to give you some guidance. If you’re just getting started I recommend commenting where you need to make changes first and adding the code as a prefix second.

Hi @jacob.small

This script serves me really well.

Onderdoorgang LS

But now I have an issue in quantification.

In order to make a redundant workflow, i would have to manage in advance, whether or not a part will meet a reference plane.

The way I understand it, the little triangle on the left is 2nd generation part (Parts.DivideParts node) , where the bigger triangle on the right is first generation part, a result of the standard command on the original system family.

Is there a way I can identify this kind of parts, in order to manage them?

If not, I would have to make a script that parts parts that do not encounter reference planes a second time…but i do not know whether these will be accepted.

That’s an awesome animation!

Not entirely sure I understand the ask on the parts though… are you looking to not split a part by a plane which doesn’t intersect it? That should be happening already based on how the tool works… or I think it should be. Do you have a simple illustration (two parts, one with the issue one without) or dataset that you can share?

Hi Jacob,
it would be the other way round.
I would like to manage 3 levels.
-The original system families.
-1st generation parts, by standard command.
-2nd generation part, by the Parts.DivideParts
Consider two floors, where the red one encounters reference plane for chopping, and the green one not.

image

This results in double counting of the elements which are being parted twice.

image

But there is a Node “Parts Genealogy”, and if you extend it a bit, you can sort out second generation parts with heirs.

This doesn’t look like a UI I am familiar with - is it a Revit add-in?

This is the browser based Autodesk application “Assemble”.

So now we have a interactive display on the model, to check wheter the original system families & last generation parts have the same quantity.

The next step is to do the same thing in Power BI, in order to handle the Autodesk copy that cannot to publish to assemble, handle more models simultaneously, and have better management of the applied filters.

Ah! I am not familiar enough with assemble to catch the partial screenshot from before.

If you manually divide a part in the UI do you get the same behavior in assemble? If so then it may not be avoidable.