Find the outer envelope of a layer

Hello, here’s a script to get the total exterior line of a part’s geometry.

There must be a better approach, as always.

I’ve commented out the Python node to follow the methodology used.

import sys,clr
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import Options,UV,CompoundStructure,XYZ,Line

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
#Function to Find the points of a surface
def findpts(f):
    lpts=[]
    clt=f.GetEdgesAsCurveLoops()
    for cl in clt:
        for c in cl:
            lpts.append(c.GetEndPoint(0))
    return lpts

elt=UnwrapElement(IN[0]) #elt is a Part#
#Get the Document
doc=elt.Document
#Get the Solid
opt=Options()
geoe=elt.get_Geometry(opt)
sol=[g for g in geoe][0]
#Get the wall source of the Part
w=doc.GetElement(elt.GetSourceElementIds()[0].HostElementId)
#Get the WallType
wt=w.WallType

#Find the outgoing vector of the outer face
veco=w.Orientation

vecon=veco.Negate()
#Finding the face of the solid coinciding with the exterior vector of the exterior face
faceext=[f for f in sol.Faces if round(f.ComputeNormal(UV.Zero).X,3)==round(veco.X,3) and round(f.ComputeNormal(UV.Zero).Y,3)==round(veco.Y,3)][0]
#Finding the face of the solid coinciding with the interior vector of the interior face
faceint=[f for f in sol.Faces if round(f.ComputeNormal(UV.Zero).X,3)==round(vecon.X,3) and round(f.ComputeNormal(UV.Zero).Y,3)==round(vecon.Y,3)][0]
#Searching the text index from the element properties (text is in French)
idxv=int(elt.LookupParameter("Index de calque").AsValueString())
#Find the thickness of the layer
epv=wt.GetCompoundStructure().GetLayerWidth(idxv-1)
#Find the Z of the location line
lc=w.Location.Curve
pstartloc=lc.GetEndPoint(0)
#Find all curveloop points and send them to the middle face
lptsext=findpts(faceext)
lptsint=findpts(faceint)
for p in lptsint:
    lptsext.append(p.Add(veco.Multiply(epv)))
#Lower all points to the same level as the location line
newp=[XYZ(p.X,p.Y,pstartloc.Z) for p in lptsext]
#Arrange the points by increasing X and take the first and last to create the line
np2=sorted(newp,key=lambda p:(p.X))
pstart=np2[0].Add(veco.Multiply(-epv/2))
pend=np2[-1].Add(veco.Multiply(-epv/2))
lineht=Line.CreateBound(pstart,pend).ToProtoType()
OUT=lineht

Sincerely,
christian.stan

4 Likes

Very nice, thanks for sharing!

1 Like

An alternative using only ProtoGeometry API with OrientedBoundingBox

import sys
import clr
import System
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)

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

wrapElement = IN[0]
align_bbx_solid = wrapElement.Geometry().First().OrientedBoundingBox.ToCuboid()

bottomCurves = align_bbx_solid.Faces\
    .FirstOrDefault(System.Func[Face, System.Boolean](lambda f : f.SurfaceGeometry().NormalAtParameter(0.5, 0.5).Z < 0)).Edges\
    .Select(System.Func[System.Object, System.Object](lambda x : x.CurveGeometry))\
    .ToList()
    
ordered_Curves = sorted(bottomCurves, key=lambda x : x.Length)
# draw the axis with the 2 smallest lines
line_axis_part = Line.ByStartPointEndPoint(ordered_Curves[0].PointAtParameter(0.5),
                                            ordered_Curves[1].PointAtParameter(0.5))
                                            
OUT = line_axis_part
2 Likes

Hi, more efficient as always.
I tried to find a bounding box class aligned with the Revit API, but I couldn’t find one.
I’ll have to look at how this dynamo node works. Sincerely
christian.stan

1 Like