Confused about behavior of curve.ClosestPoint

This is likely a dumb question, so my apologies in advance.

Inside some GHPython code I’m trying to test points for whether they are on a given curve. Unfortunately it seems that all points say they’re on the curve.

If anyone could kindly explain what I’m doing wrong, that would be splendid.

I’ve attached a simple example showing my problem.

Thanks so much,
Marytest_point.3dm (25.9 KB)
test_point.gh (3.9 KB)

Hello @mary.baker,

First of all, there are no dumb questions! :wink:

When you call curve.ClosestPoint(point, tolerance) in your Python script, you get for instance (True, 6.81) returned. The first value informs about, wether the method was successfully called, and not about your points proximity to the curve. The second value is simply a curve parameter, of where the closest point to your point is situated on the curve.

So what you need to do, is simply take the parameter to get the closest point, measure the distance between the closest point and your point, and compare this distance to your tolerance value.

curve.ClosestPoint() is also not a method from rhinoscriptsyntax, but from RhinoCommon. Since, you already imported the rhinoscriptsyntax library, here’s how to solve your problem in it:

import rhinoscriptsyntax as rs

# Get the curve parameter
t = rs.CurveClosestPoint(curve, point)
# Get the closest point on the curve to your point 
crv_pt = rs.EvaluateCurve(curve, t) 
# Measure the distance between both points
dist = rs.Distance(point, crv_pt)

# Compare the distance to your tolerance
if dist <= tolerance:
    print "Point was indeed within %.2f distance." %(tolerance)
    a = True
else:
    print "Point was not within %.2f distance." %(tolerance)
    a = False
2 Likes

Thank you so much! That is tremendously helpful, and now I better understand a few other RhinoCommon functions as well :slight_smile:

Thanks again!
Mary

You’re welcome! :slight_smile:

If your interested in a RhinoCommon version, it is done very similarly.
However, you might have to set your component inputs to curve and point inputs respectively, for it to work.

import Rhino.Geometry as rg

# Get the curve parameter
success, t = curve.ClosestPoint(point)
# Get the closest point on the curve to your point 
crv_pt = curve.PointAt(t)
# Measure the distance between both points
dist = point.DistanceTo(crv_pt)

# Compare the distance to your tolerance
if dist <= tolerance:
    print "Point was indeed within %.2f distance." %(tolerance)
    a = True
else:
    print "Point was not within %.2f distance." %(tolerance)
    a = False

In RhinoCommon, as you may already know, you call the methods/functions differently.
Rhinoscriptsyntax is basically a “repackaged” Python API of RhinoCommon that makes the latter more straightforward to use.
In order to understand the structural differences between both, you need to understand, what namespaces, classes, constructors, methods/functions, etc. are in programming.

Here’s a simple example:

curve.ClosestPoint(point) calls the method ClosestPoint() from the Curves class that exists in the Rhino.Geometry namespace. Your variable curve is in fact an instance of the Curves class and ClosestPoint() one of its class methods.
Your variable point is an instance of the Point class from Rhino.Geometry that has its own class methods to chose from.
This is also, why you need to change the inputs of your component to curve and point inputs. I’m not totally sure about this, but I guess that when you declare the types of your inputs, the component passes class instances to your code, which is essential for RhinoCommon.
The rhinoscriptsyntax, on the other hand mostly works with guids, some type of geometry reference (?), an identification of sorts of geometry objects (?). Why it doesn’t need the type declarations on component input level, is because it mostly does the conversions internally (see below).

In rhinoscriptsyntax, you basically call the method CurveClosestPoint() from the rhinosciptsyntax library that you previously imported.
And, here’s what happens under the hood (cf. source):

def CurveClosestPoint(curve_id, test_point, segment_index=-1 ):
    """Returns parameter of the point on a curve that is closest to a test point.
    Parameters:
      curve_id (guid): identifier of a curve object
      point (point): sampling point
      segment_index (number, optional): curve segment index if `curve_id` identifies a polycurve
    Returns:
      number: The parameter of the closest point on the curve
    Example:
      import rhinoscriptsyntax as rs
      id = rs.GetObject("Select a curve")
      if id:
          point = rs.GetPointOnCurve(id, "Pick a test point")
          if point:
              param = rs.CurveClosestPoint(id, point)
              print "Curve parameter:", param
    See Also:
      EvaluateCurve
      IsCurve
    """
    curve = rhutil.coercecurve(curve_id, segment_index, True)
    point = rhutil.coerce3dpoint(test_point, True)
    rc, t = curve.ClosestPoint(point, 0.0)
    if not rc: raise Exception("ClosestPoint failed")
    return t

First, the method converts its parameter curve_id, which is the guid of the passed-in curve, to an instance of the Curve class from Rhino.Geometry. The parameter test_point is converted to an instance of the Point class from Rhino.Geometry (?).
After that, the method simply calls the CurveClosestPoint() method, like I did in the RhinoCommon example above, from the instance of the Curve class, saved in the variable curve. Remember, the Curve class lives in the Rhino.Geometry namespace.

I’ve added a question marks between parenthesis to all the parts that I’m not 100% sure of. :wink:

5 Likes

Wow – thank you so much! Yes, that is exactly the kind of guidance I was missing. This will make so many things easier for me now.

Thanks again!
Mary

A universally unique identifier

Randomly generated with 128 random bits, ie approximately zero chance of any two objects in the world having the same value so you can safely use them without checking for naming conflicts.
import uuid to make your own or use this recipe

2 Likes

Thanks for clarifying!

Mary

Great explanation @diff-arch!!
Any idea on why Curve.ClosestPoint(Pt) does not work when calling Rhino Common from outside of Rhino (Pycharm)?
Btw, I`m using rhinoinside to call Rhino Common.

Ty!

Welcome @thomas.takeuchi,

The only thing I can think of, is that you might need to provide a second tolerance argument (i.e. 0.01), but I’m not exactly sure about this?
Generally, I don’t have a lot of experience with rhinoinside. What do you use it for? And does it use RhinoCommon, or compute_rhino3d, or even rhino3dm?

1 Like

I already answered the question in the other post from @thomas.takeuchi here Rhino inside Python

2 Likes

Thank you for the reply!
I was just doing some tests: opening a rhino file, make some evaluations and save a txt.
I’m using rhinoinside to call Rhino common. It works like this:

import rhinoinside
rhinoinside.load()
import System
import Rhino

@nathanletwory answered me perfectly in Rhino inside Python