/*
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
13 July 2016
p5.js 0.5.2
Note about the JavaScript port to p5.js:
This software is now significantly different because
the "glow" effects created by Tarbell worked well as
in a Java Applet in 2004, but this same process made
the code too slow in JavaScript in 2016. Early Processing
from 2004 was quick with pixel operations.
Restored by Casey Reas <http://reas.com>
22 June 2016
Processing v.3.1.1 <http://processing.org>
Implemented by J. Tarbell <http://levitated.net>
8 April 2004
Processing v.68 <http://processing.org>
*/
var num = 100;
var time = 0;
// object array
var discs = [];
// initialization
function setup() {
createCanvas(500, 500);
frameRate(30);
// make discs, arrange in anti-collapsing circle
for (var i = 0; i < num; i++) {
var fx = 0.4 * width * cos(TWO_PI * i / num);
var fy = 0.4 * width * sin(TWO_PI * i / num);
var x = random(width / 2) + fx;
var y = random(width / 2) + fy;
var r = 5 + random(45);
var bt = 1;
if (random(100) < 50) {
bt = -1;
}
discs[i] = new Disc(i, x, y, bt * fx / 1000.0, bt * fy / 1000.0, r);
discs[i].createPixelRiders();
}
}
// main
function draw() {
background(0);
loadPixels(); // Added CR -- 13 July 2016
// move discs
for (var c = 0; c < num; c++) {
discs[c].move();
discs[c].render();
discs[c].renderPxRiders();
}
time++;
}
// disc object
function Disc(Id, X, Y, Vx, Vy, R) {
this.id = Id;
this.x = X;
this.y = Y;
this.vx = Vx;
this.vy = Vy;
this.dr = R;
this.r = 0;
this.numr = 0;
this.maxr = 40;
this.pxRiders = [];
this.createPixelRiders = function() {
this.numr = int(R / 1.62);
if (this.numr > this.maxr) {
this.numr = this.maxr;
}
// create pixel riders
for (var n = 0; n < this.maxr; n++) {
this.pxRiders[n] = new PxRider();
}
}
this.draw = function() {
stroke(0, 50);
noFill();
ellipse(this.x, this.y, this.r, this.r);
}
this.render = function() {
// find intersecting points with all ascending discs
for (var n = this.id + 1; n < num; n++) {
if (n != this.id) {
// find distance to other disc
var dx = discs[n].x - this.x;
var dy = discs[n].y - this.y;
var d = sqrt(dx * dx + dy * dy);
// intersection test
if (d < (discs[n].r + this.r)) {
// complete containment test
if (d > abs(discs[n].r - this.r)) {
// find solutions
var a = (this.r * this.r - discs[n].r * discs[n].r + d * d) / (2 * d);
var p2x = this.x + a * (discs[n].x - this.x) / d;
var p2y = this.y + a * (discs[n].y - this.y) / d;
var h = sqrt(this.r * this.r - a * a);
var p3ax = p2x + h * (discs[n].y - this.y) / d;
var p3ay = p2y - h * (discs[n].x - this.x) / d;
var p3bx = p2x - h * (discs[n].y - this.y) / d;
var p3by = p2y + h * (discs[n].x - this.x) / d;
// P3a and P3B may be identical - ignore this case (for now)
stroke(255, 204); // Modified CR -- 13 July 2016
strokeWeight(4); // Modified CR -- 13 July 2016
point(p3ax, p3ay);
point(p3bx, p3by);
}
}
}
}
}
this.move = function() {
// add velocity to position
this.x += this.vx;
this.y += this.vy;
// bound check
if (this.x + this.r < 0) this.x += width + this.r + this.r;
if (this.x - this.r > width) this.x -= width + this.r + this.r;
if (this.y + this.r < 0) this.y += width + this.r + this.r;
if (this.y - this.r > width) this.y -= width + this.r + this.r;
// increase to destination radius
if (this.r < this.dr) {
this.r += 0.1;
}
}
this.renderPxRiders = function() {
for (var n = 0; n < this.numr; n++) {
this.pxRiders[n].move(this.x, this.y, this.r);
}
}
}
// pixel rider object
function PxRider() {
this.t = random(TWO_PI);
this.vt = 0.0;
this.mycharge = 0.0;
this.move = function(x, y, r) {
strokeWeight(1);
// add velocity to theta
this.t = (this.t + this.vt + PI) % TWO_PI - PI;
this.vt += random(-0.001, 0.001);
// apply friction brakes
if (abs(this.vt) > 0.02) this.vt *= 0.9;
// draw
var px = int(x + r * cos(this.t));
var py = int(y + r * sin(this.t));
//var c = get(int(px), int(py)); // Removed CR -- 13 July 2016
var c = pixels[py * height + px]; // Added CR -- 13 July 2016
//if (brightness(c) > 48) { // Removed CR -- 13 July 2016
if (int(c) > 48) { // Modified CR -- 13 July 2016
//glowpoint(px, py); // Removed CR -- 13 July 2016
this.mycharge = 164;
} else {
stroke(this.mycharge);
point(px, py);
this.mycharge *= 0.98;
}
}
}
// methods
function glowpoint(px, py) { // Too slow in browser 13 July 2016
for (var i = -2; i < 3; i++) {
for (var j = -2; j < 3; j++) {
var a = 0.8 - i * i * 0.1 - j * j * 0.1;
tpoint(int(px + i), int(py + j), '#FFFFFF', a);
}
}
}
function tpoint(x1, y1, mycolor, a) {
// place translucent point
var r, g, b;
var c;
var myc = color(mycolor);
c = get(x1, y1);
r = int(red(c) + (red(myc) - red(c)) * a);
g = int(green(c) + (green(myc) - green(c)) * a);
b = int(blue(c) + (blue(myc) - blue(c)) * a);
var nc = color(r, g, b);
stroke(nc);
stroke(255, 204, 0);
point(x1, y1);
}