Animating with Python in Grasshopper

I’m looking for a general method to animate loops with python in grasshopper. Attached is a basic example of what I mean. In this example I have 3 points chasing each other. I am animating by using and internal timer that increases the range of the for loop incrementally and continuously. The problem is this method is that each “frame” it has to run through the entire loop which gets longer and longer as time passes.

Would there be some method that could bypass running though the entire loop? For example would there be someway to overwrite the intial variable with an operation store that value and then update the compnenent again using the new values without creating an ever increasing range? Does this make sense?

import rhinoscriptsyntax as rs
import time
import Grasshopper as gh

def updateComponent():
    
    """ Updates this component, similar to using a grasshopper timer """
    
    # Define callback action
    def callBack(e):
        ghenv.Component.ExpireSolution(False)
        
    # Get grasshopper document
    ghDoc = ghenv.Component.OnPingDocument()
    
    # Schedule this component to expire
    ghDoc.ScheduleSolution(1,gh.Kernel.GH_Document.GH_ScheduleDelegate(callBack))

# Instantiate/reset persisent starting time variable
if "startTime" not in globals() or Reset or not Run:
    startTime = time.time()

# Calculate the elapsed time (in seconds)
ElapsedTime = round((time.time() - startTime), 2) * 100
steps = int(ElapsedTime)

for h in range(steps):
    for i in range(len(pts)):
        index_repel = (i - 1) % len(pts)
        index_attract = (i + 1) % len(pts)
        vec_repel = pts[i] - pts[index_repel]
        vec_attract = pts[index_attract] - pts[i]
        sum_vec = (vec_repel + vec_attract) * 0.5
        sum_vec = rs.VectorUnitize(sum_vec) * vec_multiplier
        pts[i] = pts[i] + sum_vec

updateComponent()

3body.gh (7.9 KB)

I think what you are trying to do is really an opportunity for OOP rather than procedural. You define an object like so:

class PtMover():
    def __init__(self, pt):
        self.loc = pt
    def Move(self):
        # some code to implement movement
# some more code to finish this class if need to

Then your initiate three objects into a list in the globals()

if 'movers' not in globals(): movers = [PtMover(pt) for pt in pts]
for m in movers:
    m.Move()

Now all you have to do is to use a GH timer. Each time this recomputes, the PtMovers will update.
I did not test this but the idea is here. Hope it helps in some regards…

What @Will_Wang probably forgot to mention is that you need save your current object(s) to the sticky dictionary before the component “refreshes”. If you don’t your data will reset at each iteration.

from scriptcontext import sticky

if Reset and "MyMovers" in sticky.keys(): # reset animation
    sticky.pop("MyMovers", False)
    ghenv.Component.Message = "Reset"

if Run and not Reset: # run animation
    if not "MyMovers" in sticky.keys(): # first iteration
        # Initialise the movers
        movers = [PtMover(pt) for pt in pts]
    else: # other iteration
        # Fetch the movers from the sticky dictionary
        movers = sticky["MyMovers"]
        # Update each mover
        for m in movers:
            m.Move()
   
    # Save the current iteration to the sticky dictionary
    sticky["MyMovers"] = movers
    ghenv.Component.Message = "Running..."

if "MyMovers" in sticky.keys(): 
    movers = sticky["MyMovers"]
    if not Run:
        ghenv.Component.Message = "Paused"

    # Outputs
    a = movers   

Thanks for the replies. I’ll have to take a deeper look into sticky keys and classes.