Click to make bubble. Hover to see connections. Up Arrow/Down Arrow to toggle network.
A fork of Bubble Trouble by Striplenaut
xxxxxxxxxx
let bubbles = [];
let maxSpeedSlider;
let maxForceSlider;
let mousePos;
let keyIsPressed = false;
function setup() {
createCanvas(windowWidth, windowHeight);
colorMode(HSB);
}
function draw() {
mousePos = createVector(mouseX, mouseY);
background(240, 100, 15);
// do bubble stuff for every bubble created:
for (let i = 0; i < bubbles.length; i++) {
for (let j = 0; j < bubbles.length; j++) {
if (bubbles[i] !== bubbles[j]) {
bubbles[i].attract(bubbles[j]);
bubbles[i].hover(bubbles[j]);
bubbles[i].update();
if(keyIsPressed) {
bubbles[i].drawLine(bubbles[j]);
}
}
}
bubbles[i].show();
}
}
// DEFINE THE BUBBLE CLASS /////////////////////////////////////////////////////////////////////////////////////////////////
class Bubble {
constructor(pos, r, color) {
this.pos = pos; //p5.Vector
this.r = r;
this.color = color;
this.vel = createVector(0,0); //p5.Vector
this.accel = createVector(0,0); //p5.Vector
this.maxSpeed = 5;
this.maxForce = 0.03; // max allowed "adjustment" force (acceleration) to attract/repel
}
// method to display bubble
show() {
fill(this.color);
ellipse(this.pos.x, this.pos.y, this.r);
}
// method to update position based on accumulation of velocity and acceleration each frame
update() {
this.vel.add(this.accel);
this.pos.add(this.vel);
this.vel.mult(0.97);
// reset acceleration to zero
this.accel.mult(0);
}
// method to attract bubbles to within a radius, then repel them
attract(other) {
let target = other.pos; // where they want to be
let current = this.pos; // where they are
let direction = p5.Vector.sub(target, current); // direction they want to move
let d = direction.mag(); // distance apart
direction.setMag(this.maxSpeed); // use max speed as magnitude, instead of distance (distance is too large a value)
let magnitude;
// if bubbles are within 300px of eachother, repel
if (d < 250) {
direction.mult(-1);
magnitude = this.maxForce * map(d, 0,250, 7,1); // maps distance b/w 0 and 300 px to a force multiplier b/w 7 and 1 (proportional to distance)
} else {
magnitude = this.maxForce;
}
// "steer" force is the "correction" force that directs the bubble from where it is to where it wants to be
let steer = p5.Vector.sub(direction, this.vel); // subtract its current velocity from it's desired velocity
steer.setMag(magnitude); // set the magnitude of the force
this.accel.add(steer); // add all resulting "steer" forces to the acceleration for a combined overall direction and magnitude
//// display distance on top of line, for debugging
// line(this.pos.x, this.pos.y, other.pos.x, other.pos.y);
// push();
// translate((this.pos.x + other.pos.x) / 2, (this.pos.y + other.pos.y) / 2);
// rotate(atan2(other.pos.y - this.pos.y, other.pos.x - this.pos.x));
// text(nfc(d, 1), 0, -5);
}
// method to draw a color lerp'd line between bubbles
drawLine(other) {
//y = mx + b
let slope = (other.pos.y - this.pos.y) / (other.pos.x - this.pos.x);
let b = this.pos.y - slope*this.pos.x;
// calc difference in x pos and y pos separately
let xDif = other.pos.x - this.pos.x;
let yDif = other.pos.y - this.pos.y;
let lineIncrement;
let startX = this.pos.x;
let startY = this.pos.y;
let endX, endY;
// draw the line based on the bigger of the two coordinate position differences xDif and yDif
if (abs(xDif) <= abs(yDif)) {
// split distance into 20 equal sized distance chunks
lineIncrement = yDif / 20;
// for each chunk, draw a line with a different color
for (let i = 0; i < 20; i ++) {
endY = startY + lineIncrement;
endX = (endY - b) / slope; // calc end X pos based on equation of line
// draw line
push();
let colorFrom = this.color;
let colorTo = other.color;
stroke(lerpColor(colorFrom, colorTo, map(i, 0,20, 0,1)));
line(startX, startY, endX, endY);
pop();
// set the next start point to the current end point
startY = endY;
startX = endX;
}
}
else {
lineIncrement = xDif / 20;
for (let i = 0; i < 20; i ++) {
endX = startX + lineIncrement;
endY = (slope * endX) + b;
push();
let colorFrom = this.color;
let colorTo = other.color;
stroke(lerpColor(colorFrom, colorTo, map(i, 0,20, 0,1)));
line(startX, startY, endX, endY);
pop();
startY = endY;
startX = endX;
}
}
}
// method to trigger the drawLine method if mouse is hovered
hover(other) {
if (dist(mouseX, mouseY, this.pos.x, this.pos.y) <= this.r) {
this.drawLine(other);
}
}
}
//// HELPER FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function makeBubble() {
let bubble = new Bubble(mousePos, random(10, 40), color(random(255), random(50,80), 100));
bubbles.push(bubble);
}
function mouseClicked() {
makeBubble();
}
function keyPressed() {
if (keyCode === UP_ARROW) {
keyIsPressed = true;
}
if (keyCode === DOWN_ARROW) {
keyIsPressed = false;
}
}
// function mouseDragged() {
// makeBubble();
// }