click or drag to spawn; max 400 balloons in the breathing/writhing bunch
xxxxxxxxxx
var gasExchange;
var O2inhale;
var CO2exhale;
let coreSize = 50;
let maxBalloons = 400;
let bunch;
function setup() {
createCanvas(windowWidth, windowHeight);
background(255);
bunch = new Bunch();
for (let i = 0; i < 0; i++) {
let b = new Balloon(random(windowWidth), random(windowHeight));
bunch.addBalloon(b);
}
noStroke();
frameRate(18);
gasExchange = createSlider(0.01, 0.1, 0.01, 0.01);
gasExchange.position(windowWidth/2 - 100, 10);
gasExchange.style('width', '200px');
O2inhale = createSlider(0.01, 0.1, 0.03, 0.01);
O2inhale.position(windowWidth/2 - 100, 25);
O2inhale.style('width', '200px');
CO2exhale = createSlider(0.01, 0.1, 0.08, 0.01);
CO2exhale.position(windowWidth/2 - 100, 40);
CO2exhale.style('width', '200px');
}
function mouseDragged() {
if (mouseY < 75) {
return;
}
addBalloon()
}
function mouseClicked() {
if (mouseY < 75) {
return;
}
addBalloon();
}
function addBalloon() {
bunch.addBalloon(new Balloon(mouseX, mouseY));
if (bunch.balloons.length > maxBalloons) {
bunch.balloons.shift(); //remove the first element of the array
}
}
function draw() {
background(getBloodColor(bunch.mainMeasure()));
//background(255)
bunch.run();
}
function Bunch() {
this.balloons = []; // Initialize the array
}
function getBloodColor(mm, alpha=255) {
return color(220*mm, (1-mm)*150, 255*(1-mm), alpha);
}
Bunch.prototype.run = function() {
for (let i = 0; i < this.balloons.length; i++) {
this.balloons[i].run(this.balloons); // Passing the entire list of boids to each boid individually
}
}
Bunch.prototype.mainMeasure = function() {
let total = 0;
for (let i = 0; i < this.balloons.length; i++) {
total += this.balloons[i].mainMeasure();
}
if (this.balloons.length === 0) {
return 0;
}
return total / this.balloons.length;
}
Bunch.prototype.addBalloon = function(b) {
this.balloons.push(b);
}
function Balloon(x, y) {
this.position = createVector(x, y);
this.O2 = 0.5;
this.CO2 = 0;
this.V = 1.5 + random()*2;
this.breathCycle = floor(random(60));
this.nextGaspCountdown = 400;
}
Balloon.prototype.isInhale = function() {
return (this.breathCycle < 80);
}
Balloon.prototype.isExhale = function() {
return (this.breathCycle < 200 && this.breathCycle >= 120);
}
Balloon.prototype.heldInhaleRemaining = function() {
if (this.breathCycle >= 80 && this.breathCycle < 120) {
return 120 - this.breathCycle;
}
return 0;
}
Balloon.prototype.heldExhaleRemaining = function() {
if (this.breathCycle >= 200) {
return 240 - this.breathCycle;
}
return 0;
}
Balloon.prototype.update = function(balloons) {
let inhale = this.isInhale();
let exhale = this.isExhale();
this.nextGaspCountdown = min(500, this.nextGaspCountdown + 1);
// If any neighbor are close to inhale - ie within 10 steps, and they are farther than I am, ie I'm less than 10, I will hold my breath; same with exhale.
let keepHolding = false;
let neighbordist = 100; // neighborhood distance
for (let i = 0; i < balloons.length; i++) {
let d = p5.Vector.dist(this.position,balloons[i].position);
if ((d > 0) && (d < neighbordist)) {
if (balloons[i].heldInhaleRemaining() > this.heldInhaleRemaining() && this.heldInhaleRemaining() > 5 && (balloons[i].heldInhaleRemaining() - this.heldInhaleRemaining()) <= 3){
// easier to hold inhale than exhale
keepHolding = true;
}
if (balloons[i].heldExhaleRemaining() > this.heldExhaleRemaining() && this.heldExhaleRemaining() > 5 && (balloons[i].heldExhaleRemaining() - this.heldExhaleRemaining()) <= 1){
keepHolding = true;
}
}
}
// gasp when run out of O2
if (this.O2 == 0 && this.nextGaspCountdown >= 500) {
inhale = true;
exhale = false;
keepHolding = false;
this.breathCycle = 0;
this.nextGaspCountdown = 0;
}
let mm = this.mainMeasure();
if (mm < 0.1 && this.V > 2 && this.nextGaspCountdown >= 500) {
exhale = true;
inhale = false;
keepHolding = false;
this.breathCycle = 120;
this.nextGaspCountdown = 0;
}
if (!keepHolding) {
this.breathCycle = (this.breathCycle + 1) % 240;
}
if (inhale) {
this.O2 += O2inhale.value();
this.V += 0.05;
}
if (exhale) {
this.CO2 = max(0, this.CO2 - CO2exhale.value());
this.V -= 0.05;
}
// gas exchange happens if possible
this.O2 = max(0, this.O2 - gasExchange.value());
this.CO2 += gasExchange.value();
}
Balloon.prototype.mainMeasure = function() {
//console.log(this.V+" "+this.O2+" "+this.CO2);
return this.O2 / (this.O2 + this.CO2);
}
Balloon.prototype.render = function() {
stroke(color(255,255,0));
if (this.nextGaspCountdown < 500){
drawingContext.setLineDash([3,3]);
}
strokeWeight(1);
fill(getBloodColor(this.mainMeasure(), 50));
//TODO alpha can be sensitivity to others
ellipse(this.position.x, this.position.y, coreSize + 20*sqrt(this.V));
noStroke();
drawingContext.setLineDash([]);
fill(getBloodColor(this.mainMeasure(), 190));
ellipse(this.position.x, this.position.y, coreSize);
}
Balloon.prototype.run = function(balloons) {
this.update(balloons);
this.render();
}