How to use ClosestParameter function on rhino inside cpython

I’m trying to get closest point projected to the sphere and it’s plane(frame) and ultimately get the vector out of it. Which should be something like above in grasshopper

I have to implement this into cpython for some reason but I’m stuck at even getting the closest parameter work in rhinoinside cpython.

I’m mostly new to using rhino common and obviously also the rhino inside cpython so I might be doing something wrong.

import rhinoinside
rhinoinside.load()
import System
import Rhino.Geometry as rg

origin = rg.Point3d(0.0,0.0,0.0)
p = rg.Point3d(1.0, 0.0, 1.732051) # Polar Point (0.0, 60, 2.0)
s = rg.Sphere(origin,1.0)
s2 = s.ToNurbsSurface()
s4 = s.ToRevSurface()

pr = s.ClosestParameter(p)
>>> TypeError: No method matches given arguments for ClosestParameter: (<class 'Rhino.Geometry.Point3d'>)

u1 = 0 # should have got this from clsptparam
v1 = 1.047198 # should have got his from clsptparam

fr2 = s2.FrameAt(u1,v1)
fr4 = s4.FrameAt(u1,v1)

>>> TypeError: No method matches given arguments for ClosestParameter: (<class 'Rhino.Geometry.Point3d'>)

in the ghpython this works so I’m wondering what is wrong with my code…

import Rhino.Geometry as rg

origin = rg.Point3d(0,0,0)
p = rg.Point3d(1.0, 0.0, 1.732051) #Polar Point (0.0, 60, 2.0)

s = rg.Sphere(origin,1.0) 

c1 = s.ClosestPoint(p) # sphere
p1 = s.ClosestParameter(p)

u1 = p1[1]
v1 = p1[2]

I could instead use rhino3dm but now the ClosestPoint doesn’t work if I turn the sphere into ToNurbsSurface which I need to get the FrameAt function.

import rhino3dm

origin = rhino3dm.Point3d(0,0,0)
p = rhino3dm.Point3d(1.0, 0.0, 1.732051)
s = rhino3dm.Sphere(origin,1.0)

s1 = s
c1 = s1.ClosestPoint(p)
p1 = s1.ClosestParameter(p)

u1 = p1[1]
v1 = p1[2]

n1 =s1.NormalAt(u1,v1)

In the end I was able to make this work by passing the uv from sphere’s ClosestParameter to the one converted into NurbsSurface. But now I wonder if there is more sane way to do this

import rhino3dm

origin = rhino3dm.Point3d(0,0,0)
p = rhino3dm.Point3d(1.0, 0.0, 1.732051)
s = rhino3dm.Sphere(origin,1.0)

s1 = s
s2 = s1.ToNurbsSurface()

c1 = s1.ClosestPoint(p)
p1 = s1.ClosestParameter(p)

u1 = p1[1]
v1 = p1[2]

n1 =s1.NormalAt(u1,v1)

WXY = rhino3dm.Plane.WorldXY()

f2 = s2.FrameAt(u1,v1,WXY)
f2p = f2[1]

PO = f2p.Origin
PX = f2p.XAxis
PY = f2p.YAxis
PZ = f2p.ZAxis

Here are attached files described above incae…
python-project.zip (24.3 KB)

There seems to be a bug. You don’t seem to do anything wrong here. A thing to maybe note is that Rhino.Geometry.Sphere().ClosestParameter() returns two angular dimensions in radians, not uv parameters.

You could simply find the closest point yourself! First get the vector pointing from the sphere center point to the search point. You can simply subtract the center point from the search point to do that. Then normalize the resulting vector and multiply the unit vector by the sphere radius. Now, you can get the closest point by simply adding the scaled vector to your sphere center. The only things you really need are the two points and the sphere radius.
Fun fact, the unit vector that was previously scaled by the sphere radius, is the normal at the closest point on the sphere that you could derive a plane from. It would have the normal vector as z-axis and the closest point as origin.

Yeah… maybe I should have tried to look into math. I thought this should be easy work but it almost took like 4 hours to find out what works!

Looks like one of those methods where the out parameters in C# did not get properly removed from the signature in Python. I can get it to work like this:

import rhinoinside
rhinoinside.load()
import System
import Rhino.Geometry as rg

origin = rg.Point3d(0.0,0.0,0.0)
p = rg.Point3d(1.0, 0.0, 1.732051) # Polar Point (0.0, 60, 2.0)
s = rg.Sphere(origin,1.0)
u = 0.
v = 0.

pr = s.ClosestParameter(p, u, v)
print(pr, u, v)
>>> (True, 0.0, 1.0471975993043745) 0.0 0.0
2 Likes

Tracked here: https://mcneel.myjetbrains.com/youtrack/issue/RH-66031