Bounding Box contains geometry node?

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.

Thank you

BoundingBox.Cuboid into the Geometry.DoesIntersect should suffice.

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.

if the geometry or any of its subparts of the toposolid intersects means contains lol?

Thank you guys, great suggestions.

Jacob is correct noticing that there is a difference between surfaces intersecting the BBox and surfaces contained within a BBox.

At the end, I did without the Geometry.Doesintersect node altogether.

Instead, I found all vertices of each surface, and test if they were contained in the BBox.

Then AnyTrue gave me all the surfaces that had at least one point inside the BBox, which means the intersecting AND the completely contained surfaces.

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.

OMG, yes, I totally missed that… thank you Jacob.

I have to use Geometry.DoesItersect after all, to catch those surfaces that might intersect the Bbox but whose vertices are all outside of it …:

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.