I am using a Geometry.DoesIntesect node to find all surfaces of a toposolid intersecting a certain Bounding box (which I turned into a polysurface) but surfaces completely within the Bbox (polysurface) are not returned.
There is no Geometry Contains geometry node? only points?
How do I go from Surface > points?
It’s crazy to use point at parameter, yes? There got to be a more elegant way…
The goal is: find all surfaces that intersect, or are contained within, a Bounding box.
A fast and purely mathematical way to determine if a geometry lies perfectly within a bounding box is that the corners of the boundingbox for that object should fall within the main bounding box. So minX > minX of main box, maxX < maxX of main bb, so on so forth - assuming they are aligned on the same axis.
you should get the edges of the surfaces of a solid, which are lines and check the intersection of lines with a solid which is coming from the boundingbox you want to check against, if you want to know if the geometry is just inside the F****** box then get one significative point of that whole complex geometry.
maybe something like this:
# IronPython 2.7 – Dynamo
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import Point, Line, BoundingBox, Cuboid, Geometry
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import Options, ViewDetailLevel, Solid, Mesh, GeometryInstance, Curve, Face, \
BoundingBoxXYZ, Transform, XYZ, UV
clr.AddReference('RevitNodes')
from Revit.GeometryConversion import RevitToProto
DOC = DocumentManager.Instance.CurrentDBDocument
TOL = 1e-6
def to_list(x): return list(x) if isinstance(x,(list,tuple)) else [x]
def xyz2pt(p): return Point.ByCoordinates(p.X,p.Y,p.Z)
def ds_curve(rc): return RevitToProto.ToProtoType(rc, True)
def as_bbox(src):
if isinstance(src, BoundingBox): return src
if isinstance(src, Geometry): return BoundingBox.ByGeometry(src)
if isinstance(src, list) and len(src)==2 and all(isinstance(p, Point) for p in src):
return BoundingBox.ByCorners(src[0], src[1])
raise Exception("IN[1] must be BoundingBox / DS Geometry / [minPt,maxPt].")
def pt_in_bb(p, bb):
mn, mx = bb.MinPoint, bb.MaxPoint
return (mn.X - TOL <= p.X <= mx.X + TOL and
mn.Y - TOL <= p.Y <= mx.Y + TOL and
mn.Z - TOL <= p.Z <= mx.Z + TOL)
pairs12 = [(0,1),(0,2),(0,4),(3,1),(3,2),(3,7),(5,1),(5,4),(5,7),(6,2),(6,4),(6,7)]
def edge_lines_from_corners(c):
return [Line.ByStartPointEndPoint(c[i], c[j]) for (i,j) in pairs12]
def aabb_corners(bb):
mn, mx = bb.MinPoint, bb.MaxPoint
return [Point.ByCoordinates(x,y,z)
for x in (mn.X,mx.X)
for y in (mn.Y,mx.Y)
for z in (mn.Z,mx.Z)]
def obb_corners_from_bbxyz(bbxyz):
t = bbxyz.Transform
xs = (bbxyz.Min.X, bbxyz.Max.X)
ys = (bbxyz.Min.Y, bbxyz.Max.Y)
zs = (bbxyz.Min.Z, bbxyz.Max.Z)
corners = []
for x in xs:
for y in ys:
for z in zs:
wp = t.OfPoint(XYZ(x,y,z))
corners.append(xyz2pt(wp))
return corners
def center_point_bbxyz(bbxyz):
t=bbxyz.Transform
cx = 0.5*(bbxyz.Min.X+bbxyz.Max.X)
cy = 0.5*(bbxyz.Min.Y+bbxyz.Max.Y)
cz = 0.5*(bbxyz.Min.Z+bbxyz.Max.Z)
return xyz2pt(t.OfPoint(XYZ(cx,cy,cz)))
def collect_edges_and_point(el):
"""Try REAL geometry first: edges + one significant point."""
opt = Options(); opt.ComputeReferences=False; opt.IncludeNonVisibleObjects=False; opt.DetailLevel=ViewDetailLevel.Fine
edges, sig_pt = [], None
def add_mesh_edges(m, xf):
vs = [xf.OfPoint(m.GetVertex(i)) for i in range(m.Vertices.Count)]
for ti in range(m.NumTriangles):
tri = m.get_Triangle(ti); ids = [tri.get_Index(k) for k in (0,1,2)]
p0,p1,p2 = (xyz2pt(vs[i]) for i in ids)
edges.extend([Line.ByStartPointEndPoint(p0,p1),
Line.ByStartPointEndPoint(p1,p2),
Line.ByStartPointEndPoint(p2,p0)])
def add_solid_edges(s, xf):
nonlocal sig_pt
try:
c = s.ComputeCentroid(); cp = xyz2pt(xf.OfPoint(c))
if cp and sig_pt is None: sig_pt = cp
except: pass
for e in s.Edges:
rc = e.AsCurve()
if rc is None: continue
if xf and not xf.IsIdentity: rc = rc.CreateTransformed(xf)
edges.append(ds_curve(rc))
# also use face midpoint if centroid failed
if sig_pt is None:
for f in s.Faces:
try:
bbuv=f.GetBoundingBox(); u=(bbuv.Min.U+bbuv.Max.U)/2.0; v=(bbuv.Min.V+bbuv.Max.V)/2.0
p = f.Evaluate(UV(u,v)); p = xf.OfPoint(p) if xf and not xf.IsIdentity else p
sig_pt = xyz2pt(p); break
except: pass
def add_face_edges(f, xf):
nonlocal sig_pt
for loop in f.EdgeLoops:
for ed in loop:
rc = ed.AsCurve()
if xf and not xf.IsIdentity: rc = rc.CreateTransformed(xf)
edges.append(ds_curve(rc))
if sig_pt is None:
try:
bbuv=f.GetBoundingBox(); u=(bbuv.Min.U+bbuv.Max.U)/2.0; v=(bbuv.Min.V+bbuv.Max.V)/2.0
p=f.Evaluate(UV(u,v)); p = xf.OfPoint(p) if xf and not xf.IsIdentity else p
sig_pt = xyz2pt(p)
except: pass
def add_curve(c, xf):
rc = c if (xf is None or xf.IsIdentity) else c.CreateTransformed(xf)
edges.append(ds_curve(rc))
def walk(geom, xf):
for g in geom:
if isinstance(g, GeometryInstance):
walk(g.GetInstanceGeometry(), xf.Multiply(g.Transform) if xf else g.Transform)
elif isinstance(g, Solid) and g.Volume>0:
add_solid_edges(g, xf if xf else Transform.Identity)
elif isinstance(g, Face):
add_face_edges(g, xf if xf else Transform.Identity)
elif isinstance(g, Curve):
add_curve(g, xf if xf else Transform.Identity)
elif isinstance(g, Mesh):
add_mesh_edges(g, xf if xf else Transform.Identity)
rvt = UnwrapElement(el)
ge = rvt.get_Geometry(opt)
walk(ge, Transform.Identity)
if not edges and sig_pt is None: # complete failure
return None, None
if sig_pt is None and edges: # fallback: avg endpoints
xs=ys=zs=n=0.0
for crv in edges:
try:
p0,p1 = crv.StartPoint, crv.EndPoint
xs += p0.X+p1.X; ys += p0.Y+p1.Y; zs += p0.Z+p1.Z; n += 2
except: pass
if n>0: sig_pt = Point.ByCoordinates(xs/n, ys/n, zs/n)
return edges, sig_pt
# ------- Inputs -------
elements = to_list(IN[0])
reg_bb = as_bbox(IN[1])
region_solid = Cuboid.ByCorners(reg_bb.MinPoint, reg_bb.MaxPoint)
hits, intersects, contained = [], [], []
for el in elements:
try:
# 1) real-geometry edges + point
edges, sp = collect_edges_and_point(el)
if edges is not None:
if any(c and c.DoesIntersect(region_solid) for c in edges):
intersects.append(el); hits.append(el); continue
if sp and pt_in_bb(sp, reg_bb):
contained.append(el); hits.append(el); continue
# 2) FALLBACK: OBB from BoundingBoxXYZ
rvt = UnwrapElement(el)
bbxyz = rvt.get_BoundingBox(None)
if isinstance(bbxyz, BoundingBoxXYZ) and bbxyz is not None:
corners = obb_corners_from_bbxyz(bbxyz)
obb_edges = edge_lines_from_corners(corners)
if any(ln.DoesIntersect(region_solid) for ln in obb_edges):
intersects.append(el); hits.append(el); continue
cp = center_point_bbxyz(bbxyz)
if cp and pt_in_bb(cp, reg_bb):
contained.append(el); hits.append(el); continue
# 3) FINAL FALLBACK: axis-aligned DS bbox edges
if isinstance(bbxyz, BoundingBoxXYZ) and bbxyz is not None:
# convert to DS bbox by transforming min/max to world
t = bbxyz.Transform
mn = xyz2pt(t.OfPoint(bbxyz.Min)); mx = xyz2pt(t.OfPoint(bbxyz.Max))
dsbb = BoundingBox.ByCorners(mn, mx)
aabb_edges = edge_lines_from_corners(aabb_corners(dsbb))
if any(ln.DoesIntersect(region_solid) for ln in aabb_edges):
intersects.append(el); hits.append(el); continue
# center
cx = Point.ByCoordinates((mn.X+mx.X)/2.0, (mn.Y+mx.Y)/2.0, (mn.Z+mx.Z)/2.0)
if pt_in_bb(cx, reg_bb):
contained.append(el); hits.append(el); continue
except:
pass
OUT = [hits, intersects, contained]
Good solution, but a quick note that ‘intersects’ is different than ‘contains’. Assuming that @gsucciXHCQR’s post is related to the work I believe they were doing on topography in another thread a ‘contains’ method is insufficient as topography can have triangles which are very oddly shaped which can result in surfaces with one vertex inside the box but other vertices exceeding the maximum and not reaching g the minimum. Intersection testing is faster in such cases as it is one test written in the super fast C# engine vs one test plus edge case handling for bounding boxes.
Contains means entirely inside the bounding box. A triangle defined by the points [(-100,-100,0), (100,-100,0), (100,100,0)] intersects the bounding box defined by points [(-10,-10,-10), (10,10,10)] but it doesn’t not fit inside the bounding box and so the bounding box doesn’t contain it.
@gsucciXHCQR you should note that the example above would not be caught by your method of testing each of the points but likely would want to be involved in your test.
I recommend doing the intersection test on a solid, not the PolySurface, as the solid of the cuboid by points [(-10,-10,-10), (10,10,10)] will intersect a triangle from points [(0,0,0), (1,0,0), (1,1,0)] but the PolySurface will not.
Ok, but any surface entirely within the BBox will trigger the other test (at least one of tis points contained in the BBox), right? So it should work…?
I mean, I wish to save a step and calculation time, if I can.
Thank you
PS: maybe I could skip the first test and just do the solid versus the surfaces? Not sure if it would be slower…
That is what I have been recommending - does intersect is as optimal as you can get, and you only have one test to run at that too. Simplifying the topography to reduce the scope is the only way you’re getting faster without jumping into some C# code.