Is ProjectPointsToMeshesEx unstable for simple Meshes?

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)
  • arbitrary complex meshes

I created a simple script that shows the random (?) character of this issue: (2.5 KB)

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.

Is ProjectPointsToMeshesEx broken?

Hi @trademacher,

I can repeat the oddness. I’ve logged the issue.

– Dale

Thanks a lot Dale!

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.

Hi @trademacher,

This seems to fail less often:

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:
            print('Intersect failed {0} = {1}'.format(i, in_points[i].ToString()))

if __name__ == "__main__":

– Dale

Thanks @dale,

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 :thinking:
BTW: ProjectPointsToMeshesEx even fails for subsets of points that are strictly inside of the box.