When I try to apply the ProjectPointsToMeshesEx (Rhino.Intersect.Intersection) method to meshes that are created by a simple _BoundingBox rhino command (option: Output=Meshes) it randomly fails. None of the points that are inside of the box are found.
The strange thing is that the following works perfectly fine with ProjectPointsToMeshesEx:

creating the BoundingBox as a Brep first and meshing it afterwards

creating a mesh of the BoundingBox via RhinoCommon (PointCloud.GetBoundingBox → Mesh.CreateFromBox)

It simply adds a random PointCloud to the document, creates a bounding box via rhino command and finally tries to find the projections of the points on its bounding box mesh.
On my machine, using the ModelDocumentTolerance and the z-axis as direction of projection ProjectPointsToMeshesEx fails on approx. every 4th attempt. Choosing different tolerances or directions can help a little bit, but still, it’s not watertight.

That was driving me crazy.
What do you think? Is that caused by a special feature of the BBox-meshes (e.g. perfectly ray-perpendicular faces) or could this happen for other, more complex meshes as well and I just didn’t realize it yet?
Just asking because we use ProjectPointsToMeshesEx in our framework/libs quite often. And rewriting everything with Intersection.MeshLine would require some effort. Furthermore, testing many points against many meshes would take much longer.

import Rhino
import scriptcontext as sc
import System
import System.Collections.Generic.IEnumerable as IEnumerable
# Cook up a random 3d point
def randomPoint(rand, min, max):
x = rand.NextDouble() * (max - min) + min
y = rand.NextDouble() * (max - min) + min
z = rand.NextDouble() * (max - min) + min
return Rhino.Geometry.Point3d(x, y, z)
# Project a point onto a mesh
def projectPointToMesh(mesh, point, tol):
# axis-aligned bounding box
bbox = mesh.GetBoundingBox(True)
# some line to intersect
line = Rhino.Geometry.Line(point, point + Rhino.Geometry.Vector3d.ZAxis)
# extend the line through the bbox
# +- 1.0 makes the line a little bigger than the box
p0 = line.From
p1 = line.To
p0.Z = bbox.Min.Z - 1.0
p1.Z = bbox.Max.Z + 1.0
line = Rhino.Geometry.Line(p0, p1)
# do the intersection
return Rhino.Geometry.Intersect.Intersection.MeshLine(mesh, line)
# Project random points onto a mesh generated from the
# boundinng box of the points
def testProject():
# random number generator
rand = System.Random(System.Guid.NewGuid().GetHashCode())
# random 3d points
in_points = []
for i in range(0, 100):
in_points.append(randomPoint(rand, 0.0, 10.0))
# mesh from points
bbox = Rhino.Geometry.BoundingBox(in_points)
mesh = Rhino.Geometry.Mesh.CreateFromBox(bbox, 1, 1, 1)
# project points onto mesh
#tol = Rhino.RhinoMath.ZeroTolerance
tol = 0.0
for i in range(0, len(in_points)):
out_points = projectPointToMesh(mesh, in_points[i], tol)
if out_points:
sc.doc.Objects.AddPoints.Overloads[IEnumerable[Rhino.Geometry.Point3d]](out_points)
else:
print('Intersect failed {0} = {1}'.format(i, in_points[i].ToString()))
sc.doc.Objects.AddMesh(mesh)
sc.doc.Views.Redraw()
if __name__ == "__main__":
testProject()

All ‘failed’ points in your example are exactly on the box surface. This would be an edge case and for our purpose it’s good enough.
But it’s strange anyway. By design, you’ll always will have points on the box surface and it works fine in most cases
BTW: ProjectPointsToMeshesEx even fails for subsets of points that are strictly inside of the box.