Python MAP Function

Hi,

Can anyone please provide an example of using Python MAP function with a Rhino syntax command instead of a loop, tried and failed so need a little guidance.
Something simple like a “surfaceclosestpoint” select one surface and a list of existing 3d points that was what I was trying and return the list of the 3d closest points on the surface.

Regards
Roger

Hi @RogerD,

How about this?

#! python3
import rhinoscriptsyntax as rs

def __MyFunc(srf, point):
    uv = rs.SurfaceClosestPoint(srf, point)
    return rs.EvaluateSurface(srf, uv[0], uv[1])

def Test():
    srf = rs.GetObject("Select a surface", rs.filter.surface)
    if not srf:
        return

    points = rs.GetPoints(False, False, "First point", "Next point. Press Enter when done")
    if not points:
        return

    results = map(lambda x: __MyFunc(srf, x), points)
    for p in list(results):
        print(p)

if __name__ == '__main__':
    Test()

– Dale

1 Like

Hi Dale,

Thank you for this I have applied it to a few other functions and it seems quicker than a loop when dealing with lots of points maybe a couple of examples on the help files could assist other users.

Regards

Roger

Hi Dale,

I have been playing with the map function and find it quite useful however I have come across a problem, using BrepClosestPoint if I try to get both arrCP[0] and arrCP[3] at the same time I get the output like

arrCP = (<Rhino.Geometry.Point3d object at 0x00000226F371E640>, <Rhino.Geometry.Vector3d object at 0x00000226F371E6C0>)

however if I get them one at a time using two functions one for the arrCP[0] and one for arrCP[3]
I get what I expect a 3d point -115.73961229639679,-45.38981434783973,0 and a vector 0,-0,-1
why does the output change if I get both together which is what I want so the vector is relative to the correct point if I add them to the workspace.

Roger

#! python3

results = []
resultsa = []

import rhinoscriptsyntax as rs

def __MyFunc(srf, point):
    arrCP = rs.BrepClosestPoint(srf, point)
    #uv = rs.SurfaceClosestPoint(srf, point)
    return (arrCP[0], arrCP[3])
    #return rs.EvaluateSurface(srf, uv[0], uv[1])

#def __MyFunc1(srf, point):
    #arrCP = rs.BrepClosestPoint(srf, point)
    #uv = rs.SurfaceClosestPoint(srf, point)
    #return arrCP[3]

def Test():
    srf = rs.GetObject("Select a surface", rs.filter.surface)
    if not srf:
        return

    points = rs.GetObjects("Pick some points", rs.filter.point)
    if not points:
        return

    results = map(lambda x: __MyFunc(srf, x), points)

    #resultsb = map(lambda x: __MyFunc1(srf, x), points)

    for p in results:
        print("arrCP = ", str(p))

What you print out now is a tuple of a point and vector. You still get what you want, it is just not the same string representation. with the function you have now in use you can print("arrCP = ", str(p[0]), str(p[1])), which will give the same stringified version of the tuple elements.

What you are seeing is the repr() result of the elements in the tuple.

Thank you Nathan great explanation

Hi Nathan and Dale,

I am still playing with the map function and I have question once I get my results from the map of my function how would I add the points generated to the workspace using another map and not having to go through a loop “for (p) in results”.
I have attached a test file if you select a surface and some points it should generate two points and a curve between them representing the closest point and the surface normal from that point.

Roger
test1.3dm (398.4 KB)

#! python3
import rhinoscriptsyntax as rs


def __MyFunc(srf, point):
    uv = rs.SurfaceClosestPoint(srf, point)
    return [rs.EvaluateSurface(srf,uv[0],uv[1]), rs.SurfaceNormal(srf, uv)]


def Test():
    srf = rs.GetObject("Select a surface", rs.filter.surface)
    if not srf:
        return

    points = rs.GetObjects("Pick some points", rs.filter.point)

    if not points:
        return

    results = map(lambda x: __MyFunc(srf, x), points)



    if results:
        
        for p in list(results):
            
            rs.AddPoints([(p[0]), (p[0])+(p[1])])
            rs.AddLine((p[0]), (p[0])+(p[1]))


if __name__ == '__main__':
    Test()

You can always do another map using the map object in results. You could for instance use it to gather the GUIDs of the points and lines you add:

#! python3
import rhinoscriptsyntax as rs


def __MyFunc(srf, point):
    uv = rs.SurfaceClosestPoint(srf, point)
    return [rs.EvaluateSurface(srf,uv[0],uv[1]), rs.SurfaceNormal(srf, uv)]


def __RealizeObjects(p):
    point_guids = rs.AddPoints([(p[0]), (p[0])+(p[1])])
    line_guid = rs.AddLine((p[0]), (p[0])+(p[1]))
    return (point_guids, line_guid)


def Test():
    srf = rs.GetObject("Select a surface", rs.filter.surface)
    if not srf:
        return

    points = rs.GetObjects("Pick some points", rs.filter.point)

    if not points:
        return

    results = map(lambda x: __MyFunc(srf, x), points)
    new_guids = map(lambda p: __RealizeObjects(p), results)
    print(list(new_guids))

if __name__ == '__main__':
    Test()

thank you Nathan I will give it a go

Hi Nathan,

Why does this not work when trying to use BrepClosestPoint but the concept work with SurfaceClosestPoint. I cannot get the second map function to add the points and curves, so much to learn and so little time to do it.

Roger

#! python3


points = []

results = []



import rhinoscriptsyntax as rs

def __MyFunc(srf, point):
    arrCP = rs.BrepClosestPoint(srf, point)
    return [arrCP[0],arrCP[3]]


def __RealizeObjects(p):
    point_guids = rs.AddPoints([(p[0]), (p[0])+(p[1])])
    line_guid = rs.AddLine((p[0]), (p[0])+(p[1]))
    return (point_guids,line_guid)


def Test():
    srf = rs.GetObject("Select a surface", rs.filter.surface)
    if not srf:
        return

    points = rs.GetObjects("Pick some points", rs.filter.point)
    
    if not points:
        return

      
    results = list(map(lambda x: __MyFunc(srf, x), points))

    new_guids = map(lambda p: __RealizeObjects(p), results)


        
    #if results:
        
        #for p in list(results):
            #rs.AddPoints([(p[0]), (p[0])+(p[1])])
            #rs.AddLine((p[0]), (p[0])+(p[1]))

            #print((p[0]))
            #print((p[1]))

    #new_guids = map(lambda p: __RealizeObjects(p), results)

    #if results:
        
        #for p in list(results):
            #rs.AddPoints([(p[0]), (p[0])+(p[1])])
            #rs.AddLine((p[0]), (p[0])+(p[1]))

            #print((p[0]))
            #print("pt = ",pt)

        #for p in points:
            #print(p)

if __name__ == '__main__':
    Test()

Works just fine for me:

#! python3
import rhinoscriptsyntax as rs


def __MyFunc(srf, point):
    res = rs.BrepClosestPoint(srf, point)
    return (res[0], res[3])


def __RealizeObjects(p):
    point_guids = rs.AddPoints([(p[0]), (p[0])+(p[1])])
    line_guid = rs.AddLine((p[0]), (p[0])+(p[1]))
    return (point_guids, line_guid)


def Test():
    srf = rs.GetObject("Select a surface", rs.filter.surface | rs.filter.polysurface)
    if not srf:
        return

    points = rs.GetObjects("Pick some points", rs.filter.point)
    if not points:
        return

    results = map(lambda x: __MyFunc(srf, x), points)
    new_guids = map(lambda p: __RealizeObjects(p), results)


if __name__ == '__main__':
    Test()

Hi Nathan,

Didn’t work for me and I couldn’t work out why so back to the drawing board, thanks again.

This is the Rhino version I am using

Version 8 SR14
(8.14.24337.13001, 2024-12-02)
Commercial
SN: 6-1802-0101-1-39636-34008
Roger Davies

the script runs with no error but does not add the points and curves to the workspace if you try it with the simple model I sent you maybe you will get the same as me.