Hi all, @c.poupin
In the code below, I am trying to create rebars for intersecting beams by splitting and overlapping each beam’s curve so that the Bar Length parameter of every rebar does not exceed 12.00 m, including hooks, for that I need to handle three main cases in the split_curve function, wher I defined three maximum straight segments to manage these scenarios:
-
case1 :
total_length <= unit_length2- a single U-shaped rebar is generated
-
case2 :
unit_length2 < total_length <= ref_length- two overlapped L-shaped rebar are generated where four sub-cases are considered:
- sub_case1:
total_length <= unit_length1: to avoid a single L-shaped rebar the main curve is shortened with a minimun distance eqaul totol_near_fullallowing the creation of a second overlapping L-shaped rebar - sub_case2:
total_length == ref_length: Exactly two overlapping L-shaped rebars are generated, each with a length of 12.00 m. - sub_case3:
ref_length - total_length <= tol_near_full: Similar to Sub-case 2, I force the creation of two overlapping L-shaped rebars, each 12.00 m long, this is achieved by extending the second rebar at its start point by a distance equal totol_near_fulland in this case the overlap length becomeslr + tol_near_full - sub_case4:
ref_length - total_length > tol_near_full:two overlapping L-shaped rebar are generated where the straight segment for the first rebar is equal tounit_length1
- sub_case1:
- two overlapped L-shaped rebar are generated where four sub-cases are considered:
-
case3: a list of multiples overlapping rebars are generated where:
- At least one intermediate straight rebar of 12.00 m is created
- The first rebar is L-shaped, with
hook_90at the start andNoneat the end. - The last rebar is L-shaped, with
Noneat the start andhook_90at the end. - the same rules in sub_case2 and sub_case3 are applied for the last bar in the list.
In the attached revit model below, I tested the code for the specific cases sub_case1 and sub_case2 where the results are as follow:
-
For both beams of type 400 x 800 along the X direction:
- As expected, two overlapping L-shaped rebars (top and bottom) are generated, following the logic of Sub-case 1.
-
For both beams of type 300 x 600 along the Y direction:
- Two overlapping L-shaped rebars are generated for the top rebars, following Sub-case 4.
- No bottom rebar is generated, although I am handling Sub-case 2, where I expect exactly two overlapping L-shaped rebars, each 12.00 m long.
My question:
Could this issue be caused by floating-point imprecision when evaluating
total_length == ref_length, or is it more likely due to incorrect logic in my implementation?
beams_rebars
import clr
import sys
import System
import math
from System.Collections.Generic import IList, List
from System import Array
# ProtoGeometry
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
# Revit API
clr.AddReference('RevitAPI')
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import *
# Revit Nodes
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
# Revit Services
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
# System.Core
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
import functools
# collecting rebar type in the active document
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() == "14 mm"), None)
btm_bar_type = next((r for r in rebar_types if r.LookupParameter('Diamètre de barre').AsValueString() == "16 mm"), None)
hook_types = list(FilteredElementCollector(doc).OfClass(RebarHookType).ToElements())
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)
# collecting beams cover BuiltInParameter
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 decoTransaction(commit):
def subDecoTransaction(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
TransactionManager.Instance.ForceCloseTransaction()
t = Transaction(doc, func.__name__)
t.Start()
ret = func(*args, **kwargs)
if commit:
t.Commit()
else:
t.RollBack()
t.Dispose()
return ret
return wrapper
return subDecoTransaction
def get_cover_type(distance):
for ct in cover_types:
if abs(ct.CoverDistance - distance) < 1e-6:
return ct
name = "Enrobage_{:.0f}cm".format(distance / 0.0328084)
new_ct = RebarCoverType.Create(doc, name, distance)
cover_types.append(new_ct)
return new_ct
@decoTransaction(commit = True)
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)
else:
print("test")
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
# getting beams solid geometry
def get_solid(elem):
opt = Options()
opt.ComputeReferences = True
opt.IncludeNonVisibleObjects = True
geoSet = elem.get_Geometry(opt)
for geo in geoSet:
if isinstance(geo, Solid) and geo.Volume > 0:
return geo
for geo in geoSet:
if isinstance(geo, GeometryInstance):
geoSetInst = geo.GetInstanceGeometry()
for geoI in geoSetInst:
if isinstance(geoI, Solid) and geoI.Volume > 0:
return geoI
return None
#Retrieving the beam’s side face to extract its longest curve.
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):
v1 = curve1.Direction.Normalize()
v2 = curve2.Direction.Normalize()
return v1.CrossProduct(v2).GetLength() <= tolerance
# Retrieving data from the ends of each intersected beam to generate the main rebar curve.
def get_intersecting_end_beams_data(single_beam, all_beams, tolerance=1e-3):
loc_curve = single_beam.Location.Curve
w = single_beam.Symbol.LookupParameter("b").AsDouble()
h = single_beam.Symbol.LookupParameter("h").AsDouble()
covers = get_beam_covers(single_beam)
top_cover = covers["Top"]
btm_cover = covers["Bottom"]
side_cover = covers["Other"]
start = loc_curve.GetEndPoint(0)
end = loc_curve.GetEndPoint(1)
w_start = w_end = None
start_side_cover = end_side_cover = None
perpendicular_beams = []
for beam in all_beams:
if beam.Id == single_beam.Id:
continue
other_curve = beam.Location.Curve
# Skip parallel
if is_curves_parallel(loc_curve, other_curve):
continue
results = clr.Reference[IntersectionResultArray]()
result = loc_curve.Intersect(other_curve, results)
if result != SetComparisonResult.Overlap:
continue
intersection_points = []
interResult = results.Value
intersection_points.append(interResult[0].XYZPoint)
if not intersection_points:
continue
# Handle each intersection point
for pt in intersection_points:
b = beam.Symbol.LookupParameter("b").AsDouble()
other_covers = get_beam_covers(beam)
other_side_cover = other_covers["Other"]
if start.DistanceTo(pt) < tolerance and w_start is None:
w_start = b / 2
start_side_cover = other_side_cover
elif end.DistanceTo(pt) < tolerance and w_end is None:
w_end = b / 2
end_side_cover = other_side_cover
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
}
# transform function to create top and bottom rebar curve for each beam
def curve_multiply_offset(curve, vector, intersection_data):
h = 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"]
curve = curve.CreateTransformed(Transform.CreateTranslation(XYZ(0, 0, -1).Multiply(top_cover)))
vertical_offset = h - (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
@decoTransaction(commit = False)
def get_corrected_beam_length(beam, intersection_data, 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
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"] or 0
end_side_cover = intersection_data["beam_end_side_cover"] or 0
# Identify main face and edge
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
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))
if max_edge_length <= 1e-9:
return None
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)
# Generate top/bottom offset curves
top_curve, btm_curve = curve_multiply_offset(new_line, face_normal, intersection_data)
return top_curve, btm_curve, face_normal
# splitting top/bottom curve for each beam
def split_curve(curve, bar_type, hook_type):
# the main curve length
total_length = curve.Length
print(total_length * 0.3048)
# bar diameter
d = bar_type.BarModelDiameter
# standard hook Bend Diameter
D = bar_type.StandardHookBendDiameter
# hook extension length
l = hook_type.GetHookExtensionLength(bar_type)
# hook total length
b = l + D / 2 + d
# referance length to generate a staright bar without hooks.
unit_length = 12 / 0.3048
# referance length to generate an L-shapped rebar.
unit_length1 = (12 / 0.3048) - b - ((D + d) * math.pi / 4) + D + 2 * d
# referance length to generate an U-shapped rebar.
unit_length2 = (12 / 0.3048) - 2 * b - ((D + d) * math.pi / 2) + 2 * D + 4 * d
# overlap distance
lr = 50 * d
# tolerence distance needed to forcing creation of an L-shapped rebar with 12.00m long
tol_near_full = 0.15 / 0.3048
# referance length to exactely generate two overlaped L-shapped rebar with 12.00m long
ref_length = 2 * unit_length1 - lr
print(ref_length * 0.3048)
direction = curve.Direction
curves = []
start = curve.GetEndPoint(0)
end = curve.GetEndPoint(1)
k = 0
n = int((total_length-unit_length1-lr) // unit_length)
# Case 1: a single U-shaped rebar is generated
if total_length <= unit_length2:
curves.append(curve)
# Case 2: Two overlapped L-shaped rebars are generated
elif unit_length2 < total_length <= ref_length:
if total_length <= unit_length1:
end1 = start + direction.Multiply(total_length - tol_near_full)
start2 = end1 - direction.Multiply(lr)
new_line1 = Line.CreateBound(start, end1)
new_line2 = Line.CreateBound(start2, end)
curves.append(new_line1)
curves.append(new_line2)
else:
if ref_length - total_length <= 1e-3:
end1 = start + direction.Multiply(unit_length1)
start2 = end1 - direction.Multiply(lr)
new_line1 = Line.CreateBound(start, end1)
new_line2 = Line.CreateBound(start2, end)
curves.append(new_line1)
curves.append(new_line2)
elif ref_length - total_length <= tol_near_full:
end1 = start + direction.Multiply(unit_length1)
start2 = end1 - direction.Multiply(lr + tol_near_full)
new_line1 = Line.CreateBound(start, end1)
new_line2 = Line.CreateBound(start2, end)
curves.append(new_line1)
curves.append(new_line2)
else:
end1 = start + direction.Multiply(unit_length1)
start2 = end1 - direction.Multiply(lr)
new_line1 = Line.CreateBound(start, end1)
new_line2 = Line.CreateBound(start2, end)
curves.append(new_line1)
curves.append(new_line2)
return curves
# Collect beams
beams = FilteredElementCollector(doc)\
.OfCategory(BuiltInCategory.OST_StructuralFraming)\
.WhereElementIsNotElementType()\
.Where(System.Func[DB.Element, System.Boolean](lambda b: b.StructuralUsage == StructuralInstanceUsage.Girder))\
.ToList()
btm_curves = []
top_curves = []
normal_vects = []
for beam in beams:
set_beam_covers(beam, 3, 3, 5)
for beam in beams:
intersection_data = get_intersecting_end_beams_data(beam, beams)
result = get_corrected_beam_length(beam, intersection_data, beams)
if not result:
continue
top_curve, btm_curve, normal_vect = result
top_curves.append(split_curve(top_curve, top_bar_type, hook_90))
btm_curves.append(split_curve(btm_curve, btm_bar_type, hook_90))
normal_vects.append(normal_vect)
# function to create rebars
def create_rebars_from_segments(beam, top_sublist, btm_sublist, normal_vect, top_bar_type, btm_bar_type, hook_type,n):
rebars = []
rebars_length = []
direction = beam.Location.Curve.Direction
width = beam.Symbol.LookupParameter("b").AsDouble()
covers = get_beam_covers(beam)
side_cover = covers["Other"]
first_top = top_sublist[0]
if normal_vect.CrossProduct(direction).IsAlmostEqualTo(XYZ(0, 0, -1)):
norm = first_top.Direction.CrossProduct(XYZ.BasisZ)
hook_orient = RebarHookOrientation.Right
else:
norm = first_top.Direction.CrossProduct(XYZ.BasisZ).Negate()
hook_orient = RebarHookOrientation.Left
l1 = len(top_sublist)
l2 = len(btm_sublist)
for idx,top in enumerate(top_sublist):
if l1 <= 2:
if l1 ==1:
start_hook = hook_type
end_hook = hook_type
else:
if idx == 0:
start_hook = hook_type
end_hook = None
else:
start_hook = None
end_hook = hook_type
else:
if idx == 0:
start_hook = hook_type
end_hook = None
elif idx == l1 - 1:
start_hook = None
end_hook = hook_type
else:
start_hook = None
end_hook = None
# --- create top rebar ---
top_list = List[Curve]()
top_list.Add(top)
top_rebar = Rebar.CreateFromCurves(
doc,
RebarStyle.Standard,
top_bar_type,
start_hook,
end_hook,
beam,
norm,
top_list,
hook_orient,
hook_orient,
True, True
)
top_rebar.GetShapeDrivenAccessor().SetLayoutAsFixedNumber(
n, width - 2 * side_cover, True, True, True
)
rebars.append(top_rebar)
rebars_length.append(top_rebar.get_Parameter(BuiltInParameter.REBAR_ELEM_LENGTH).AsDouble() * 304.8)
for idx,btm in enumerate(btm_sublist):
if l2 <= 2:
if l2 ==1:
start_hook = hook_type
end_hook = hook_type
else:
if idx == 0:
start_hook = hook_type
end_hook = None
else:
start_hook = None
end_hook = hook_type
else:
if idx == 0:
start_hook = hook_type
end_hook = None
elif idx == l2 - 1:
start_hook = None
end_hook = hook_type
else:
start_hook = None
end_hook = None
# --- create bottom rebar ---
btm_list = List[Curve]()
btm_list.Add(btm)
btm_rebar = Rebar.CreateFromCurves(
doc,
RebarStyle.Standard,
btm_bar_type,
start_hook,
end_hook,
beam,
norm,
btm_list,
hook_orient,
hook_orient,
True, True
)
btm_rebar.GetShapeDrivenAccessor().SetLayoutAsFixedNumber(
n, width - 2 * side_cover, True, True, True
)
rebars.append(btm_rebar)
rebars_length.append(btm_rebar.get_Parameter(BuiltInParameter.REBAR_ELEM_LENGTH).AsDouble() * 304.8)
return rebars, rebars_length
all_beam_rebars = []
all_beam_rebars_length = []
with Transaction(doc, "Create Beam Rebars from Segments") as t:
t.Start()
for beam, top_curves_list, btm_curves_list, normal_vect in zip(beams, top_curves, btm_curves, normal_vects):
beam_rebars, rebars_length = create_rebars_from_segments(
beam, top_curves_list, btm_curves_list, normal_vect,
top_bar_type, btm_bar_type, hook_90, 3
)
all_beam_rebars.extend(beam_rebars)
all_beam_rebars_length.extend(rebars_length)
t.Commit()
OUT = all_beam_rebars, all_beam_rebars_length, [c.ToProtoType() for lst in top_curves for c in lst], [c.ToProtoType() for lst in btm_curves for c in lst]
beams_rebar_test4.rvt (3.6 MB)
Any help would be appreciated
Thanks.