In my Revit model, I have an orthogonal beam network with different symbols, as shown in the image below.
To create longitudinal rebars on the top and bottom faces of each beam, I need to retrieve their total length, measured along their local longitudinal axis from outer face to outer face, as illustrated in the image.
However, since the beams use different symbol, where their b (width) and h (height) parameters are flipped along either the X or Y axis, I haven’t found a reliable way to get the total length.
I tried using Location.Curve, but it only returns the length between the beam’s endpoints, which doesn’t account for the full length from outer face to outer face?
setting up perimeters by picking the outer faces (4 in this case), projecting each mid point onto the faces which their normal direction is either the same or opposite of the direction of beam’s location curve, then DistanceTo(proj 1, proj 2) to get the total length. each length would be paired nicely with its corresponding beam.
I tried this code where I got faces and their Normal vectors for each solid geometry representing a beam as you can see in the output below, what’s next and how can I filter them to get only the outer faces as suggested by @BimAmbit here:
import sys
import clr
import math
from System.Collections.Generic import IList, List
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB.Structure import *
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Selection import *
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
# Collect structural framing elements (beams)
beams = FilteredElementCollector(doc)\
.OfCategory(BuiltInCategory.OST_StructuralFraming)\
.WhereElementIsNotElementType()\
.ToElements()
def get_side_face(beam):
options = Options()
options.IncludeNonVisibleObjects = False
options.ComputeReferences = True
options.DetailLevel = ViewDetailLevel.Fine
geoElement = beam.get_Geometry(options)
solid = next((g for g in geoElement if isinstance(g, Solid) and g.Volume > 0), None)
if not solid:
return None
if solid:
Solid_faces = [f for f in solid.Faces]
faces_normal = [f.FaceNormal for f in Solid_faces]
return Solid_faces,faces_normal
OUT = [get_side_face(beam) for beam in beams]
edit:
I was using vectors, but if your beam is oriented at 45°, you need to test the x and y components with their sign compared to the direction of the curve, whether oriented or flipped (a bit tedious).
With surfaces (less problems after trimming up and down), except in special cases, it’s faster.
python Solid_faces = [f for f in solid.Faces if f.FaceNormal == 1 or f.FaceNormal== -1]
Face up an down
other_faces= [f for f in solid.Faces if f.FaceNormal == 0]
@christian.stan
I tried this updated code, where I was able to get the side faces of beams along their locations by filtering them using FaceNormal and Area properties, as shown in the output below. However, I’m only getting the visible face in the current view!..hidden faces are not being taken into account.?.
In the image below, you can see that I’m getting only one face matching the criteria I’m targeting.
import sys
import clr
import math
from System.Collections.Generic import IList, List
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB.Structure import *
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Selection import *
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
# Collect structural framing elements (beams)
beams = FilteredElementCollector(doc)\
.OfCategory(BuiltInCategory.OST_StructuralFraming)\
.WhereElementIsNotElementType()\
.ToElements()
def get_side_face(beam):
options = Options()
options.IncludeNonVisibleObjects = False
options.ComputeReferences = True
options.DetailLevel = ViewDetailLevel.Fine
geoElement = beam.get_Geometry(options)
solid = next((g for g in geoElement if isinstance(g, Solid) and g.Volume > 0), None)
if not solid:
return None
if solid:
Solid_faces = []
for face in solid.Faces:
local_x = beam.Location.Curve.Direction
product_vect = face.FaceNormal.CrossProduct(local_x)
if product_vect.IsAlmostEqualTo(XYZ(0,0,1)) or product_vect.IsAlmostEqualTo(XYZ(0,0,-1)):
Solid_faces.append(face)
faces_normal = [f.FaceNormal for f in Solid_faces]
faces_area = [f.Area*(0.3048**2) for f in Solid_faces]
return Solid_faces,faces_normal, faces_area
OUT = [get_side_face(beam) for beam in beams]
import clr
import sys
import System
from System.Collections.Generic import List, IList, Dictionary
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
import functools
def rollbackTransaction(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
TransactionManager.Instance.ForceCloseTransaction()
t = Transaction(doc, func.__name__)
t.Start()
ret = func(*args, **kwargs)
t.RollBack()
t.Dispose()
return ret
return wrapper
def get_solid(beam):
options = Options()
options.IncludeNonVisibleObjects = False
options.DetailLevel = ViewDetailLevel.Fine
geoElement = beam.get_Geometry(options)
solid = next((g for g in geoElement if isinstance(g, DB.Solid) and g.Volume > 0), None)
return solid
@rollbackTransaction
def get_full_length_beam(elem):
cut_elems = [doc.GetElement(xId) for xId in DB.SolidSolidCutUtils.GetCuttingSolids(elem)]
# reverse all jonctions to get full beam solid uncuted
for e in cut_elems:
DB.JoinGeometryUtils.SwitchJoinOrder(doc, elem, e)
doc.Regenerate()
max_edge = max(get_solid(elem).Edges, key = lambda x : x.ApproximateLength )
return max_edge.AsCurve().ToProtoType().Length
#
beam = UnwrapElement(IN[0])
OUT = get_full_length_beam(beam)
I tested your code, and it works for the beams located at the ends, where I was able to obtain the desired edge with the max length except for the intersecting intermediate beams, as you can see in the image below.