Floor By Nested Loops

Trying to create a series of floors from pairs of closed polycurves (an outer boundary and a hole) for a tower project that has an atrium - and every level is different.

Is this seemingly basic operation not possible with dynamo? Tried DirectShape.ByGeometry, which seemed promising however then it doesn’t behave like a floor, so we can’t add slab edges or cut with a shaft for stairs, lifts and services. Any love of nested loops in floor sketches?

I believe that floor creation methods don’t like holes, and I am not aware of a good workaround. Try using a shaft instead to create the opening?

might help?

Quick proof of concept to show it is somewhat possible. (needs some work for sure)

Basically you do the following:

  1. Create floor with outer sketch

  2. Generate inner sketch

  3. Retrieve model curves from sketch

  4. In one transaction, copy modelcurves in place, translate to new location.

3 Likes

Nice work John!

Any chance a similar approach would work for Filled Regions?

Theory seems sound… perhaps John can post the code so we can test it for him? :wink:

The create method for filled region expects a list of curve loops and doesn’t need any special workarounds.

For nested loops, holes in the Filled Region, too?

Hi Greg,

I’ve adapted the python code written by Kulkul here Filled Region from Room Boundary to accept an outer curve and a list of nested loops.

I’ve also used GroupCurves from archi-lab to arrange the curves in a continuous loop.

If you are working in the metric system you need to adjust the points scale before creating the filled region.

FilledRegion03.dyn (18.1 KB)

3 Likes

Awesome! Thanks, @Giovanni_Brogiolo!

Hi Giovanni,

I came across your code and it’s just what I was looking for. Thanks for that.
Still, I’m not able to have it working. I have a couple of question as I’m a total newbie regarding Python.

First, what do you mean by adjust points scale? I’m in metric system.

Second, I’m guessing I’m not setting things correctly as I intend to get a separated result for each room.

Third, I get an error on the dynamo node, but perhaps it’s related to the previous questions. I’ll join a srceenshot.

If ever you have some time to check it out.

Thanks!
2018-02-16_18h29_18
01 - Rooms outer hatch.dyn (28.8 KB)

Hi Vincent,

  1. Revit internally works with a fixed set of database units and Filled Regions require a list of points in Feet. One way to convert a Dynamo Point to a Revit Point is using ToRevitType(). See here for more info: https://github.com/DynamoDS/Dynamo/wiki/Python-0.6.3-to-0.7.x-Migration and a brief summary here https://giobel.github.io/Dynamo-Python/

  2. The original script did not work with multiple inputs (i.e. it works with 1 room at a time). Also I think in your script you swap the inner and the outer curves.

  3. Yes the error is related to the fact that you were feeding a list of inputs. Also the input lists must have a certain structure (rank) otherwise the for loop in the python node will not work.

image

This converts the dynamo points in revit points:

import clr

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

clr.AddReference("RevitNodes")
import Revit
# Import ToProtoType, ToRevitType geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)


pts = IN[0]
elementlist = []

for i in range(0,len(pts)):
	p = []
	elementlist.append(p)
	for j in range(0,len(pts[i])):
		p.append(pts[i][j].ToRevitType())

newList = []

for el in elementlist:
	newList.append([el])


OUT = newList

And this creates the filled regions:

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application


regions = []
activeViewId = doc.ActiveView.Id;

outerCurves = UnwrapElement(IN[0])
innerCurves = UnwrapElement(IN[1])

filledRegionType = UnwrapElement(IN[2])

TransactionManager.Instance.EnsureInTransaction(doc)

for pts,innerPts in zip(outerCurves,innerCurves):
	profileLoops = []
	outerProfileLoop = CurveLoop()
	profileLoops.Add(outerProfileLoop)
	#create filled region
	for i in range(0,len(pts)-1):
		outerProfileLoop.Append(Autodesk.Revit.DB.Line.CreateBound(XYZ(pts[i].X,pts[i].Y,pts[i].Z),XYZ(pts[i+1].X,pts[i+1].Y,pts[i+1].Z)))

	for j in range(0,len(innerPts)):
		innerProfileLoop = CurveLoop()
		profileLoops.Add(innerProfileLoop)
		for i in range(0,len(innerPts[j])-1):
			innerProfileLoop.Append(Autodesk.Revit.DB.Line.CreateBound(XYZ(innerPts[j][i].X,innerPts[j][i].Y,innerPts[j][i].Z),XYZ(innerPts[j][i+1].X,innerPts[j][i+1].Y,innerPts[j][i+1].Z)))
			
	region = FilledRegion.Create(doc,filledRegionType[0].Id,activeViewId,profileLoops)
	regions.Add(region)

TransactionManager.Instance.TransactionTaskDone()

#Assign your outputs to the OUT variable
OUT = regions

You could also directly extract the boundaries (both inner and outer) from an element instead of reconstructing them by points. This is more efficient than the previous method:

image

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

# Import DocumentManager and TransactionManager
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
activeViewId = doc.ActiveView.Id;

area = UnwrapElement(IN[0])

filledRegionType = UnwrapElement(IN[1])

opt = SpatialElementBoundaryOptions()
opt.StoreFreeBoundaryFaces = False
opt.SpatialElementBoundaryLocation.Center

listRegions = []

TransactionManager.Instance.EnsureInTransaction(doc)

for i in range(0,len(area)):
	profileLoops = []
	segs = area[i].GetBoundarySegments(opt)
	for i in range(0,len(segs)):
		pL = CurveLoop()
		profileLoops.Add(pL)	
		for j in range(0,len(segs[i])):
			pL.Append(segs[i][j].GetCurve())
	listRegions.append(FilledRegion.Create(doc,filledRegionType[0].Id,activeViewId,profileLoops))

TransactionManager.Instance.TransactionTaskDone()

OUT = listRegions

01 - Rooms outer hatch.dyn (36.9 KB)

Cheers
Giovanni

3 Likes

Hi Giovanni,

Thanks so much for this complete answer! I love the didactic turn of your post and this forum in general! Very eager to give it a try!

It confirms, if I didn’t know already, that I absolutely need to learn Python.

Cheers

1 Like

Hi Giovanni,

I’m not sure if I should go on with posting about filled regions here as the topic is about floors. Should this sub-topic be moved to new post?

I’ve ran your python and it worked perfectly! Still, I wanted to customize it as my goal is to create every filled region related to a specific room in a specifically created view. I figured your Code calls the active view as destination view. I tried my luck tweaking the python code in order to feed my list of views. Not a success.
I tried a workaround with nodes but i guess it’s both inefficient and inelegant.

I’d prefer to ask for hints to change the code, so I can at least learn something.

I have my two lists of loops (from rooms), my list of views (created after the same rooms):
How could I combine those to create the filled region in separate views in the Python code?

Thanks

Hi Vincent,

You could use the Element.CopyFromViewToView from Rhythm or add a loop in the Python code (if you look at the python code inside Rhythm it shows how to do it).

1 Like

Thanks Giovanni,

I’ll check this out.

Cheers

Trying to cap bottom boundaries of hanging walls with ceilings.
Well, if we can’t create ceilings, i want to try copy some ceiling and replace it’s boundary.
I’ve got a set of curves and lines, sorted with group.curves and checked (with creating floors with them).

So, i need something like this:
Input: host (ceiling), curve(new boundary of ceiling with removed inner holes).
Output: newhost( ceiling with new boundary)

Is it possible? Thanks in advance!

P.S. Successfully used Springs.FloorOpening.ByCurves node for cutting openings in ceilings!

Sadly, no. We still don’t have access to ceiling creation exposed in the API.

1 Like

Please please please please please don’t do this.

You can use the API to copy and modify existing ceilings (so they become the new one). Direct creation isn’t possible though, so you have to already have a ceiling element in the model. That code is not mine so I am not at liberty to share though.

1 Like