Find center point of cylinder with RS


#1

Is there a way to find the center point of a cylinder with RhinoScript? I have looked at rs.IsCylinder and rs.SurfaceCylinder but neither gives a center point from what I can see. I noticed RhinoCommon has a property called “Center” for cylinders - http://developer.rhino3d.com/api/RhinoCommon/html/P_Rhino_Geometry_Cylinder_Center.htm - Is there a way to get this property through RhinoScript?


#2

SurfaceCylinder(surface_id)

Returns:

tuple(plane, number, number): of the cylinder plane, height, radius on success

I believe the origin of the plane returned is the center of the cylinder’s defining circle.


#3

Hi,
Probably found not the most effective way (using Python), but at least it works

import rhinoscriptsyntax as rs

def midPt():
input = rs.GetObject(“pick a cylinder”)
diag = rs.AddLine(rs.BoundingBox(input)[0], rs.BoundingBox(input)[6])
diag_domain = rs.CurveDomain(diag)
mid_pt = rs.EvaluateCurve(diag, diag_domain[1]/2)
cleanup = []
cleanup.append(diag)
rs.DeleteObject(cleanup)
rs.AddPoint(mid_pt)

midPt()

Best of luck,
Ilja


#4

You are correct, the origin should give me what I need thanks.


#5

ilmar_ik - This is handy but it doesn’t work on partial cylinders because of the bounding box method.


#6

@Helvetosaur - It appears this is not correct after all. I thought I had tried using origin in the past and it didn’t give me what I needed, which is why I asked the question originally. I just found a situation where it does not give me the center of a defining circle. In this file, it appears to give the center of the defining arc’s domain.

Cylinder.3dm (34.7 KB)

Sample program ↓

import rhinoscriptsyntax as rs

cyl = rs.GetObject(" Pick Cylinder ")
cyl_properties = rs.SurfaceCylinder(cyl)
cyl_origin = cyl_properties[0][0]
rs.AddPoint(cyl_origin)
print("Cylinder Properties: " + str(cyl_properties))
print("Cylinder Origin Rounded X: " + str(round(cyl_origin[0],3)))
print("Cylinder Origin Rounded Y: " + str(round(cyl_origin[1],3)))
print("Cylinder Origin Rounded Z: " + str(round(cyl_origin[2],3)))

#7

I found a way to get my point, but this is still not foolproof for all cylinders. Is there a better way?

import rhinoscriptsyntax as rs

cyl = rs.GetObject(" Pick Cylinder ")
cyl_properties = rs.SurfaceCylinder(cyl)
cyl_rad = cyl_properties[2]

cent_pt_list =[]

edge_list = rs.DuplicateEdgeCurves(cyl)
for edge in edge_list:
    if rs.IsArc(edge):
        edge_rad = rs.ArcRadius(edge)
        if edge_rad == cyl_rad:
            arc_cent = rs.ArcCenterPoint(edge)
            cent_pt_list.append(arc_cent)
    rs.DeleteObject(edge)

cyl_center = (cent_pt_list[0] + cent_pt_list[1])/2
rs.AddPoint(cyl_center)

#8

Yep, looks like you are correct, it just gets the plane of the cylinder and the origin has nothing to do with the center of the arc - my bad.

Your method might work if you fix it a bit - doesn’t right now, I can explain later - assuming that all circular ends of the cylinder fragment haven’t been trimmed off - i.e. it can find an arc edge. If not, it will fail…

Your fixed script might look like the following:

import rhinoscriptsyntax as rs

#get file tolerance
tol=rs.UnitAbsoluteTolerance()
cyl = rs.GetObject(" Pick Cylinder ")
cyl_properties = rs.SurfaceCylinder(cyl)
cyl_rad = cyl_properties[2]
cent_pt_list =[]
edge_list = rs.DuplicateEdgeCurves(cyl)
for edge in edge_list:
    #need to check for arcs *or circles*
    if rs.IsArc(edge) or rs.IsCircle(edge):
        edge_rad = rs.ArcRadius(edge)
        #find values *within tolerance*
        if abs(edge_rad-cyl_rad)<tol:
            arc_cent = rs.ArcCenterPoint(edge)
            cent_pt_list.append(arc_cent)
    rs.DeleteObject(edge)
#make sure you got *exactly* two points
if len(cent_pt_list)==2:
    cyl_center = (cent_pt_list[0] + cent_pt_list[1])/2
    rs.AddPoint(cyl_center)

One of the easy mistakes to make is to have a line of code like this:

if a == b: do something

But in the world of floating point math, a will never equal b exactly - there will always be a tiny difference, even if it’s out at 12 decimal points. So it will virtually always return False. The way to check for “equality” between floating point values is to see if they are equal to within a given tolerance - the commonly accepted way is to see if the absolute value of the difference between the two is less than the tolerance. So you have:

if abs(a - b)<tol: do something

As to completely trimmed cylinder bits, as I said, your script above will not find the center. The scriptlet below should get you the center - albeit it’s the center of the axis line of the untrimmed underlying surface. It uses some RhinoCommon to avoid duplicating edges and then deleting them, as well as accessing a function .Center that doesn’t to be currently available via rhinoscriptsyntax.

import rhinoscriptsyntax as rs

cylID=rs.GetObject("Pick Cylinder Surface",8,True)
if cylID:
    #get the underlying surface geometry (it's a brep face actually)
    cyl_srf = rs.coercesurface(cylID)
    if cyl_srf:
        #try to find the cylinder from the surface
        rc,cyl = cyl_srf.TryGetCylinder()
        #if successful, add a point at the cylinder "center"
        if rc: rs.AddPoint(cyl.Center)

HTH, --Mitch


#9

I see, I ran into this issue and I was working around it by using round()

I was working around this by using rs.IsSurfaceTrimmed() and un-trimming if necessary.

Your RhinoCommon example looks much cleaner than what I came up with using just rhinoscript. Thanks for the help.