{Software} Structures

/* 
   
   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 J. Tarbell <http://levitated.net>
   8 April 2004 
   Processing v.68 <http://processing.org> 
   
*/

// widthensions 
var num = 100;
var time = 0;

// display flag 
var showStructure = false;

// object array 
var discs = [];


function setup() {
  createCanvas(500, 500);
  background(0);
  frameRate(30);

  // arrange linearly 
  for (var i = 0; i < num; i++) {
    var x = random(width);
    var y = random(width);
    var fy = 0;
    var fx = random(-1.0, 1.0);
    var r = 20 + random(20);
    discs[i] = new Disc(i, x, y, fx, fy, r);
    discs[i].createPainters();
  }
}

function draw() {
  if (showStructure) {
    background(0);
    // render circles and intersections 
    for (var c = 0; c < num; c++) {
      discs[c].draw();
    }
  }
  // move discs 
  for (var c = 0; c < num; c++) {
    discs[c].move();
    discs[c].render();
  }
  time++;
}

function keyReleased() {
  if (key == ' ') {
    background(0);
    if (showStructure) {
      showStructure = false;
    } else {
      showStructure = true;
    }
  }
}

// translucent point 
function tpoint(x1, y1, gc, a) {
  noStroke();
  fill(gc, a * 255);
  rect(x1, y1, 1, 1);
}

// 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 = 1.0;

  // sand painters 
  this.numsands = 1;
  this.sands = [];
  //SandPainter[] sands = new SandPainter[numsands];

  this.createPainters = function() {
    for (var n = 0; n < this.numsands; n++) {
      this.sands[n] = new SandPainter();
    }
  }

  this.draw = function() {
    stroke(64);
    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++) {
      // 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 circle intersection 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) 

          if (showStructure) {
            stroke(255);
            point(p3ax, p3ay);
            point(p3bx, p3by);
          } else {
            tpoint(int(p3ax - 1), int(p3ay), 255, 0.21);
            tpoint(int(p3ax + 1), int(p3ay), 0, 0.21);
            tpoint(int(p3bx - 1), int(p3by), 255, 0.21);
            tpoint(int(p3ax + 1), int(p3ay), 0, 0.21);
          }
          // draw sand painters 
          for (var s = 0; s < this.numsands; s++) {
            this.sands[s].render(p3ax, p3ay, p3bx, p3by);
          }
        }
      }
    }
  }

  this.move = function() {
    // move radius towards destination radius 
    if (this.r < this.dr) this.r += 0.02;
    // 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;
      this.y = random(width);
    }
    if (this.x - this.r > width) {
      this.x -= width + this.r + this.r;
      this.y = random(width);
    }
    if (this.y + this.r < 0) {
      this.y += width + this.r + this.r;
      this.x = random(width);
    }
    if (this.y - this.r > width) {
      this.y -= width + this.r + this.r;
      this.x = random(width);
    }
  }

}

// sandpainter object 
function SandPainter() {

  this.p = random(1.0);
  this.c = int(random(256));
  this.g = random(0.01, 0.1);

  this.render = function(x, y, ox, oy) {
    // draw painting sweeps 
    tpoint(int(ox + (x - ox) * sin(this.p)), int(oy + (y - oy) * sin(this.p)), this.c, 0.11);

    this.g += random(-0.050, 0.050);
    var maxg = 0.5;
    if (this.g < -maxg) g = -maxg;
    if (this.g > maxg) g = maxg;
    this.p += random(-0.050, 0.050);
    if (this.p < 0) this.p = 0;
    if (this.p > 1.0) this.p = 1.0;

    var w = this.g / 10.0;
    for (var i = 0; i < 11; i++) {
      tpoint(int(ox + (x - ox) * sin(this.p + sin(i * w))), int(oy + (y - oy) * sin(this.p + sin(i * w))), this.c, 0.1 - i / 110);
      tpoint(int(ox + (x - ox) * sin(this.p - sin(i * w))), int(oy + (y - oy) * sin(this.p - sin(i * w))), this.c, 0.1 - i / 110);
    }
  }
}