Roof Slope script

Hi,
I’ve search all the forum and google, and I couldn’t find an example in Python to Set Slope to Roof. There are RevitAPI example but in C# and VB I can’t figure out how to code them in Python.
So the code to create the roof creates a roof as a slab with no slope

import clr

#Import Revit Nodes
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

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

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

#The inputs to this node will be stored as a list in the IN variables.
lines = IN[0].ToRevitType()
level = UnwrapElement(IN[1])
type = UnwrapElement(IN[2])
output = []

doc = DocumentManager.Instance.CurrentDBDocument

#Create Curve Array
cArray = CurveArray()
mcArray = clr.StrongBox[ModelCurveArray](ModelCurveArray())
#Loop through lines
for line in lines:
cArray.Append(line)

#Start Transaction
TransactionManager.Instance.EnsureInTransaction(doc)

#Create Roof
roof = doc.Create.NewFootPrintRoof(cArray, level, type, mcArray)
output.append(roof.ToDSType(False))

#End Transaction
TransactionManager.Instance.TransactionTaskDone()

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

I know I have to do something with the mcArray to set the slope parameter to the sketch model line or to set a curve as a slope arrow for the Roof.
Perhaps there is something to do from Link with this part,

ModelCurveArrayIterator iterator
    = footPrintToModelCurveMapping.ForwardIterator();
 
  iterator.Reset();
  while( iterator.MoveNext() )
  {
    ModelCurve modelCurve = iterator.Current as ModelCurve;
    footprintRoof.set_DefinesSlope( modelCurve, true );
    footprintRoof.set_SlopeAngle( modelCurve, 0.5 );
  }

but as I just started using Python (especially RevitAPI) I can’t figure the syntax.
Please help

Based on C# example, I’ve changed the code

import clr

#Import Revit Nodes
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)

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

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

#The inputs to this node will be stored as a list in the IN variables.
lines = IN[0].ToRevitType()
level = UnwrapElement(IN[1])
type = UnwrapElement(IN[2])
output = []

doc = DocumentManager.Instance.CurrentDBDocument

#Create Curve Array
footprint = CurveArray()
footPrintToModelCurveMapping = ModelCurveArray()
#Loop through lines
for line in lines:

footprint.Append(line.Curve)
footPrintToModelCurveMapping.Append(line.GeometryCurve)

iterator = footPrintToModelCurveMapping.ForwardIterator()
iterator.Reset()

while( iterator.MoveNext() )
line = iterator.Current as ModelCurve
footprintRoof.set_DefinesSlope( line, true )
footprintRoof.set_SlopeAngle( line, 0.5 )

#Start Transaction
TransactionManager.Instance.EnsureInTransaction(doc)

#Create Roof
roof = doc.Create.NewFootPrintRoof(footprint, level, type, out footPrintToModelCurveMapping)
output.append(roof.ToDSType(False))

#End Transaction
TransactionManager.Instance.TransactionTaskDone()

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

but it gives a error:
Warning: IronPythonEvaluator.EvaluateIronPythonScript operation failed.
unexpected token ‘’

please help

Can’t really help you with your script as it is not properly formatted. Can you try fixing it first? Especially the indenting within the for and while loops. Also make sure the clr.AddReference()‘s are using single quote (’) instead of double quote (")

1 Like

Thank you kennib6 ,
please have a look as I tried to do what you said, unfortunately I’m not sure regarding while

import clr

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

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

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

#The inputs to this node will be stored as a list in the IN variables.
lines = IN[0].ToRevitType()
level = UnwrapElement(IN[1])
type = UnwrapElement(IN[2])
output = []

doc = DocumentManager.Instance.CurrentDBDocument

#Create Curve Array
footprint = CurveArray()
footPrintToModelCurveMapping = ModelCurveArray()		
#Loop through lines
for line in lines:
	
	footprint.Append(line.Curve)
	footPrintToModelCurveMapping.Append(line.GeometryCurve)
	
iterator = footPrintToModelCurveMapping.ForwardIterator()
iterator.Reset()

while iterator.MoveNext() 
	line = iterator.Current as ModelCurve	
	footprintRoof.set_DefinesSlope( line, true )
	footprintRoof.set_SlopeAngle( line, 0.5 )
	
#Start Transaction
TransactionManager.Instance.EnsureInTransaction(doc)

#Create Roof
roof = doc.Create.NewFootPrintRoof(footprint, level, type, out footPrintToModelCurveMapping)
output.append(roof.ToDSType(False))


#End Transaction
TransactionManager.Instance.TransactionTaskDone()

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

while iterator.MoveNext():

Unfortuantely then you get an unexpected token ‘as’

line = iterator.Current as ModelCurve

hopefully Kenny can help tell us the correct Python syntax for this :slight_smile:

I’m also very curious as to why the iterator is being used as compared to a for loop, perhaps Kenny would be good enough to explain?

Thanks a lot,

Mark

EDIT: @Jonathan.Olesen would you mind giving some advice? :slight_smile:

2 Likes

Unfortunately I am leaving the office for the night and I am pretty bad at C# to python. However, the footprint = CurveArray() is unneeded as you don’t need to initialize variables in python, except to make it a list. So replace that with footprint = [] and footPrintToModelCurveMapping = []

Inside the for loop, .append should be lowercase. Anything more would take me a bit to look at that I would rather go home :joy:

The reason why iterator.MoveNext() can be used in the while loop is that it will return a bool of false if it is not able to do so. So instead of explicitly stating when the loop ends, it will just continue iterating until it cannot anymore.

Edit: Also, as @Mark.Ackerley said, the as doesn’t work in Python.

2 Likes

I’ll have a look when I get a second, @TeoBlack please do provide a sample Revit file to test on though :slight_smile:

1 Like

You’re a gent!

I am attaching all the files although the revit project is a blank project started with default structural template 2016
Project1.rvt (2.8 MB)

Creating Line Based Roof with slope.dyn (9.2 KB)
pycode roof.txt (1.5 KB)

1 Like

See if this makes sense :slight_smile:

Think I got lost along the way, but tried to show two approaches (both using the "while statement and the approach originally by @Dimitar_Venkov) in this post NewFootPrintRoof Troubles :wink:

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

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

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

#The inputs to this node will be stored as a list in the IN variables.
lines = IN[0].ToRevitType()
level = UnwrapElement(IN[1])
type = UnwrapElement(IN[2])
output = []

doc = DocumentManager.Instance.CurrentDBDocument

footprint = CurveArray()
for o in lines:
    footprint.Append(o )
TransactionManager.Instance.EnsureInTransaction(doc)

roofCurves = StrongBox[ModelCurveArray](ModelCurveArray() )
type = UnwrapElement(IN[2])
footprintRoof = doc.Create.NewFootPrintRoof(footprint, level, type, roofCurves)

TransactionManager.Instance.TransactionTaskDone()

ftprintRoof = footprintRoof

iterator = roofCurves.ForwardIterator()
iterator.Reset()
while iterator.MoveNext():
    line = iterator.Current
    ftprintRoof.set_DefinesSlope( line, True )
    ftprintRoof.set_SlopeAngle( line, 0.5 )

OUT = footprintRoof.ToDSType(False), roofCurves.Value, ftprintRoof

image

Jonathan, thanks for your help
I am trying to figure out what did you do with this script, if I replace it, still doesn’t work
could you please give some more details?

Thanks, @Jonathan.Olesen as @TeoBlack says, it works in making the roof and it sets all lines to sloped…

Unfortunately that makes it flat, which is seemingly what the author intended, but isn’t so useful…

Can you help define which line will be sloped?

Unfortunately I don’t understand enough about how the Iterator is working…

I would have expected something like this to work, but it doesn’t, I can’t iterate over the strong box…

for r in range(len(IN[3])):
    if r == 1:
        for line in roofCurves:  
            ftprintRoof.set_DefinesSlope( line, True )
            ftprintRoof.set_SlopeAngle( line, 0.5 )   

Thank you for showing a C# interpreted to Python, very useful for using the API.

Also interesting that you finished the roof transaction before going back to make it sloped. But that sloping isn’t part of a transaction…

Thanks a lot,

Mark

I wish I can make one or all with a slope value to understand how it works, than I can decide which I need to make as a slope. For instance, I would set all the foot print lines to have a slope value and later to modify the script if I want it to be only the last one

@Mark.Ackerley & @TeoBlack

See if this gives a better idea of how you could change e.g. one angle:

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

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

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

#The inputs to this node will be stored as a list in the IN variables.
lines = IN[0].ToRevitType()
level = UnwrapElement(IN[1])
type = UnwrapElement(IN[2])
output = []

doc = DocumentManager.Instance.CurrentDBDocument

footprint = CurveArray()
for o in lines:
    footprint.Append(o)
TransactionManager.Instance.EnsureInTransaction(doc)

roofCurves = StrongBox[ModelCurveArray](ModelCurveArray() )
type = UnwrapElement(IN[2])
footprintRoof = doc.Create.NewFootPrintRoof(footprint, level, type, roofCurves)

TransactionManager.Instance.TransactionTaskDone()

ftprintRoof = footprintRoof
alllines = []
iterator = roofCurves.ForwardIterator()
iterator.Reset()
i = 0
while iterator.MoveNext():
    line = iterator.Current
    ftprintRoof.set_DefinesSlope( line, True )
    if i == 0:
        ftprintRoof.set_SlopeAngle( line, 0.2 )
        i = i + 1
    else:
        ftprintRoof.set_SlopeAngle( line, 0.6 )
        i = i + 1
    alllines.append(line)
#The value is a "slope" measurement. For example, 0.5 is one unit of rise for each 2 units of run. 
#This creates a slope of 26.57 degrees (the arctangent of 0.5).
OUT = footprintRoof.ToDSType(False), roofCurves.Value, ftprintRoof,alllines

image

image

image

Mind that the SlopeAngle is actually powered as follows according to the API:
The value is a “slope” measurement. For example, 0.5 is one unit of rise for each 2 units of run. This creates a slope of 26.57 degrees (the arctangent of 0.5).
http://www.revitapidocs.com/2018.1/4d58f862-472b-0cc1-a250-5ed1100911d9.htm

2 Likes

@Jonathan.Olesen that’s fantastic, thank you… I just added an input so that @TeoBlack can work out a way of defining which he wants sloped…

Would you mind saying a little about why an iterator is required here? It somehow allows you to iterate over a ModelCurveArray? It would be easier if we could use an enumerator so we could get an index, but that fails? Google isn’t giving me any answers I’m afraid :frowning:

i = 0
j = IN[3]

while iterator.MoveNext():
    line = iterator.Current
    ftprintRoof.set_DefinesSlope( line, True )
    if i == j:
        ftprintRoof.set_SlopeAngle( line, 0.2 )
        i = i + 1
    else:
        ftprintRoof.set_SlopeAngle( line, 0.6 )
        i = i + 1
    alllines.append(line)
1 Like

I’m not sure I can give you a concrete answer on the iterator method…
What I can say is that it has to do with working with the ModelCurveArray (Which is in fact model curves as can be seen in List 3 in my screenshot.) :slight_smile: But for an in depth description I have to refer to @Dimitar_Venkov who might be able to better explain :slight_smile:

(Mind this is the first time I have ever worked with RoofSlopes and generally not doing a lot atm with the Revit API :slight_smile: )

2 Likes

Thanks @Jonathan.Olesen :slight_smile:

I am going to try my best but I could be horribly wrong. Please someone with more knowledge can clarify any mistakes I made:

To understand why the iterator.MoveNext() is required, you have to go back through all of the commands, starting here:
roofCurves = StrongBox[ModelCurveArray](ModelCurveArray() )

This gives you an object of class ModelCurveArray. With a Revit type of array, there is a built in class for moving through each item inside of it, better than a for loop. That class is called using the method .ForwardIterator(), which is called for in this line:
iterator = roofCurves.ForwardIterator()

This returns the class ModelCurveArrayIterator, which has the method .MoveNext() used here:
while iterator.MoveNext():

All this to say why use .MoveNext(): The method basically is a ‘if another item in the list is possible, continue with the function using the next item. If it is not possible, return false, thus ending the while loop’. In a way, it is like a built in while loop using try: #function, except IndexError: break.

It is better than a for loop because you do not need to specify a length nor an iteration variable, as the property .Current acts as the i in for i in array:

It is basically a Revit version of python commands built into the class, I think.

2 Likes

I was trying it in Revit 2016 and it were giving me errors and couldn’t run the script, now I’ve tried in 2019 and it works. thanks a lot Gentleman.
now I will try to make it work in 2016 with 1.3 dynamo.
Thanks again for help

1 Like

@kennyb6 sweeet yes…

As you say, I was thinking that this was some kind of python method, but actually it’s a Revit class allowing the same functionality…

And the class ModelCurveArrayIterator also has property Current and method Reset… It all makes sense.

That is really good to know, thanks :slight_smile:

Mark