/*
A surface filled with one hundred medium to small sized circles.
Each circle has a different size and direction, but moves at the same slow rate.
Display:
A. The instantaneous intersections of the circles
>>> B. The aggregate intersections of the circles
Ported to p5.js by Casey Reas
11 July 2016
p5.js 0.5.2
Restored by Casey Reas <http://reas.com>
22 June 2016
Processing v.3.1.1 <http://processing.org>
Implemented by William Ngan <http://metaphorical.net>
4 April 2004
Processing v.68 <http://processing.org>
*/
var field = [];
var fieldShade = [];
var gaph, gapv;
var marginh, marginv; // margin
var cnt = 0;
var circles = new Array(100);
var counter = 0;
var cCounter = 0;
var cTimer = 0;
function setup() {
createCanvas(500, 500);
frameRate(30);
gaph = 3;
gapv = 3;
marginh = 20;
marginv = 20;
// field
field = new Array(int((width - marginh * 2) / gaph));
for (var i = 0; i < field.length; i++) {
field[i] = new Array(int((height - marginv * 2) / gapv));
}
fieldShade = new Array(field.length);
for (var i = 0; i < fieldShade.length; i++) {
fieldShade[i] = new Array(field[0].length);
}
for (var i = 0; i < field.length; i++) {
for (var k = 0; k < field[0].length; k++) {
field[i][k] = TWO_PI - PI / 3;
fieldShade[i][k] = 1;
}
}
}
function draw() {
background(50);
var ax, ay;
cTimer++;
if (cTimer > 5 && cCounter < circles.length - 1) {
circles[cCounter] = new Circle(250, 250, 40, cCounter);
cCounter++;
cTimer = 0;
}
var len = 10;
stroke(255, 255, 255, 50);
// Draw every other one for better frame rate
for (var i = 0; i < field.length; i += 2) {
for (var k = 0; k < field[0].length; k += 2) {
ax = marginh + i * gaph;
ay = marginv + k * gapv;
line(ax, ay, ax + len * cos(field[i][k]), ay + len * sin(field[i][k]));
}
}
noStroke();
noFill();
for (var i = 0; i < cCounter; i++) {
circles[i].draw();
circles[i].getGrid();
}
}
function getLocation(i, k) {
return [marginh + i * gaph, marginv + k * gapv];
}
function Circle(px, py, pr, id) {
this.x = px;
this.y = py;
this.r = pr;
this.rr = this.r * this.r;
this.d = this.r * 2;
this.d2 = this.d * this.d;
this.id = id;
this.sp1 = random(2);
this.sp2 = random(2);
this.sp3 = random(2);
this.ac1 = random(0.5) - random(0.5);
this.ac2 = random(0.5) - random(0.5);
this.ac3 = random(0.5) - random(0.5);
this.inx = 0;
this.iny = 0;
this.over = true;
this.draw = function() {
this.move();
}
this.move = function() {
var angle = sin(this.sp1) - cos(this.sp2);
this.sp1 += this.ac1;
this.sp2 += this.ac2;
this.sp3 += this.ac3;
angle = (angle < 0) ? angle + TWO_PI : ((angle >= TWO_PI) ? angle - TWO_PI : angle);
this.x += sin(angle);
this.y -= cos(angle);
this.checkBounds();
this.checkOverlap();
}
this.checkBounds = function() {
if (this.x > width) this.x = 0;
if (this.x < 0) this.x = width;
if (this.y > height) this.y = 0;
if (this.y < 0) this.y = height;
}
this.repel = function(angle) {
this.x = this.x + cos(angle) / 10.0;
this.y = this.y + sin(angle) / 10.0;
}
this.setState = function(px, py) {
this.inx = px;
this.iny = py;
this.over = true;
}
this.checkOverlap = function() {
for (var i = this.id + 1; i < cCounter; i++) {
var dx = circles[i].x - this.x;
var dy = circles[i].y - this.y;
var drr = dx * dx + dy * dy;
var ds = sqrt(drr);
if (ds > this.r + circles[i].r || ds < abs(this.r - circles[i].r)) {
continue; // no solution
}
var ang = atan2(dy, dx);
this.repel(ang + PI);
circles[i].repel(ang);
var a = (this.rr - circles[i].rr + drr) / (2 * ds);
var x2 = this.x + (a * (circles[i].x - this.x) / ds);
var y2 = this.y + (a * (circles[i].y - this.y) / ds);
this.setState(x2, y2);
circles[i].setState(x2, y2);
}
}
this.getGrid = function() {
var sx = Math.ceil((this.x - this.r - marginh) / gaph);
var sy = Math.ceil((this.y - this.r - marginv) / gapv);
var numx = Math.floor(this.d / gaph);
var numy = Math.floor(this.d / gapv);
var pos = [];
for (var i = sx; i < sx + numx; i++) {
if (i >= 0 && i < field.length) {
for (var k = sy; k < sy + numy; k++) {
if (k >= 0 && k < field[0].length) {
if (this.over) {
pos = getLocation(i, k);
var dx = pos[0] - this.x;
var dy = pos[1] - this.y;
if (dist(this.x, this.y, pos[0], pos[1]) < this.r) {
var da = atan2(pos[1] - this.iny, pos[0] - this.inx);
if (field[i][k] < da) {
field[i][k] += PI / 20;
} else if (field[i][k] > da) {
field[i][k] -= PI / 20;
}
fieldShade[i][k] = 2;
}
}
}
}
}
}
this.over = false;
}
}