/ p5.js / P_2_2_5_02

// P_2_2_5_02
//
// Generative Gestaltung, ISBN: 978-3-87439-759-9
// First Edition, Hermann Schmidt, Mainz, 2009
// Hartmut Bohnacker, Benedikt Gross, Julia Laub, Claudius Lazzeroni
// Copyright 2009 Hartmut Bohnacker, Benedikt Gross, Julia Laub, Claudius Lazzeroni
//
// http://www.generative-gestaltung.de
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
 * pack as many cirlces as possible together
 * 
 * MOUSE
 * press + position x/y : move area of interest
 * 
 * KEYS
 * 1                    : show/hide svg modules
 * 2                    : show/hide lines
 * 3                    : show/hide circles
 * arrow up/down        : resize area of interest
 * f                    : freeze process. on/off
 * s                    : save png
 * p                    : save pdf
 */
var freeze = false;
var maxCount = 5000; //max count of the cirlces
var currentCount = 1;
var x = [maxCount];
var y = [maxCount];
var r = [maxCount]; //radius
var closestIndex = [maxCount]; //index
var minRadius = 3;
var maxRadius = 50;
// mouse and arrow up/down interaction
var mouseRect = 30;
// style selector, hotkeys 1,2,3
var showSvg = true;
var showLine = false;
var showCircle = false;

function preload(){
    module1 = loadImage("08.png");
    module2 = loadImage("07.png");
}

function setup() {
  createCanvas(780,780);
  noFill();
  smooth();
  cursor(CROSS);

  // first circle
  x[0] = 200;
  y[0] = 100;
  r[0] = 50;
  closestIndex[0] = 0;
}

function draw() {
   imageMode(CENTER);
  ellipseMode(CENTER);
  background(255);

  for (var j = 0; j < 40; j++) {
    // create a random position
    var tx = random(0+maxRadius,width-maxRadius);
    var ty = random(0+maxRadius,height-maxRadius);
    var tr = minRadius;
    // create a random position according to mouse position
    if (mouseIsPressed == true) {
      tx = random(mouseX-mouseRect/2,mouseX+mouseRect/2);
      ty = random(mouseY-mouseRect/2,mouseY+mouseRect/2);
      tr = 1;
    } 
    var insection = true;
    // find a pos with no intersection with others circles
    for(var i=0; i < currentCount; i++) {
      var d = dist(tx,ty, x[i],y[i]);
      //println(d);
      if (d >= (tr + r[i])) insection = false;     
      else {
        insection = true; 
        break;
      }
    }
    // stop process by pressing hotkey 'F'
    if (freeze) insection = true;
    // no intection ... add a new circle
    if (insection == false) {
      // get closest neighbour and closest possible radius
      var tRadius = width;
      for(var i=0; i < currentCount; i++) {
        var d = dist(tx,ty, x[i],y[i]);
        if (tRadius > d-r[i]) {
          tRadius = d-r[i];
          closestIndex[currentCount] = i;
        }
      }
      if (tRadius > maxRadius) tRadius = maxRadius;
      x[currentCount] = tx;
      y[currentCount] = ty;
      r[currentCount] = tRadius;
      currentCount++;
    }
  }
  // draw them
  for (var i=0 ; i < currentCount; i++) {
    push();
    translate(x[i],y[i]);
    // we abuse radius as random angle :)
    rotate(radians(r[i]));

    if (showSvg) {
      // draw SVGs
      if (r[i] == maxRadius) image(module1, 0, 0, r[i]*2,r[i]*2);
      else image(module2, 0, 0, r[i]*2,r[i]*2);
    }
    if (showCircle) {
      // draw circles
      stroke(0);
      strokeWeight(1.5);
      ellipse(0,0, r[i]*2,r[i]*2);
    }
    pop();
    if (showLine) {
      // draw connection-lines to the nearest neighbour
      stroke(150);
      strokeWeight(1);
      var n = closestIndex[i];
      line(x[i],y[i], x[n],y[n]); 
    }
  } 
  // visualize the random range of the new positions
  if (mouseIsPressed == true) {
    stroke(255,200,0);
    strokeWeight(2);
    rect(mouseX-mouseRect/2,mouseY-mouseRect/2,mouseRect,mouseRect);
  } 
  if (currentCount >= maxCount) noLoop();
}

function keyTyped() {
  if (key == 's' || key == 'S') save("P_2_2_5_02.png");

  // freeze process, toggle on/off
  if (key == 'f' || key == 'F') freeze = !freeze;

  // skin style, toggle on/off
  if (key == '1') showSvg = !showSvg;
  if (key == '2') showLine = !showLine;
  if (key == '3') showCircle = !showCircle;
}

function keyPressed() {
  // mouseRect ctrls arrowkeys up/down 
  if (keyCode == UP_ARROW) mouseRect += 4;
  if (keyCode == DOWN_ARROW) mouseRect -= 4; 
}