Creating stairs in Dynamo

Hello everyone,

I am trying to create stairs in Revit with a help of Dynamo. Just as exercise but it is not so easy how I expected to be. Unfortunatelly there is not specific built In Node just for stairs.Only what I found to creating elements are the Nodes under Family Instance. So there is a Node with Input Family Type and set up the coordinates.

I was trying to figured out how the structure with stairs really works.What here is Family Type, Family name etc.? I tried in other direction, from run Type to stairs. I got a Family name right but now I have a problem with the Type name. How to get that?

Thank you in advance for your answers :slight_smile:

The elements which defines stairs cannot be built from scratch and have your stair function as you would expect. You need to build them as stairs via the UI or the actionable API method. I am not aware of any node to do so, but there is sample code for creating a new stair available in a few places online, my preferred starting point would be here: Creating and Editing Stairs

You will need to build your own node for this, which can be done by converting that c# code to Python, or by producing a zero touch node.

2 Likes

Thanks Jacob for the link and your answer. Man this is a new world to me.I am not fimilar with Python so maybe I need to start learning it to be able to do what you’re suggesting.I will try it. Thank you…

1 Like

Hi Jacob,
I try to give a shot at converting to Python the c# code of the link you provided.
I feel I’m close to the target but I’m getting an error when calling the function (see image below)
immagine

The code is below:

# BOILERPLATE
import clr
import sys
sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib')
import System
from System import Array
from System.Collections.Generic import *
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager 
from RevitServices.Transactions import TransactionManager 

clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")

import Autodesk 
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *

#from Autodesk.Revit.DB import XYZ, Line, CurveLoop, StairsEditScope, FailureProcessingResult

# FUNCTIONS
class StairsFailurePreprocessor:
	def PreprocessFailures(self, failuresAccessor):
		#Use default failure processing
		return FailureProcessingResult.Continue
		
	def CreateStairs(document, levelBottom, levelTop):
		newStairsId = None
		
		with StairsEditScope(document, "New Stairs") as newStairsScope:
			newStairsId = newStairsScope.Start(levelBottom.Id, levelTop.Id)
			
			with Transaction(document, "Add Runs and Landings to Stairs") as stairsTrans:
				stairsTrans.Start()

				# create a sketched run for the stairs
				bdryCurves = []
				riserCurves = []
				pathCurves = []
				
				# create points
				pt1 = XYZ(0, 0, 0)
				pt2 = XYZ(15, 0, 0)
				pt3 = XYZ(0, 10, 0)
				pt4 = XYZ(15, 10, 0)
				
				# define boundaries
				bdryCurves.append(Line.CreateBound(pt1, pt2))
				bdryCurves.append(Line.CreateBound(pt3, pt4))
				
				#riser curves
				riserNum = 20
				
				for i in range(0,riserNum+1):
					end0 = (pt1 + pt2) * i / riserNum
					end1 = (pt3 + pt4) * i / riserNum
					end2 = XYZ(end1.X, 10, 0)
					riserCurves.append(Line.CreateBound(end0, end2))
				
				# stairs path curves
				pathEnd0 = (pt1 + pt3) / 2
				pathEnd1 = (pt2 + pt4) / 2
				pathCurves.append(Line.CreateBound(pathEnd0, pathEnd1))
				
				newRun1 = StairsRun.CreateSketchedRun(document, newStairsId, levelBottom.Elevation, bdryCurves, riserCurves, pathCurves)
				
				# add a straight run
				locationLine = Line.CreateBound(XYZ(20, -5, newRun1.TopElevation), XYZ(35, -5, newRun1.TopElevation))
				newRun2 = StairsRun.CreateSketchedRun(document, newStairsId, locationLine, StairsRunJustification.Center)
				newRun2.ActualRunWidth = 10
				
				# add a landing between the runs
				landingLoop = CurveLoop()
				p1 = XYZ(15, 10, 0)
				p2 = XYZ(20, 10, 0)
				p3 = XYZ(20, -10, 0)
				p4 = XYZ(15, -10, 0)
				curve_1 = Line.CreateBound(p1, p2)
				curve_2 = Line.CreateBound(p2, p3)
				curve_3 = Line.CreateBound(p3, p4)
				curve_4 = Line.CreateBound(p4, p1)
				
				# append the curves to the landing loop
				landingLoop.Append(curve_1)
				landingLoop.Append(curve_2)
				landingLoop.Append(curve_3)
				landingLoop.Append(curve_4)
				
				# create the landing
				newLanding = StairsLanding.CreateSketchedLanding(document, newStairsId, landingLoop, newRun1.TopElevation)
				
				stairsTrans.Commit()
				
			# a failure preprocessor is to handle possible failures during the edit mode commitment process.
			
			newStairsScope.Commit(StairsFailurePreprocessor())
		
		return newStairsId


# INPUTS
startLevel = IN[0]
endLevel = IN[1]

# CODE
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication 
app = uiapp.Application 
uidoc = uiapp.ActiveUIDocument
	
sfp = StairsFailurePreprocessor()

# OUTPUTS
OUT = sfp.CreateStairs(doc, startLevel, endLevel)

Any help would be appreciated to solve the issue (and provide me and my friend Damir getting a new custom node :wink: )
Thanks in advance

P

1 Like

Oooo there is Paolo, the Master of Python hehe…Thank you Paolo for your effort finding the answer for this script.I appreciate it very much :slight_smile:

1 Like

Ahah, if I was a true master I wouldn’t be asking for help as I’m stuck :sweat_smile:
Glad to help you in this quest. :wink:

Bye
Paolo

Hi there,
got some steps further with the code and debug it.
I’m at the very end now but I can’t manage to get the FailuresPreprocessor working.
See my updated code below (i left a few commented lines for other unsuccessful tests I did).

# BOILERPLATE
import clr
import sys
sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib')
import System
from System import Array
from System.Collections.Generic import *
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager 
from RevitServices.Transactions import TransactionManager 

clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")

import Autodesk 
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *

#from Autodesk.Revit.DB import XYZ, Line, CurveLoop, StairsEditScope, FailureProcessingResult

# FUNCTIONS
class StairsFailurePreprocessor:
	#def PreprocessFailures(self, failuresAccessor):
	def PreprocessFailures(failuresAccessor):
		#Use default failure processing
		return FailureProcessingResult.Continue
		
def CreateStairs(document, levelBottom, levelTop):
	newStairsId = None
	
	with StairsEditScope(document, "New Stairs") as newStairsScope:
		newStairsId = newStairsScope.Start(levelBottom.Id, levelTop.Id)
		
		with Transaction(document, "Add Runs and Landings to Stairs") as stairsTrans:
			stairsTrans.Start()

			# create a sketched run for the stairs
			bdryCurves = []
			riserCurves = []
			pathCurves = []
			
			# create points
			pt1 = XYZ(0, 0, 0)
			pt2 = XYZ(15, 0, 0)
			pt3 = XYZ(0, 10, 0)
			pt4 = XYZ(15, 10, 0)
			
			# define boundaries
			bdryCurves.append(Line.CreateBound(pt1, pt2))
			bdryCurves.append(Line.CreateBound(pt3, pt4))
			
			#riser curves
			riserNum = 20
			
			for i in range(0,riserNum+1):
				end0 = (pt1 + pt2) * i / riserNum
				end1 = (pt3 + pt4) * i / riserNum
				end2 = XYZ(end1.X, 10, 0)
				riserCurves.append(Line.CreateBound(end0, end2))
			
			# stairs path curves
			pathEnd0 = (pt1 + pt3) / 2
			pathEnd1 = (pt2 + pt4) / 2
			pathCurves.append(Line.CreateBound(pathEnd0, pathEnd1))
			
			newRun1 = Autodesk.Revit.DB.Architecture.StairsRun.CreateSketchedRun(document, newStairsId, levelBottom.Elevation, bdryCurves, riserCurves, pathCurves)
			
				# add a straight run
			locationLine = Line.CreateBound(XYZ(20, -5, newRun1.TopElevation), XYZ(35, -5, newRun1.TopElevation))
			newRun2 = Autodesk.Revit.DB.Architecture.StairsRun.CreateStraightRun(document, newStairsId, locationLine, Autodesk.Revit.DB.Architecture.StairsRunJustification.Center)
			newRun2.ActualRunWidth = 10
			
			# add a landing between the runs
			landingLoop = CurveLoop()
			p1 = XYZ(15, 10, 0)
			p2 = XYZ(20, 10, 0)
			p3 = XYZ(20, -10, 0)
			p4 = XYZ(15, -10, 0)
			curve_1 = Line.CreateBound(p1, p2)
			curve_2 = Line.CreateBound(p2, p3)
			curve_3 = Line.CreateBound(p3, p4)
			curve_4 = Line.CreateBound(p4, p1)
			
			# append the curves to the landing loop
			landingLoop.Append(curve_1)
			landingLoop.Append(curve_2)
			landingLoop.Append(curve_3)
			landingLoop.Append(curve_4)
			
			# create the landing
			newLanding = Autodesk.Revit.DB.Architecture.StairsLanding.CreateSketchedLanding(document, newStairsId, landingLoop, newRun1.TopElevation)
			
			stairsTrans.Commit()
			
		# a failure preprocessor is to handle possible failures during the edit mode commitment process.
		
		sfp = StairsFailurePreprocessor()
		newStairsScope.Commit(sfp.PreprocessFailures())
		
		#newStairsScope.Commit(sfp)
		#newStairsScope.Commit(StairsFailurePreprocessor())
	
	return [newStairsId, doc.GetElement(newStairsId)]


# INPUTS
startLevel = UnwrapElement(IN[0])
endLevel = UnwrapElement(IN[1])

# CODE
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
uidoc = uiapp.ActiveUIDocument

# OUTPUTS
OUT = CreateStairs(doc, startLevel, endLevel)

The output is the following:

If I remove the line newStairsScope.Commit(sfp.PreprocessFailures()) the code seems to go fine (I get an ElementId) but no results is showing in Revit and such ElementId is not searchable in Revit either.
See output below:


And Revit warning if I try to access the element by its Id:
immagine

Thanks again for your help.

Paolo

Hehe man in my eyes you’re the master.Thank you again :)…