Skip to content

Instantly share code, notes, and snippets.

@gonzam88
Last active June 6, 2021 07:07
Show Gist options
  • Save gonzam88/84f3b83a43aba66d4a9f968f435fc0b0 to your computer and use it in GitHub Desktop.
Save gonzam88/84f3b83a43aba66d4a9f968f435fc0b0 to your computer and use it in GitHub Desktop.
// 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
// 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 = 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();
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 (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;
}
// 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;
if (isSimulating) {
mold[i].tick();
mold[i].show();
}
}
}
function keyPressed() {
if (isSimulating) {
showTrailMap = true;
isSimulating = false;
} else {
showTrailMap = false;
isSimulating = true;
}
}
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;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment