In the code below, I am trying to create a ‘U’ shape (as a model line for now) from a selected wall face, which I intend to use later for creating shape-driven rebar using the CreateFromCurves method. Is there a better approach or code logic for this, or can you suggest an alternative?
rebar Shape U
import clr
import sys
import System
import math
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import specify namespace
from Autodesk.Revit.DB.Structure import *
#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.GeometryReferences)
#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
# select all walls within the project
Walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()
# get walls faces
Faces = []
for w in Walls:
face_Ref = list(HostObjectUtils.GetSideFaces(w, ShellLayerType.Interior))[0]
wall_face = w.GetGeometryObjectFromReference(face_Ref)
Faces.append(wall_face)
# choose one face end get its edges serving as the base of creating the searched rebar shape
face = Faces[0]
boundary = face.EdgeLoops[0]
edges = []
for edge in boundary:
edges.append(edge.AsCurve())
# iterate over the edges list and choose the first vertical edge as a begening curve for the rebar shape
for idx, edge in enumerate(edges):
if idx == 3:
edges = edges[idx:] + edges[:idx]
# obtaining the searched rebar shape " U "
edges = edges[:-1]
curves = []
#Creating a temporary 2D plane to visualize the rebar shape
Points = [i.GetEndPoint(0) for i in edges]
plan = Plane.CreateByThreePoints(Points[0], Points[1], Points[2])
# get the Normal of the plane for calculation purposes
Ref_vect = plan.Normal.Normalize()
# Differentiate the horizontal curve from the two vertical ones for calculation purposes.
horiz_vect = [e.Direction for e in edges if e.GetEndPoint(0).Z == e.GetEndPoint(1).Z][0]
# Create a transformation function to obtain the final rebar shape from the offset face edges
def trans(vector, cover):
t = Transform.CreateTranslation(vector.Normalize().Multiply(cover))
return(t)
curves = []
# offset distance c1 for the vertical edge
c1 = 0.1/0.3048
# offset distance c2 for the horizontal edge
c2 = 0.1/0.3048
# create the final rebar shape " U "
for e in edges:
st_pt = e.GetEndPoint(0)
End_pt = e.GetEndPoint(1)
direct = e.Direction
vect = direct.CrossProduct(Ref_vect)
# select the horizontal edge and offset it with the desired parameters
if direct.IsAlmostEqualTo(horiz_vect):
e = e.CreateTransformed(trans(vect, c2))
param1 = c1/e.Length
param2 = 1 - param1
st_pt = e.Evaluate(param1, True)
End_pt = e.Evaluate(param2, True)
e = Line.CreateBound(st_pt, End_pt)
curves.append(e)
# select the vertical edges and offset them with the desired parameters
else:
# check if the two vertical edge have the same orientation
if direct.IsAlmostEqualTo(XYZ.BasisZ) :
if vect.IsAlmostEqualTo(horiz_vect):
vect = vect.Negate()
e = e.CreateTransformed(trans(vect, c1))
# new start point for the offseted curve
p = XYZ(e.GetEndPoint(0).X, e.GetEndPoint(0).Y, e.GetEndPoint(0).Z - c2)
e = Line.CreateBound(p, e.GetEndPoint(1))
curves.append(e)
else:
e= Line.CreateBound(End_pt, st_pt)
vect = vect.Negate()
e = e.CreateTransformed(trans(vect, c1))
# new start point for the offseted curve
p = XYZ(e.GetEndPoint(0).X, e.GetEndPoint(0).Y, e.GetEndPoint(0).Z - c2)
e = Line.CreateBound(p, e.GetEndPoint(1))
curves.append(e)
# create a sketch plan to temporary visualize the rebar shape
with Transaction(doc, 'create Sketch plan') as t:
t.Start()
skp = SketchPlane.Create(doc, plan)
t.Commit()
print(skp)
# create a tomporary model lines to visualize the rebar shape
Lines = []
with Transaction(doc, 'create Model Lines') as t:
t.Start()
for l in curves:
Ml = doc.Create.NewModelCurve(l, skp)
Lines.append(Ml)
t.Commit()
OUT = Lines
You could simplify the code by using the available methods in the API
Using the CurveLoop class can save a lot of fiddling - edges and new_edges are both curve loops. Using CreateViaOffset and CreateTranslation methods do all the geometry work for you.
# Your code up to face = Faces[0]
units = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()
cover = UnitUtils.ConvertToInternalUnits(0.1, units)
edges = face.GetEdgesAsCurveLoops()
plane = edges.GetPlane()
new_edges = edges.CreateViaOffset([cover, cover, cover, cover], plane.Normal.Negate())
trans = Transform.CreateTranslation(plane.Normal.Negate().Multiply(cover))
new_edges.Transform(trans)
# Inside Transaction
sketch_plane = SketchPlane.Create(doc, new_edges.GetPlane())
# Remove unwanted lines from new_edges
curves = [doc.Create.NewModelCurve(e, sketch_plane) for e in new_edges]
# Close Transaction
OUT = curves
Is there another correct way, besides the one I used in my code, to remove the top curve and obtain the desired ‘U’ shape relative to the selected wall?
Please note the spirit of this forum is for people to try, or at least think something through before asking
Your code ‘rolls’ the lines in the CurveLoop to the last Curve (index 3) - this may be true for a rectangular wall, and if that is the intent of you code then that may work - but it is not guaranteed, then you remove the last item - the Curve originally at index 2.
So it is not the way I would remove the top horizontal line to create the U shape because I have not checked if it is the top horizontal.
Checking if a line is horizontal is straight forward - the dot product with the Z axis will be zero i.e. perpendicular. Python evaluates 0 as False
def is_horizontal(line):
return not XYZ.BasisZ.DotProduct(line.Direction)
I think one of the ways to efficiently find the highest line element is to evaluate the Z coordinate of the mid point which is an average of the two end points. We can also use python list methods to remove it and the key argument of the max() function with a lambda function
curves = list(curve_loop)
curves.remove(max(curves, key=lambda line: line.Evaluate(0.5, True).Z)) # In place
OUT = curves
I completely agree with you, and I regret not having tried something new. My problem is that I don’t have enough time to dedicate to development due to my work, and I haven’t documented myself enough on the Revit API, where everything is well explained. Sometimes, I tend to complicate things instead of simplifying them, as you are doing so well.
I tried your approach to remove the top line by evaluating the Z coordinate in each line, tested the code for each wall, and it worked perfectly, which definitively solved my issue.
Here the final code:
import Autodesk.Revit.DB as DB
#import specify namespace
from Autodesk.Revit.DB.Structure import *
#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.GeometryReferences)
#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
# select all walls within the project
Walls = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsNotElementType().ToElements()
units = doc.GetUnits().GetFormatOptions(SpecTypeId.Length).GetUnitTypeId()
# get walls faces
Faces = []
for w in Walls:
face_Ref = list(HostObjectUtils.GetSideFaces(w, ShellLayerType.Interior))[0]
wall_face = w.GetGeometryObjectFromReference(face_Ref)
Faces.append(wall_face)
Thick_wall = w.Width
# cover parameters
c1 = UnitUtils.ConvertToInternalUnits(0.03, units)
c2 = UnitUtils.ConvertToInternalUnits(0.05, units)
c3 = UnitUtils.ConvertToInternalUnits(0.05, units) + Thick_wall
# choose one face end get its edges serving as the base of creating the searched rebar shape
face = Faces[0]
edges = face.GetEdgesAsCurveLoops()[0]
plane = edges.GetPlane()
# new offseted edges in their plane
new_edges = edges.CreateViaOffset([c2, c1, c2, c1], plane.Normal.Negate())
# Translate the edges in their final position
trans = Transform.CreateTranslation(plane.Normal.Negate().Multiply(c3))
new_edges.Transform(trans)
u_plane = new_edges.GetPlane()
# remove the top horizontal edge to get the desired shape " U "
new_edges = list(new_edges)
new_edges.remove(max(new_edges, key=lambda line: line.Evaluate(0.5, True).Z))
# create a sketch plan to temporary visualize the rebar shape
with Transaction(doc, 'create Sketch plan') as t:
t.Start()
skp = SketchPlane.Create(doc, u_plane)
t.Commit()
# create a tomporary model lines to visualize the rebar shape
with Transaction(doc, 'create Model Lines') as t:
t.Start()
curves = [doc.Create.NewModelCurve(e, skp) for e in new_edges]
t.Commit()
OUT = curves