toothpicks

// made by alex
// iamalexbaker@gmail.com
// dailygenerative.art.blog

int len = 8;
int numLines = 80;
boolean realTime = false;

float[] hueRange = {0, TWO_PI};
float[] satRange = {0, 0.7};
float[] briRange = {0.4, 0.8};
float alpha = 255;
float strokeWeight = 1;
int nc = 3;
color[] c = new color[nc];
color bgc;
float hue, sat, bri;

PVector origin;
int generation = 0;
ArrayList<toothpick> picks;
ArrayList<toothpick> checkpicks;
boolean finished = false;

class toothpick{
  PVector a, b;
  int dir, gen, contacts;
  boolean is_new, activated;
  toothpick parent;
  toothpick(float x, float y, int d, int g, toothpick prnt){
    dir = d; is_new = true; gen = g; contacts = 0; parent = prnt;
    if(dir==1){//horiz
      a = new PVector(x - len/2, y);
      b = new PVector(x + len/2, y);
    }else{
      a = new PVector(x, y - len/2);
      b = new PVector(x, y + len/2);
    }
  }
  
  boolean intersects(PVector p){
    if( (a.x == p.x && a.y == p.y) || (b.x == p.x && b.y == p.y) ){
      contacts++;
      if(contacts==2) checkpicks.remove(this);
      return true;
    }
    else return false;
  }
  
  toothpick create(ArrayList<toothpick> others, int which_pt){
    PVector pt;
    if(which_pt == 0) pt = a;
    else pt = b;
    if(pt.x < 0 || pt.x > width || pt.y < 0 || pt.y > height){contacts++; return null;}
    for(toothpick other: others){
      if(other != this && other.intersects(pt)) return null;
    }
    return new toothpick(pt.x, pt.y, -dir, generation, this);
  }
  
  void show(){
    if(activated){
      stroke(c[2], alpha);
    } else {
      stroke(lerpColor(c[0], c[1], float(gen) / generation), alpha);
    }
    strokeWeight(strokeWeight);
    line(a.x, a.y, b.x, b.y);
  }
  
  void activateLine(){
    activated = true;
    if(parent != null) parent.activateLine();
  }
}

void setup(){
  size(800,800);
  colorMode(HSB, TWO_PI, 1, 1);
  bgc = color(0, 0, 0.2);
  finished = false;
  generation = 0;
  hue = random(hueRange[0], hueRange[1]);
  sat = random(satRange[0], satRange[1]) * TWO_PI;
  bri = random(briRange[0], briRange[1]) * TWO_PI;
  for(int i = 0; i < nc; i++){
    c[i] = color(random(hueRange[0], hueRange[1]), random(satRange[0], satRange[1]), random(briRange[0], briRange[1]));
  }
  origin = new PVector(int(random(width)), int(random(height)));
  picks = new ArrayList<toothpick>();
  checkpicks = new ArrayList<toothpick>();
  picks.add(new toothpick(origin.x, origin.y, (round(random(1))==0)?1:-1, generation, null));
  //picks.add(new toothpick(origin.x + len*int(random(-30,30)), origin.y + len*int(random(-30,30)), 1, generation));
  
}

void draw(){
  background(bgc);
  if(realTime) tick();
  else{
    while(!finished){
      tick();
    }
    
  }
  for(toothpick t: picks){
    t.show();
  }
}

void tick(){
  generation++;
  
  ArrayList<toothpick> next = new ArrayList<toothpick>();
  for(toothpick t: picks){
    //t.show();
    if(t.is_new){
      toothpick na = t.create(checkpicks, 0);
      toothpick nb = t.create(checkpicks, 1);
      if(na != null) next.add(na);
      if(nb != null) next.add(nb);
      t.is_new = false;
    }
  }
  picks.addAll(next);
  checkpicks.addAll(next);
  if(next.size() == 0){ println("end "+generation); finished = true; }
  else{ println("gen: "+generation+", picks: "+picks.size()+", check: "+checkpicks.size());}
  if(finished){
      for(int i = 0; i < numLines; i++){
        picks.get(int(random(picks.size()))).activateLine();
      }
    }
}

void keyPressed()
{
  if (keyCode==32) { // space
    String fn = "tp-"+hour()+"-"+minute()+"-"+second()+".png";
    saveFrame(fn);
    println("Saved image "+fn);
  }
  if (keyCode==10) { // enter
    setup();
    println("Initialising new state");
  }
}

Leave a comment