Looking Outwards 6

Underwater by David Bowen

underwater from david bowen on Vimeo.

Basically, this piece is a real-time water simulator and visualization interface that records the topology of a field of water via Kinect and relays that information into a matrix of servomotors controlling a purely mechanical representation of the same water surface. What inspires me about the project is that the recording is being done in a far away lake, possibly on the other side of the globe, and that it controls the servos in an installation where there is no water nearby. David Bowen has done a few other Kinect-based mechanical water recorders, but this is his most advaned one. I do wonder what happens if the real waves get very turbulent though. I think the piece would be even more interesting if he make made the installation interactive in a way, though I am not sure how. Perhaps he could even reverse the recorder and the manipulator, such that instead of the waveform controlling the mechanical interface, the mechanical interface controls the waveform.

Moviestar@Todays Art by Marieke Verbiesen & Neeltje Sprengers

Moviestar@Todays Art from marieke v on Vimeo.

I found this piece really cool – basically, it is real-time movie making interface, where you are the hero and you are exploring around in an unknown alien planet with dinosaurs, UFOs, and jet fighters. The thing is in real time – you are greenscreened into a generative film being recorded live with little animatronics and other real-time screen effects. While you act, you see youself in a little TV embedded with other random goodies just like a scifi flick from the 1960s. The concept of this is very similar to the notion of a videogame; you are immersed in another world and you are the hero, where the environment acts and reacts in real time according to your actions. I think this piece vividly demonstrates the potential of real-time generative film making, and could be well improved upon by simply adding more environments the user can interact with.

Sound Fountain by Todd Vanderlin

Sound Fountain from Todd vanderlin on Vimeo.

I can summarize this piece in two words: laser harp. This piece is more a less a musical intrument where you play notes by blocking the laser at various points of the device. Interaction is very similar to a harp; in a harp, you strum and pluck the strings from left to right to create music, whereas in this piece, you strum and pluck the lasers from left to right to create harmonies. There is another touch I find particularly clever – because lasers are more or less invisible in the open, Todd uses water as a refractive substance in the lasers’ trajectories so one can see where the lasers are. To improve this piece, I would make it resemble even more of a harp than what it already is – add more notes (at least a full octave), and make it so that it plays a sound once when the finger hits the laser, and another sound when the finger is released, rather than continuous sound.

FlowerFace

I don’t know why, but it appears to be simply so that whenever one mentions face tracking, one of the first things that comes to mind is a flower. Even before Golan explicitly stated “Don’t all make daisies,” I was thinking about building a face-tracking flower, even while I was still in high school. Now, having learned to use to OSC with Kyle’s FaceOSC demo, I now have a method to make it into reality. So here I am with my flower-y face. In light of the monotony of petal-laced visages, I decided to make mine in 3 dimensions. Tis’ a simple design, but it works decently well. It tracks the orientation of the whole head, the positions of both eyes and eyebrows, and also the dimensions of the mouth. I threw in some wavy little leaf-arms for the heck of it (not tracked). In implementing this 3D flower face, I’ve also learned that Processing’s 3D transformation system is based on local spaces rather than world space – which is why the eyes look slightly “off” when I tilt my head. All in all, this was an interesting learning experience for me and now I know how to track and map faces in real time.

 
/* FlowerFace by John Choi.
 * Built off of Golan's sample.  Runs off of FaceOSC:
 * Kyle McDonald's FaceOSC https://github.com/kylemcdonald/ofxFaceTracker */

/* --- PRE SETUP --- */
//import necessray libraries
import oscP5.*;
OscP5 oscP5;

//create a sky background
PImage sky;
//create 3d object vars
PShape leftEye;
PShape rightEye;
PShape mouth;
PShape face;
PShape stem;
PShape leftLeaf;
PShape rightLeaf;
// num faces found
int found;
// pose
float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();
// gesture
float mouthHeight;
float mouthWidth;
float eyeLeft;
float eyeRight;
float eyebrowLeft;
float eyebrowRight;
float jaw;
float nostrils;

float timer = 0;

/* ----- SETUP ----- */
void setup() {
  //set up 3D view
  size(640, 480,P3D);
  perspective();
  
  //load sky image
  sky = loadImage("sky.png");
  
  //load 3d objects
  leftEye = loadShape("eye.obj");
  rightEye = loadShape("eye.obj"); 
  mouth = loadShape("mouth.obj"); 
  face = loadShape("face.obj"); 
  stem = loadShape("stem.obj"); 
  leftLeaf = loadShape("leftLeaf.obj"); 
  rightLeaf = loadShape("rightLeaf.obj"); 

  //setup FaceOSC
  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseScale", "/pose/scale");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");

}

/* --- MAIN LOOP --- */
void draw() {  
  background(0);
  lights();
  timer += 1;

  //draw skybox
  hint(DISABLE_DEPTH_MASK);
  image(sky, 0, 0, width, height);
  hint(ENABLE_DEPTH_MASK);
  
  //draw 3D objects
  drawShape(leftEye,-.1*eyeLeft,.03*eyebrowLeft,0, 
            poseOrientation.x,poseOrientation.y,-poseOrientation.z, 
            1,.1*eyebrowLeft,1);
  drawShape(rightEye,.1*eyeRight,.03*eyebrowRight,0,
            poseOrientation.x,poseOrientation.y,-poseOrientation.z, 
            1,.1*eyebrowRight,1);
  drawShape(mouth,0,-.5,0, 
            poseOrientation.x,poseOrientation.y,-poseOrientation.z,  
            .1*mouthWidth,.2*mouthHeight,1);
  drawShape(face,0,0,0, 
            poseOrientation.x,poseOrientation.y,-poseOrientation.z, 
            1,1+.02*(eyebrowLeft+eyebrowRight),1);
  drawShape(stem,0,0,0, 0,0,0, 1,1,1);
  drawShape(leftLeaf,0,-2.75,0, 
            0,.2*sin(timer*0.01)-.1,.5*sin(timer*0.04)-.25, 
            1,1,1);
  drawShape(rightLeaf,0,-2.75,0,
            0,.2*sin(timer*0.02)-.0,.5*sin(timer*0.03)-.25, 
            1,1,1);

}

//helper function to shape
void drawShape(PShape obj, float x, float y, float z, 
               float rx, float ry, float rz,
               float sx, float sy, float sz) {
  pushMatrix();
  translate(width/2,height/2,300);
  scale(20,-20,20);
  translate(x,y,z);
  rotateY(ry);
  rotateX(rx);
  rotateZ(rz);
  scale(sx,sy,sz);
  shape(obj);
  popMatrix();
}

/* --- OSC CALLBACK FUNCTIONS --- */
public void found(int i) {
  println("found: " + i);
  found = i;
}
public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}
public void posePosition(float x, float y) {
  println("pose position\tX: " + x + " Y: " + y );
  posePosition.set(x, y, 0);
}
public void poseOrientation(float x, float y, float z) {
  println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.set(x, y, z);
}
public void mouthWidthReceived(float w) {
  println("mouth Width: " + w);
  mouthWidth = w;
}
public void mouthHeightReceived(float h) {
  println("mouth height: " + h);
  mouthHeight = h;
}
public void eyeLeftReceived(float f) {
  println("eye left: " + f);
  eyeLeft = f;
}
public void eyeRightReceived(float f) {
  println("eye right: " + f);
  eyeRight = f;
}
public void eyebrowLeftReceived(float f) {
  println("eyebrow left: " + f);
  eyebrowLeft = f;
}
public void eyebrowRightReceived(float f) {
  println("eyebrow right: " + f);
  eyebrowRight = f;
}
public void jawReceived(float f) {
  println("jaw: " + f);
  jaw = f;
}
public void nostrilsReceived(float f) {
  println("nostrils: " + f);
  nostrils = f;
}
// all other OSC messages end up here
void oscEvent(OscMessage m) {
  /* print the address pattern and the typetag of the received OscMessage */
  println("#received an osc message");
  println("Complete message: "+m);
  println(" addrpattern: "+m.addrPattern());
  println(" typetag: "+m.typetag());
  println(" arguments: "+m.arguments()[0].toString());
  if(m.isPlugged() == false) { println("UNPLUGGED: " + m); }
}

color target clock

 

 

clair chin target clock

each section of my target changes color within a range gradually here’s 12:29-12:31.

 

targetgif

float secondHsbVal;

void setup() {
size(600, 600);
strokeWeight(35);
stroke(0);
}

void draw() {
colorMode(HSB,360,100,100);
background(0);
float s = map(second(), 0, 60,178, 302);
float m = map(minute(), 0, 60, 61, 245);
float h = map(hour(), 0, 24, 310, 160);
fill(s,54,99);
ellipse(300,300,600,600);
fill(m,75,99);
ellipse(300,300, 400, 400);
fill(h,73,99);
ellipse(300, 300,200, 200);
}

 

more than 1000 lines

 

 

clair chin 1000 lines

umbrellagif

 

float speed = 0.05;
float y=-400;
float x=0;

void setup() {
size(400, 400);
smooth();
}

void draw(){
int stagger = 0;
background(0);
stroke(255);
for (int j= 0; j < 2* height;j=j+height/20){
if (stagger==5){
stagger=0;}
else {
stagger=5;}
for(int i=0; i < width;i = i + width/40){
line(x+i+stagger,y+j,x+i+stagger,y+j+15);
if (y>0) {
y = -400;
}
}
y= y+speed;
}
int cx=mouseX;
int cy=mouseY-15;
int rectx=cx-15;
int recty=cy;
noStroke();
fill(255);
ellipse(cx,cy,30,30);
fill(0);
stroke(0);
rect(rectx,recty,30,420);
stroke(255);
line(cx,cy,cx,cy+20);
}

Catch me you won’t 1 line

 

 

clair chin one line

catchmegif

 

PFont f;
int oneHidingSpotX = 58;
int oneHidingSpotY = 93;
int twoHidingSpotX = 339;
int twoHidingSpotY=142;
int threeHidingSpotX=500;
int threeHidingSpotY=276;
int fourHidingSpotX=190;
int fourHidingSpotY=269;
int fiveHidingSpotX=121;
int fiveHidingSpotY=464;
int lineX=360;
int lineY=360;
int currentTree;
float hidingSpotX=0;
float hidingSpotY=0;
float speed=3;

void setup (){
size(550,550);
smooth();
background(255);
f= createFont(“Helvetica”,20,true);
}

void draw(){
background(255);
///i like trees
///trees from left to right top to bottom
int treeRadius=70;
fill(0);
///tree 1
triangle(58, 20, 30, 75, 86, 75);
triangle(58,50,20,120,98,120);
strokeWeight(15);
strokeCap(SQUARE);
line(58,120,58,160);
///tree 2
strokeWeight(25);
triangle(340,70,320,100,360,100);
triangle(340,100,300,170,380,170);
line(340,160,340,195);
///tree 3
strokeWeight(15);
triangle(500,210,480,310,520,310);
triangle(500,300,480,400,520,400);
line(500,400,500,440);
///tree 4
triangle(190,230,170,250,210,250);
triangle(190,240,160,280,220,280);
strokeWeight(20);
line(190,270,190,310);
///tree 5
triangle(120,430,100,450,140,450);
triangle(120,450,90,480,150,480);
line(120,480,120,510);

//line
strokeWeight(3);
textFont(f);
fill(0);
text(“Catch me- you won’t.”,270,310);
line(lineX,lineY,lineX,lineY+20);
if (distance(mouseX,mouseY,lineX,lineY)<15 && currentTree==0)
{
int tree = (int)random(1,6);
while (currentTree == tree)
{
tree = (int)random(1,6);
}
currentTree = tree;
if (tree==1)
{
hidingSpotX=oneHidingSpotX;
hidingSpotY=oneHidingSpotY;
}
else if (tree==2)
{
hidingSpotX=twoHidingSpotX;
hidingSpotY=twoHidingSpotY;
}
else if (tree==3)
{
hidingSpotX=threeHidingSpotX;
hidingSpotY=threeHidingSpotY;
}
else if (tree==4)
{
hidingSpotX=fourHidingSpotX;
hidingSpotY=fourHidingSpotY;
}
else
{
hidingSpotX=fiveHidingSpotX;
hidingSpotY=fiveHidingSpotY;
}
}
else if (currentTree>0)
{
if (lineX<hidingSpotX)
{
lineX+=speed;
}
else if (lineX>hidingSpotX)
{
lineX-=speed;
}
if (lineY<hidingSpotY)
{
lineY+=speed;
}
else if (lineY>hidingSpotY)
{
lineY-=speed;
}
if (distance(hidingSpotX,hidingSpotY,lineX,lineY)<(1.5*speed))
{
currentTree=0;
}
}
}

float distance (float x,float y,float x1, float y1)
{
float xs=pow(x1-x,2);
float ys=pow(y1-y,2);
float d = pow(xs+ys,0.5);
return d;
}

sinWave 10 lines

 

 

clair chin

10 lines moving along sin wave with amplitude according to mouse X sinwave

<pre lang="java" line="1">


int startingX;
int increment;
float x;
float pi;


void setup()
{
 size(800,300);
 increment=width/11;
 x=0;
 pi=3.141592654;
}


void draw()
{
 background(0);
 println(x<2*pi);
 if (x<2*pi)
 {
 x=x+(2*(pi/100));
 } 
 else
 {
 x=0;
 }
 float waveLevel=map(mouseX,0,width,0,70);
 for (int i=1;i<11;i++)
 {
 stroke(255);
 float shift = sin(x+i*2*pi/10)*waveLevel;
 line((increment*i),140+shift,(increment*i),155+shift);
 }
 
}

</pre>

 

 

Screen Shot 2014-10-01 at 8.25.22 PM

Beyoncé time

1 PM 1 PM 4 AM 4 AM 10 PM 10 PM

Screen Shot 2014-10-02 at 1.20.29 AM

This is a one-hour sample of a full 24 hour clock (because what even are arrays??? The way I know how, it would have taken me 300 lines HA). Each hour has a different artist/playlist corresponding with the time of day. The hours are divided only by the song and not the actual minutes and seconds.

  void setup() {
    size(600, 150);
  }
  void draw () {
    background (0);
    strokeWeight (3);
    noFill();
    rect (50, 50, 200, 100);
  
  
    //int S=  (  (((hour()*60) +minute()) *60) +second() );
    int S = second()+ 250;
  
  
    println (S);
    textAlign(CENTER);
    PFont font;
    font = loadFont("OratorStd-Slanted-48.vlw");
    textFont(font);
    textSize(18);
    fill (255);
  
    if (S<=257) {                         
      text("Beyoncé \"Pretty Hurts \"", 300, 75);
    } else if (S<=626) {
      text("Beyoncé \"Haunted \"", 300, 75);
    } else if (S<= 1001) {
      text("Beyoncé \"Drunk In Love (feat. Jay-Z) \"", 300, 75);
    } else if (S<= 1504) {
      text("Beyoncé \" Blow\"", 300, 75);
    } else if (S<= 1895) {
      text("Beyoncé \"Partition \"", 300, 75);
    } else if (S<= 2273) {
      text("Beyoncé \" Jealous\"", 300, 75);
    } else if (S<= 2524) {
      text("Beyoncé \"Mine(feat. Drake)\"", 300, 75);
    } else if (S<= 2800) {
      text("Beyoncé \" ***Flawless (feat. Chimamanda Ngozi Adiche)\"", 300, 75);
    } else if (S<=3030 ) {
      text("Beyoncé \"Superpower (feat. Frank Ocean) \"", 300, 75);
    } else if (S<= 3296) {
      text("Beyoncé \" Heaven\"", 150, 100);
    } else if (S<=3600 ) {
      text("Beyoncé \" Blue (feat. Blue Ivy)\"", 300, 75);
    }
  }

MAJ: Looking Outwards #4

Admiration: Les Objets Impossibles

Les Objects Impossibles is a live audiovisual concert in two parts, created by Pedro Mari and Natan Sinigaglia (also known as Abstract Birds) in collaboration with Arcadi, Cité de la musique and IRCAM. Les Objects Impossibles visualizes two pieces of music composed by Dmitri Kourliandski in real time as they are performed by Ensemble Intercontemporain. Max-MSP is used to analyze the sound, while vvvv creates the visuals.

I’m fascinated by the use of music to manipulate a semi-realistic 3D-space. I’d be quite interested to see the debugging environment for this impossible object. How was it decided what sounds modify which parameters? How was this impossible object envisioned in the first place?

For more about Les Objects Impossibles, click here. For more about Abstract Birds, click here.

Surprise: City Symphonies

City Symphonies is  a traffic simulator created by Mark McKeague that uses the motion of traffic to create synthesized soundscapes. Processing powers the movement of the traffic, while Max-MSP takes the simulation and turns it into music.

I was pleasantly surprised by the elegant simplicity of this soundscape. As someone who has lived in a city all of their life, it’s refreshingly disorienting to have the familiar vroom of a vehicle replaced with classically synthetic sound.

For more about City Symphonies, click here. For more about Mark McKeague, click here.

What Could Have Been (Or Once Was): Talking Tree

Talking Tree is a project created by Happiness Brussels where a hundred-year-old tree was hooked up to various equipment which measured the tree’s living circumstances. A custom bit of MaxMSP software was written to translate the tree’s sensing information into social media content, which can be found on Twitter, Flickr, and SoundCloud.

Unfortunately, the Talking Tree website is no longer active, so there in no nexus for the tree’s sensing information. I’m a bit dubious of how the MaxMSP program “translates this information into human language.” (See first link) While I enjoy the quirkiness of turning a tree into a social media presence, this project seems too much in the vein of something like a dog with Facebook. (However, if the tree’s custom website was still active, I would consider this project two notches up from the aforementioned Facebook dogs. From what little of it is seen in the video, the website looked quite well designed.)

More information about Happiness Brussels can be found here.

Clock : Distortion

My clock concept underwent some serious changes as it progressed. At first, my plan was to have a city which built itself and then destroyed itself every 24 hours, as shown in the schematics below:
clock sketch
Using toxiclibs, I managed to create a class which would create a flat plane in 3D space. As time progressed, boxes would emerge from the plane and grow upwards to a predetermined stop height. The problem was that if the plane was slanted in any direction, the boxes would still go straight up. I spent roughly 12 hours trying to fix this, but without any specific plan or debugging methodology, I only managed to waste loads of time and create more errors with the box generation. I ditched the idea of boxes for lines, which were much less time consuming to deal with. In the end, I managed to create a class which would accept any mesh, and generate lines which grow out of the given mesh in a specific direction. Thus, when a mesh that looks like this:

clock mesh

is fed into the class, this is the result:

screencap 4

captured in motion:

clock-example1

I inserted a shape into a 3D environment:

screencap 5

Originally, I planned to make the lines simply grow in different directions based on the time of day, but that didn’t seem to involve the surrounding terrain at all. Instead, I raised the shape high above the land, and altered the topography:

screencap 0

The beam of light moves to predetermined points (the intersections of the crevices between hills) around the terrain once per minute. A sped up section of random movement is shown here:

moving-round

While at these points, the light will distort the surface of the terrain in one of five ways:

terraform-1 The light pulls the land up toward it terraform-2 The light expands the land, making it grow outwards terraform-3 The light collapses the land down to a single point terraform-4 The light stirs the land around terraform-5 The light pushes the land away from it

Every hour, the mesh is reset. One can look at the mesh and, judging its degree of distortion, tell how far into the current hour one is. Implementing a way to tell which hour is the next step, but currently this is only a one hour clock.

Here is a timelapse of the clock in action:

one-hour

Code:

import toxi.color.*;
import toxi.color.theory.*;
import toxi.physics2d.constraints.*;
import toxi.physics.*;
import toxi.physics.constraints.*;
import toxi.physics.behaviors.*;
import toxi.math.conversion.*;
import toxi.geom.*;
import toxi.math.*;
import toxi.util.datatypes.*;
import toxi.util.events.*;
import toxi.geom.mesh.subdiv.*;
import toxi.geom.mesh.*;
import toxi.math.waves.*;
import toxi.util.*;
import toxi.math.noise.*;
import java.util.*;
import toxi.processing.*;
import java.util.*;

import processing.opengl.*;
int timeChange;
Viewer v;
Mesh3D shape;
ToxiclibsSupport gfx;
SuperStructure struc;
VerletPhysics world;
Land land;
Agent3D zapPos;
int imginc, curMin, curHour;
void setup(){
  size(1700,1000,OPENGL);
  v = new Viewer(-1280,-776,-1109, new Vec3D());
  struc = new SuperStructure(0,-1000,0,(TriangleMesh)new AABB(20).toMesh(),new Vec3D(0,1,0),100,83,2000,1);
  gfx = new ToxiclibsSupport(this);
  world = new VerletPhysics();
  land = new Land(0,50,0,new Vec3D());
  imginc = 0;
  timeChange = 0;
  zapPos = new Agent3D(0,0,0,new Vec3D());
  zapPos.maxSpeed = 15.0;
  zapPos.maxForce = 5.0;
  curMin = minute();
  curHour = hour();
  zapPos.setSeekPt(land.marks.get(int(random(land.marks.size()))));
  struc.zapPoint(zapPos);
}

void draw(){
  noCursor();
  background(30,40,90);
  
  if(curMin != minute()){
    zapPos.setSeekPt(land.marks.get(int(random(land.marks.size()))));
    struc.zapPoint(zapPos);
    curMin = minute();
    land.changeRule(int(random(5)));
    imginc++;
  }
  if(curHour != hour()){
    land.reconfiguring = true;
    curHour = hour();
  }
  
  world.update();  
  zapPos.run(gfx);
  Vec3D lightPos = struc.interpolateTo(zapPos,0.8);
  pointLight(220,180,200,lightPos.x,lightPos.y,lightPos.z);
  noStroke();
  struc.run(gfx);
  land.run(gfx);
  v.run();
  if(key == 'c'){
    println(v.campos, v.direction);
  }
}
class Agent3D extends VerletParticle{
  Vec3D targ, direction;
  TriangleMesh shape;
  float maxSpeed, maxForce;
  Agent3D(float x, float y, float z){
    super(x,y,z);
    direction = null;
    shape = new TriangleMesh();
    targ = null;
    maxSpeed = 5.0;
    maxForce = 1.2;
  }
  Agent3D(float x, float y, float z, Vec3D rot){
    this(x,y,z);
    direction = rot;
  }
  Agent3D(float x, float y, float z, Vec3D rot, TriangleMesh shape){
    this(x,y,z,rot);
    this.shape = shape;
  }
  void setSeekPt(Vec3D target){
    targ = target;
  }
  void setDirectionToFace(Vec3D pt){
    direction.set(this.sub(pt).normalize());
  }
  void seek(){
    if(targ != null){
      Vec3D c = this.sub(targ);
      float m = map(c.magnitude(),0,100,0,maxSpeed);
      c = c.normalizeTo(m);
      Vec3D steer = this.getVelocity().sub(c);
      steer = steer.limit(maxForce); 
      this.addVelocity(steer);
    }
  }
  
  void display(ToxiclibsSupport tls){
   tls.translate(this);
   shape = shape.pointTowards(direction);
   tls.mesh((Mesh3D)shape, false);
   tls.translate(new Vec3D().sub(this));
  }
  void run(ToxiclibsSupport tls){
    seek();
    update();
    display(tls);
    clearVelocity();
  }
}
class SuperStructure extends Agent3D{
  int timeOfBirth, age, maxAge, maxBlocks;
  Structure[] structs;
  SuperStructure(float x, float y, float z, Mesh3D m, Vec3D dir, int lifespan, int blockNum, int blockH, int blockW){
    super(x,y,x,dir);
    maxBlocks = blockNum;
    maxAge = lifespan;
    shape = (TriangleMesh)new STLReader().loadBinary(sketchPath("god.stl"),STLReader.TRIANGLEMESH);
    structs = new Structure[m.getFaces().size()];
    for(int i = 0; i < structs.length; i++){
      Face f = (Face)m.getFaces().toArray()[i];
      structs[i] = new Structure(this.x,this.y,this.z,this.direction, blockH, maxAge, blockW, blockH, maxBlocks, f);
    }
  }
  void zapPoint(Vec3D pt){
    setDirectionToFace(pt);
    for(Structure s: structs){
      s.setDirectionToFace(pt);
    }
  }
  void run(ToxiclibsSupport tls){  
    zapPoint(zapPos);
    for(Structure s: structs){
      if(s != null){
        s.grow();
      }
    }
    fill(255);
    this.display(tls);
  }
}

class Structure extends Agent3D{
  float baseRadius, maxBlockWidth, maxBlockHeight, blockWidth, blockHeight;
  int timeOfBirth, age, maxAge, maxBlockNum, blockSlot, lifeStage;
  Block[] blocks;
  Triangle3D base;
  
  Structure(float x, float y, float z, Vec3D dir, float top, int lifespan, float wid, float hei, int blockNum, Face baseMake){
    super(x,y,z,dir);
    timeOfBirth = frameCount;
    maxBlockWidth = wid;
    blockSlot = 0;
    maxBlockHeight = hei;
    maxBlockNum = blockNum;
    maxAge = lifespan;
    blocks = new Block[maxBlockNum];
    base = new Triangle3D((Vec3D)baseMake.a, (Vec3D)baseMake.b, (Vec3D)baseMake.c);
    for(int i = 0; i < maxBlockNum; i++){
       Vec3D newPoint = base.a.interpolateTo(base.b.interpolateTo(base.c,random(1)),random(1));  
       blocks[i] = new Block(newPoint.x,newPoint.y,newPoint.z,direction,maxBlockWidth,maxBlockHeight,(int)random(maxAge));
    }
  }
  void setDirectionToFace(Vec3D pt){
    direction = pt.sub(this).normalize();
  }
  Triangle3D buildBase(){
    Vec3D[] corners = new Vec3D[3];
    for(int i = 0; i < 3; i++){
      //creates corners by making flat random vector and then rotating to match structures rotation
      Vec3D newCorner = new Vec3D(random(-1,1)*baseRadius,0,random(-1,1)*baseRadius).rotateX(direction.x).rotateY(direction.y);
      corners[i] = newCorner;
    }
    return new Triangle3D(corners[0],corners[1],corners[2]);
  }
  void grow(){
    gfx.translate(this);
    for(int i = 0; i < maxBlockNum; i++){
      Block b = blocks[i];
        b.grow();
        b.display(gfx);
        if(frameCount-b.birth > b.maxAge){
        Vec3D newPoint = base.a.interpolateTo(base.b.interpolateTo(base.c,random(1)),random(1));  
        blocks[i] = new Block(newPoint.x,newPoint.y,newPoint.z,direction,maxBlockWidth,maxBlockHeight,maxAge);
      }
    }
    gfx.translate(new Vec3D().sub(this));
  }
}

class Block extends Agent3D{
  float wdth, hght;
  int tone, birth, maxAge;
  TColor col;
  Vec3D up;
  Vec3D box;
  Block(float x,float y,float z,Vec3D rot, float wt, float ht,int maxAge){
    super(x,y,z,rot);
    birth = frameCount;
    this.maxAge = maxAge;
    wdth = wt;
    hght = ht;
    box = blockFace();
    tone = int(noise(frameCount*0.03)*30);
  }
  
  Vec3D blockFace(){
    return this.add(direction.normalizeTo((frameCount+1-birth)*1.0/maxAge*hght));
  }

  void grow(){
    box = blockFace();
  }
  void display(ToxiclibsSupport tls){
    strokeWeight(wdth);
    stroke(180,180,200,tone);
    tls.line(this,box);
  }
}
class Viewer{
  Vec3D direction, campos;
  Viewer(float x, float y, float z, Vec3D dir) {
    direction = dir;
    campos = new Vec3D(x,y,z);
  }

  void run() {
    direction.set(sin(map(mouseX,0,width,2*PI,0)),map(mouseY,0,height,-1.5,1.5),cos(map(mouseX,0,width,2*PI,0)));
    direction.addSelf(campos);
  
  // Change height of the camera with mouseY
    camera(campos.x, campos.y, campos.z, // eyeX, eyeY, eyeZ
         direction.x, direction.y, direction.z, // centerX, centerY, centerZ
         0.0, 1.0, 0.0); // upX, upY, upZ
      if(keyPressed){
        Vec3D dir = direction.copy();
        dir.subSelf(campos);
        switch(key){      
          case('a'): 
            dir.set(sin(map(mouseX,0,width,2.5*PI,PI/2)),0,cos(map(mouseX,0,width,2.5*PI,PI/2)));
            campos.addSelf(dir.normalizeTo(3));    
            break;
          case('w'):
            campos.addSelf(dir.normalizeTo(3));
            break;
          case('d'):           
            dir.set(sin(map(mouseX,0,width,2.5*PI,PI/2)),0,cos(map(mouseX,0,width,2.5*PI,PI/2)));
            campos.subSelf(dir.normalizeTo(3));
            break;
          case('s'):
            campos.subSelf(dir.normalizeTo(2));
            break;
        }
    }
  }
}
class Land extends Agent3D{
  TriangleMesh originalShape;
  Terrain terrain;
  ArrayList<Agent3D> marks;
  int ruleNum;
  boolean reconfiguring;
  float reconfigRatio;
  float xCenter = 5.2;
  float zCenter = 5.2;
  Land(float xx,float yy,float zz, Vec3D dir){
    super(xx,yy,zz,dir);
    marks = new ArrayList();
    int DIMS = 40;
    reconfiguring = false;
    reconfigRatio = 0.0;
    terrain = new Terrain(DIMS,DIMS, 50);
  float[] el = new float[DIMS*DIMS];
  noiseSeed((long)random(500));
  ruleNum = int(random(5));
  for (int z = 0, i = 0; z < DIMS; z++) {
    for (int x = 0; x < DIMS; x++) {
      el[i++] = noise(x * 0.12, z * 0.12) * 400/PApplet.max((PApplet.abs(xCenter - (x%10))*PApplet.abs(zCenter-(z%10))),4);
      if((x+5)%10 == 0 && (z+5)%10 == 0){
        IsectData3D sec = new IsectData3D();
        sec = terrain.intersectAtPoint(x,z);
        marks.add(new Agent3D(sec.pos.x()*50-1000,150,sec.pos.z()*50-1000,new Vec3D()));
      }
    }
  }
  terrain.setElevation(el);
  // create mesh
  shape = (TriangleMesh)terrain.toMesh();
  originalShape = shape.copy();
  }
  void display(ToxiclibsSupport tls){
    noStroke();
    super.display(tls);
    stroke(255,0,0);
  }
  
  void run(ToxiclibsSupport tls){
    if(reconfiguring) {
      reconfigure();
    }
    super.run(tls);
    for(Agent3D mark: marks){
      mark.run(tls);
    }
    alterTerrain(zapPos);
  }
  
  ArrayList<Vec3D> getVertexPoints(TriangleMesh s){
    ArrayList<Vec3D> start = new ArrayList();
    Iterator<Vertex> getter = shape.getVertices().iterator();
    while(getter.hasNext()){
      start.add((Vec3D)getter.next());
    }
    return start;
  }
  
  ArrayList<Vec3D> getCloseVertices(Vec3D pt){
    ArrayList<Vec3D> result = new ArrayList();
    Vec3D ptFlat = new Vec3D(pt.x,0,pt.z);
    ListIterator<Vec3D> remover = getVertexPoints(shape).listIterator();
    while(remover.hasNext()){
      Vec3D f = (Vec3D)remover.next();
      Vec3D flat = new Vec3D(f.x,0,f.z);
      if(flat.distanceToSquared(ptFlat) < 10000){
        result.add(f);
      }
    }
    return result;
  }
  
  Vec3D average(ArrayList<Vec3D> vecs){
    Vec3D result = new Vec3D();
    for(Vec3D v: vecs){
      result = result.add(v);
    }
    return result.scale(1.0/vecs.size());
  }
  
  void reconfigure(){
    shape = originalShape.copy();
    reconfiguring = false;
  }
  
  void changeRule(int r){
    ruleNum = r;
  }
  
  void alterTerrain(Agent3D cursor){
    for(Agent3D mark: marks){
      if(cursor.distanceToSquared(mark) < 4){
        ArrayList<Vec3D> nearPts = getCloseVertices(cursor);
        for(Vec3D pt: nearPts){
          Vec3D rule = new Vec3D();
          switch(ruleNum){
            case(0):
              rule = pt.sub(average(nearPts));
              break;
            case(1):
              rule = average(nearPts).sub(pt);
              break;
            case(2):
              rule = new Vec3D(cos(second()*PI/30),sin(second()*PI/15),sin(second()*PI/30));
              break;
            case(3):
              rule = new Vec3D(pt.sub(struc));
              break;
            case(4):
              rule = new Vec3D(struc.sub(pt));
              break;
          }
          shape.updateVertex(pt, pt.add(rule.normalizeTo(2)));
        }
        mark.setSeekPt(average(getCloseVertices(cursor)));
      }
    }
  }
}

 

 

One Thousand Liners

Years passed and the colony of my creations flourished. But a great xenophobia emerged. Intruders were immediately caught and exiled. The lines were finally left in peace.

import java.awt.Robot;
import java.awt.AWTException;
import processing.serial.*;
import java.awt.MouseInfo;
import java.awt.Point;

Robot rob;

ArrayList<ZoomLine> lines;

boolean mouseInWindow;

void setup() {
  size(700,500);
  lines = new ArrayList<ZoomLine>();
  createLines();
  //Set up Bot to move mouse
  try {
  rob = new Robot();
  }
  catch (AWTException e) {
    e.printStackTrace();
  }
}

void draw() {  
  background(255);
  mouseInWindow();
  moveMouse();
  updateLines();
}

void moveMouse() {
  if (mouseInWindow){
      float distance = dist(mouseX,mouseY,width/2,height/2);
      float speed = 3;
      int x = frame.getLocation().x;
      int y = frame.getLocation().y;
      float xChange;
      float yChange;
      if (mouseX < width/2) {
        xChange = -1;
      } else {
        xChange = +1;
      }
      if (mouseY < height/2) {
        yChange = 1;
      } else {
        yChange = -1;
      }
      rob.mouseMove((x + mouseX),(int) (y + mouseY));
  }
}

void createLines() {
  for (int i = 0; i < 100; i++) { // Top Left to center
    lines.add(new ZoomLine(0,0,width*.75,height*.5,false,-50,-50,0,0));
  }
  for (int i = 0; i < 100; i++) { //Top right to center
    lines.add(new ZoomLine(width,0,width*.75,height*.5,false,50,-50,0,0));
  }
  for (int i = 0; i < 100; i++) { //Bottom Right to Center
    lines.add(new ZoomLine(width,height,width*.75,height*.5,false,50,50,0,0));
  }
  for (int i = 0; i < 100; i++) { //Bottom Left to Center
    lines.add(new ZoomLine(0,height,width*.75,height*.5,false,-50,50,0,0));
  }
  for (int i = 0; i < 100; i++) { //Top bar
    lines.add(new ZoomLine(width/2 + 50,height/2 - 50,
                           width/2 - 50,height/2 - 50 ,true,-50,-50,50,-50));
  }
  for (int i = 0; i < 100; i++) { //Right Bar
    lines.add(new ZoomLine(width/2 + 50,height/2 + 50,
                           width/2 + 50,height/2 - 50 ,true,+50,-50,50,50));
  }
  for (int i = 0; i < 100; i++) { // Left Bar
    lines.add(new ZoomLine(width/2 - 50,height/2 - 50,
                           width/2 - 50,height/2 + 50 ,true,-50,+50,-50,-50));
  }
  for (int i = 0; i < 100; i++) { //Bottom Bar
    lines.add(new ZoomLine(width/2 - 50,height/2 + 50,
                           width/2 + 50,height/2 + 50 ,true,+50,+50,-50,+50));
  }
  for (int i = 0; i < 50; i++) { //Top center to center
    lines.add(new ZoomLine(width/2,0,height/2,height/2 - 50,false,0,-50,0,0));
  }
  for (int i = 0; i < 50; i++) { //Left to center
    lines.add(new ZoomLine(0,height/2,width/2 - 50,height/2,false,-50,0,0,0));
  }
  for (int i = 0; i < 50; i++) { //Bottom to center
    lines.add(new ZoomLine(width/2,height,width/2,height/2 + 50,false,0,50,0,0));
  }
  for (int i = 0; i < 50; i++) { //Right to center
    lines.add(new ZoomLine(width,height/2,width/2 + 50,height/2,false,50,0,0,0));
  }
}

void updateLines() {
  for (int i = 0; i < lines.size(); i++) {
    lines.get(i).update();
    lines.get(i).drawMe();

  }
}

class ZoomLine {
  Position start;
  Position lineLoc;
  Position lineEnd;
  Position goal;
  
  Position velocity;
  
  float lineLength;
  float angle;
  
  boolean moving;
  
  float noiseStart = random(100);
  float noiseChange = .007;
  
  float speed;
  
  float offSetX;
  float offSetY;
  
  float mouseOffX;
  float mouseOffY;
  
  float startOffX;
  float startOffY;
  
  ZoomLine(float startX, float startY, float goalX, float goalY, 
           boolean moving, float mouseOffX, float mouseOffY, 
           float startOffX, float startOffY) {
    
    this.start = new Position(startX, startY);
    this.goal = new Position(goalX, goalY);
    
    this.mouseOffX = mouseOffX;
    this.mouseOffY = mouseOffY;
    
    this.startOffX = startOffX;
    this.startOffY = startOffY;
    
    this.moving = moving;
    this.speed = 5;
   
    this.lineLoc = pointBetween(this.start.x, this.start.y,
                                this.goal.x, this.goal.y); 
    updateAngle();
    updateLength();
    updateLineEnd();
    float largest = max(width,height);
    offSetX = random(-largest * .03,largest * .03);
    offSetY = random(-largest * .03,largest * .03);
  }
  
  void updateSpeed() {
    float distance = dist(this.goal.x, this.goal.y,
                          this.start.x, this.start.y);
    this.speed = distance / this.lineLength;
  }
      
  void updateAngle() {
    float dx = this.goal.x - this.lineLoc.x;
    float dy = this.goal.y - this.lineLoc.y;
    
    this.angle = atan2(dy,dx);
  }
  
  void updateLength() {
    float distance = dist(this.goal.x, this.goal.y,
                          this.start.x, this.start.y);
    
    this.noiseStart += this.noiseChange;
    
    this.lineLength = (distance * .3) * noise(this.noiseStart);
  }
  
  Position pointBetween(float x1, float y1, float x2, float y2) { 
    float w = random(0,1);
    
    float xm = x1 * w + (1 - w) * x2;
    float ym = y1 * w + (1 - w) * y2;
   
    return new Position(xm, ym); 
  }
  
  void updateLineEnd() {
    float yChange = sin(this.angle) * this.lineLength;
    float xChange = cos(this.angle) * this.lineLength;
    this.lineEnd = new Position(this.lineLoc.x + xChange, this.lineLoc.y + yChange);
  }
  void drawMe() {
    strokeWeight(4);
    line(this.lineLoc.x + offSetX, this.lineLoc.y + offSetY, 
         this.lineEnd.x + offSetX, this.lineEnd.y + offSetY);
  } 
  
  void updateGoal() {
    if (mouseInWindow) {
      this.goal.x = mouseX + this.mouseOffX;
      this.goal.y = mouseY + this.mouseOffY;
    } else {
      this.goal.x = width/2;
      this.goal.y = height/2;
    }
    if ((this.moving) && (mouseInWindow)){
      this.start.x = mouseX + this.startOffX;
      this.start.y = mouseY + this.startOffY;
    }
  }
  void update() {
    this.updateGoal();
    this.updateAngle();
    this.updateLineEnd();
    this.move();
  }
  
  void move() {
    float distance = dist(this.lineLoc.x,this.lineLoc.y,this.goal.x, this.goal.y);
    this.lineLoc.x += this.speed*((this.goal.x - this.lineLoc.x)/distance);
    this.lineLoc.y += this.speed*((this.goal.y - this.lineLoc.y) / distance);
    
    if (dist(this.lineLoc.x, this.lineLoc.y,this.goal.x, this.goal.y) <= width * .02) {
          this.lineLoc.x = this.start.x;
          this.lineLoc.y = this.start.y;
          this.updateLength();
          this.updateSpeed();
    }
  }
}
  
void mouseInWindow() {
  Point mousePos = (MouseInfo.getPointerInfo().getLocation());
  int mWinX = mousePos.x;
  int mWinY = mousePos.y;
  int fX = frame.getLocation().x;
  int fY = frame.getLocation().y;
  if ((mWinX > fX && mWinX < fX + width) &&
     (mWinY > fY && mWinY < fY + height)) {
   mouseInWindow = true;
  } else {
   mouseInWindow = false;
  } 
}
    
class Position {
    float x;
    float y;
    Position(float x, float y) {
      this.x = x;
      this.y = y;
    }
}
 
class RandomPosition extends Position {
      RandomPosition(float startX, float endX, float startY, float endY) {
      super(random(startX, endX),random(startY, endY));
    }
}