Rotate / Transform BoundingBox

Hello Dynamiters,

I delete element instances that are inside or touching a boundingBox (the gray box in the picture).
Unfortunatelly this boundingBox is not paralel to the geometry and as you see on the picture it also touches other house.
I searched on dynamobim.org forum but I didnt find any suitable example or solution.
I thought then the best way is to rotate the boundingBox, but unfortunately I can’t find a way to do this.
In Autodesk.Revit.DB.BoundingBoxXYZ you have the property Transform(). Could I possibly rotate the boundingBox with this property by specifying a different coordinate system and in this case how? I’m having trouble figuring out the solution.

Thanks in advance!

1 Like

Nope, Revit uses AABBs. The transform property of the bb is used by Revit to fake rotations since bb’s are used to crop views and crops can be rotated, but the bb itself will never be rotated. Its best if performance isn’t that important to convert to either 8 points or a solid then transform it.

3 Likes

hello @Thomas_Mahon, please correct me if I do or dont understand you. I should create a solid out the boundingBox, then rotate the solid and to obtain the objects within this solid I presume by using ElementIntersectsSolidFilter Constructor (Solid, Boolean) instead of trying to obtain the elements within the boundingBox by using BoundingBoxIntersectsFilter(outline). To rotate a solid out of a boundingbox it is developed here Bounding Box in rotated coordinate system / rotated bounding box

Hi @igpema,

Yes, that is correct. I have modified the code in the link you provided to do just this by constructing a Revit Solid and then doing the intersection test.

"""
Awesome code by Archiways found on Dynamo Forum https://forum.dynamobim.com/t/bounding-box-in-rotated-coordinate-system-rotated-bounding-box/50597
Expanded by Dan Woodcock for demonstration...
https://forum.dynamobim.com/t/rotate-transform-boundingbox/63435/3
"""

import clr

import sys 
sys.path.append(r'C:\Program Files (x86)\IronPython 2.7\Lib')

clr.AddReference('ProtoGeometry') 
import Autodesk.DesignScript.Geometry as DG
#from Autodesk.DesignScript.Geometry import * 

clr.AddReference("DSCoreNodes") 
import DSCore as DS
#from DSCore import *

clr.AddReference("RevitNodes") 
import Revit.Elements as DR
#from Revit.Elements import * 

import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion) 
#
clr.AddReference("RevitAPI") 
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Mechanical import *
from Autodesk.Revit.DB.Structure import *
clr.AddReference("RevitAPIUI")
from Autodesk.Revit.UI import *

clr.AddReference("RevitServices") 
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
doc = DocumentManager.Instance.CurrentDBDocument

import System 
from System import Array as CArray
clr.AddReference("System") 
from System.Collections.Generic import List as CList


import math 

def RotatedBox(elements,angle):

	def SumBoxes(boxes):
		
		minx = min([b.Min.X for b in boxes])
		miny = min([b.Min.Y for b in boxes])
		minz = min([b.Min.Z for b in boxes])
		maxx = max([b.Max.X for b in boxes])
		maxy = max([b.Max.Y for b in boxes])
		maxz = max([b.Max.Z for b in boxes])
		
		bb = BoundingBoxXYZ()
		bb.Min = XYZ(minx,miny,minz)
		bb.Max = XYZ(maxx,maxy,maxz)
		return bb


	def GeoBox(g):
	
		box = g.GetBoundingBox()
		origin = box.Transform.Origin
		box.Min = box.Min.Add(origin)
		box.Max = box.Max.Add(origin)
		return box


	if hasattr(elements, '__iter__') == False:
		elements = [elements]


	boxes = []
	opt = Options()
	opt.DetailLevel = ViewDetailLevel.Fine
	
	solids,boxes = [],[]
	for element in elements:
		instGeo = element.get_Geometry(opt)
		
		for ig in instGeo:
			if ig.GetType() == Solid:
				box = GeoBox(ig)
				boxes.append(box)
				solids.append(ig)
			else:			
				geo = DS.List.Flatten(list(ig.GetInstanceGeometry()))
				for g in geo:
					if g.GetType() == Solid:
						box = g.GetBoundingBox()
						if box.Max.X < 99999:					
							box = GeoBox(g)
							boxes.append(box)
							solids.append(g)
					
	sumBox = SumBoxes(boxes)
	center = box.Max.Add(box.Min).Multiply(0.5)
	
	rotatedBoxes = []
	
	for i in range(len(boxes)):
	
		t = Transform.CreateRotationAtPoint(XYZ.BasisZ,-angle,center)
		g = SolidUtils.CreateTransformed(solids[i],t)
		rotatedBoxes.append(GeoBox(g))
					
	
	sumBox = SumBoxes(rotatedBoxes)

	minp = sumBox.Min
	maxp = sumBox.Max
	
	h = maxp.Z-minp.Z
	
	p1 = minp
	p3 = XYZ(maxp.X,maxp.Y,minp.Z)
	p2 = XYZ(minp.X,maxp.Y,minp.Z)
	p4 = XYZ(maxp.X,minp.Y,minp.Z)
	
	t2 = Transform.CreateRotationAtPoint(XYZ.BasisZ,angle,center)
	points = [t2.OfPoint(p).ToPoint() for p in [p1,p2,p3,p4]]
	
	poly = DG.PolyCurve.ByPoints(points,True)
	
	# New code...
	rvtCrvLoop = CurveLoop()
	for c in DG.PolyCurve.Curves(poly):
		rvtCrvLoop.Append(c.ToRevitType())		

	loopList = System.Collections.Generic.List[CurveLoop]()
	loopList.Add(rvtCrvLoop)
	
	rSld = GeometryCreationUtilities.CreateExtrusionGeometry(loopList, XYZ(0,0,1), h)
	# End new code...
	
	solid = poly.ExtrudeAsSolid(DG.Vector.ZAxis(),h*304.8)
	
	return [solid, rSld]
	
	
angle = math.radians(IN[1])
elements = UnwrapElement(IN[0])
invert = IN[2] # New Param...

# Code Removed...
#OUT = RotatedBox(elements,angle)

# New Code...
solids = RotatedBox(elements,angle)
dynSolid = solids[0]
rvtSolid = solids[1]

filter = ElementIntersectsSolidFilter(rvtSolid, invert)
elems = FilteredElementCollector(doc).WherePasses(filter).ToElements()

OUT = elems, dynSolid

Hope that helps.

Cheers,
Dan

5 Likes

thanks a lot @Daniel_Woodcock1, unfortunatelly, DB. BoundingBoxIntersectsFilter doesnt function the same way that DB.ElementIntersectsSolidFilter. The first one gives you the elements that intersect the bbx and also within the boundingbox, the second, gives you only the elements that intersect with the solid element, but that leaves many elements out of the filter and in the picture you can see that the house within the red solid wont be deleted. That lead us to the only way would be to rotate the bbx, but this is unfortunatelly not possible. am I doing something wrong? am I missing something in the workflow? Thanks in any case


When applying an ElementIntersectsSolid filter with a given solid to a FilteredElementCollector, this will get all Elements that intersect that solid, so any Element (that has geometry) that is partially or fully intersecting with the solid. You can see below that this is true…

The grey box is the rotated BoundingBox (as a Dynamo solid) and you can see all the elements within it are selected (in my script I have a node to make selected the output of the code I posted earlier).

Note: When using the code above, there is an IN[2] port, this is the Invert Filter input. Please ensure that this is set to false otherwise you’ll get all the Elements NOT intersecting the solid.

TIP: This is more for the benefit of this thread than for you specifically, but typically (in this and similar cases) you would use one or more quick filters to will narrow down the set of Elements before executing the more expensive ElementIntersectsSolid filter - anything using solids has to expand the Elements in memory to access the Geometry and other deeper properties. It’s always good to use Quick Filters and then use the SlowFilters. You can also use the Logical And/Or filters to combine these. I haven’t done this in the code so thought I would mention it. In the case above, I would get the BoundingBox of the new solid, then use a BoundingBoxIntersects filter to reduce the set of Elements, then do the ElementIntersectsSolid filter. :slight_smile:

1 Like

Hello
without bounding box, here is an alternative by testing if the elements are inside (or outside) of a building outline (polycurve)

https://s3.gifyu.com/images/In-or-Out.gif

import clr
import sys
import System
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 isInsidePolyg(perimeterPoints, element):
	loc = element.Location
	if isinstance(loc, LocationCurve) and loc.Curve.IsBound :
		lstPtCheck = [loc.Curve.GetEndPoint(0).ToPoint(), loc.Curve.GetEndPoint(1).ToPoint()]
	elif isinstance(loc, LocationPoint):
		lstPtCheck = [loc.Point.ToPoint()]	
	elif isinstance(element, CeilingAndFloor):
		refs = HostObjectUtils.GetTopFaces(element)
		face = element.GetGeometryObjectFromReference(refs[0])
		curves = [edg.AsCurve() for subarray in face.EdgeLoops for edg in subarray]
		lstPtCheck = [c.GetEndPoint(0).ToPoint() for c in curves ]
	else:	
		bbx = element.get_BoundingBox(None)
		midPoint = (bbx.Min + bbx.Max) * 0.5
		lstPtCheck = [midPoint.ToPoint()]

		
	lstBool = []		
	for pointCheck in  lstPtCheck:
		flatPt = DS.Point.ByCoordinates(pointCheck.X, pointCheck.Y, perimeterPoints[0].Z)
		lstangle =[]
		for idx, pt in enumerate(perimeterPoints):
			if idx > 0:
				vecta = Vector.ByTwoPoints(flatPt, perimeterPoints[idx -1])
				vectb = Vector.ByTwoPoints(flatPt, pt)
				cross = vecta.Cross(vectb).Normalized()
				angle = vecta.AngleWithVector(vectb)
				lstangle.append(angle * cross.Z)
				vecta.Dispose()
				vectb.Dispose()
				cross.Dispose()
		lstBool.append(abs(sum(lstangle)) > 180 )

	return any(lstBool)

#Preparing input from dynamo to revit
polyrcurve = IN[0]
isInsideElement = []
outInsideElement = []
if polyrcurve:
	lstPoints = [x.StartPoint for x in polyrcurve.Explode()]
	elems = FilteredElementCollector(doc, doc.ActiveView.Id).WhereElementIsNotElementType().ToElements()
	elems = [e for e in elems if e.get_BoundingBox(None) is not None]
	for e in elems:
		if isInsidePolyg(lstPoints, e):
			isInsideElement.append(e)
		else:
			outInsideElement.append(e)
			
OUT = isInsideElement , outInsideElement
5 Likes

Thanks a lot @c.poupin !
how it is suppossed to check if the point is inside? I am getting lost with the geometry analysis
vecta = perimeterPoints[i-1] - flatPt
vectb = perimeterPoints[i] - flatPt
cross = CrossProduct(vecta, vectb).Normalized.Z
angle = angle(vecta, vectb)
final_angle = angle * cross
if the absolute value of the final_angle list sum is bigger than 180°, True. Then if any True is in the list, the point is in? Unfortunately, I don’t understand the geometric logic… Could you please explain to me how? for sure is my bad at understanding geometry…

The function calculates the sum of the consecutive angles between the point to be tested, and the perimeter points here is a tweet which helps to understand

2 Likes