How to apply a repulsion force to a particle system ?

edited February 2018 in Python Mode

Hi !

I've recently found this article from Zachary Lieberman where he provides some insights on his work. I'm particularly interested in his "recipe" to make a blob moving "organically" (almost like a fluid).

This is a 2-step process:

  • connect particles with springs (and connect the last particle to the first particle, making a loop)
  • give the particles a repulsion force from each other that is proportional to distance (stronger force when closer) and has a maximum radius for interaction (i.e., if particles are beyond this distance, they don’t have an impact on each other)

I made both a particle and a spring class but got lost when trying to implement the repulsion force. What should I change (add ?) in the following snippet (a spring class using basic Hooke's law) to "give the particles a repulsion force from each other that is proportional to distance" ?

spring class

class Spring(object):

def __init__(self, x, y, l):
    self.startpoint = PVector(x, y)
    self.constant = .2
    self.lngth = l
    self.g = 10
    self.mass = 4

def connect(self, p):    # p refers to the particle_class
    force = PVector.sub(p.location, self.startpoint)
    distance = force.mag()
    stretch = distance - self.lngth

    force.normalize()
    force *= -1 * self.constant * stretch   # Hooke's law
    force.limit(.4) 
    p.applyForce(force) 


def displayLine(self, p):  
    stroke(255)
    strokeWeight(1)
    line(p.location.x, p.location.y, self.startpoint.x, self.startpoint.y) 

On a side note, I'm also curious to know how to display particles following a splatter shape (GIF above).

Answers

  • edited February 2018 Answer ✓

    UPDATE

    I think I've managed to give the particles a repulsion force from each other that is proportional to distance but the result is very (very) different from what I expected:

    https://media.giphy.com/media/aiFDgXqkEuD6MJWleK/giphy.gif

    As you can see all the particles are interconnected with springs but shortly after the start they bounce all over the place, the springs get mixed up and the original vertex eventually split in many pieces.

    Either there's something wrong with my repulsion force or I've missed something in the process.

    About the repulsion force: I just applied a negative graviational force to each particle and divided this force by the distance separating a particle from another (the closer the stronger).

    Repulsion force snippet

    for i, e in enumerate(particules):
            for j, f in enumerate(particules):
                if e != f:
                    force = PVector.sub(e.location, f.location)
                    dsq = force.magSq()
                    strength = (e.g * e.mass * f.mass) / dsq  
                    force *= -strength 
                    f.applyForce(force)
    

    2 QUESTIONS:

    • Does the repulsion force above seem correct to you ?
    • Do you have an idea of what the problem could be ? (just looking at the GIF)

    Full code:

    from particle import Particle
    from spring import Spring
    
    def setup():
        global particules, elastiques, n_particle
        size(800, 600)
    
        particules = []
        elastiques = []
        n_particle = 10
    
        for e in range(n_particle):
            radius = 200
            angle = TWO_PI  / n_particle
            x = radius * sin(angle * e) + map(noise(random(-20, 20)), 0, 1, -20, 20) 
            y = radius * cos(angle * e) 
            particules.append(Particle(width / 2 + x, height / 2 + y))
    
    
    def draw():
        global particules, elastiques
        background(0)
    
    
        for e in particules:
            e.display()
            e.update()
    
        for e in range(n_particle):
            n = 1
            if e+n == n_particle:
                n = - (n_particle - 1)
            elastiques.append(Spring(particules[e+n].location.x, particules[e+n].location.y, 1))
    
            if len(elastiques) - 1 == n_particle:
                del elastiques[0]
    
        for j, e in enumerate(elastiques):
            for i, p in enumerate(particules):
                e.displayLine(p)
                e.connect(p)
    
    
        beginShape()
        fill(255)
        stroke(255)
        for e in range(n_particle):
            vertex(particules[e].location.x,particules[e].location.y)
        endShape()
    
    
    
        for i, e in enumerate(particules):
            for j, f in enumerate(particules):
                if e != f:
                    force = PVector.sub(e.location, f.location)
                    dsq = force.magSq()
                    strength = (e.g * e.mass * f.mass) / dsq
                    force *= -strength / dsq 
                    f.applyForce(force)
                    e.applyForce(force)
    

    Particle class

    class Particle(object):
    
        def __init__(self, x, y):
            self.location = PVector(x, y)
            self.velocity = PVector()
            self.acceleration = PVector()
            self.mass = 4
            self.g = 1
    
    
        def applyForce(self, f):
            f = f / self.mass
            self.velocity += f
    
        def update(self):
            self.velocity += self.acceleration
            self.location += self.velocity
            self.acceleration *= 0
    
        def display(self):
            fill(255)
            noStroke()
            ellipse(self.location.x, self.location.y, 6, 6)
    

    Spring class

    class Spring(object):
    
        def __init__(self, x, y, longueur):
            self.startpoint = PVector(x, y)
            self.constante = .2
            self.longueur = longueur
            self.g = 1
            self.mass = 4
    
        def connect(self, p):
            force = PVector.sub(p.location, self.startpoint)
            distance = force.mag()
            stretch = distance - self.longueur
    
            force.normalize()
            force *= -1 * self.constante * stretch
            force.limit(.1) 
            p.applyForce(force)
    
    
        def displayLine(self, p):
            stroke(255)
            strokeWeight(.2)
            line(p.location.x, p.location.y, self.startpoint.x, self.startpoint.y) 
    
  • Anyone ?

  • Anyone ?

  • Your gif shows points repeatedly falling through the center and out the other side. If there is repulsion force, it is Set nowhere near strong enough to overcome gravity and cause a bounce -- which is presumably what you want...?

  • edited February 2018

    @jeremydouglass, thank you for your feedback. I do want the particles to bounce but simply not that way. For example, when a particle originally placed on the left goes too far to the right and crosses other particles (originally placed on the right side of the circle), the circular shape formed by the vertices then splits up and divides into portions. I do not want that behavior. I think now the problem lies in the way the particles are connected (fully interconnected network vs simple linear connection)

    Now, did I managed to apply a repulsion force to every single particles ? I believe so and in this regard I would understand if my original question was marked as answered.

  • I think I'm just having a hard time understanding what you are trying to accomplish. For example, in the animation you share, line segments don't cross because of strong repulsive forces. That is why the example looks like a blob.

    You seem to want your points to cross due to very weak repulsive forces... but I don't understand what you think should happen visually when you allow crossing to occur. Is there a reference image of what you want it to look like when crossing happens? For example, do you always want to draw a convex hull around your collection of bouncing points, rather than maintaining inter-point springs?

  • edited February 2018

    @jeremydouglass I'm sorry I couldn't make myself clear. I don't want points to cross at all. I would like to be able to make blobs behave exactly like in the GIF made by Zachary Lieberman.

    As mentionned in my last comment, one of the problems was that I had connected particles the wrong way: each particle was connected to every other particles instead of just being connected successively (like a ring). It's now fixed (see link below).

    https://media.giphy.com/media/1k2wspz8guv3PZ7dYq/giphy.gif

    The last and only problem (let's hope) I'm facing now is concerning the repulsion force. In the GIF I made above the repulsion force is unchanging and triggered at a specific threshold.

    Instead, It should be proportional to distance (the closer a particle is to another particle, the stronger is the repulsion) but i don't know how to implement such behavior with toxiclibs (hence the new question I posted yesterday).

  • Got it. Glad you figured it out for the non-toxiclibs setup!

Sign In or Register to comment.