Paper space limits.py
# purpose and IO: IN[0]=sheet Ids (list/int/ElementId or None), IN[1]=tolerance_ft (float, optional), OUT=per-sheet dict with offending element Ids
# Reports any paper-space elements whose bounding box (in the sheet view) lies outside the largest title block's bbox.
# Ignores title blocks and CAD Import/Link instances named like "0,0,0" / "0,0-Origin".
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from Autodesk.Revit.DB import (
FilteredElementCollector, ViewSheet, FamilyInstance, BuiltInCategory,
ImportInstance, ElementId
)
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
input_ids = IN[0] if len(IN)>0 else None
tol = float(IN[1]) if len(IN)>1 and IN[1] not in (None, "") else 0.0 # feet
def _to_elemid(e):
if isinstance(e, ElementId): return e
try: return ElementId(int(e))
except: return None
target_sheet_ids = None
if input_ids:
ids = []
seq = input_ids if isinstance(input_ids, (list, tuple)) else [input_ids]
for x in seq:
eid = _to_elemid(x)
if eid: ids.append(eid)
if ids: target_sheet_ids = set(ids)
def tblocks_on_sheet(sheet):
return list(
FilteredElementCollector(doc, sheet.Id)
.OfCategory(BuiltInCategory.OST_TitleBlocks)
.OfClass(FamilyInstance)
.WhereElementIsNotElementType()
)
def largest_tb_bbox(sheet):
ranked = []
for tb in tblocks_on_sheet(sheet):
bb = tb.get_BoundingBox(sheet)
if bb:
area = abs((bb.Max.X - bb.Min.X) * (bb.Max.Y - bb.Min.Y))
ranked.append((area, bb))
if not ranked: return None
ranked.sort(key=lambda x:x[0], reverse=True)
return ranked[0][1]
def is_origin_cad_import(elem):
if not isinstance(elem, ImportInstance):
return False
name = ""
try:
name = elem.Name or ""
except:
name = ""
if not name:
try:
sym = doc.GetElement(elem.GetTypeId())
name = sym.Name if sym else ""
except:
name = ""
s = name.lower().replace(" ", "")
return ("0,0,0" in s) or ("0,0-origin" in s) or ("00,0-origin" in s)
def paper_elems(sheet):
return list(FilteredElementCollector(doc, sheet.Id).WhereElementIsNotElementType())
def inside(bb_elem, bb_ref, tol=0.0):
# bb_elem is fully inside bb_ref with tolerance (in feet)
if not bb_elem or not bb_ref: return True
return (bb_elem.Min.X >= bb_ref.Min.X - tol and
bb_elem.Min.Y >= bb_ref.Min.Y - tol and
bb_elem.Max.X <= bb_ref.Max.X + tol and
bb_elem.Max.Y <= bb_ref.Max.Y + tol)
results = []
sheets = list(FilteredElementCollector(doc).OfClass(ViewSheet))
if target_sheet_ids:
sheets = [s for s in sheets if s.Id in target_sheet_ids]
for s in sheets:
tb_bb = largest_tb_bbox(s)
if not tb_bb:
results.append({"sheet": s.SheetNumber, "name": s.Name, "status": "no title block", "offenders": []})
continue
offenders = []
for e in paper_elems(s):
# skip title blocks and "origin" CAD imports
if e.Category and e.Category.Id.IntegerValue == int(BuiltInCategory.OST_TitleBlocks):
continue
if is_origin_cad_import(e):
continue
bb = e.get_BoundingBox(s)
if not bb: continue
if not inside(bb, tb_bb, tol):
cat = e.Category.Name if e.Category else "NoCategory"
offenders.append({"Id": e.Id.IntegerValue, "Category": cat})
results.append({
"sheet": s.SheetNumber,
"name": s.Name,
"status": "audited",
"tolerance_ft": tol,
"offender_count": len(offenders),
"offenders": offenders
})
OUT = results
Comments
Post a Comment