compute.rhino3d icon indicating copy to clipboard operation
compute.rhino3d copied to clipboard

Can Rhino.Curve.GetFilletPoints be made available in compute

Open JoshuaWilkes opened this issue 3 years ago • 20 comments

https://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_Geometry_Curve_GetFilletPoints.htm

Currently implementing a script that requires this function from RhinoCommon which is not currently available via rhino_compute.

Can this functionality be implemented?

Thanks

JoshuaWilkes avatar Dec 14 '20 05:12 JoshuaWilkes

Linked with COMPUTE-148

mcneel-build avatar Dec 14 '20 05:12 mcneel-build

I believe it is there: image For some reason it isn't showing up in the client libraries. In any case, you should be able to call it with the computeFetch method. (warning: untested pseudocode)

  • js: computeFetch('/rhino/geometry/curve/getfilletpoints', ...)
  • py: Util.ComputeFetch("/rhino/geometry/curve/getfilletpoints", ...)
  • cs: ComputeServer.Post<bool>(ComputeServer.ApiAddress(typeof(Curve), null)...

We'll investigate why it isn't showing up in the clients.

fraguada avatar Dec 15 '20 09:12 fraguada

Following up on this issue,

I don't seem to get anything other than the webpage response from the api when calling it manually.

<Response [200]>

GetFilletPoints

[bool, double t0, double t1, Rhino.Geometry.Plane filletPlane] GetFilletPoints(Rhino.Geometry.Curve curve0, Rhino.Geometry.Curve curve1, double radius, double t0Base, double t1Base)

I tried with both of the below and get the same response. in practice this throws an error in compute fetch at the r.json() line

def curve_fillet_points(curve0, curve1, radius, t0Base, t1Base, multiple=False): url = "/rhino/geometry/curve/getfilletpoints-curve_curve_double_double_double_double_double_plane" if multiple: url += "?multiple=true" args = [curve0, curve1, radius, t0Base, t1Base] if multiple: args = list(zip(curve0, curve1, radius, t0Base, t1Base)) response = rc.Util.ComputeFetch(url, args) return response

def curve_fillet_points(curve0, curve1, radius, t0Base, t1Base, multiple=False): url = "/rhino/geometry/curve/getfilletpoints" if multiple: url += "?multiple=true" args = [curve0, curve1, radius, t0Base, t1Base] if multiple: args = list(zip(curve0, curve1, radius, t0Base, t1Base)) response = rc.Util.ComputeFetch(url, args) return response

JoshuaWilkes avatar Jan 05 '21 05:01 JoshuaWilkes

I haven't figured out yet why GetFilletPoints isn't included in the python client library (we use code generation for the client libraries) but I can confirm that this sample works using the latest version of Rhino Compute from the master branch.

import compute_rhino3d.Util
import rhino3dm

compute_rhino3d.Util.url = 'http://localhost:8081/'

def curve_fillet_points(curve0, curve1, radius, t0Base, t1Base, multiple=False):
    url = "/rhino/geometry/curve/getfilletpoints-curve_curve_double_double_double_double_double_plane"
    if multiple: url += "?multiple=true"
    args = [curve0, curve1, radius, t0Base, t1Base]
    if multiple: args = list(zip(curve0, curve1, radius, t0Base, t1Base))
    response = compute_rhino3d.Util.ComputeFetch(url, args)
    return response


pt0 = rhino3dm.Point3d(0,0,0)
pt1 = rhino3dm.Point3d(10,0,0)
pt2 = rhino3dm.Point3d(0,10,0)
l1 = rhino3dm.LineCurve(pt0, pt1)
l2 = rhino3dm.LineCurve(pt0, pt2)

rv = curve_fillet_points(l1, l2, 1.5, 0, 0)

print(rv)

# [True, 1.4999999999999998, 1.4999999999999998, {'Origin': {'X': 1.5, 'Y': 1.5, 'Z': 0.0}, 'XAxis': {'X': 0.0, 'Y': -1.0, 'Z': 0.0}, 'YAxis': {'X': -1.0, 'Y': 0.0, 'Z': 0.0}, 'ZAxis': {'X': 0.0, 'Y': 0.0, 'Z': -1.0}, 'Normal': {'X': 0.0, 'Y': 0.0, 'Z': -1.0}}]

pearswj avatar Jan 19 '21 16:01 pearswj

I am unable to duplicate your experience @pearswj . We have updated to commit 49dba9f: "rhino":"7.2.21012.11001","compute":"1.0.0.607","git_sha":"49dba9fb"}

I've copy/pasted your code above:

import compute_rhino3d.Util
import rhino3dm
import os

if os.environ.get('userdomain') == "XXX":
	internal_proxy_address = "http://ZZZZ"
	os.environ["HTTP_PROXY"] = f"{internal_proxy_address}:999999"
	os.environ["HTTPS_PROXY"] = f"{internal_proxy_address}:999999"

compute_rhino3d.Util.apiKey = "ABCD"
compute_rhino3d.Util.url = "https://YYYY"	

def curve_fillet_points(curve0, curve1, radius, t0Base, t1Base, multiple=False):
    url = "/rhino/geometry/curve/getfilletpoints-curve_curve_double_double_double_double_double_plane"
    if multiple: url += "?multiple=true"
    args = [curve0, curve1, radius, t0Base, t1Base]
    if multiple: args = list(zip(curve0, curve1, radius, t0Base, t1Base))
    response = compute_rhino3d.Util.ComputeFetch(url, args)
    return response

pt0 = rhino3dm.Point3d(0,0,0)
pt1 = rhino3dm.Point3d(10,0,0)
pt2 = rhino3dm.Point3d(0,10,0)
l1 = rhino3dm.LineCurve(pt0, pt1)
l2 = rhino3dm.LineCurve(pt0, pt2)

rv = curve_fillet_points(l1, l2, 1.5, 0, 0)

print(rv)

I get the following result:

File "C:\Program Files\Python37\lib\json\__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "C:\Program Files\Python37\lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Program Files\Python37\lib\json\decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

FergusH avatar Jan 21 '21 06:01 FergusH

What happens if you temporarily replace return r.json() at the end of computeFetch() (compute_rhino3d/Util.py) with return r.text? That will allow us to see what text the json parser is failing to parse.

pearswj avatar Jan 22 '21 10:01 pearswj

I had to use str(r) (r is a requests.mode.Response but r.text() gives "TypeError: 'str' object is not callable").

From print(str(r)) I get: "<Response [200]>"

FergusH avatar Jan 25 '21 02:01 FergusH

r.text returns the body as a string – that's why it complains if you call it as a method with (). Good to know it's returning 200 though!

We need to add some request error handling to the client library, but for now this should get us some useful information.

pearswj avatar Jan 25 '21 09:01 pearswj

I read a set of parentheses that you hadn't written...

FergusH avatar Jan 27 '21 01:01 FergusH

r.text is:

<!DOCTYPE html><html><body><H1>GetFilletPoints</H1>
<p>
[bool, double t0, double t1, Rhino.Geometry.Plane filletPlane] GetFilletPoints(Rhino.Geometry.Curve curve0, Rhino.Geometry.Curve curve1, double radius, double t0Base, double t1Base)<br>
</p></body></html>

FergusH avatar Jan 27 '21 01:01 FergusH

@pearswj bumping...

FergusH avatar Feb 03 '21 00:02 FergusH

Very strange. The response is the same as if you called GET /rhino/geometry/curve/getfilletpoints-curve_curve_double_double_double_double_double_plane, but the ComputeFetch() function should be using POST. Are you able to call other functions, such as compute_rhino3d.Curve.GetLength()? Can you test it without the proxy?

pearswj avatar Feb 03 '21 13:02 pearswj

Removing the proxy doesn't change the behaviour (we run from both our company network, where the proxy is necessary, and from AWS instances where it isn't. Currently we're working from home due to covid lockdown, so the proxy isn't needed):

import compute_rhino3d.Util
import rhino3dm
import os


compute_rhino3d.Util.apiKey = ""
compute_rhino3d.Util.url = ""	

def curve_fillet_points(curve0, curve1, radius, t0Base, t1Base, multiple=False):
	url = "/rhino/geometry/curve/getfilletpoints-curve_curve_double_double_double_double_double_plane"
	if multiple: url += "?multiple=true"
	args = [curve0, curve1, radius, t0Base, t1Base]
	if multiple: args = list(zip(curve0, curve1, radius, t0Base, t1Base))
	response = compute_rhino3d.Util.ComputeFetch(url, args)
	return response

pt0 = rhino3dm.Point3d(0,0,0)
pt1 = rhino3dm.Point3d(10,0,0)
pt2 = rhino3dm.Point3d(0,10,0)


l1 = rhino3dm.LineCurve(pt0, pt1)
l2 = rhino3dm.LineCurve(pt0, pt2)

rv = curve_fillet_points(l1, l2, 1.5, 0, 0)

print(rv)

Ouput (with Util.ComputeFetch() still returning r.text):

<!DOCTYPE html><html><body><H1>GetFilletPoints</H1>
<p>
[bool, double t0, double t1, Rhino.Geometry.Plane filletPlane] GetFilletPoints(Rhino.Geometry.Curve curve0, Rhino.Geometry.Curve curve1, double radius, double t0Base, double t1Base)<br>
</p></body></html>

FergusH avatar Feb 04 '21 08:02 FergusH

compute_rhino3d.Curve.GetLength() doesn't work (I actually ran into this problem with GetLength() a long time ago but had forgotten, because I got around it by using Curve.GetLength1() which works just fine).


import compute_rhino3d.Util
import compute_rhino3d.Curve
import rhino3dm
import os


compute_rhino3d.Util.apiKey = ""
compute_rhino3d.Util.url = ""	

pt0 = rhino3dm.Point3d(0,0,0)
pt1 = rhino3dm.Point3d(10,0,0)


l1 = rhino3dm.LineCurve(pt0, pt1)

rv = compute_rhino3d.Curve.GetLength( l1 )

print(rv)

Outputs: {"statusCode":404,"message":"The resource you have requested cannot be found.","details":""}

If I use GetLength1():

import compute_rhino3d.Util
import compute_rhino3d.Curve
import rhino3dm
import os

compute_rhino3d.Util.apiKey = ""
compute_rhino3d.Util.url = ""	

pt0 = rhino3dm.Point3d(0,0,0)
pt1 = rhino3dm.Point3d(10,0,0)

l1 = rhino3dm.LineCurve(pt0, pt1)

#rv = compute_rhino3d.Curve.GetLength( l1 )
rv = compute_rhino3d.Curve.GetLength1( l1, 1 )

print(rv)

it outputs: 10.0

FergusH avatar Feb 04 '21 08:02 FergusH

@FergusH I'm perplexed. I've tested the script as you wrote it against several instances of Compute on different machines and I can't reproduce this behaviour. Can you try calling our test server to see if you get the same result as I do?

# mcneel test server connection settings
compute_rhino3d.Util.url = "https://compute.rhino3d.com/"
compute_rhino3d.Util.authToken = "" # token from http://rhino3d.com/compute/login

p.s. The GetLength() was fixed recently (#147). Try build 1.0.0.619 or newer.

pearswj avatar Feb 04 '21 19:02 pearswj

I've just tried your server @pearswj , I still get the same results.

I've tried from my local machine; in fresh venvs; from vms in the cloud - all the same. I've had colleagues here try it, they get the same result as me. JoshuaWilkes who started this thread is from an external contractor that is doing work for us, he gets the same.

I can't think of any other commonality between everything I've tried at this end....

FergusH avatar Feb 05 '21 02:02 FergusH

I appreciate you being thorough. Here's a functionally equivalent python 3 script that doesn't use any modules, except the built-in urllib. Could you test this against our compute.rhino3d.com server? It might be worth trying this script (and the previous script, if successful) against an instance of Compute running on your local machine too. It should be clear in the logs whether Compute is receiving a GET or a POST request.

import urllib.request

baseurl = 'https://compute.rhino3d.com'
token = '' # token from https://rhino3d.com/compute/login
headers = { 'Authorization': 'Bearer ' + token }

# baseurl = 'https://my.server.com'
# headers = { 'RhinoComputeKey': 'API_KEY' }

# baseurl = 'http://localhost:8081'

url = baseurl + '/rhino/geometry/curve/getfilletpoints-curve_curve_double_double_double_double_double_plane'
data = b'[{"version": 10000, "archive3dm": 60, "opennurbs": -1912572244, "data": "+n8CAIEAAAAAAAAA+/8CABQAAAAAAAAA29TXTkfp0xG/5QAQgwEi8PSb6SP8/wIASQAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQAMAAAAexxcp/38CgAAAAAAAAAAA"}, {"version": 10000, "archive3dm": 60, "opennurbs": -1912572244, "data": "+n8CAIEAAAAAAAAA+/8CABQAAAAAAAAA29TXTkfp0xG/5QAQgwEi8PSb6SP8/wIASQAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQAMAAACIZNn3/38CgAAAAAAAAAAA"}, 1.5, 0, 0]'

req = urllib.request.Request(url, data, headers, method='POST')

with urllib.request.urlopen(req) as f:
    print(f.getcode())
    print(f.read())

# 200
# b'[true,1.4999999999999998,1.4999999999999998,{"Origin":{"X":1.5,"Y":1.5,"Z":0.0},"XAxis":{"X":0.0,"Y":-1.0,"Z":0.0},"YAxis":{"X":-1.0,"Y":0.0,"Z":0.0},"ZAxis":{"X":0.0,"Y":0.0,"Z":-1.0},"Normal":{"X":0.0,"Y":0.0,"Z":-1.0}}]'

pearswj avatar Feb 05 '21 18:02 pearswj

Thanks very much @pearswj for your patience and responses throughout this - the new code works correctly with your server, but not with ours.

I can't explain why I was getting unexpected responses from your server earlier, but in any case it's now obvious that our server has a problem. As luck would have it it's being rebuilt tomorrow anyway, I'll ask for this to be investigated/checked during.

FergusH avatar Feb 08 '21 07:02 FergusH

@pearswj it was the trailing slash, e.g.: compute_rhino3d.Util.url = "https://compute.rhino3d.com/"

Your web server apparently strips it away if it's present, ours doesn't - removing the trailing slash in our url has fixed the problem...

FergusH avatar Feb 10 '21 03:02 FergusH

Glad you figured it out and thanks for letting me know. I've opened #218 to make sure that the client constructs well-formed urls without missing or double slashes. I'll leave this open to track getting GetFilletPoints added to the client.

pearswj avatar Feb 10 '21 10:02 pearswj