Stuck in Grasshopper-Python Recursion

Hi everyone,
Really appreciate it if anyone could help me with this.
The attached code includes 3 parts.
This image is done by #1 (which is obviously not a smart solution!)

I’m trying to do the recursion by code #2 where I’m stuck!
I have also come across two issues while doing code #2:

  1. Why Python does not have access to rhinoscript “addPolygon” method?
  2. How to make a “Degree 1” interpolated curve Periodic in python?

My last question is about chunk #3 where the python code does not do anything as opposed to the same code when implemented in Rhino Python Editor.
Here is the code: (32.7 KB)


while this addresses zero of your questions and is python only, there may be something of use for you…
sort of a sloppy first go of it :wink:

import rhinoscriptsyntax as rs
amp = 4
amt = 12
crv = rs.GetObject()

def squares(crv,amp,amt):

    if amt > 0:
        pts = rs.PolylineVertices(crv,-1)
        pts = rs.AddPoints(pts)
        knots = rs.CurveKnots(crv)
        ptlist = []

        for k in range(0, len(knots) - 1):

            vec = rs.CurveTangent(crv, knots[k])
            vec = rs.VectorScale(vec, amp)
            newpt = rs.MoveObject(pts[k],vec)

        crv = rs.AddPolyline(ptlist)
        amt = amt - 1


ha- hold on… that thing is making a whole bunch of curves (like 4000 of them in that picture)… i see why but i don’t have time to clean it up right now… hopefully tomorrow

EDIT 2 – ok… fixed

1 Like

Thanks for your time,
However, as you said, the issue remains unsolved,
as the code limps in Grasshopper Python Editor.

Also Iam not certain if it is a bug or something in your code that generates sort of anomaly in specific settings. For example amp = 6 , amt = 20
like this:

it’s because the vertices of the new polygon has to go around the corner of the existing one… but the code i did has the points moving along a single vector as opposed to following the curve… (when the amplitude becomes greater than the length of a side, the problem occurs)

to see that problem in my code a little more clearly, do this-- use a polygon that has a side length of 10 – set amount to 1… set amplitude to 12 (or something greater than 10)

i’ll try to mess around with it some more tonight as well as doing it in grasshopper

There was another conversation floating around about this…I forget where. There were a few different flavors of suggestions. Here is a ghpython example, based on those conversations:( I made that component for fun from pieces of the code suggested…the code is below to make your own).

"""RecursionThing: A classic recursion thing. 
        C: (Curve) Boundary Curves
        L: (int) Number of internal offsets, (loops)
        t: (float) Interior curve parameter for offset
        D: (float) If making 3D offset distance in Z relative to World XY
        B: (bool) Boolean, Make 3D
        IC: Interior Curves"""

import Rhino
import Grasshopper
import sys

def SpiroGraph2():
    current_crv = C
    results = []
    for i in xrange(L+1):
        segments = current_crv.DuplicateSegments()
        if len(segments) < 2: return
        mid_points = []
        for segment in segments:
            mid_pt = segment.PointAtNormalizedLength(t)
            if B == True:
                newmid_pt = Rhino.Geometry.Point3d(mid_pt[0], mid_pt[1], mid_pt[2]+D)
        new_crv = Rhino.Geometry.Polyline(mid_points)
        current_crv = new_crv.ToNurbsCurve()
    return results

    IC = SpiroGraph2()

(pretty sure this was the convo)


Thanks for the thread.
However, I was hoping to figure out the missing step in my code.
to get the code, @jeff_hammond provided, implemented in Grasshopper Python rather than in Rhino Python

You can cut and paste the code I provided into a GH Python component. Sorry for not clarifying.

I’ve already done that and it does not work either.
Trying to figure out why,
apologies for clumsy questions.

Make sure you have your inputs set with the correct type hints and they are set to item access. (I included the type hints in the code. I should note that it is just there for visual reference, you will need to right click on the C input, and set it’s type hint to Curve).

"""C: (Curve) Boundary Curves

That works,
I would also be thankful to learn why this code works in Rhino Python and jammed in GhPython.
RecursionTest (8.5 KB)

ok, I took a look at your script. Try this: commented on additions

import rhinoscriptsyntax as rs
amp = 1
amt = 12
newCrvs = [] #need to store the curves somewhere
def squares(crv, amp, amt):
    if amt > 0:
        pts = rs.PolylineVertices(crv) #yields list of points
        knots = rs.CurveKnots(crv)
        pts.pop() # get rid of last point in list (which is a duplicate.. start and end points)
        ptList = [] 
        for k in range(0, len(knots)-1):
            vec = rs.CurveTangent(crv, knots[k])
            vec = rs.VectorScale(vec, amp)
            newpts = rs.MoveObject(pts[k],vec)
        crv = rs.AddPolyline(ptList)

        amt = amt -1
        b= squares(crv,amp,amt)

b = newCrvs

here’s a better way in python (better than the first way i did it at least):

import rhinoscriptsyntax as rs

crv = rs.GetObject()

amp = 6
amt = 11
cpt = (rs.CurveAreaCentroid(crv))[0]

def RecursivePolygon(crv, amp, amt):

    if amt > 0:
        pt1 = rs.CurveStartPoint(crv)
        pt2 = rs.EvaluateCurve(crv,amp)
        crv = rs.OrientObject(crv,[cpt,pt1],[cpt,pt2],1|2)
        amt = amt - 1
        RecursivePolygon(crv, amp, amt)

RecursivePolygon(crv, amp, amt)

in Grasshopper, i do it like this: (7.8 KB)

import rhinoscriptsyntax as rs

cpt = (rs.CurveAreaCentroid(crv))[0]
count = 1

def RecursivePolygon(crv, amp, amt, count):

    if amt > 0:

        pt1 = rs.CurveStartPoint(crv)
        pt2 = rs.EvaluateCurve(crv,amp)
        crv = rs.OrientObject(crv,[cpt,pt1],[cpt,pt2],2)

        ang = (rs.Angle2((cpt, pt1), (cpt,pt2)))[0]
        radius = rs.Distance(cpt,pt2)

        angle = ang * count
        amt = amt - 1
        count = count + 1

        print radius
        print angle

        RecursivePolygon(crv, amp, amt, count)

RecursivePolygon(crv, amp, amt, count)

it’s basically the same thing i did in Rhino Python except it outputs the rotation angles and radius’ numbers which are then plugged into grasshopper components… or, the additional polygons are being created by Grasshopper components instead of within the python script.

i don’t know how to make python create Grasshopper geometry… i’m on Mac and i don’t think all the necessary elements are in place to do that… (?)… so i just use GHPython to generate the numbers.

1 Like

an alternative approach is to use the line:

pt2 = rs.CurveArcLengthPoint(crv,amp)

…in place of:

pt2 = rs.EvaluateCurve(crv,amp)

doing it like that will have each additional polygon starting at the same distance away from a vertex… whereas the way i posted earlier has only the first polygon starting at the ‘amplitude’ distance then all others are in the same position… scaled… the actual distance gets shorter as the polygons shrink… (which i think is the better approach?)

here’s a way to do it in only Grasshopper… no Python (14.1 KB)

• make a polygon
• evaluate a point on the polygon
• measure the angle between the polygon start point and the evaluated point
• create a Series using the angle measurement
• rotate the original plane with this series

• measure the distance from centerpoint to evaluated point
• divide that distance by the radius (to get scale factor)
• use Mass Multiplication to generate a series of scale factors
• multiply those by the radius

• use the list of radius’ and the planes in a polygon component

not really tested… i think it works though :smiley:

@jeff_hammond & @chanley
Thank you both,
I learned about the recursion a lot.
But, I am still struggling with a very minute issue accordingly.

The following short codes are identical.
I just need to know why the GhPython code does not do anything as opposed the same Python code.
I think the GhPython generates something as it is reflected in the yellow panel in the image.
Not sure what it is though.
RecursionTest (4.7 KB)
Recursion test (256 Bytes)

It sounds like you might be wrestling with the concept of scriptcontext.

not that you have to switch between the two for your script to work, it just may help you understand how Rhino sees geometry VS how Grasshopper “sees” geometry.

see if this helps:

import rhinoscriptsyntax as rs

#you need to store the geometry that GH creates somewhere
circles = []

def RecursiveCircle(pt, r):
    if r == 0:
        #recursion loop starts here
        #do something: make circle and add to list (Grasshopper document)
        circles.append(rs.AddCircle(pt, r))
        #change your variable by something
        r -=1
        #call your function again with updated variable
        RecursiveCircle(pt, r)
        #do this until your if statement/condition is met

    #call your function
    RecursiveCircle(pt, r)

a = circles
1 Like