# Help thinking out an attraction behavior

**URL:** <https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537>\
**Category:** Coding Questions\
**Created:** [June 5, 2024, 12:15pm UTC](https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537 "2024-06-05T12:15:16Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![vkbr](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/vkbr/32/16844_2.png) [@vkbr](https://discourse.processing.org/u/vkbr)\
**Post date:** [June 5, 2024, 12:15pm UTC](https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537/1 "2024-06-05T12:15:16Z")

</div>

Hello.  
I’m wondering how, or if, I could make this idea happens. I’m have some code already, it’s based on @shiffman’s wonderful [Nature of Code](https://natureofcode.com/) vectors and attractions chapters. Thanks Daniel, you are amazing! But not posting code, cause I’m after the logic yet.

Anyway I would like to have an ‘attractor’ that attracts some ‘movers’, but then, when they get close they stop organizing around the atractor. They should not overlap, instead (perhaps they repel each other) they kind of organize around the attractor. See the picture.

Is this insanely complicated? Don’t need code, just the thinking of how would this be acomplished. If this is to much for my league (a jr one : ) i can just set the spots for the movers to go to, and that will also work, but I’d like to have the representation I’m making based on forces… (I’m making - trying to make - a representation of brazilian congress - attractors would be parties and movers deputies)  
any thoughts?

thanks

 ![stb](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/f/3/f3ebf658e71d0ea464c69926c8589ed529d3865f.jpeg)

\*around - sorry

---

<div class="post-metadata">

**Author:** ![vkbr](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/vkbr/32/16844_2.png) [@vkbr](https://discourse.processing.org/u/vkbr)\
**Post date:** [June 5, 2024, 1:45pm UTC](https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537/2 "2024-06-05T13:45:32Z")

</div>

Ok. Here goes some code:  
This is getting the attracted to the same place of the attractor (in this pic it still moving veeeery slowly), but that last mile where they arrange them selfs…

 ![Screenshot 2024-06-05 at 10.45.07](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/2/8/288d0b7e2e5d3e451ab3cd64301eb67a1a8c1864.png)

[live in p5 editor](https://editor.p5js.org/v-k-/sketches/hN8AQl9txw)

The code.

```auto
let m,a;
let c;
let force;

function setup() {
    createCanvas(900, 500);
    c = color(200, 200, 170);
    m = new Mover(0, height / 2, 150, c);
    a = new Attractor(width / 2, height /2, 80);
    force = createVector(0, 0, 1);
}

function draw() {
    background(255, 155, 255);
    m.log(20, 450)
    const f = a.attract(m);
    m.applyForce(f);
    m.update();
    m.display();

    a.display();

}

class Mover {
    constructor(x, y, m, c) {
        this.pos = createVector(x, y, 1);
        this.mass = m;
        this.vel = createVector(0, 0);
        this.acc = createVector(0, 0);
        this.col = c || color(0, 150);
        this.lastAcc = createVector(0, 0);

    }

    applyForce(force) {
        // a copy so force vector is not changed and can be further used in other instances
        const f = force.copy();

        f.div(this.mass);
        this.acc.add(f);
    }

    update() {
        const fr = p5.Vector.mult(this.vel, -1);
        fr.setMag(0.02);
        this.acc.add(fr)
        this.vel.add(this.acc);
        this.vel.add(this.fr);
        this.pos.add(this.vel);
        this.lastAcc.set(this.acc);
        //clear acc each frame
        this.acc.mult(0);
    }

    display() {
        push();
        fill(110);
        noStroke()
        circle(this.pos.x, this.pos.y, this.mass);
        pop();
    }

    log(x, y) {
        const data = `pos= .x_${nf(this.pos.x, 0, 2)}, .y_${nf(this.pos.y, 0, 2)}\nvel= .x_${nf(this.vel.x, 0, 2)}, .y_${nf(this.vel.y, 0, 2)}\nacc= .x_${nf(this.lastAcc.x, 0, 2)}, .y_${nf(this.lastAcc.y, 0, 2)}\n`
        textSize(17);
        text(data, x, y)
        this.lastAcc.mult(0);
    }

} // <== eof mover class

class Attractor {
    constructor(x, y, m, c) {
        this.pos = createVector(x, y, 1);
        this.mass = m;
    }

    attract(mover){
      let force = p5.Vector.sub(this.pos, mover.pos);
      const dist = force.mag();
      const strength = dist/10;
      force.setMag(strength);
      return (force);
    }

    display() {
        push();
        fill(110,50,60);
        noStroke()
        circle(this.pos.x, this.pos.y, this.mass);
        pop();
    }

} //<== eof attractor

```

---

<div class="post-metadata">

**Author:** ![scudly](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/scudly/32/5597_2.png) [@scudly](https://discourse.processing.org/u/scudly)\
**Post date:** [June 5, 2024, 3:16pm UTC](https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537/3 "2024-06-05T15:16:31Z")

</div>

This video [https://www.youtube.com/watch?v=xiUpAeos168](https://www.youtube.com/watch?v=xiUpAeos168) (not mine) gives a quite nice algorithm for a more general artificial life simulation with a mix of attraction and repulsion behaviors. You could hard-code some of the parameters to get the behavior you are looking for.

---

<div class="post-metadata">

**Author:** ![vkbr](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/vkbr/32/16844_2.png) [@vkbr](https://discourse.processing.org/u/vkbr)\
**Post date:** [June 5, 2024, 3:24pm UTC](https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537/4 "2024-06-05T15:24:52Z")

</div>

> [@scudly](#):
>
> [https://www.youtube.com/watch?v=xiUpAeos168](https://www.youtube.com/watch?v=xiUpAeos168)

tanks, gonna look. ; )

Great link, watching now. Thanks again 😉

---

<div class="post-metadata">

**Author:** ![vkbr](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/vkbr/32/16844_2.png) [@vkbr](https://discourse.processing.org/u/vkbr)\
**Post date:** [June 5, 2024, 9:26pm UTC](https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537/5 "2024-06-05T21:26:29Z")

</div>

I’m wondering if you could help me with this particular (no pun : ) aspect of the code. His `update()` code directly accsses the global `ArrayList`, `swarm`, inside one of it’s methods. I thought that would not be a good practice, but indeed it makes things easier. Should I worry about this approach?

As I gonna have some unique particles, I’ll need to have an array (i’m in `js`) holding only one? Well not really sure how i will do things yet, but this makes a worse modularity? Right? (not a rethorical ‘right?’ : )

---

<div class="post-metadata">

**Author:** ![scudly](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/scudly/32/5597_2.png) [@scudly](https://discourse.processing.org/u/scudly)\
**Post date:** [June 5, 2024, 9:58pm UTC](https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537/6 "2024-06-05T21:58:38Z")

</div>

Given that the swarm interactions is the whole point of the app, having it as a global variable makes perfect sense and simplifies the rest of the code. If you are bothered by the reference to a global, just pass in the swarm as a parameter to the particle `update()` and then it’s no longer a global. It’s “cleaner” but slower so unless you really have some reason to care, I wouldn’t bother.

I made a similar sketch many years ago where I also used a global list of entities and a 2-D grid for quicker neighbor detection. [https://codepen.io/scudly/pen/NmoQWq](https://codepen.io/scudly/pen/NmoQWq) The rules in that youtube video are closer to the behaviors you described, though.

---

<div class="post-metadata">

**Author:** ![vkbr](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/vkbr/32/16844_2.png) [@vkbr](https://discourse.processing.org/u/vkbr)\
**Post date:** [June 5, 2024, 11:30pm UTC](https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537/7 "2024-06-05T23:30:40Z")

</div>

Ok, got it. Thanks again. Very nice sketch 🙂

---

<div class="post-metadata">

**Author:** ![vkbr](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/vkbr/32/16844_2.png) [@vkbr](https://discourse.processing.org/u/vkbr)\
**Post date:** [June 7, 2024, 1:31am UTC](https://discourse.processing.org/t/help-thinking-out-an-attraction-behavior/44537/8 "2024-06-07T01:31:45Z")

</div>

@scudly Thanks a lot. The particles of life nailed it totally.

I’m goint to integrate this in the rest of my code.  
I’ll leave here an example of what I cooked so far. It’s a fun stuff to play with, xhanging forces and limits cool stuff… I made a very controlled one.

[live in p5.editor](https://editor.p5js.org/v-k-/sketches/KGPrfKzDR)

it’s a little oppressesd in the half a page of the editor though…

also the code is not very clean yet.

```auto
let bodies = []
let cnv;
let force;
const K = 0.5;
const friction = 0.5;
let bodiesNumber = 200;
let pressedX, pressedY;
let c;

let forces = [
    [0, 0],
    [5, -9]
]

let minDist = [
    [-1, -1],
    [100, 10]
]
let maxDist = [
    [-1, 0],
    [2000, 100]
]

function setup() {
    cnv = createCanvas(900, 900);
    C = color(100, 40, 60);

    for (var i = 0; i < bodiesNumber; i++) {
        // bodies.push(new Body(random(180,220), random(180,220), 30, c));
        bodies.push(new Body(random(200, 700), random(100, 700), 30, c));
    }
    bodies[0].mass = 80;
    bodies[0].type = 0;
}

function draw() {
    background(185, 175, 185);
    // translate(width/2 , height/2 );
    // scale(scrollOff/200);
    for (const b of bodies) {
        b.display();
        b.update();
    }
}

function mouseDragged() {
    bodies[0].pos.x = mouseX;
    bodies[0].pos.y = mouseY;
}

class Body {

    constructor(x, y, m, c) {
        this.pos = createVector(x, y, 1);
        this.mass = m;
        this.vel = createVector(0, 0);
        this.strength = 0.2;
        this.minDist = this.mass * 1.5; // <=== experiences
        this.maxDist = 100;
        this.type = 1;
        this.color1 = color(110, 50, 60, 50)
        this.color2 = color(210, 150, 160, 50)
        this.color3 = color(11, 5, 6, 50)
        this.dispColor = this.color1;

    }

    update() {
        let dir = createVector(0, 0);
        let totalForce = createVector(0, 0);
        let acc = createVector(0, 0);
        let dist = 0;

        for (const body of bodies) {
            if (body !== this) {
                this.maxDist = maxDist[this.type][body.type];
                this.minDist = minDist[this.type][body.type]
                // console.log(body)
                //clear for this particle
                dir.mult(0);

                //copy to keep from messing origina value
                dir = body.pos.copy();

                // get dir to other
                dir.sub(this.pos);

                //store distance before normalizing
                dist = dir.mag();

                //normalize
                dir.normalize();

                //repel based on dist
                if (dist < minDist[this.type][body.type]) {
                    this.dispColor = this.color2
                    // don't mess with dir
                    const force = dir.copy();

                    // an arbitrary value - in the example we had a table with a unique
                    // value for each combination. Let's see what i'll need...
                    force.mult(forces[this.type][body.type] * -10); // negative => repel

                    //map dist to positive 0~1 and multiply
                    const mappedD = abs(map(dist, 0, this.minDist, 1, 0)); // <== note 1 e 0 not 0 e 1
                    force.mult(mappedD);

                    // a constant to scale down the forces 0.5 in the example
                    force.mult(K)

                    //accumulate all the forces of all other particles interacting with this one
                    totalForce.add(force);
                }
                if (dist < maxDist[this.type][body.type]) {
                    this.dispColor = this.color3;
                    // don't mess with dir
                    const force = dir.copy();

                    // an arbitrary value - in the example we had a table with a unique
                    // value for each combination. Let's see what i'll need...
                    force.mult(forces[this.type][body.type]);

                    //map dist to positive 0~1 and multiply
                    const mappedD = abs(map(dist, 0, maxDist[this.type][body.type], 1, 0)); // <== note 1 e 0 not 0 e 1
                    force.mult(mappedD);

                    // a constant to scale down the forces 0.5 in the example
                    force.mult(K)

                    //accumulate all the forces of all other particles interacting with this one
                    totalForce.add(force);
                }
            }
        }
        acc.add(totalForce); // if mass totalForce/this.mass
        this.vel.add(acc);
        this.pos.add(this.vel);
        this.vel.mult(friction);
    }

    display() {
        push();
        noStroke()
        fill(255, 30);
        circle(this.pos.x, this.pos.y, this.maxDist)
        stroke(255, 0, 0, 50);
        fill(255, 250, 250, 20);
        circle(this.pos.x, this.pos.y, this.minDist)
        fill(this.dispColor);
        fill(this.dispColor);
        circle(this.pos.x, this.pos.y, this.mass);

        // if (this.type === 1) {
        // image(img, this.pos.x, this.pos.y);
        // textSize(7)
        // text('Nome Filhadaputa', this.pos.x -25, this.pos.y +30);
        // } else {
        // fill(this.dispColor);
        // noStroke()
        // circle(this.pos.x, this.pos.y, this.mass);
        // }
        pop();
    }

} //<== eof attractor

```

 ![1](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/1/9/19beec731c8b677744e5d744babdaf837757b640.jpeg)  
 ![2](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/e/b/ebcc1c7ddb744cc75a28c042bc45d995831ae514.jpeg)
