Solid Contains Point API

I’m trying to figure out how to tell if a point is in a solid. I know there are node out there that can do this but can’t find any that allow me to see their code. I’ve checked the API but can only find the ability to check with curves. Does anyone know how the nodes do it? I was considering:

  1. Create a new point based off the original with a small offset
  2. Create a curve based on the two points
  3. Use IntersectWithCurve to check.

In python:

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import Geometry

geom1 = IN[0]
geom2 = IN[1]

OUT = Geometry.DoesIntersect(geom1, geom2)

Oh you want to work with Revit.DB elements?

You can make two lines from the point and intersection for point is only true if both lines intersect. This will not give a true result if the point is on the face of the solid but only if it is really “inside”.

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import XYZ, Line, Solid, SolidCurveIntersectionOptions,CurveLoop, GeometryCreationUtilities

def create_solid_revit():
	width = 10.0
	length = 20.0
	p0 = XYZ(0, 0, 0)
	p1 = XYZ(width, 0, 0)
	p2 = XYZ(width, length, 0)
	p3 = XYZ(0, length, 0)
	line1 = Line.CreateBound(p0, p1)
	line2 = Line.CreateBound(p1, p2)
	line3 = Line.CreateBound(p2, p3)
	line4 = Line.CreateBound(p3, p0)
	curveLoop = CurveLoop()
	curveLoop.Append(line1)
	curveLoop.Append(line2)
	curveLoop.Append(line3)
	curveLoop.Append(line4)
	height = 15.0
	solid_revit = GeometryCreationUtilities.CreateExtrusionGeometry([curveLoop], XYZ.BasisZ, height)
	return solid_revit

def create_vertical_lines(point, length=10000):
    upward_point = point.Add(XYZ(0, 0, length))
    downward_point = point.Add(XYZ(0, 0, -length))
    return Line.CreateBound(point, upward_point), Line.CreateBound(point, downward_point)

revit_solid = create_solid_revit()
revit_xyz = XYZ(1, 1, 1)

upward_line, downward_line = create_vertical_lines(revit_xyz)

options = SolidCurveIntersectionOptions()
upward_intersect = revit_solid.IntersectWithCurve(upward_line, options)
downward_intersect = revit_solid.IntersectWithCurve(downward_line, options)

inside = upward_intersect.SegmentCount > 0 and downward_intersect.SegmentCount > 0

OUT = inside
2 Likes

Another easy approach to also consider points on faces:

Check for intersection with two curves.
If a curve intersects and the start parameter of the intersection curve on the original curve is 0, then intersection of point is true.

Utilizing the startparameter of the segmentextents of the intersection result is pretty much like using the point itself…

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import XYZ, Line, Solid, SolidCurveIntersectionOptions,CurveLoop, GeometryCreationUtilities

def create_solid_revit():
	width = 10.0
	length = 20.0
	p0 = XYZ(0, 0, 0)
	p1 = XYZ(width, 0, 0)
	p2 = XYZ(width, length, 0)
	p3 = XYZ(0, length, 0)
	line1 = Line.CreateBound(p0, p1)
	line2 = Line.CreateBound(p1, p2)
	line3 = Line.CreateBound(p2, p3)
	line4 = Line.CreateBound(p3, p0)
	curveLoop = CurveLoop()
	curveLoop.Append(line1)
	curveLoop.Append(line2)
	curveLoop.Append(line3)
	curveLoop.Append(line4)
	height = 15.0
	solid_revit = GeometryCreationUtilities.CreateExtrusionGeometry([curveLoop], XYZ.BasisZ, height)
	return solid_revit

def create_vertical_lines(point, length=10000):
    upward_point = point.Add(XYZ(0, 0, length))
    downward_point = point.Add(XYZ(0, 0, -length))
    return Line.CreateBound(point, upward_point), Line.CreateBound(point, downward_point)

revit_solid = create_solid_revit()
revit_xyz = XYZ(0, 0,0)

upward_line, downward_line = create_vertical_lines(revit_xyz)

options = SolidCurveIntersectionOptions()
upward_intersect = revit_solid.IntersectWithCurve(upward_line, options)
downward_intersect = revit_solid.IntersectWithCurve(downward_line, options)

if upward_intersect.SegmentCount > 0 and upward_intersect.GetCurveSegmentExtents(0).StartParameter == 0:
    OUT = True

elif downward_intersect.SegmentCount > 0 and downward_intersect.GetCurveSegmentExtents(0).StartParameter == 0:
    OUT = True

else:
    OUT = False
2 Likes

I recommend making a small line from the XYZ (say 1mm long, on the X axis), and then intersecting the curve with the solid using this method.

From there you can parse all the curve segments, and project the original XYZ onto each resulting curve. If the projected XYZ is almost equal to the original XYZ, the point touches or is inside the solid.

2 Likes

Thanks for the posts everyone. I’m currently testing another idea. I can get the elements directly rather than using their GetSpatialElementCalculationPoint. I’m using ElementIntersectsSolidFilter and I’m getting elements! I need to double check the lists and see if any elements show up in multiple solids before I move on. If not I’m going to try creating the small line.

Hi, was browsing by this form, and may have something to add to something to the discussion. Below is a algorithm based on the Revit API, which checks for point inclusion in all kinds of solids, also including torus-like shapes :slight_smile:

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import XYZ, Line, SolidCurveIntersectionOptions, SolidCurveIntersectionMode

def point_inclusion(xyz, solid):
    bounding_box = solid.GetBoundingBox()
    
    # Make a point which is guaranteed to be outside the solid
    max = bounding_box.Max
    max += bounding_box.Transform.Origin # Adjust for bounding box origin transformation, if bounding box has a more complex transformation, this will need to be accounted for as well
    max += XYZ(1, 1, 1)  # Just in case

    line = Line.CreateBound(xyz, max)
    
    intersection_options = SolidCurveIntersectionOptions()

    intersection_options.ResultType = SolidCurveIntersectionMode.CurveSegmentsInside
    intersection_result_inside = solid.IntersectWithCurve(line, intersection_options)
    
    intersection_options.ResultType = SolidCurveIntersectionMode.CurveSegmentsOutside
    intersection_result_outside = solid.IntersectWithCurve(line, intersection_options)
    
    return (intersection_result_inside.SegmentCount + intersection_result_outside.SegmentCount) % 2 == 0  # See Jordan curve theorem
1 Like

another quicker way is to make a boundingbox from the solid and check is the point inside this boundingbox
this is a quick filter which perform faster
BoundingBoxContainsPointFilter