Tutorials | (back to the list of tutorials) |
import processing.opengl.*; import igeo.*; void setup(){ size(960, 540, IG.GL); // set the window size IConfig.syncDrawAndDynamics=true; IG.bg(1.0); // setting background color. to set RGB color, put 3 numbers like IG.bg(1.0,0.0,0.0) IG.fillWire(); // solid shading mode with wireframe. Solid shading without wireframe is IG.fill(). transparent with wireframe is IG.transWire(). transparent without wireframe is IG.trans(). IRand.init(3); // initial seed of random number // First agent is generated here. The first argument is position, the second is front direction and size, the third is upward normal direction and size, the fourth is a parent agent which needs to be null in the first agent new ClockStackAgent(new IVec(0,0,0), new IVec(1,0,0), new IVec(0,0,1), null).clr(0.6); } class Orientation{ IVec dir, nml; // as xyz coordinates system, dir corresponds to Y and nml corresponds to Z boolean righthand; // right hand coordinates system or not double sideLen; // length of side vector. Front length is length of dir. Up length is length of nml. IVec translate; // just to implement jumping behavior Orientation(IVec d, IVec n, double sideLength, boolean righthandsys){ dir=d; nml=n; sideLen=sideLength; righthand=righthandsys; } Orientation(IVec d, IVec n, boolean righthandsys){ dir=d; nml=n; sideLen=dir.len(); righthand=righthandsys; } Orientation(IVec d, IVec n, IVec side){ dir=d; nml=n; sideLen=side.len(); righthand= dir.cross(nml).dot(side)>0; } // only length and righthand is set by side, not direction. Orientation(IVec d, IVec n, double sideLength){ this(d,n,sideLength,true); } Orientation(IVec d, IVec n){ this(d,n,true); } Orientation(Orientation o){ dir=o.dir.cp(); nml=o.nml.cp(); sideLen=o.sideLen; righthand=o.righthand; } Orientation cp(){ return new Orientation(this); } boolean eq(Orientation o){ return o.righthand==righthand && o.dir.eq(dir) && o.nml.eq(nml) && Math.abs(sideLen-o.sideLen) < IConfig.tolerance && (o.translate==null && translate==null || o.translate!=null&&translate!=null&&o.translate.eq(translate)); } boolean isParallel(Orientation o){ return o.righthand==righthand && o.dir.isParallel(dir) && o.nml.isParallel(nml); } IVec dir(){ return dir.cp(); } IVec front(){ return dir(); } IVec back(){ return dir().neg(); } IVec nml(){ return nml.cp(); } IVec up(){ return nml(); } IVec down(){ return nml().neg(); } IVec side(){ if(righthand){ return dir.cross(nml).len(sideLen); }else{ return nml.cross(dir).len(sideLen); } } IVec right(){ return side(); } IVec left(){ return side().neg(); } double sideLen(){ return sideLen; } double frontLen(){ return dir.len(); } double upLen(){ return nml.len(); } Orientation setSideLen(double len){ sideLen=len; return this; } Orientation setFrontLen(double len){ dir.len(len); return this; } Orientation setUpLen(double len){ nml.len(len); return this; } Orientation rot(double ang){ dir.rot(nml, ang); return this; } Orientation rot(IVec ax, double ang){ dir.rot(ax,ang); nml.rot(ax,ang); return this; } Orientation pitch(double ang){ IVec ax = dir.cross(nml); dir.rot(ax, ang); nml.rot(ax, ang); return this; } Orientation yaw(double ang){ dir.rot(nml, ang); return this; } Orientation roll(double ang){ nml.rot(dir, ang); return this; } Orientation ref(IVec refNml){ dir.ref(refNml); nml.ref(refNml); righthand = !righthand; return this; } Orientation flip(){ dir.flip(); righthand = !righthand; return this; }// flip front/back Orientation flipNml(){ nml.flip(); righthand = !righthand; return this; }// flip up/down Orientation flipSide(){ righthand = !righthand; return this; }// flip left/right Orientation mul(double v){ dir.mul(v); nml.mul(v); sideLen*=v; return this; } Orientation div(double v){ dir.div(v); nml.div(v); sideLen/=v; return this; } Orientation scaleFront(double v){ dir.mul(v); return this; } Orientation scaleUp(double v){ nml.mul(v); return this; } Orientation scaleSide(double v){ sideLen*=v; return this; } Orientation add(Orientation o){ dir.add(o.dir); nml.add(o.nml()); return this; } Orientation add(Orientation o, double f){ dir.add(o.dir, f); nml.add(o.nml(), f); return this; } Orientation sum(Orientation o, double f){ dir.mul(1-f).add(o.dir, f); nml.mul(1-f).add(o.nml(), f); sideLen=sideLen*(1-f)+o.sideLen*f; return this; } Orientation mid(Orientation o){ return sum(o,0.5); } Orientation translate(IVec t){ return jump(t); } Orientation jump(IVec move){ translate=move; return this; } Orientation jump(double x, double y, double z){ return jump(new IVec(x,y,z)); } Orientation jump(Orientation or){ return jump(or.dir.cp()); } Orientation jump(double factor){ return jump(dir.cp().mul(factor)); } Orientation jump(){ return jump(dir.cp()); } } class Attribute extends IAttribute{ int delay=0; boolean noCollision=false; boolean noGeometry=false; boolean noDeform=true; int moduleIndex=-1; // default is no module Attribute(){ super(); } Attribute(IAttribute at){ super(at); } Attribute(Attribute at){ super(at); delay = at.delay; noCollision = at.noCollision; noGeometry = at.noGeometry; moduleIndex=at.moduleIndex; } Attribute cp(){ return new Attribute(this); } Attribute delay(int d){ delay = d; return this; } Attribute noCollision(){ noCollision=true; return this; } Attribute collision(){ noCollision=false; return this; } Attribute noGeometry(){ noGeometry=true; return this; } Attribute geometry(){ noGeometry=false; return this; } Attribute noDeform(){ noDeform=true; return this; } Attribute deform(){ noDeform=false; return this; } Attribute module(int module){ moduleIndex=module; return this; } int module(){ return moduleIndex; } Attribute layer(String layerName){ super.layer = IG.layer(layerName); return this; } } class ClockStackAgent extends IAgent{ final double threshold = 0.5; //collision threshold IVec pos, pos2, prevPos; Orientation orient, prevOrient; int[] clocks; boolean isColliding = false, isStopped = false; boolean addGeometry=false; ArrayList< IVec > pts; ArrayList< Orientation > nextOrient; ArrayList< int[] > nextClocks; ArrayList< Attribute > nextAttr; IBounds bounds; int delayCount; ClockStackAgent parent; ClockStackAgent(IVec p, Orientation o, int[] clok, ClockStackAgent parentAgent){ pos = p; orient = o; clocks = clok; parent = parentAgent; if(parent!=null){ prevPos=parent.pos.cp(); prevOrient=parent.orient.cp(); } delayCount=0; } ClockStackAgent(IVec p, IVec d, IVec n, int[] clok, ClockStackAgent parentAgent){ this(p, !d.isParallel(n)?new Orientation(d,d.cross(n).icross(d).len(n.len())): n.isParallel(IG.zaxis)?new Orientation(d,d.cross(0,1,0).icross(d).len(n.len())):new Orientation(d,d.cross(0,0,1).len(n.len())), clok, parentAgent); } ClockStackAgent(IVec p, IVec d, IVec n, ClockStackAgent parentAgent){ this(p,d,n,new int[0], parentAgent); } ClockStackAgent(IVec p, IVec d, ClockStackAgent parentAgent){ this(p,d,new IVec(0,0,1),null, parentAgent); } IVec pos2(){ if(pos2==null) pos2 = pos.cp(orient.dir); return pos2; } IVec prevPos(){ return prevPos; } Orientation prevOrient(){ return prevOrient; } Attribute defaultAttribute(){ return new Attribute(); } ClockStackAgent module(int module){ IAttribute attr = attr(); if(attr==null){ attr = defaultAttribute(); attr(attr); } ((Attribute)attr).module(module); return this; } ClockStackAgent delay(int d){ IAttribute attr = attr(); if(attr==null){ attr = defaultAttribute(); attr(attr); } ((Attribute)attr).delay(d); return this; } ClockStackAgent noCollision(){ IAttribute attr = attr(); if(attr==null){ attr = defaultAttribute(); attr(attr); } ((Attribute)attr).noCollision(); return this; } ClockStackAgent collision(){ IAttribute attr = attr(); if(attr==null){ attr = defaultAttribute(); attr(attr); } ((Attribute)attr).collision(); return this; } boolean isDelayed(){ if(attr()==null) return false; if(((Attribute)attr()).delay <= delayCount) return true; return false; } int delayedTime(){ if(attr()==null) return time(); return delayCount - ((Attribute)attr()).delay; } boolean isCollidable(){ if(attr()==null) return true; if(((Attribute)attr()).noCollision) return false; if(((Attribute)attr()).delay <= delayCount) return true; return false; } void interact(ArrayList< IDynamics > agents){ if(threshold > 0 && !isStopped && isCollidable()){ IVec pt2 = pos2(); // collision to the ground for(int i=0; i < agents.size() && !isColliding; i++){ if(agents.get(i) instanceof ClockStackAgent){ ClockStackAgent a = (ClockStackAgent)agents.get(i); if(a==this){ // check self collision for(int j=0; pts!=null && j < pts.size()-2 && !isColliding; j++){ // exclude last segment if(IVec.isSegCloserThan(pos, pt2, pts.get(j), pts.get(j+1), threshold)){ isColliding = true; if(pos.distToSegment(pts.get(j), pts.get(j+1))>threshold){ addGeometry=true; } } } } else if(a.delayedTime() >= 0){ // a!=this if(a.bounds!=null && bounds!=null){ IBounds newbounds = bounds.cp(); newbounds.compare(pt2); if(!newbounds.isCloserThan(a.bounds,threshold)){ continue; } } IVec apt2 = a.pos2(); if(!a.isColliding && IVec.isSegCloserThan(pos, pt2, a.pos, apt2, threshold) && !(pos.eq(a.pos) && !pt2.eq(apt2))/*not sibling*/ && !(a.time() > 0&&apt2.eq(pos))/*not parent*/ ){ isColliding = true; if(pos.distToSegment(a.pos, apt2)>threshold){ addGeometry=true; } } for(int j=0; a.pts!=null && j < a.pts.size() && !isColliding; j++){ IVec apt3 = a.pos; if(j < a.pts.size()-1) apt3 = a.pts.get(j+1); if(IVec.isSegCloserThan(pos, pt2, a.pts.get(j), apt3, threshold) && (!pos.eq(a.pos) || pt2.eq(apt2) || j < a.pts.size()-1) ){ if(delayedTime() > 0 || !pos.isOnSegment(a.pts.get(j),apt3)){ // exclude if it came from that line isColliding = true; if(pos.distToSegment(a.pts.get(j), apt3)>threshold){ addGeometry=true; } } } } } } } } } int clock(int i){ if(i >= clocks.length) return 0; return clocks[i]; } Attribute next(int incrementClock){ return next(orient, incrementClock); } Attribute next(Orientation o, int incrementClock){ if(nextOrient==null){ nextOrient = new ArrayList< Orientation >(); nextClocks = new ArrayList< int[] >(); nextAttr = new ArrayList< Attribute >(); } nextOrient.add(o); int[] clocks2 = new int[incrementClock+1 > clocks.length?incrementClock+1:clocks.length]; for(int i=0; i < clocks2.length; i++){ if(i < incrementClock) clocks2[i] = 0; else if(i < clocks.length) clocks2[i] = clocks[i]; } clocks2[incrementClock]++; nextClocks.add(clocks2); Attribute attr = null; if(attr()==null) attr = new Attribute(); else{ IAttribute at = attr(); if(at instanceof Attribute) attr = ((Attribute)at).cp(); else attr = new Attribute(at); } nextAttr.add(attr); return attr; } void generate(){ if(nextOrient==null || nextOrient.size()==0){ isStopped=true; return; } for(int i=0; i < nextOrient.size(); i++){ Orientation orient2 = nextOrient.get(i); if(i > 0 || orient2.translate!=null){ if(orient2.translate!=null){ // jump IVec pos2 = pos.cp(orient2.translate); orient2.translate = null; // jump happens only once new ClockStackAgent(pos2, orient2, nextClocks.get(i), null).attr(nextAttr.get(i)); if(i==0) isStopped=true; } else{ new ClockStackAgent(pos2(), orient2, nextClocks.get(i), this).attr(nextAttr.get(i)); } } else{ if(pts==null){ pts = new ArrayList< IVec >(); bounds = new IBounds(pos); bounds.compare(pos2); } if(pts.size() > 1 && pts.get(pts.size()-1).isOnLine(pos, pts.get(pts.size()-2))){ pts.set(pts.size()-1, pos); } else{ pts.add(pos); } // past points prevPos = pos.cp(); pos = pos2(); prevOrient = orient.cp(); orient = orient2; clocks = nextClocks.get(i); attr(nextAttr.get(i)); bounds.compare(pos2); // next pointy delayCount=0; } } pos2 = null; // reset pos2 nextOrient=null; nextClocks=null; nextAttr=null; } IVec dir(){ return orient.dir(); } IVec front(){ return orient.front(); } IVec back(){ return orient.back(); } IVec nml(){ return orient.nml(); } IVec up(){ return orient.up(); } IVec down(){ return orient.down(); } IVec right(){ return orient.right(); } IVec left(){ return orient.left(); } IVec side(){ return orient.side(); } IVec prevDir(){ return prevOrient==null?null:prevOrient().dir(); } IVec prevFront(){ return prevOrient==null?null:prevOrient().front(); } IVec prevBack(){ return prevOrient==null?null:prevOrient().back(); } IVec prevNml(){ return prevOrient==null?null:prevOrient().nml().len(prevOrient.dir().len()); } IVec prevUp(){ return prevOrient==null?null:prevOrient().up().len(prevOrient.dir().len()); } IVec prevDown(){ return prevOrient==null?null:prevOrient().down().len(prevOrient.dir().len()); } IVec prevRight(){ return prevOrient==null?null:prevOrient().right().len(prevOrient.dir().len()); } IVec prevLeft(){ return prevOrient==null?null:prevOrient().left().len(prevOrient.dir().len()); } IVec prevSide(){ return prevOrient==null?null:prevOrient().side().len(prevOrient.dir().len()); } // transformation methods Orientation rot(double angle){ return orient.cp().rot(angle); } Orientation rot(IVec axis, double angle){ return orient.cp().rot(axis,angle); } Orientation pitch(double angle){ return orient.cp().pitch(angle); } Orientation yaw(double angle){ return orient.cp().yaw(angle); } Orientation roll(double angle){ return orient.cp().roll(angle); } Orientation mul(double factor){ return orient.cp().mul(factor); } Orientation div(double factor){ return orient.cp().div(factor); } Orientation ref(IVec axis){ return orient.cp().ref(axis); } Orientation flip(){ return orient.cp().flip(); } Orientation flipNml(){ return orient.cp().flipNml(); } Orientation flipSide(){ return orient.cp().flipSide(); } Orientation jump(IVec move){ return orient.cp().jump(move); } Orientation jump(double x, double y, double z){ return orient.cp().jump(new IVec(x,y,z)); } Orientation jump(Orientation or){ return orient.cp().jump(or.dir); } Orientation jump(double factor){return orient.cp().jump(orient.dir.cp().mul(factor)); } Orientation jump(){ return orient.cp().jump(orient.dir); } void update(){ if(isStopped){ return; } if(attr()==null || ((Attribute)attr()).delay <= delayCount){ if(isColliding){ if(attr()==null && time()==0 || ((Attribute)attr()).delay==time()){ del(); } else isStopped=true; if(addGeometry) makeGeometry(); // last one before disappearing return; } pos2 = pos2(); rules(); makeGeometry(); // make geometry generate(); delayCount=0; } else{ delayCount++; } } IPoint makePoint(){ return new IPoint(pos.cp()).attr(attr().cp()); } ICurve makeLine(){ return new ICurve(pos, pos2).attr(attr()); } ISurface makeSurface(){ IVec[][] pts = new IVec[2][2]; double len = orient.dir().len()/2; IVec side = right().cp().len(len); pts[0][0] = pos.cp().add(side); pts[0][1] = pos.cp().sub(side); pts[1][0] = pos2.cp().add(side); pts[1][1] = pos2.cp().sub(side); return new ISurface(pts).attr(attr()); } IBox makeBox(){ IVec[][][] pts = new IVec[2][2][2]; IVec p2 = pos.cp(); IVec p1 = p2.dif(orient.front()); IVec uvec2 = orient.right();; IVec vvec2 = orient.front(); IVec wvec2 = orient.up(); IVec uvec1 = uvec2; IVec vvec1 = vvec2; IVec wvec1 = wvec2; if(prevOrient != null && !((Attribute)attr()).noDeform){ p1 = prevPos; uvec1 = prevOrient.right(); vvec1 = prevOrient.front(); wvec1 = prevOrient.up(); } pts[0][0][1] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5); pts[1][0][1] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5); pts[0][0][0] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5).add(wvec1); pts[1][0][0] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5).add(wvec1); pts[0][1][1] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5); pts[1][1][1] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5); pts[0][1][0] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5).add(wvec2); pts[1][1][0] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5).add(wvec2); return (IBox)new IBox(pts).attr(attr()); } void makeGeometry(){ if(attr()!=null && ((Attribute)attr()).noGeometry) return; //makePoint(); //makeLine(); //makeSurface(); makeBox(); } void rules(){ if(clock(1) < 1){ if(clock(0) < 8){ next(0).module(0); if(clock(0)==0 && clock(2)==0 && clock(3) < clock(4)){ next(jump(up().add(back())).rot(PI/40), 3).module(1); if(clock(3)==0){ if(clock(5)==0){ if(IRand.pct(20) && clock(6) < 3 && clock(4) > 3){ next(jump(side()).rot(-PI/2), 5).module(1); } } else if(clock(5)==1){ if(IRand.pct(20) && clock(6) < 2 && clock(4) > 3){ next(jump(left()).rot(PI/2), 6).module(1); } } } } } else{ next(rot(PI/2),1).module(2); if(clock(3)==0 && clock(2)==0){ if(clock(5)==0){ if(IRand.pct(95) || clock(6)==0){ next(jump(right()).rot(PI/10), 4).module(1); } } else{ if(IRand.pct(95) || clock(6)==0){ next(jump(right()).rot(-PI/6), 4).module(1); } } } } } else{ if(clock(0) < 4){ next(0).module(0); } else{ next(rot(PI/2),2).module(2); } } } }
void rules(){ if(clock(1) < 1){ if(clock(0) < 8){ next(0); } else{ next(rot(PI/2),1); } } else{ if(clock(0) < 4){ next(0); } else{ next(rot(PI/2),2); } } }
void rules(){ if(clock(1) < 1){ if(clock(0) < 8){ next(0); if(clock(0)==0 && clock(2)==0 && clock(3) < 8){ next(jump(up().add(back())), 3); } } else{ next(rot(PI/2),1); } } else{ if(clock(0) < 4){ next(0); } else{ next(rot(PI/2),2); } } }
void rules(){ if(clock(1) < 1){ if(clock(0) < 8){ next(0); if(clock(0)==0 && clock(2)==0 && clock(3) < 8){ next(jump(up().add(back())), 3); } } else{ next(rot(PI/2), 1); if(clock(3)==0 && clock(2)==0){ next(4); } } } else{ if(clock(0) < 4){ next(0); } else{ next(rot(PI/2), 2); } } }
void rules(){ if(clock(1) < 1){ if(clock(0) < 8){ next(0); if(clock(0)==0 && clock(2)==0 && clock(3) < 8){ next(jump(up().add(back())), 3); } } else{ next(rot(PI/2), 1); if(clock(3)==0 && clock(2)==0){ next(jump(right()), 4); } } } else{ if(clock(0) < 4){ next(0); } else{ next(rot(PI/2), 2); } } }
void rules(){ if(clock(1) < 1){ if(clock(0) < 8){ next(0); if(clock(0)==0 && clock(2)==0 && clock(3) < 8){ next(jump(up().add(back())), 3); } } else{ next(rot(PI/2), 1); if(clock(3)==0 && clock(2)==0){ next(jump(right()).rot(PI/10), 4); } } } else{ if(clock(0) < 4){ next(0); } else{ next(rot(PI/2), 2); } } }
void rules(){ if(clock(1) < 1){ if(clock(0) < 8){ next(0); if(clock(0)==0 && clock(2)==0 && clock(3) < 8){ next(jump(up().add(back())), 3); if(clock(3)==0){ if(clock(5)==0){ if((clock(4)==15 || clock(4)==6) && clock(6) < 2){ next(jump(side()).rot(-PI/2), 5); } } else if(clock(5)==1){ if(clock(4)==4 && clock(6) < 1){ next(jump(left()).rot(PI/2), 6); } } } } } else{ next(rot(PI/2), 1); if(clock(3)==0 && clock(2)==0){ if(clock(5)==0){ next(jump(right()).rot(PI/10), 4); } else{ next(jump(right()).rot(-PI/6), 4); } } } } else{ if(clock(0) < 4){ next(0); } else{ next(rot(PI/2),2); } } }
import processing.opengl.*; import igeo.*; IGeometry[][] modules; ISurface ground; ICurve siteBoundary; ICurve pondBoundary; void setup(){ size(960, 540, IG.GL); IConfig.syncDrawAndDynamics=true; modules = new IGeometry[2][]; IG.open("module1.3dm"); modules[0] = IG.geometries(); IG.delAll(); IG.open("module2.3dm"); modules[1] = IG.geometries(); IG.delAll(); IG.open("ground.3dm"); ground = IG.surface(0); siteBoundary = IG.layer("site_boundary").curve(0); // comment out this line if you don't need the site boundary collision detection pondBoundary = IG.layer("pond_boundary").curve(0); // comment out this line if you don't need the pond boundary collision detection if(siteBoundary !=null) siteBoundary.del(); if(pondBoundary !=null) pondBoundary.del(); IG.showTime(); IRand.init(5); // initialize random number seed IG.bg(1.0); IG.pers(); //IG.pers(new IVec(200,200,200), new IVec(-1,-1,-1)); // when you need to set camera position and direction IG.fill(); //new FocusAgent(); // to automatically focus on all geometry during animatin capture new ClockStackAgent(new IVec(-60,10,58), new IVec(5,0,0), new IVec(0,0,5), null).module(1); } /* // to capture animation frames int frameCount=0; void draw(){ if(frameCount < 1000){ saveFrame("capture/frame_#####.jpg"); } else{ System.exit(1); } frameCount++; } // to focus on any generated objects all the time class FocusAgent extends IAgent{ void update(){ IG.focus(); } } */ class Orientation{ IVec dir, nml; // as xyz coordinates system, dir corresponds to Y and nml corresponds to Z boolean righthand; // right hand coordinates system or not double sideLen; // length of side vector. Front length is length of dir. Up length is length of nml. IVec translate; // just to implement jumping behavior Orientation(IVec d, IVec n, double sideLength, boolean righthandsys){ dir=d; nml=n; sideLen=sideLength; righthand=righthandsys; } Orientation(IVec d, IVec n, boolean righthandsys){ dir=d; nml=n; sideLen=dir.len(); righthand=righthandsys; } Orientation(IVec d, IVec n, IVec side){ dir=d; nml=n; sideLen=side.len(); righthand= dir.cross(nml).dot(side)>0; } // only length and righthand is set by side, not direction. Orientation(IVec d, IVec n, double sideLength){ this(d,n,sideLength,true); } Orientation(IVec d, IVec n){ this(d,n,true); } Orientation(Orientation o){ dir=o.dir.cp(); nml=o.nml.cp(); sideLen=o.sideLen; righthand=o.righthand; } Orientation cp(){ return new Orientation(this); } boolean eq(Orientation o){ return o.righthand==righthand && o.dir.eq(dir) && o.nml.eq(nml) && Math.abs(sideLen-o.sideLen) < IConfig.tolerance && (o.translate==null && translate==null || o.translate!=null&&translate!=null&&o.translate.eq(translate)); } boolean isParallel(Orientation o){ return o.righthand==righthand && o.dir.isParallel(dir) && o.nml.isParallel(nml); } IVec dir(){ return dir.cp(); } IVec front(){ return dir(); } IVec back(){ return dir().neg(); } IVec nml(){ return nml.cp(); } IVec up(){ return nml(); } IVec down(){ return nml().neg(); } IVec side(){ if(righthand){ return dir.cross(nml).len(sideLen); }else{ return nml.cross(dir).len(sideLen); } } IVec right(){ return side(); } IVec left(){ return side().neg(); } double sideLen(){ return sideLen; } double frontLen(){ return dir.len(); } double upLen(){ return nml.len(); } Orientation setSideLen(double len){ sideLen=len; return this; } Orientation setFrontLen(double len){ dir.len(len); return this; } Orientation setUpLen(double len){ nml.len(len); return this; } Orientation rot(double ang){ dir.rot(nml, ang); return this; } Orientation rot(IVec ax, double ang){ dir.rot(ax,ang); nml.rot(ax,ang); return this; } Orientation pitch(double ang){ IVec ax = dir.cross(nml); dir.rot(ax, ang); nml.rot(ax, ang); return this; } Orientation yaw(double ang){ dir.rot(nml, ang); return this; } Orientation roll(double ang){ nml.rot(dir, ang); return this; } Orientation ref(IVec refNml){ dir.ref(refNml); nml.ref(refNml); righthand = !righthand; return this; } Orientation flip(){ dir.flip(); righthand = !righthand; return this; }// flip front/back Orientation flipNml(){ nml.flip(); righthand = !righthand; return this; }// flip up/down Orientation flipSide(){ righthand = !righthand; return this; }// flip left/right Orientation mul(double v){ dir.mul(v); nml.mul(v); sideLen*=v; return this; } Orientation div(double v){ dir.div(v); nml.div(v); sideLen/=v; return this; } Orientation scaleFront(double v){ dir.mul(v); return this; } Orientation scaleUp(double v){ nml.mul(v); return this; } Orientation scaleSide(double v){ sideLen*=v; return this; } Orientation add(Orientation o){ dir.add(o.dir); nml.add(o.nml()); return this; } Orientation add(Orientation o, double f){ dir.add(o.dir, f); nml.add(o.nml(), f); return this; } Orientation sum(Orientation o, double f){ dir.mul(1-f).add(o.dir, f); nml.mul(1-f).add(o.nml(), f); sideLen=sideLen*(1-f)+o.sideLen*f; return this; } Orientation mid(Orientation o){ return sum(o,0.5); } Orientation translate(IVec t){ return jump(t); } Orientation jump(IVec move){ translate=move; return this; } Orientation jump(double x, double y, double z){ return jump(new IVec(x,y,z)); } Orientation jump(Orientation or){ return jump(or.dir.cp()); } Orientation jump(double factor){ return jump(dir.cp().mul(factor)); } Orientation jump(){ return jump(dir.cp()); } } class Attribute extends IAttribute{ int delay=0; boolean noCollision=false; boolean noGeometry=false; boolean noDeform=true; int moduleIndex=-1; // default is no module Attribute(){ super(); } Attribute(IAttribute at){ super(at); } Attribute(Attribute at){ super(at); delay = at.delay; noCollision = at.noCollision; noGeometry = at.noGeometry; moduleIndex=at.moduleIndex; } Attribute cp(){ return new Attribute(this); } Attribute delay(int d){ delay = d; return this; } Attribute noCollision(){ noCollision=true; return this; } Attribute collision(){ noCollision=false; return this; } Attribute noGeometry(){ noGeometry=true; return this; } Attribute geometry(){ noGeometry=false; return this; } Attribute noDeform(){ noDeform=true; return this; } Attribute deform(){ noDeform=false; return this; } Attribute module(int module){ moduleIndex=module; return this; } int module(){ return moduleIndex; } Attribute layer(String layerName){ super.layer = IG.layer(layerName); return this; } } class ClockStackAgent extends IAgent{ final double threshold = 0.5; //collision threshold IVec pos, pos2, prevPos; Orientation orient, prevOrient; int[] clocks; boolean isColliding = false, isStopped = false; boolean addGeometry=false; ArrayList< IVec > pts; ArrayList< Orientation > nextOrient; ArrayList< int[] > nextClocks; ArrayList< Attribute > nextAttr; IBounds bounds; int moveCount; int delayCount; ClockStackAgent parent; ClockStackAgent(IVec p, Orientation o, int[] clok, ClockStackAgent parentAgent){ pos = p; orient = o; clocks = clok; parent = parentAgent; if(parent!=null){ prevPos=parent.pos.cp(); prevOrient=parent.orient.cp(); } moveCount=0; delayCount=0; } ClockStackAgent(IVec p, IVec d, IVec n, int[] clok, ClockStackAgent parentAgent){ this(p, !d.isParallel(n)?new Orientation(d,d.cross(n).icross(d).len(n.len())): n.isParallel(IG.zaxis)?new Orientation(d,d.cross(0,1,0).icross(d).len(n.len())):new Orientation(d,d.cross(0,0,1).len(n.len())), clok, parentAgent); } ClockStackAgent(IVec p, IVec d, IVec n, ClockStackAgent parentAgent){ this(p,d,n,new int[0], parentAgent); } ClockStackAgent(IVec p, IVec d, ClockStackAgent parentAgent){ this(p,d,new IVec(0,0,1),null, parentAgent); } IVec pos2(){ if(pos2==null) pos2 = pos.cp(orient.dir); return pos2; } IVec prevPos(){ return prevPos; } Orientation prevOrient(){ return prevOrient; } Attribute defaultAttribute(){ return new Attribute(); } ClockStackAgent module(int module){ IAttribute attr = attr(); if(attr==null){ attr = defaultAttribute(); attr(attr); } ((Attribute)attr).module(module); return this; } ClockStackAgent delay(int d){ IAttribute attr = attr(); if(attr==null){ attr = defaultAttribute(); attr(attr); } ((Attribute)attr).delay(d); return this; } ClockStackAgent noCollision(){ IAttribute attr = attr(); if(attr==null){ attr = defaultAttribute(); attr(attr); } ((Attribute)attr).noCollision(); return this; } ClockStackAgent collision(){ IAttribute attr = attr(); if(attr==null){ attr = defaultAttribute(); attr(attr); } ((Attribute)attr).collision(); return this; } boolean isDelayed(){ if(attr()==null) return false; if(((Attribute)attr()).delay <= delayCount) return true; return false; } int delayedTime(){ if(attr()==null) return time(); return delayCount - ((Attribute)attr()).delay; } boolean isCollidable(){ if(attr()==null) return true; if(((Attribute)attr()).noCollision) return false; if(((Attribute)attr()).delay <= delayCount) return true; return false; } void interact(ArrayList< IDynamics > agents){ if(threshold > 0 && !isStopped && isCollidable()){ IVec pt2 = pos2(); if(ground!=null){ // collision to the ground IVec gpt = ground.closePt(pt2); if(pt2.dif(gpt).z < 0){ // below the ground isColliding = true; } } if(!isColliding && siteBoundary!=null){ // collision to the site boundary // check it by polyline of the control points if(!pos2.isInside2d(siteBoundary.cps())){ isColliding = true; } } if(!isColliding && pondBoundary!=null){ // collision to the pond boundary if(pos2.isInside2d(pondBoundary.cps())){ isColliding = true; } } for(int i=0; i < agents.size() && !isColliding; i++){ if(agents.get(i) instanceof ClockStackAgent){ ClockStackAgent a = (ClockStackAgent)agents.get(i); if(a==this){ // check self collision for(int j=0; pts!=null && j < pts.size()-2 && !isColliding; j++){ // exclude last segment if(IVec.isSegCloserThan(pos, pt2, pts.get(j), pts.get(j+1), threshold)){ isColliding = true; if(pos.distToSegment(pts.get(j), pts.get(j+1))>threshold){ addGeometry=true; } } } } else if(a.delayedTime() >= 0){ // a!=this if(a.bounds!=null && bounds!=null){ IBounds newbounds = bounds.cp(); newbounds.compare(pt2); if(!newbounds.isCloserThan(a.bounds,threshold)){ continue; } } IVec apt2 = a.pos2(); if(!a.isColliding && IVec.isSegCloserThan(pos, pt2, a.pos, apt2, threshold) && !(pos.eq(a.pos) && !pt2.eq(apt2))/*not sibling*/ && !(a.time() > 0&&apt2.eq(pos))/*not parent*/ ){ isColliding = true; if(pos.distToSegment(a.pos, apt2)>threshold){ addGeometry=true; } } for(int j=0; a.pts!=null && j < a.pts.size() && !isColliding; j++){ IVec apt3 = a.pos; if(j < a.pts.size()-1) apt3 = a.pts.get(j+1); if(IVec.isSegCloserThan(pos, pt2, a.pts.get(j), apt3, threshold) && (!pos.eq(a.pos) || pt2.eq(apt2) || j < a.pts.size()-1) ){ if(delayedTime() > 0 || !pos.isOnSegment(a.pts.get(j),apt3)){ // exclude if it came from that line isColliding = true; if(pos.distToSegment(a.pts.get(j), apt3)>threshold){ addGeometry=true; } } } } } } } } } int clock(int i){ if(i >= clocks.length) return 0; return clocks[i]; } Attribute next(int incrementClock){ return next(orient, incrementClock); } Attribute next(Orientation o, int incrementClock){ if(nextOrient==null){ nextOrient = new ArrayList< Orientation >(); nextClocks = new ArrayList< int[] >(); nextAttr = new ArrayList< Attribute >(); } nextOrient.add(o); int[] clocks2 = new int[incrementClock+1 > clocks.length?incrementClock+1:clocks.length]; for(int i=0; i < clocks2.length; i++){ if(i < incrementClock) clocks2[i] = 0; else if(i < clocks.length) clocks2[i] = clocks[i]; } clocks2[incrementClock]++; nextClocks.add(clocks2); Attribute attr = null; if(attr()==null) attr = new Attribute(); else{ IAttribute at = attr(); if(at instanceof Attribute) attr = ((Attribute)at).cp(); else attr = new Attribute(at); } nextAttr.add(attr); return attr; } void generate(){ if(nextOrient==null || nextOrient.size()==0){ isStopped=true; return; } for(int i=0; i < nextOrient.size(); i++){ Orientation orient2 = nextOrient.get(i); if(i > 0 || orient2.translate!=null){ if(orient2.translate!=null){ // jump IVec pos2 = pos.cp(orient2.translate); orient2.translate = null; // jump happens only once new ClockStackAgent(pos2, orient2, nextClocks.get(i), null).attr(nextAttr.get(i)); if(i==0) isStopped=true; } else{ new ClockStackAgent(pos2(), orient2, nextClocks.get(i), this).attr(nextAttr.get(i)); } } else{ if(pts==null){ pts = new ArrayList< IVec >(); bounds = new IBounds(pos); bounds.compare(pos2); } if(pts.size() > 1 && pts.get(pts.size()-1).isOnLine(pos, pts.get(pts.size()-2))){ pts.set(pts.size()-1, pos); } else{ pts.add(pos); } // past points prevPos = pos.cp(); pos = pos2(); prevOrient = orient.cp(); orient = orient2; clocks = nextClocks.get(i); attr(nextAttr.get(i)); bounds.compare(pos2); // next point moveCount++; delayCount=0; } } pos2 = null; // reset pos2 nextOrient=null; nextClocks=null; nextAttr=null; } IVec dir(){ return orient.dir(); } IVec front(){ return orient.front(); } IVec back(){ return orient.back(); } IVec nml(){ return orient.nml(); } IVec up(){ return orient.up(); } IVec down(){ return orient.down(); } IVec right(){ return orient.right(); } IVec left(){ return orient.left(); } IVec side(){ return orient.side(); } IVec prevDir(){ return prevOrient==null?null:prevOrient().dir(); } IVec prevFront(){ return prevOrient==null?null:prevOrient().front(); } IVec prevBack(){ return prevOrient==null?null:prevOrient().back(); } IVec prevNml(){ return prevOrient==null?null:prevOrient().nml().len(prevOrient.dir().len()); } IVec prevUp(){ return prevOrient==null?null:prevOrient().up().len(prevOrient.dir().len()); } IVec prevDown(){ return prevOrient==null?null:prevOrient().down().len(prevOrient.dir().len()); } IVec prevRight(){ return prevOrient==null?null:prevOrient().right().len(prevOrient.dir().len()); } IVec prevLeft(){ return prevOrient==null?null:prevOrient().left().len(prevOrient.dir().len()); } IVec prevSide(){ return prevOrient==null?null:prevOrient().side().len(prevOrient.dir().len()); } // transformation methods Orientation rot(double angle){ return orient.cp().rot(angle); } Orientation rot(IVec axis, double angle){ return orient.cp().rot(axis,angle); } Orientation pitch(double angle){ return orient.cp().pitch(angle); } Orientation yaw(double angle){ return orient.cp().yaw(angle); } Orientation roll(double angle){ return orient.cp().roll(angle); } Orientation mul(double factor){ return orient.cp().mul(factor); } Orientation div(double factor){ return orient.cp().div(factor); } Orientation ref(IVec axis){ return orient.cp().ref(axis); } Orientation flip(){ return orient.cp().flip(); } Orientation flipNml(){ return orient.cp().flipNml(); } Orientation flipSide(){ return orient.cp().flipSide(); } Orientation jump(IVec move){ return orient.cp().jump(move); } Orientation jump(double x, double y, double z){ return orient.cp().jump(new IVec(x,y,z)); } Orientation jump(Orientation or){ return orient.cp().jump(or.dir); } Orientation jump(double factor){return orient.cp().jump(orient.dir.cp().mul(factor)); } Orientation jump(){ return orient.cp().jump(orient.dir); } void update(){ if(isStopped){ return; } if(attr()==null || ((Attribute)attr()).delay <= delayCount){ if(isColliding){ if(attr()==null && time()==0 || ((Attribute)attr()).delay==time()){ del(); } else isStopped=true; if(addGeometry) makeGeometry(); // last one before disappearing return; } pos2 = pos2(); rules(); makeGeometry(); // make geometry generate(); delayCount=0; } else{ delayCount++; } } IPoint makePoint(){ return new IPoint(pos.cp()).attr(attr().cp()); } ICurve makeLine(){ return new ICurve(pos, pos2).attr(attr()); } ISurface makeSurface(){ IVec[][] pts = new IVec[2][2]; double len = orient.dir().len()/2; IVec side = right().cp().len(len); pts[0][0] = pos.cp().add(side); pts[0][1] = pos.cp().sub(side); pts[1][0] = pos2.cp().add(side); pts[1][1] = pos2.cp().sub(side); return new ISurface(pts).attr(attr()); } IBox makeBox(){ IVec[][][] pts = new IVec[2][2][2]; IVec p2 = pos.cp(); IVec p1 = p2.dif(orient.front()); IVec uvec2 = orient.right();; IVec vvec2 = orient.front(); IVec wvec2 = orient.up(); IVec uvec1 = uvec2; IVec vvec1 = vvec2; IVec wvec1 = wvec2; if(prevOrient != null && !((Attribute)attr()).noDeform){ p1 = prevPos; uvec1 = prevOrient.right(); vvec1 = prevOrient.front(); wvec1 = prevOrient.up(); } pts[0][0][1] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5); pts[1][0][1] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5); pts[0][0][0] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5).add(wvec1); pts[1][0][0] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5).add(wvec1); pts[0][1][1] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5); pts[1][1][1] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5); pts[0][1][0] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5).add(wvec2); pts[1][1][0] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5).add(wvec2); return (IBox)new IBox(pts).attr(attr()); } ISphere makeSphere(){ IVec mid = pos.mid(pos2); double len = pos.dist(pos2); return (ISphere)new ISphere(mid, len/2).attr(attr()); } ICurve makeTangentCurve(){ if(prevPos() != null){ IVec m1 = prevPos().mid(pos); IVec m2 = pos.mid(pos2); double len = orient.dir().len()/2; if(!prevOrient().dir.isParallel(orient.dir) || !prevOrient().nml.isParallel(orient.nml)){ IVec[] pts = new IVec[3]; Orientation ori = orient.cp().mid(prevOrient()); pts[0] = m1; pts[1] = pos; pts[2] = m2; return new ICurve(pts, 2).attr(attr()); } return new ICurve(m1, m2).attr(attr()); } return null; } ISurface makeTangentSurface(){ if(prevPos() != null){ IVec m1 = prevPos().mid(pos); IVec m2 = pos.mid(pos2); double len = orient.dir().len()/2; if(!prevOrient().dir.isParallel(orient.dir) || !prevOrient().nml.isParallel(orient.nml)){ IVec[][] pts = new IVec[3][2]; Orientation ori = orient.cp().mid(prevOrient()); pts[0][0] = m1.cp(prevRight().cp().len(len)); pts[1][0] = pos.cp(ori.right().cp().len(len)); pts[2][0] = m2.cp(right().cp().len(len)); pts[0][1] = m1.cp(prevLeft().cp().len(len)); pts[1][1] = pos.cp(ori.left().cp().len(len)); pts[2][1] = m2.cp(left().cp().len(len)); return new ISurface(pts, 2, 1).attr(attr()); } return new ISurface(m1.cp(prevRight().cp().len(len)), m1.cp(prevLeft().cp().len(len)), m2.cp(left().cp().len(len)), m2.cp(right().cp().len(len))).attr(attr()); } return null; } ICurve makeCSLine(){ IVec[] pts = new IVec[3]; double len = pos2.dist(pos)/2; pts[0] = orient.nml().cp().len(len).add(pos2); pts[1] = pos2.cp(); pts[2] = pos2.cp(orient.front().cp().len(len)); return new ICurve(pts).attr(attr()); } IBrep makeTetrahedron(){ return makeTetrahedron(pos2, orient.front(), orient.nml(), orient.dir.len()).attr(attr()); } IBrep makeTetrahedron(IVec center, IVec front, IVec nml, double size){ double len1 = size/4.082*6.124; double len2 = size/4.082*2.041; double len3 = size/4.082*5.774; IVec dir1 = front.cp().len(len1); IVec dir2 = dir1.cp().flip().len(len2); IVec dir3 = nml.cp().len(len3).add(dir2); IVec dir4 = dir3.cp().rot(dir1, PI/3*2); IVec dir5 = dir3.cp().rot(dir1, PI/3*4); IVec p1 = center.cp(dir1); IVec p2 = center.cp(dir3); IVec p3 = center.cp(dir4); IVec p4 = center.cp(dir5); ISurfaceGeo[] srf = new ISurfaceGeo[4]; srf[0] = new ISurfaceGeo(p1,p2,p3); srf[1] = new ISurfaceGeo(p2,p3,p4); srf[2] = new ISurfaceGeo(p1,p3,p4); srf[3] = new ISurfaceGeo(p1,p2,p4); return new IBrep(srf); } IGeometry[] getModuleGeometry(int idx){ if(modules==null){ IG.err("modules is null."); return null; } if(idx < 0 || idx >= modules.length){ IG.err("module index ("+idx+") is out of boundary. "); return null; } return modules[idx]; } IGeometry[] deformModuleGeometry(int idx){ IGeometry[] geom = getModuleGeometry(idx); if(geom!=null){ IGeometry[] geom2 = new IGeometry[geom.length]; for(int i=0; i < geom.length; i++){ geom2[i] = geom[i].cp(); IVec[][][] target = new IVec[2][2][2]; IVec p2 = pos.cp(); IVec p1 = p2.dif(orient.front()); IVec uvec2 = orient.right();; IVec vvec2 = orient.front(); IVec wvec2 = orient.up(); IVec uvec1 = uvec2; IVec vvec1 = vvec2; IVec wvec1 = wvec2; if(prevOrient != null && !((Attribute)attr()).noDeform){ p1 = prevPos; uvec1 = prevOrient.right(); vvec1 = prevOrient.front(); wvec1 = prevOrient.up(); } target[0][0][0] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5); target[1][0][0] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5); target[0][0][1] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5).add(wvec1); target[1][0][1] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5).add(wvec1); target[0][1][0] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5); target[1][1][0] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5); target[0][1][1] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5).add(wvec2); target[1][1][1] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5).add(wvec2); deform(geom2[i], target); geom2[i].layer(geom[i].layer().name()); String name = String.valueOf(this.hashCode()); geom2[i].name(String.valueOf(this.hashCode())+"_"+String.valueOf(moveCount)); // set object hash code number and moveCount as module geometry name } return geom2; } return null; } IVec deform(IVec uvw, IVec[][][] targetBox){ IVec ret = new IVec(); ret.add(targetBox[0][0][0], (1-uvw.x)*(1-uvw.y)*(1-uvw.z)); ret.add(targetBox[1][0][0], (uvw.x)*(1-uvw.y)*(1-uvw.z)); ret.add(targetBox[0][1][0], (1-uvw.x)*(uvw.y)*(1-uvw.z)); ret.add(targetBox[1][1][0], (uvw.x)*(uvw.y)*(1-uvw.z)); ret.add(targetBox[0][0][1], (1-uvw.x)*(1-uvw.y)*(uvw.z)); ret.add(targetBox[1][0][1], (uvw.x)*(1-uvw.y)*(uvw.z)); ret.add(targetBox[0][1][1], (1-uvw.x)*(uvw.y)*(uvw.z)); ret.add(targetBox[1][1][1], (uvw.x)*(uvw.y)*(uvw.z)); return ret; } IGeometry[] deform(IGeometry[] geom,IVec[][][] targetBox){ for(int i=0; i < geom.length; i++){ geom[i] = deform(geom[i], targetBox); } return geom; } IGeometry deform(IGeometry geom, IVec[][][] targetBox){ if(geom instanceof IPoint){ IPoint point = (IPoint)geom; point.pos.set(deform(point.pos, targetBox)); point.updateGraphic(); } else if(geom instanceof ICurve){ ICurve curve = (ICurve)geom; IVecI[] cps = curve.cps(); for(int i=0; i < cps.length&&cps[0]!=cps[cps.length-1] || i < cps.length-1; i++){ cps[i].set(deform(cps[i].get(), targetBox)); } curve.updateGraphic(); } else if(geom instanceof ISurface){ ISurface surface = (ISurface)geom; IVecI[][] cps = surface.cps(); for(int i=0; i < cps.length; i++){ for(int j=0; j < cps[i].length; j++){ cps[i][j].set(deform(cps[i][j].get(), targetBox)); } } surface.updateGraphic(); } else if(geom instanceof IMesh){ IMesh mesh = (IMesh)geom; for(int i=0; i < mesh.vertexNum(); i++){ mesh.vertex(i).pos().set(deform(mesh.vertex(i).pos().get(), targetBox)); } mesh.updateGraphic(); } else if(geom instanceof IBrep){ IBrep brep = (IBrep)geom; for(int i=0; i < brep.surfaceNum(); i++){ IVecI[][] cps = brep.surface(i).cps(); for(int j=0; j < cps.length; j++){ for(int k=0; k < cps[j].length; k++){ cps[j][k].set(deform(cps[j][k].get(), targetBox)); } } } brep.updateGraphic(); } return geom; } IGeometry[] makeModule(){ int module = ((Attribute)attr()).module(); if(module>=0){ return deformModuleGeometry(module); } return null; } void makeGeometry(){ if(attr()!=null && ((Attribute)attr()).noGeometry) return; //makePoint(); //makeLine(); //makeSurface(); //makeBox(); //makeSphere(); //makeTangentCurve(); //makeTangentSurface(); //makeCSLine(); //makeTetrahedron(); makeModule(); } void rules(){ int maxClock6 = 7; if(clock(6)==maxClock6) return; if(clock(5)==0){ // cube going up if(clock(2)==0){ if(clock(0)==8){ next(rot(PI/2), 1).module(1); } else{ if(clock(0)==0){ if(clock(4)==0){ next(jump(up()), 2).module(0); } } next(rot(0),0).module(0).deform(); } } else{ if(clock(2)==7){ next(jump(up()), 4).module(1); } else{ next(jump(up()), 2).module(1); if(IRand.pct(10)){ if(clock(6) < maxClock6){ next(rot(PI/3), 5).module(1); // branch a new cube going down } } } } } else{ // cube going down if(clock(2)==0){ if(clock(0)==8){ next(rot(PI/2), 1).module(1); } else{ if(clock(0)==0){ if(clock(4)==0){ next(jump(down()), 2).module(1); } } next(rot(0), 0).module(0).deform(); } } else{ if(clock(2)==7){ next(jump(down()), 4).module(0); } else{ next(jump(down()), 2).module(1); if(IRand.pct(10)){ if(clock(6) < maxClock6){ next(rot(PI/3), 6).module(1); // branch a new cube going up } } } } } } }