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.
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.
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
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
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.
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
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