@Mike.Buttery @c.poupin
As shown in the image below, I noticed that the intersection points of beams Location.Curve @B-B and @2-2 align with the midpoints of perpendicular beams’ Location.Curve.
To address the unexpected lengths of beams @B-B and @2-2, I implemented an alternative approach: I extracted the width parameter b from the perpendicular beams at each end of the current beam. In my code, these are referred to as b_start and b_end, which I then use to generate a new_line with the expected corrected length.
When I run the code, the new_line for beam @B-B was generated correctly, with accurate coordinates for both EndPoints, as shown in the output below:
However, for beam @2-2, although the new_line had the correct length, the coordinates of both EndPoints were incorrect. I’ve struggled to resolve this and suspect that an incorrect edge direction is causing the start point of the new_line to be miscalculated.
Here my tested code:
import clr
import sys
import System
from System.Collections.Generic import List, IList, Dictionary
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB 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
import functools
def rollbackTransaction(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
TransactionManager.Instance.ForceCloseTransaction()
t = Transaction(doc, func.__name__)
t.Start()
try:
result = func(*args, **kwargs)
finally:
t.RollBack()
t.Dispose()
return result
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, Solid) and g.Volume > 0), None)
return solid
def get_beam_widths_at_midpoint_connections(single_beam, all_beams):
loc_curve = single_beam.Location.Curve
start = loc_curve.GetEndPoint(0)
end = loc_curve.GetEndPoint(1)
width_info = []
b_start = 0.0
b_end = 0.0
for beam in all_beams:
if beam.Id == single_beam.Id:
continue
mid = beam.Location.Curve.Evaluate(0.5, True)
b = beam.Symbol.LookupParameter("b").AsDouble() * 0.3048
if start.IsAlmostEqualTo(mid):
width_info.append(("start", b))
b_start = b
elif end.IsAlmostEqualTo(mid):
width_info.append(("end", b))
b_end = b
return width_info, b_start, b_end
@rollbackTransaction
def get_corrected_beam_length(beam, all_beams):
beam_curve = beam.Location.Curve
beam_length = beam_curve.Length * 0.3048
start = beam_curve.GetEndPoint(0)
end = beam_curve.GetEndPoint(1)
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 {
"beam_id": str(beam.Id),
"error": "No solid found"
}
max_edge = max(solid.Edges, key=lambda x: x.ApproximateLength)
max_edge_length = max_edge.ApproximateLength * 0.3048
max_edge_curve = max_edge.AsCurve()
edge_direction = (max_edge_curve.GetEndPoint(1) - max_edge_curve.GetEndPoint(0)).Normalize()
edge_start = max_edge_curve.GetEndPoint(0).ToPoint()
edge_end = max_edge_curve.GetEndPoint(1).ToPoint()
width_info, b_start, b_end = get_beam_widths_at_midpoint_connections(beam, all_beams)
if max_edge_length > beam_length:
total_length = max_edge_length
new_line = max_edge_curve.ToProtoType()
else:
direction = (end - start).Normalize()
total_length = beam_length + b_start / 2 + b_end / 2
new_start = max_edge_curve.GetEndPoint(0)
new_end = new_start + direction.Multiply(total_length / 0.3048)
new_line = Line.CreateBound(new_start, new_end).ToProtoType()
return {
"beam_id": str(beam.Id),
"edge_start": edge_start,
"edge_end": edge_end,
"edge_direction": edge_direction,
"width_info": width_info,
"b_start": b_start,
"b_end": b_end,
"total_length": total_length,
"new_line": new_line
}
# Collect all beams
beams = FilteredElementCollector(doc)\
.OfCategory(BuiltInCategory.OST_StructuralFraming)\
.WhereElementIsNotElementType()\
.ToElements()
OUT = [get_corrected_beam_length(beam, beams) for beam in beams]