Hi All, @c.poupin
In the main purpose to create beams rebars and in continuation of my previous topic Beams length from outer faces, where I was able to retrieve and extend the max_edge curves from an intersecting beam network using uncut solid geometry, my main goal now is to improve the code and make it cleaner starting by properly setting and getting beam covers, and then applying the necessary transformations to the extracted curves accordingly.
When I run the code, the cover parameters are updated as expected from the input values and correctly displayed in the properties tab for all beams., however unlike top_cover and btm_cover I’m having an issue retrieving the side_cover parameter within curve_multiply_offset function as you can see in the error displayed in the image below and this prevents the required curve transformations from being applied within the main function get_corrected_beam_length.
Please check my attached code and revit file:
Transform curves
import clr
import sys
import System
from System.Collections.Generic import IList, List
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('RevitAPI')
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import *
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
rebar_types = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_Rebar)
.WhereElementIsElementType()
.WherePasses(ElementClassFilter(RebarBarType))
.ToElements())
top_bar_type = next((r for r in rebar_types if r.LookupParameter('Diamètre de barre').AsValueString() == "12 mm"), None)
btm_bar_type = next((r for r in rebar_types if r.LookupParameter('Diamètre de barre').AsValueString() == "14 mm"), None)
stirrup_type = next((r for r in rebar_types if r.LookupParameter('Diamètre de barre').AsValueString() == "6 mm"), None)
hook_types = list(FilteredElementCollector(doc)
.OfClass(RebarHookType)
.ToElements())
stirrup_hook_types = [hk for hk in hook_types if hk.Style == RebarStyle.StirrupTie]
standard_hook_types = [hk for hk in hook_types if hk.Style == RebarStyle.Standard]
hook_90 = next((h for h in standard_hook_types if Element.Name.GetValue(h) == "Standard - 90 deg."), None)
hook_135 = next((h for h in stirrup_hook_types if Element.Name.GetValue(h) == "Etrier/épingle - 135 deg."), None)
cover_faces = {
"Top": BuiltInParameter.CLEAR_COVER_TOP,
"Bottom": BuiltInParameter.CLEAR_COVER_BOTTOM,
"Other": BuiltInParameter.CLEAR_COVER_OTHER
}
cover_types = list(FilteredElementCollector(doc).OfClass(RebarCoverType).ToElements())
def get_cover_type(distance):
for ct in cover_types:
if abs(ct.CoverDistance - distance) < 1e-6:
return ct
name = "Enrobage_{} cm".format(distance / 0.0328084)
new_ct = RebarCoverType.Create(doc, name, distance)
cover_types.append(new_ct)
return new_ct
def set_beam_covers(beam, top, btm, side):
cover_cm = {"Top": top, "Bottom": btm, "Other": side}
cover_ft = {face: cover_cm[face] * 0.0328084 for face in cover_cm}
for face, bip in cover_faces.items():
param = beam.get_Parameter(bip)
if param and param.StorageType == StorageType.ElementId:
ct = get_cover_type(cover_ft[face])
if ct:
param.Set(ct.Id)
def get_beam_covers(beam):
result = {}
for face, bip in cover_faces.items():
param = beam.get_Parameter(bip)
if param and param.StorageType == StorageType.ElementId:
ct = doc.GetElement(param.AsElementId())
if isinstance(ct, RebarCoverType):
result[face] = ct.CoverDistance
return result
def get_solid(beam):
options = Options()
options.IncludeNonVisibleObjects = False
options.DetailLevel = ViewDetailLevel.Fine
geoElement = beam.get_Geometry(options)
return next((g for g in geoElement if isinstance(g, Solid) and g.Volume > 0), None)
def get_side_face_normal(solid, edge):
for face in solid.Faces:
if edge in [e for loop in face.EdgeLoops for e in loop]:
normal = face.FaceNormal
if not (normal.IsAlmostEqualTo(XYZ.BasisZ) or normal.IsAlmostEqualTo(-XYZ.BasisZ)):
return normal
return None
def is_curves_parallel(curve1, curve2, tolerance=1e-6):
vect1 = curve1.Direction.Normalize()
vect2 = curve2.Direction.Normalize()
return vect1.CrossProduct(vect2).GetLength() <= tolerance
def curve_multiply_offset(curve, vector, distance, top_cover, btm_cover, side_cover):
curve = curve.CreateTransformed(Transform.CreateTranslation(XYZ(0, 0, -1).Multiply(top_cover)))
vertical_offset = distance - (top_cover + btm_cover)
trans = Transform.CreateTranslation(vector.Negate().Multiply(side_cover))
top_curve = curve.CreateTransformed(trans)
bottom_trans = Transform.CreateTranslation(XYZ(0, 0, -1).Multiply(vertical_offset))
btm_curve = top_curve.CreateTransformed(bottom_trans)
btm_curve = Line.CreateBound(btm_curve.GetEndPoint(1), btm_curve.GetEndPoint(0))
return top_curve, btm_curve
def get_intersecting_end_beams_data(single_beam, all_beams, tolerance=0.001):
loc_curve = single_beam.Location.Curve
w = single_beam.Symbol.LookupParameter("b").AsDouble()
h = single_beam.Symbol.LookupParameter("h").AsDouble()
single_beam_covers = get_beam_covers(single_beam)
top_cover = single_beam_covers.get("Top")
btm_cover = single_beam_covers.get("Bottom")
side_cover = single_beam_covers.get("Other")
start = loc_curve.GetEndPoint(0)
end = loc_curve.GetEndPoint(1)
w_start = None
w_end = None
start_side_cover = 0
end_side_cover = 0
for beam in all_beams:
if beam.Id == single_beam.Id:
continue
other_curve = beam.Location.Curve
b = beam.Symbol.LookupParameter("b").AsDouble()
other_beam_covers = get_beam_covers(beam)
other_side_cover = other_beam_covers.get("Other")
if is_curves_parallel(loc_curve, other_curve):
continue
projected_start = other_curve.Project(start)
if projected_start and projected_start.Distance < tolerance and w_start is None:
w_start = b / 2
start_side_cover = other_side_cover
print(start_side_cover * 0.3048)
projected_end = other_curve.Project(end)
if projected_end and projected_end.Distance < tolerance and w_end is None:
w_end = b / 2
end_side_cover = other_side_cover
print(end_side_cover * 0.3048)
return {
"beam_width": w,
"beam_height": h,
"beam_top_cover": top_cover,
"beam_btm_cover": btm_cover,
"beam_side_cover": side_cover,
"width_start": w_start,
"width_end": w_end,
"beam_start_side_cover": start_side_cover,
"beam_end_side_cover": end_side_cover
}
def get_corrected_beam_length(beam, all_beams):
beam_curve = beam.Location.Curve
direction = beam_curve.Direction
beam_length = beam_curve.Length
start = beam_curve.GetEndPoint(0)
cut_elems = [doc.GetElement(xId) for xId in SolidSolidCutUtils.GetCuttingSolids(beam)]
for e in cut_elems:
JoinGeometryUtils.SwitchJoinOrder(doc, beam, e)
doc.Regenerate()
solid = get_solid(beam)
if not solid:
return None
max_edge = max(solid.Edges, key=lambda x: x.ApproximateLength)
max_edge_curve = max_edge.AsCurve()
max_edge_direction = max_edge_curve.Direction
max_edge_length = max_edge_curve.Length
face_normal = get_side_face_normal(solid, max_edge)
if face_normal is None:
return None
intersection_data = get_intersecting_end_beams_data(beam, all_beams)
beam_width = intersection_data["beam_width"]
beam_height = intersection_data["beam_height"]
top_cover = intersection_data["beam_top_cover"]
btm_cover = intersection_data["beam_btm_cover"]
side_cover = intersection_data["beam_side_cover"]
w_start = intersection_data["width_start"] or 0
w_end = intersection_data["width_end"] or 0
start_side_cover = intersection_data["beam_start_side_cover"]
end_side_cover = intersection_data["beam_end_side_cover"]
if max_edge_length > beam_length:
if not max_edge_curve.GetEndPoint(0).Z == beam_curve.GetEndPoint(0).Z:
max_edge_curve = Line.CreateBound(XYZ(max_edge_curve.GetEndPoint(0).X, max_edge_curve.GetEndPoint(0).Y, beam_curve.GetEndPoint(0).Z), XYZ(max_edge_curve.GetEndPoint(1).X, max_edge_curve.GetEndPoint(1).Y, beam_curve.GetEndPoint(1).Z))
if not max_edge_direction.IsAlmostEqualTo(direction):
max_edge_curve = Line.CreateBound(max_edge_curve.GetEndPoint(1), max_edge_curve.GetEndPoint(0))
new_start = max_edge_curve.Evaluate(start_side_cover / max_edge_length, True)
new_end = max_edge_curve.Evaluate(1 - (end_side_cover / max_edge_length), True)
new_line = Line.CreateBound(new_start, new_end)
else:
total_length = beam_length + w_start + w_end - (start_side_cover + end_side_cover)
project_point = start + face_normal.Multiply(beam_width / 2)
new_start = project_point - direction.Multiply(w_start - start_side_cover)
new_end = new_start + direction.Multiply(total_length)
new_line = Line.CreateBound(new_start, new_end)
top_curve, btm_curve = curve_multiply_offset(new_line, face_normal, beam_height, top_cover, btm_cover, side_cover)
return btm_curve, top_curve, face_normal
def split_curve(curve, bartype):
total_length = curve.Length
unit_length = 12.0 / 0.3048
diameter = bartype.BarModelDiameter
lr = 50 * diameter
min_remainder = 2.0 / 0.3048
tol_near_full = 0.2 / 0.3048
direction = curve.Direction
curves = []
start = curve.GetEndPoint(0)
k = 0
n = int(total_length // unit_length)
if total_length <= unit_length:
curves.append(curve)
else:
while k < n:
end = start.Add(direction.Multiply(unit_length))
new_line = Line.CreateBound(start, end)
curves.append(new_line)
k += 1
next_start = start.Add(direction.Multiply(unit_length - lr))
remaining_length = curve.GetEndPoint(1).DistanceTo(end)
if remaining_length < unit_length - lr:
remainder_end = curve.GetEndPoint(1)
if remaining_length <= lr or remaining_length < min_remainder:
adjusted_start = remainder_end.Add(direction.Multiply(min_remainder).Negate())
remainder_line = Line.CreateBound(adjusted_start, remainder_end)
else:
remainder_line = Line.CreateBound(next_start, remainder_end)
if remainder_line.Length < (unit_length - tol_near_full):
remainder_line = remainder_line
else:
adjusted_start = remainder_end.Add(direction.Multiply(unit_length).Negate())
remainder_line = Line.CreateBound(adjusted_start, remainder_end)
curves.append(remainder_line)
break
start = next_start
return curves
beams = FilteredElementCollector(doc)\
.OfCategory(BuiltInCategory.OST_StructuralFraming)\
.WhereElementIsNotElementType()\
.Where(System.Func[DB.Element, System.Boolean](lambda b : b.StructuralUsage == StructuralInstanceUsage.Girder))\
.ToList()
with Transaction(doc, "Set beams Covers") as t:
t.Start()
for b in beams:
set_beam_covers(b, 10, 15, 8)
t.Commit()
btm_curves = []
top_curves = []
with Transaction(doc, "split rebars") as t:
t.Start()
for beam in beams:
direction = beam.Location.Curve.Direction
width = beam.Symbol.LookupParameter("b").AsDouble()
covers = get_beam_covers(beam)
result = get_corrected_beam_length(beam, beams)
if not result:
continue
btm_curve, top_curve, normal_vect = result
bc = split_curve(btm_curve, btm_bar_type)
tc = split_curve(top_curve, top_bar_type)
btm_curves.append(bc)
top_curves.append(tc)
t.Commit()
OUT = [c.ToProtoType() for lst in btm_curves for c in lst], [c.ToProtoType() for lst in top_curves for c in lst]
beams_network1.rvt (1.9 MB)
Any help to solve this issue would be greatly appreciated.
Thanks.






