Skip to content

Instantly share code, notes, and snippets.

@gonzamoiguer
Last active June 6, 2021 07:07
Show Gist options
  • Select an option

  • Save gonzamoiguer/84f3b83a43aba66d4a9f968f435fc0b0 to your computer and use it in GitHub Desktop.

Select an option

Save gonzamoiguer/84f3b83a43aba66d4a9f968f435fc0b0 to your computer and use it in GitHub Desktop.

Revisions

  1. gonzamoiguer revised this gist Jun 1, 2019. 1 changed file with 110 additions and 90 deletions.
    200 changes: 110 additions & 90 deletions transport network simulation.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,4 @@
    // by gonzalo moiguer www.gonzamoiguer.com.ar
    // p5.js sketch
    // simulates patterns arising from a transport network
    // Based on this 2010 paper by Jeff Jones "Characteristics of pattern formation and evolution in approximations of Physarum transport networks." http://eprints.uwe.ac.uk/15260/1/artl.2010.16.2.pdf
    @@ -12,158 +13,177 @@ var mold = [];
    var particleCount = 5000;

    //
    var particleDepositStrength = 0.4;
    var decaySpeed = 1.04;
    var diffusionStrength = 0.025;

    var showTrailMap = false;
    var particleDepositStrength = 1;
    var decaySpeed = 1.01;
    var diffusionStrength = 0.1;

    var sensorDistance = 1;
    var sensorsAngle = 22;
    var particleRotationStrength = 27;

    //

    var showTrailMap = false;
    var isSimulating = true;

    var diffusion = [
    1/16, 1/8, 1/16,
    1/8, 1/4, 1/8,
    1/16, 1/8, 1/16
    ];


    function setup() {
    createCanvas(mapWidth, mapHeight);
    for(let i=0; i<particleCount; i++){
    let temp = new Particle();

    for (let i = 0; i < particleCount; i++) {
    let temp = new Particle();
    mold.push(temp);
    }
    for(let i = 0; i < mapWidth; i++){


    for (let i = 0; i < mapWidth; i++) {
    trailMap[i] = []; // init second dimension
    for(let j = 0; j < mapWidth; j++){
    trailMap[i][j] = random(0,0.2);
    }
    for (let j = 0; j < mapWidth; j++) {
    trailMap[i][j] = random(0, 0.2);
    }
    }

    }

    function draw() {
    background(0);
    for(let i = 0; i < mapWidth; i++){
    for(let j = 0; j < mapWidth; j++){

    for (let i = 0; i < mapWidth; i++) {
    for (let j = 0; j < mapWidth; j++) {
    // 5- diffuse

    if(i>0 && i<mapWidth-1 && j>0 && j<mapHeight-1){
    trailMap[i-1][j-1] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i][j-1] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i+1][j-1] += trailMap[i][j] * 0.3 * diffusionStrength;

    trailMap[i-1][j] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i][j] -= trailMap[i][j] * 0.9 * diffusionStrength;
    trailMap[i+1][j] += trailMap[i][j] * 0.3 * diffusionStrength;

    trailMap[i-1][j+1] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i][j+1] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i+1][j+1] += trailMap[i][j] * 0.3 * diffusionStrength;
    if (isSimulating) {
    if (i > 1 && i < mapWidth - 2 && j > 1 && j < mapHeight - 2) {
    let suma = 0;
    suma += diffusion[0] * trailMap[i - 1][j - 1];
    suma += diffusion[1] * trailMap[i] [j - 1];
    suma += diffusion[2] * trailMap[i + 1][j - 1];
    suma += diffusion[3] * trailMap[i - 1][j];
    suma += diffusion[4] * trailMap[i] [j];
    suma += diffusion[5] * trailMap[i + 1][j];
    suma += diffusion[6] * trailMap[i - 1][j + 1];
    suma += diffusion[7] * trailMap[i] [j + 1];
    suma += diffusion[8] * trailMap[i + 1][j + 1];

    trailMap[i][j] = suma;
    }


    // 6- decay
    trailMap[i][j] /= decaySpeed;
    }

    // 6- decay
    trailMap[i][j] /= decaySpeed;

    // show
    if(showTrailMap){
    if (showTrailMap) {
    let color = trailMap[i][j] * 255;
    stroke(color, 0, 0);
    point(i,j);
    point(i, j);
    }
    }
    }
    }

    noStroke();
    fill(255,255,255,25);
    for(let i = 0; i < mold.length; i++){
    if(!mold[i].alive) continue;

    mold[i].tick();
    mold[i].show();
    fill(255, 255, 255, 25);
    for (let i = 0; i < mold.length; i++) {
    if (!mold[i].alive) continue;

    if (isSimulating) {
    mold[i].tick();
    mold[i].show();
    }

    }
    }

    function keyPressed() {
    if (isSimulating) {
    showTrailMap = true;
    isSimulating = false;
    } else {
    showTrailMap = false;
    isSimulating = true;
    }




    }


    function Particle(){
    function Particle() {
    this.alive = true;
    this.pos = createVector(random(50,mapWidth-50), random(50,mapHeight-50));

    this.pos = createVector(random(50, mapWidth - 50), random(50, mapHeight - 50));
    //this.pos = createVector(width/2,height/2);
    //this.acceleration = createVector(0,0)
    this.speed = random(0.5,1.5);
    this.speed = random(0.5, 1.5);
    this.maxspeed = 4;
    //this.maxforce = 0.1;
    this.heading = random();
    this.show = function(){
    //point(this.pos.x, this.pos.y);
    ellipse(this.pos.x,this.pos.y, 3);


    this.show = function() {
    //point(this.pos.x, this.pos.y);
    ellipse(this.pos.x, this.pos.y, 3);
    }
    this.tick = function(){
    if(!this.alive) return;

    this.tick = function() {
    if (!this.alive) return;
    // 1- sense
    this.maxSense = 0;
    this.maxSenseDir = 0;
    //left
    let radAngle = radians(this.heading-sensorsAngle);
    let radAngle = radians(this.heading - sensorsAngle);
    let sensorX = round(this.pos.x + (sensorDistance * cos(radAngle)));
    let sensorY = round(this.pos.y + (sensorDistance * sin(radAngle)));
    let sensorRead = trailMap[constrain(sensorY,0,mapWidth-1)][constrain(sensorX,0,mapHeight-1)];
    if(sensorRead > this.maxSense){
    let sensorRead = trailMap[constrain(sensorY, 0, mapWidth - 1)][constrain(sensorX, 0, mapHeight - 1)];
    if (sensorRead > this.maxSense) {
    this.maxSense = sensorRead;
    this.maxSenseDir = -1;
    }
    // center
    radAngle = radians(this.heading);
    sensorX = round(this.pos.x + (sensorDistance * cos(radAngle)));
    sensorY = round(this.pos.y + (sensorDistance * sin(radAngle)));
    sensorRead = trailMap[constrain(sensorY,0,mapWidth-1)][constrain(sensorX,0,mapHeight-1)];
    if(sensorRead >= this.maxSense){
    sensorRead = trailMap[constrain(sensorY, 0, mapWidth - 1)][constrain(sensorX, 0, mapHeight - 1)];
    if (sensorRead >= this.maxSense) {
    this.maxSense = sensorRead;
    this.maxSenseDir = 0;
    }
    // right
    radAngle = radians(this.heading+sensorsAngle);
    radAngle = radians(this.heading + sensorsAngle);
    sensorX = round(this.pos.x + (sensorDistance * cos(radAngle)));
    sensorY = round(this.pos.y + (sensorDistance * sin(radAngle)));
    sensorRead = trailMap[constrain(sensorY,0,mapWidth-1)][constrain(sensorX,0,mapHeight-1)];
    if(sensorRead > this.maxSense){
    sensorRead = trailMap[constrain(sensorY, 0, mapWidth - 1)][constrain(sensorX, 0, mapHeight - 1)];
    if (sensorRead > this.maxSense) {
    this.maxSense = sensorRead;
    this.maxSenseDir = 1;
    }

    // 2- rotate
    this.heading += particleRotationStrength * this.maxSenseDir;

    // 3- move
    radAngle = radians(this.heading);
    this.pos.x += this.speed * cos(radAngle);
    this.pos.x += this.speed * cos(radAngle);
    this.pos.y += this.speed * sin(radAngle);

    // collision dead
    if(this.pos.x <= 0 || this.pos.x >= mapWidth || this.pos.y <= 0 || this.pos.y >= mapHeight ){
    if (this.pos.x <= 0 || this.pos.x >= mapWidth || this.pos.y <= 0 || this.pos.y >= mapHeight) {
    this.alive = false;
    }
    }

    // 4- deposit material
    if(this.alive){
    trailMap[constrain(round(this.pos.x),0,mapWidth-1)]
    [constrain(round(this.pos.y),0,mapHeight-1)] += particleDepositStrength;
    }else{
    trailMap[constrain(round(this.pos.x),0,mapWidth-1)]
    [constrain(round(this.pos.y),0,mapHeight-1)] = 0; // les aviso que todo mal
    if (this.alive) {
    trailMap[constrain(round(this.pos.x), 0, mapWidth - 1)]
    [constrain(round(this.pos.y), 0, mapHeight - 1)] += particleDepositStrength;
    } else {
    trailMap[constrain(round(this.pos.x), 0, mapWidth - 1)]
    [constrain(round(this.pos.y), 0, mapHeight - 1)] = 0; // les aviso que todo mal
    }

    }

    /*
    this.seek = function(target) {
    this.desired = p5.Vector.sub(target, this.pos);
    @@ -178,18 +198,18 @@ function Particle(){
    this.acceleration.add(force);
    }
    */

    }


    function mousePressed() {
    for(let i = 0; i < mapWidth; i++){
    for(let j = 0; j < mapWidth; j++){
    if(dist(i,j,mouseX,mouseY)< 10){
    for (let i = 0; i < mapWidth; i++) {
    for (let j = 0; j < mapWidth; j++) {
    if (dist(i, j, mouseX, mouseY) < 10) {
    trailMap[i][j] = 99900;
    }

    }
    }

    }
  2. gonzamoiguer created this gist Jun 1, 2019.
    195 changes: 195 additions & 0 deletions transport network simulation.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,195 @@
    // p5.js sketch
    // simulates patterns arising from a transport network
    // Based on this 2010 paper by Jeff Jones "Characteristics of pattern formation and evolution in approximations of Physarum transport networks." http://eprints.uwe.ac.uk/15260/1/artl.2010.16.2.pdf
    // Also helped by Sage Jenson breakdown and awesome GPU-powered work: https://sagejenson.com/physarum

    var mapWidth = 500;
    var mapHeight = 500;

    var trailMap = [];

    var mold = [];
    var particleCount = 5000;

    //
    var particleDepositStrength = 0.4;
    var decaySpeed = 1.04;
    var diffusionStrength = 0.025;

    var showTrailMap = false;

    var sensorDistance = 1;
    var sensorsAngle = 22;
    var particleRotationStrength = 27;

    //

    function setup() {
    createCanvas(mapWidth, mapHeight);

    for(let i=0; i<particleCount; i++){
    let temp = new Particle();
    mold.push(temp);
    }


    for(let i = 0; i < mapWidth; i++){
    trailMap[i] = []; // init second dimension
    for(let j = 0; j < mapWidth; j++){
    trailMap[i][j] = random(0,0.2);
    }
    }

    }

    function draw() {
    background(0);

    for(let i = 0; i < mapWidth; i++){
    for(let j = 0; j < mapWidth; j++){
    // 5- diffuse

    if(i>0 && i<mapWidth-1 && j>0 && j<mapHeight-1){
    trailMap[i-1][j-1] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i][j-1] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i+1][j-1] += trailMap[i][j] * 0.3 * diffusionStrength;

    trailMap[i-1][j] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i][j] -= trailMap[i][j] * 0.9 * diffusionStrength;
    trailMap[i+1][j] += trailMap[i][j] * 0.3 * diffusionStrength;

    trailMap[i-1][j+1] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i][j+1] += trailMap[i][j] * 0.3 * diffusionStrength;
    trailMap[i+1][j+1] += trailMap[i][j] * 0.3 * diffusionStrength;
    }

    // 6- decay
    trailMap[i][j] /= decaySpeed;

    // show
    if(showTrailMap){
    let color = trailMap[i][j] * 255;
    stroke(color, 0, 0);
    point(i,j);
    }
    }
    }

    noStroke();
    fill(255,255,255,25);
    for(let i = 0; i < mold.length; i++){
    if(!mold[i].alive) continue;

    mold[i].tick();
    mold[i].show();
    }




    }


    function Particle(){
    this.alive = true;

    this.pos = createVector(random(50,mapWidth-50), random(50,mapHeight-50));
    //this.pos = createVector(width/2,height/2);
    //this.acceleration = createVector(0,0)
    this.speed = random(0.5,1.5);
    this.maxspeed = 4;
    //this.maxforce = 0.1;
    this.heading = random();


    this.show = function(){
    //point(this.pos.x, this.pos.y);
    ellipse(this.pos.x,this.pos.y, 3);
    }

    this.tick = function(){
    if(!this.alive) return;
    // 1- sense
    this.maxSense = 0;
    this.maxSenseDir = 0;
    //left
    let radAngle = radians(this.heading-sensorsAngle);
    let sensorX = round(this.pos.x + (sensorDistance * cos(radAngle)));
    let sensorY = round(this.pos.y + (sensorDistance * sin(radAngle)));
    let sensorRead = trailMap[constrain(sensorY,0,mapWidth-1)][constrain(sensorX,0,mapHeight-1)];
    if(sensorRead > this.maxSense){
    this.maxSense = sensorRead;
    this.maxSenseDir = -1;
    }
    // center
    radAngle = radians(this.heading);
    sensorX = round(this.pos.x + (sensorDistance * cos(radAngle)));
    sensorY = round(this.pos.y + (sensorDistance * sin(radAngle)));
    sensorRead = trailMap[constrain(sensorY,0,mapWidth-1)][constrain(sensorX,0,mapHeight-1)];
    if(sensorRead >= this.maxSense){
    this.maxSense = sensorRead;
    this.maxSenseDir = 0;
    }
    // right
    radAngle = radians(this.heading+sensorsAngle);
    sensorX = round(this.pos.x + (sensorDistance * cos(radAngle)));
    sensorY = round(this.pos.y + (sensorDistance * sin(radAngle)));
    sensorRead = trailMap[constrain(sensorY,0,mapWidth-1)][constrain(sensorX,0,mapHeight-1)];
    if(sensorRead > this.maxSense){
    this.maxSense = sensorRead;
    this.maxSenseDir = 1;
    }

    // 2- rotate
    this.heading += particleRotationStrength * this.maxSenseDir;

    // 3- move
    radAngle = radians(this.heading);
    this.pos.x += this.speed * cos(radAngle);
    this.pos.y += this.speed * sin(radAngle);

    // collision dead
    if(this.pos.x <= 0 || this.pos.x >= mapWidth || this.pos.y <= 0 || this.pos.y >= mapHeight ){
    this.alive = false;
    }

    // 4- deposit material
    if(this.alive){
    trailMap[constrain(round(this.pos.x),0,mapWidth-1)]
    [constrain(round(this.pos.y),0,mapHeight-1)] += particleDepositStrength;
    }else{
    trailMap[constrain(round(this.pos.x),0,mapWidth-1)]
    [constrain(round(this.pos.y),0,mapHeight-1)] = 0; // les aviso que todo mal
    }

    }

    /*
    this.seek = function(target) {
    this.desired = p5.Vector.sub(target, this.pos);
    this.desired.normalize();
    this.desired.mult(this.maxspeed);
    this.steer = p5.Vector.sub(this.desired,this.velocity);
    this.steer.limit(this.maxforce);
    applyForce(steer);
    }
    this.applyForce = function(force) {
    this.acceleration.add(force);
    }
    */

    }


    function mousePressed() {
    for(let i = 0; i < mapWidth; i++){
    for(let j = 0; j < mapWidth; j++){
    if(dist(i,j,mouseX,mouseY)< 10){
    trailMap[i][j] = 99900;
    }

    }
    }

    }