// W:Mute 2004 // www.wmute.org // cubeDividor is a cube that continuously divides in smaller sub-cubes. // float parameters for cubeDividor: size, disorder, survival probability of sub-cubes // int parameters for cubeDividor: number of divisions per axis, maximum number of subcubes cubeDividor cubeThing; float side, noise, prob; int divisions, maxCubes; // range of colors defined from an image, adapted from Jared Tarbell, http://www.complexification.net/ // current palette: 0=red earth, 1=water, 2=life palette[] pal; int currentPalette; // abstract object that makes it easier to manage a screen divided in a number of panel // at any time the drawing area is centered on a single panel -- no boundary clipping implemented (drawing can exceed panel) panelledScreen screen; int border, panelWidth, panelHeight, rows, cols; // checkout arielm's work for this one, http://www.chronotext.org ArcBall arcball; // interpolated mouse: bufferedMouse position is a weighted average of previous value and current mous position BufferedMouse bufferedMouse; //program flags boolean firstClick; // flag, captures first mouse-click (to enable keyboard input without resetting applet) boolean firstRun; // flag, on first frame draw borders int count; //array for Zbuffer clearing trick, credit to Toxi, http://www.toxi.co.uk/ //black magic to avoid line drawing artefacts float[] clearZ; void setup(){ //screen setup int border=20; // gray border between panels int panelWidth=200; // width of single panel int panelHeight=200; // height of single panel int rows=4;// panels per column int cols=3;// panels per row screen= new panelledScreen(rows, cols, panelWidth, panelHeight, border); //applet setup size(border+(border+panelWidth)*cols,border+(border+panelHeight)*rows);// autofit applet to panelled screen framerate(20); //arcball arcball = new ArcBall(panelWidth / 2.0f, panelHeight / 2.0f, min(panelWidth - 10, panelHeight - 10) / 2.0f); //buffered mouse bufferedMouse=new BufferedMouse(0.5); //initial program flags firstClick=true; firstRun=true; count=0; //define colors -- if working locally, put your files in the data-dir and change the lines below. pal= new palette[3]; pal[0]=new palette(256, 100, "color01.gif"); //create color palette from source pal[1]=new palette(256, 100, "color02.gif"); pal[2]=new palette(256, 100, "color03.gif"); currentPalette=int(random(3)); //cubeDividor setup side=min(panelWidth,panelHeight)/4.0;// fit cube in panel noise=side*random(0.1);// gives nice results prob=random(35,85);// each division always yields at one sub-cube, prob determines chance that other sub-cubes survive maxCubes=1024;// limits number of sub-cubes divisions=2;// divisions per axis, recommended 2 or 3 cubeThing=new cubeDividor(0,0,0,side,divisions,maxCubes,prob,noise); // make a copy of the virgin z-buffer state clearZ=new float[width*height]; System.arraycopy(g.zbuffer,0,clearZ,0,clearZ.length); } void loop(){ //do some stuff only once if (firstRun){ background(255);// white background, hides the images used to build the color palette firstRun=false; } if(count<(screen.maxPanel+1)) screen.currentPanel=count; if(count==(screen.maxPanel+1)) screen.currentPanel=0; bufferedMouse.update(); // recalculate interpolated mouse position screen.clear();// clear current panel screen.center();// origin in center of current panel arcball.center_x=screen.centerX();// reposition arcball to center of active panel arcball.center_y=screen.centerY();// reposition arcball to center of active panel arcball.run();// rotate view cubeThing.draw();// draw cubeDividor cubeThing.update(); // divide and update cubeDividor count++; } void reset(){// new cubeDividor with random parameters int currentMode=cubeThing.mode;// remember drawing mode noise=random(0.15*side);// large range noise prob=random(10.0,100.0);// large range survival probability cubeThing=new cubeDividor(0,0,0,side,divisions,maxCubes,prob,noise);// start again... cubeThing.mode=currentMode; } //USER INPUT //Mostly well-behaved, can give messy results when changing things too fast. //Flags changing during screen update might give partially refreshed panels. //Mouse input was a bit of a pain. Mouse click toggles cube division or initiates //arcball rotation without changing cube division state. Fixed by moving actions from mousePressed() //to mouseReleased() where user intentions are clear (drag or no drag) //No need for a mind-reading module, please remove USB 2.0 from ear! void mousePressed(){// // flag for cube division toggle, ignored if followed by mouseDragged(); screen.mousePressed(); arcball.mousePressed(); } void mouseDragged() { //Disable cube division while rotating, just an implementation choice, found rotation //a bit confusing with cubes suddenly popping in/out of existence. //flag for mouse drag screen.mouseDragged(); arcball.mouseDragged(); } void mouseReleased() { //resolve all flags previously set //mouse drag flag=true -> restore previous division state, ignore division toggle //mouse drag flag=false -> toggle cube division screen.mouseReleased(); } //Key input //Some limited functions. No GUI controls are used, so keep mouse is free for panel selection //and arcball rotation. // 'p' -> cycle palette // 'm' -> cycle drawing mode // any other key -> new cube void keyPressed(){ switch(key){ case 'p':// palette cycle currentPalette=(currentPalette+1)%3; cubeThing.changePalette(); break; case 'P':// palette cycle currentPalette=(currentPalette+1)%3; cubeThing.changePalette(); break; case 'm':// drawing mode cycle cubeThing.mode=(cubeThing.mode+1)%cubeThing.numModes; break; case 'M':// drawing mode cycle cubeThing.mode=(cubeThing.mode+1)%cubeThing.numModes; break; default:// reset on any other key reset(); } } //CLASSES //Collection of cubes, originating from the successive division of initial cube //Original cube is replaced with one random sub-cube, other sub-cubes are added depending on chance. //Further divisions select random sub-cube from collection, etc. //states: active=dividing, not active=static class cubeDividor{ int divisions, div3;// number of divisions per axis, sub-cubes per division int numCubes, count;// maximum and current number of cubes in collection cube[] cubes;//stores all sub-cubes - making cubeDividor a recursive object might be fun! float side;// size of initial cube float prob;// survival probability of sub-cube float noise;// position and size randomness of sub-cube boolean active;// toggle division int mode, numModes;// current drawing mode, number of implemented modes cubeDividor(float xx, float yy, float zz, float aa, int dd, int nc, float pp, float ns){ numCubes=nc; count=1; prob=pp; noise=ns; side=aa; cubes=new cube[numCubes]; cubes[0]=new cube(xx,yy,zz,aa); for(int i=1;i0.10)){ cubes[count]=new cube(tmpCubes[i].x,tmpCubes[i].y,tmpCubes[i].z,tmpCubes[i].a); cubes[count].generation=cubes[select].generation+1; count++; } } } /*String cubeTxt[]=new String[count]; for (int i=0; i 1.0f) { v.normalize(); } else { v.z = sqrt(1.0f - mag); } return (axis == -1) ? v : constrain_vector(v, axisSet[axis]); } Vec3 constrain_vector(Vec3 vector, Vec3 axis) { Vec3 res = new Vec3(); res.sub(vector, Vec3.mul(axis, Vec3.dot(axis, vector))); res.normalize(); return res; } void applyQuat2Matrix(Quat q) { // instead of transforming q into a matrix and applying it... float[] aa = q.getValue(); rotate(aa[0], aa[1], aa[2], aa[3]); } } static class Vec3 { float x, y, z; Vec3() { } Vec3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } void normalize() { float length = length(); x /= length; y /= length; z /= length; } float length() { return (float) Math.sqrt(x * x + y * y + z * z); } static Vec3 cross(Vec3 v1, Vec3 v2) { Vec3 res = new Vec3(); res.x = v1.y * v2.z - v1.z * v2.y; res.y = v1.z * v2.x - v1.x * v2.z; res.z = v1.x * v2.y - v1.y * v2.x; return res; } static float dot(Vec3 v1, Vec3 v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } static Vec3 mul(Vec3 v, float d) { Vec3 res = new Vec3(); res.x = v.x * d; res.y = v.y * d; res.z = v.z * d; return res; } void sub(Vec3 v1, Vec3 v2) { x = v1.x - v2.x; y = v1.y - v2.y; z = v1.z - v2.z; } void add(Vec3 v1, Vec3 v2) { x = v1.x + v2.x; y = v1.y + v2.y; z = v1.z + v2.z; } } static class Quat { float w, x, y, z; Quat() { reset(); } Quat(float w, float x, float y, float z) { this.w = w; this.x = x; this.y = y; this.z = z; } void reset() { w = 1.0f; x = 0.0f; y = 0.0f; z = 0.0f; } void set(float w, Vec3 v) { this.w = w; x = v.x; y = v.y; z = v.z; } void set(Quat q) { w = q.w; x = q.x; y = q.y; z = q.z; } static Quat mul(Quat q1, Quat q2) { Quat res = new Quat(); res.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z; res.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y; res.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z; res.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x; return res; } float[] getValue() { // transforming this quat into an angle and an axis vector... float[] res = new float[4]; float sa = (float) Math.sqrt(1.0f - w * w); if (sa < EPSILON) { sa = 1.0f; } res[0] = (float) Math.acos(w) * 2.0f; res[1] = x / sa; res[2] = y / sa; res[3] = z / sa; return res; } }