Another solution using vertical and horizontal rays (intersections with unbound curves)
Python Code (all engines)
import clr
import sys
import System
pyEngineName = sys.implementation.name
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
#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
def drange(start, end, step):
i = start
while round(i, 10) < end:
yield i
i += step
def flattenGen(*args):
for x in args:
if hasattr(x, "__iter__") and not isinstance(x, str):
for y in flattenGen(*x) : yield y
else: yield x
def get_geomCurves(elem):
"""
Retrieves the geometric curves from a given Revit element.
Args:
elem (Autodesk.Revit.DB.Element): The Revit element from which to extract curves.
Returns:
list: List of geometric curves extracted from the element.
"""
out = []
opt = Options()
opt.DetailLevel = ViewDetailLevel.Fine
instGeo = elem.GetOriginalGeometry(opt)
tf = elem.GetTransform()
for ig in instGeo:
if isinstance(ig, DB.Curve):
out.append(ig.CreateTransformed(tf))
elif isinstance(ig, DB.GeometryInstance):
for g in flattenGen(ig.GetInstanceGeometry()):
if isinstance(g, DB.Curve):
out.append(g.CreateTransformed(tf))
return out
def toList(x):
if isinstance(x, (list, dict)) or \
(hasattr(x, "GetType") and x.GetType().GetInterface("ICollection") is not None):
return x
else : return [x]
def search_minPts_by_vector(minInt, maxInt, vectorRay, midPt, titlebk_curves):
"""
Search for minimum points along a vector within a range.
Args:
minInt (float): The minimum value of the range.from BoundingBox
maxInt (float): The maximum value of the range.from BoundingBox
vectorRay (object): The vector along which to search for minimum points.
midPt (object): The midpoint of the bounding box.
titlebk_curves (list): The list of curves representing the title block geometry
Returns:
tuple: Tuple containing two minimum points (usable length sheet)
"""
global debug
results = []
for d in drange(minInt, maxInt, 0.05):
if vectorRay.IsAlmostEqualTo(XYZ.BasisX):
ptRay = XYZ(midPt.X, d, midPt.Z)
else:
ptRay = XYZ(d, midPt.Y, midPt.Z)
raybound = DB.Line.CreateUnbound(ptRay, vectorRay)
for c in titlebk_curves:
result = None
if pyEngineName == "ironpython":
out_resultArray = clr.Reference[IntersectionResultArray]()
result = raybound.Intersect(c, out_resultArray)
else:
dummy_out = DB.IntersectionResultArray()
result, interResult = raybound.Intersect(c, dummy_out)
#
if result == SetComparisonResult.Overlap:
interResult = out_resultArray.Value[0] if pyEngineName == "ironpython" else interResult[0]
pt = interResult.XYZPoint
distance = ptRay.DistanceTo(pt)
vector = DB.Line.CreateBound(ptRay, pt).Direction
results.append([pt, distance, vector])
#debug.append(pt.ToPoint())
raybound.Dispose()
results.sort(key = lambda x : x[1])
dataA = results.pop(0)
ptA, distanceA, vectA = dataA
dataB = next(x for x in results if x[2].DotProduct(vectA) < 0)
ptB = dataB[0]
return ptA, ptB
lst_titleBk = toList(UnwrapElement(IN[0]))
out = []
debug = []
for tb in lst_titleBk:
bbx = tb.get_BoundingBox(doc.GetElement(tb.OwnerViewId))
midPt = (bbx.Min.Add(bbx.Max)).Multiply(0.5)
titlebk_curves = get_geomCurves(tb)
ptX1, ptX2 = search_minPts_by_vector(bbx.Min.Y, bbx.Max.Y, XYZ.BasisX, midPt, titlebk_curves)
if ptX1.X > ptX2.X:
ptX1, ptX2 = ptX2, ptX1
ptY1, ptY2 = search_minPts_by_vector(ptX1.X, ptX2.X, XYZ.BasisY, midPt, titlebk_curves)
out.append([tb, [ptX1.ToPoint(), ptX2.ToPoint()], [ptY1.ToPoint(), ptY2.ToPoint()], [c.ToProtoType() for c in titlebk_curves]])
OUT = out