Problem with springs motion

Hi,

I’m working on a sketch where I’m moving specific particles on a grid. Each particle is connected to its neighbour by a spring.

Goal: I would like the moving particle to drag/pull its neighbours as it is moving up and down.

Problem: I thought putting springs between the particles would do the trick but for some reasons this doesn’t work at all.

gif is glitchy, see video format here

Here, the springs on both sides of the moving particle (in red) should normally pull its neighbours up and down, like in the following picture

Question: Why do I get this strange behaviour ? How can I fix it ?

I made a very simple example sketch using the Toxiclibs library. I also added some step-by-step explanations to be as clear as possible.

Any suggestions are more than welcomed !

add_library('verletphysics')
add_library('toxiclibscore')
from toxi.physics2d import VerletParticle2D
from toxi.physics2d import VerletPhysics2D
from toxi.physics2d.behaviors import GravityBehavior 

pointlist = []
t = 0

def setup():
    global physics
    size(800, 800, P2D)
    smooth(8)
    
    physics = VerletPhysics2D()
    
    #Adding gravity
    physics.addBehavior(GravityBehavior(Vec2D(0, 1)))
    
    
    #Creating particles and adding them to physics engine
    for e in range(31):
        p = VerletParticle2D(width/2 - 15 * 20 + e*20, height/2)
        pointlist.append(p)
        physics.addParticle(p)
        
        
    #Connecting particles with springs
    for i in range(len(pointlist)-1):
        a = pointlist[i]
        b = pointlist[i+1]
        s = VerletSpring2D(a, b, 15, .1)
        physics.addSpring(s)
        
        
    #Locking particles on the edges (far left and far right)
    pointlist[0].lock()
    pointlist[-1].lock()


def draw():
    global t, poitlist
    background(255)
    
    #Running the physics engine
    physics.update()
    
    
    #drawing points
    strokeWeight(8)
    for i, p in enumerate(pointlist):
        
        #if middle point -> change height and add it to pointlist
        if i == 15:
            y = height/2 + sin(t*.05) * 380 
            pointlist[i] = VerletParticle2D(p.x(), y)
        else: 
            y = p.y()
            
        stroke(255, 30, 30) if i == 15 else stroke(0)
        point(p.x(), y)
        
        
    #drawing springs
    strokeWeight(.6)
    for i in range(len(pointlist)-1):
        line(pointlist[i].x(), pointlist[i].y(), pointlist[i+1].x(), pointlist[i+1].y())
        
        
    t += 1
2 Likes

Hi solub,

I can’t run your code right now and you video link is broken so I can’t help much.

I just noticed 2 typos in your code:

#Locking particles on the edges (far left and far right)
    pointlist[0].lock()
    pointlist[-1].lock() // Here with the -1
def draw():
    global t, poitlist // shoudnlt it be pointlist?

I’ve never used Python but is this normal that you declare pointlist and t at the very beginning and once again at the beginning of draw?

Are you sure that when you set the positions of your points in draw, it also update the position in the physics engine?

What is the purpose of your t variable?

3 Likes

Given global t merely increases by 1 at the end of each draw(), it can easily be replaced by the PApplet::frameCount field: :stuck_out_tongue_winking_eye:

When directly assigning some value to a variable (via the = operator or 1 of its composite forms) inside a function:

Python always assumes it’s a local variable! :open_mouth:
Unless we declare it as global before using the assign operator: :globe_with_meridians:

However, b/c variable pointlist isn’t being directly reassigned, but merely its indices:
pointlist[i] = VerletParticle2D(p.x(), y)

There’s no need to declare pointlist as global inside draw() there. :face_with_hand_over_mouth:

And as I had already tipped, if t is replaced by frameCount, the global statement can be completely removed from draw(). :joy_cat:

BtW, at global t, poitlist, it shoulda been global t, pointlist. :crazy_face:

3 Likes

Nice catch! At pointlist[i] = VerletParticle2D(p.x(), y), new VerletParticle2D objects are replacing old 1s. :currency_exchange:

While in setup(), pointlist points to the same VerletParticle2D objects as physics’ own internal ArrayList called particles. :point_right:

However in draw(), the original VerletParticle2D shared objects are lost! :scream:

Therefore, pointlist doesn’t mirror VerletPhysics2D physics anymore! :disappointed:

Maybe rather than instantiating more VerletParticle2D objects, mutate them instead. :bulb:

Or directly modify VerletPhysics2D physics’ own internal ArrayList particles, like this p5.js +
toxiclibs.js sketch does: :sunglasses:

4 Likes

Index list[-1] in Python is the same as last index. That is, list[len(list) - 1]. :snake:

1 Like

I knew Python was a really short language for plenty of reasons.
That’s one more :slight_smile:

Hi @jb4x,

Thank you for the reply. Indeed, there’s a typo and as @GoToLoop rightly pointed out, there is no need to declare it here as a global variable.

1 Like

Hi @GoToLoop, thank you for all the suggestions.

… and it seems I’m facing again that old issue I couldn’t fix some time ago

Could you please tell me what you mean by “mutation” ?

At this point, I’d probably be better off trying to implement my own springs forces… hmm

1 Like

Reassign an object’s internal variables rather than instantiating another 1. :nerd_face:

1 Like

I’m sorry, isn’t that what I’m doing already ?
How would you do that ?

You’re creating new VerletParticle2D, not mutating existing 1s!

Check whether a VerletParticle2D got some setting method called set() or something like that.
Or… given you’re only interested in its y field, you may attempt to change it directly.

3 Likes

As @GoToLoop suggested, applying the addVelocity() method to the moving point was the way to go.

Thank you !

add_library('verletphysics')
add_library('toxiclibscore')
from toxi.physics2d import VerletParticle2D
from toxi.physics2d import VerletPhysics2D
from toxi.physics2d.behaviors import GravityBehavior 

pointlist = []
t = 0

def setup():
    global physics
    size(800, 800, P2D)
    smooth(8)
    
    physics = VerletPhysics2D()
    
    #Adding gravity
    physics.addBehavior(GravityBehavior(Vec2D(0, 1)))

    
    #Creating particles and adding them to physics engine
    for e in range(31):
        p = VerletParticle2D(Vec2D(width/2 - 15 * 20 + e*20, height/2))
        pointlist.append(p)
        physics.addParticle(p)
        
        
    #Connecting particles with springs
    for i in range(len(pointlist)-1):
        a = pointlist[i]
        b = pointlist[i+1]
        s = VerletSpring2D(a, b, 15, .1)
        physics.addSpring(s)
        
        
    #Locking particles on the edges (far left and far right)
    pointlist[0].lock()
    pointlist[-1].lock()


def draw():
    global t
    background(255)
    
    #Running the physics engine
    physics.update()
    
    
    #drawing points
    strokeWeight(8)
    for i, p in enumerate(pointlist):

        
        #if middle point -> change height and add it to pointlist
        if i == 15:
            y = height/2 + sin(t * .1) * 10
            p.addVelocity(Vec2D(0, y - height/2))
        stroke(255, 30, 30) if i == 15 else stroke(0)
        point(p.x(), p.y())
        
        
    #drawing springs
    strokeWeight(.6)
    for i in range(len(pointlist)-1):
        line(pointlist[i].x(), pointlist[i].y(), pointlist[i+1].x(), pointlist[i+1].y())
        
        
    t += 1

3 Likes