Tutorials | (back to the list of tutorials) |
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IConfig.syncDrawAndDynamics=true; IG.bg(1.0); new ClockStackAgent(new IVec(0,0,0), new IVec(10,0,0), new IVec(0,0,1)).clr(0); } 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 Orientation(IVec d, IVec n, boolean righthandsys){ dir=d; nml=n; righthand=righthandsys; } Orientation(IVec d, IVec n){ this(d,n,true); } Orientation(Orientation o){ dir=o.dir.cp(); nml=o.nml.cp(); righthand=o.righthand; } Orientation cp(){ return new Orientation(this); } 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); }else{ return nml.cross(dir); } } IVec right(){ return side(); } IVec left(){ if(righthand){ return nml.cross(dir); }else{ return dir.cross(nml); } } 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); return this; } Orientation div(double v){ dir.div(v); return this; } } class ClockStackAgent extends IAgent{ final double threshold = 1; // collision threshold IVec pos, pos2; Orientation orient; int[] clocks; boolean isColliding = false, isStopped = false; ArrayList< IVec > pts; ArrayList< Orientation > nextOrient; ArrayList< int[] > nextClocks; ArrayList< IAttribute > nextAttr; IBounds bounds; ClockStackAgent(IVec p, Orientation o, int[] clok){ pos = p; orient = o; clocks = clok; } ClockStackAgent(IVec p, IVec d, IVec n, int[] clok){ pos = p; if(d.isParallel(n)){ if(!n.isParallel(IG.zaxis)) n = new IVec(0,0,1); else n = new IVec(0,1,0); } if(d.dot(n)!=0) n = d.cross(n).icross(d); orient = new Orientation(d,n); clocks = clok; } ClockStackAgent(IVec p, IVec d, IVec n){ this(p,d,n,new int[0]); } ClockStackAgent(IVec p, IVec d){ this(p,d,new IVec(0,0,1),null); } IVec pos2(){ if(pos2==null) pos2 = pos.cp(orient.dir); return pos2; } void interact(ArrayList< IDynamics > agents){ if(threshold > 0 && !isStopped){ IVec pt2 = pos2(); 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; } } } else if(a.time() > 0 || !a.isColliding){ // 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(IVec.isSegCloserThan(pos, pt2, a.pos, apt2, threshold) && (!pos.eq(a.pos) || pt2.eq(apt2)) ){ isColliding = 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) ){ isColliding = true; } } } } } } } int clock(int i){ if(i >= clocks.length) return 0; return clocks[i]; } IAttribute next(int incrementClock){ return next(orient, incrementClock); } IAttribute next(Orientation o, int incrementClock){ if(nextOrient==null){ nextOrient = new ArrayList< Orientation >(); nextClocks = new ArrayList< int[] >(); nextAttr = new ArrayList< IAttribute >(); } 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); IAttribute attr = null; if(attr()==null) attr = new IAttribute(); else attr = attr().cp(); // attribute (color) for next agent nextAttr.add(attr); return attr; } void generate(){ if(nextOrient==null || nextOrient.size()==0){ isStopped=true; return; } for(int i=0; i < nextOrient.size(); i++){ if(i > 0){ new ClockStackAgent(pos2(), nextOrient.get(i), nextClocks.get(i)).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 pos = pos2(); orient = nextOrient.get(i); clocks = nextClocks.get(i); attr(nextAttr.get(i)); bounds.compare(pos2); // next point } } 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(); } // 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(); } void update(){ if(isStopped) return; if(isColliding){ if(time()==0) del(); else isStopped=true; return; } pos2 = pos2(); // make geometry ICurve line = new ICurve(pos, pos2).attr(attr()); rules(); generate(); } // update rules void rules(){ // describe update rules here // - clock( x ) : // checks the current clock number at the level x // - next( x ) : // creates next instance with incrementing the clock at level x // - next( transformation method, x ) : // creates next instance after the transfomration with incrementing the clock at level x if(clock(0) == 7) next(rot(-PI/2), 1); next(rot(PI/12), 0); } }
void rules(){ if(clock(0)==9){ if(clock(1)==0){ next(2); } next(rot(PI/2), 1); } else{ next(0); } }
void rules(){ if(clock(0)==9){ if(clock(1)==0){ if(clock(2)==3){ next(rot(-PI/2), 3); } else{ next(2); } } next(rot(PI/2), 1); } else{ next(0); } }
void rules(){ if(clock(0)==9){ if(clock(1)==0){ if(clock(2)==3){ next(rot(-PI/2), 3); } else{ next(2); } } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); } next(rot(PI/2), 1); } else{ next(0); } }
void rules(){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2)==3){ next(rot(-PI/2), 3); } else{ next(2); } } if(clock(4)==3){ if(clock(1)==2 && clock(2)==0 && clock(3)==0){ next(rot(-PI/2), 5).clr(0); } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 5).clr(0); } if(clock(1)==2 && clock(2)==0 && clock(3)==2){ next(rot(-PI/2), 5).clr(0); } } else if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); } next(rot(PI/2), 1); } else{ next(0); } }
void rules(){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2)==3){ next(rot(-PI/2), 3); } else{ next(2); } } if(clock(4)==3){ if(clock(1)==2 && clock(2)==0 && clock(3)==0){ next(rot(-PI/3), 5).clr(0); } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 5).clr(0); } if(clock(1)==2 && clock(2)==0 && clock(3)==2){ next(rot(-PI/3), 5).clr(0); } } else if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); //.clr(0, 0, (clock(4)+1)*0.3); } next(rot(PI/2), 1); } else{ next(0); } }
void rules(){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2)==3){ next(rot(-PI/2), 3); } else{ next(2); } } if(clock(4)==3){ if(clock(1)==2 && clock(2)==0 && clock(3)==0){ next(rot(-PI/2), 5).clr(0); } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 5).clr(0); } if(clock(1)==2 && clock(2)==0 && clock(3)==2){ next(rot(-PI/2), 5).clr(0); } } else if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); //.clr(0, 0, (clock(4)+1)*0.3); } next(rot(PI/2), 1); } else{ next(rot(PI/180*1), 0); } }
void rules(){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2)==3){ next(rot(-PI/2), 3); } else{ next(2); } } if(clock(4)==3){ if(clock(1)==2 && clock(2)==0 && clock(3)==0){ next(rot(-PI/2), 5).clr(0); } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 5).clr(0); } if(clock(1)==2 && clock(2)==0 && clock(3)==2){ next(rot(-PI/2), 5).clr(0); } } else if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); //.clr(0, 0, (clock(4)+1)*0.3); } next(rot(PI/2), 1); } else{ next(rot(PI/180*0.1), 0); } }
void rules(){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2)==3){ next(rot(-PI/2), 3); } else{ next(2); } } if(clock(4) >= IRand.getInt(2, 7)){ if(clock(1)==2 && clock(2)==0 && clock(3)==0){ next(rot(-PI/2), 5); //.clr(IRand.clr()); } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 5); //.clr(IRand.clr()); } if(clock(1)==2 && clock(2)==0 && clock(3)==2){ next(rot(-PI/2), 5); //.clr(IRand.clr()); } } else if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); } next(rot(PI/2), 1); } else{ next(0); } }
void rules(){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2) >= IRand.getInt(3,10)){ next(rot(-PI/2), 3); } else{ next(2); } } if(clock(4) == 3){ if(clock(1)==2 && clock(2)==0 && clock(3)==0){ next(rot(-PI/2), 5); } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 5); } if(clock(1)==2 && clock(2)==0 && clock(3)==2){ next(rot(-PI/2), 5); } } else if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); } next(rot(PI/2), 1); } else{ next(0); } }
void rules(){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2) >= IRand.getInt(3,10)){ next(rot(-PI/2), 3); } else{ next(2); } } if(clock(4) == 3){ if(clock(1)==2 && clock(2)==0 && clock(3)==0){ next(rot(-PI/2+IRand.get(-PI/12,PI/12)), 5); } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 5); } if(clock(1)==2 && clock(2)==0 && clock(3)==2){ next(rot(-PI/2+IRand.get(-PI/12,PI/12)), 5); } } else if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); } next(rot(PI/2), 1); } else{ next(0); } }
void rules(){ if(clock(0)>=IRand.getInt(3,10)){ if(clock(1)==0){ if(clock(2) >= IRand.getInt(1,5)){ next(rot(-PI/2), 3); } else{ next(rot(IRand.get(-0.15, 0.15)), 2); } } if(clock(4) == 3){ if(clock(1)==2 && clock(2)==0 && clock(3)==0){ next(rot(-PI/2), 5); } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 5); } if(clock(1)==2 && clock(2)==0 && clock(3)==2){ next(rot(-PI/2), 5); } } else if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); } next(rot(PI/2), 1); } else{ next(0); } }
void rules(){ if(clock(0)==1){ next(rot(-PI/6), 2); next(rot(PI/3*2), 1); } else{ next(0); } }
void rules(){ if(clock(0)==1){ next(rot(PI/3).flipNml(), 2); next(rot(PI/3*2), 1); } else{ next(0); } }
void rules(){ if(clock(1)==0){ if(clock(0)==5){ next(rot(PI/2), 1); } else{ next(mul(0.99),0); } } else{ if(clock(0)==15){ next(rot(PI/2), 3); } else if(clock(0)==2){ next(rot(-PI/2).flipNml(), 2); next(0); } else{ next(mul(0.99), 0); } } }
void rules(){ if(clock(1)==0){ if(clock(0)==15){ next(rot(PI/3), 1); next(rot(-PI/3), 2); } else{ next(mul(0.99), 0); } } else if(clock(1)==1){ if(clock(0)==5){ next(rot(PI/3), 1); } else{ next(mul(0.99), 0); } } else{ if(clock(0)==5){ next(rot(PI/3), 3); } else{ next(mul(0.99), 0); } } }
void rules(){ if(clock(1)==0){ if(clock(0)==15){ next(rot(PI/3), 1); next(rot(-PI/3), 2); } else{ next(0); } } else if(clock(1)==1){ if(clock(0)==5){ next(rot(PI/3), 1); } else{ next(0); } } else{ if(clock(0)==5){ next(rot(PI/3), 3); } else{ next(mul(0.99), 0); } } }
void rules(){ if(clock(1)==0){ next(rot(-PI/4), 1); next(rot(PI/2).flipNml(), 2); } else{ if(clock(0)==5){ if(IRand.pct(20)){ next(rot(-PI/4).flipNml(), 2); } } else{ next(0); } } }
void rules(){ if(clock(1)==0){ if(clock(0)==5){ next(rot(PI/2), 1); if(clock(2)==3){ if(IRand.pct(70)) next(rot(PI/3),3); if(IRand.pct(50)) next(3); if(IRand.pct(30)) next(rot(-PI/2), 3); } else{ next(2); } } else{ next(0); } } else{ if(clock(0)==5){ next(rot(PI/2), 1); } else{ next(mul(0.98), 0).clr(clock(0)*0.2, 0, clock(1)*0.1); } } }
void rules(){ if(clock(1)==0){ if(clock(0)==5){ next(rot(PI/2), 1).clr(0,0.5,1.0); if(clock(2)==0){ if(clock(3)==0){ next(rot(-PI/2), 3).clr(0,0,1.0); } else{ next(rot(-PI/4), 4).clr(0); } } } else{ next(rot(PI/20),0); } } else{ if(clock(0)==10){ next(rot(PI/2),1).clr(0.5,0,1); if(clock(2)==0){ next(3).clr(1.0,0.5,0); } } else{ next(rot(PI/20),0); } } }
void rules(){ if(clock(1)==0){ if(clock(0)==5){ next(rot(PI/2), 1).clr(0,0.5,1.0); if(clock(2)==0){ if(clock(3)==0){ if(IRand.pct(90)) next(rot(-PI/4), 3).clr(0,0,1.0); } else{ if(IRand.pct(40)) next(rot(-PI/2), 4).clr(0); } } } else{ if(clock(2)==1){ next(mul(0.99), 0); } else{ next(0); } } } else if(clock(1)==1){ if(clock(0)==10){ next(rot(PI/2),1).clr(0.5,0,1); if(clock(2)==0){ next(3).clr(1.0,0.5,0); } } else{ if(clock(2)==1){ next(mul(0.99), 0); } else{ next(0); } } } else if(clock(1)==2){ if(clock(0)==5){ next(rot(PI/2), 1); } else{ if(clock(2)==1){ next(mul(0.99), 0); } else{ next(0); } } } else{ if(clock(0)==10){ if(clock(2)==0){ next(rot(PI/2),2); } } else{ next(mul(0.99), 0); } } }
void rules(){ if(clock(4)==0){ if(clock(0)==3){ next(rot(PI/2),1); if(clock(1)==0){ if(clock(2)==2) next(rot(-PI/2),3); else next(2); } } else{ if(clock(1)==2 && clock(0)==0) next(rot(-PI/2),4).clr(1.0,0,1.0); next(0); } } else{ if(clock(0)==15) next(rot(PI/2),5).clr(0); else next(0); } }
void rules(){ if(clock(0)==1){ if(clock(1)==1){ if(clock(2)==10){ next(rot(-PI/6), 3); } else{ next(rot(-PI*5/6), 2); } } else{ next(rot(PI*5/6), 1); if(clock(2)==5){ next(rot(-PI/2),4); } } } else{ next(0); } }
void rules(){ if(clock(0)==2){ if(clock(1)==0 && clock(2)==3){ next(rot(-PI/3), 3); } if(clock(1)==2){ if(clock(2)==5){ next(rot(-PI/3), 4); } else{ next(rot(-PI/3).flipNml(), 2); } } next(rot(PI/3), 1); } else{ next(0); } }
void rules(){ if(clock(0)==2){ if(clock(1)==0 && clock(2) >= IRand.get(3,6)){ next(rot(-PI/3), 3); } if(clock(1)==2){ if(clock(2) == IRand.getInt(2,5)){ next(rot(-PI/3), 4); } else{ next(rot(-PI/3).flipNml(), 2); } } next(rot(PI/3), 1); } else{ next(0); } }
void rules(){ if(clock(6)==0){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2) >= IRand.getInt(2,10)){ next(rot(-PI/2), 3); } else{ next(2); } } if(clock(4) == 2){ if(clock(1)==2 && clock(2)==0 && clock(3)==0){ // left if(IRand.pct(70)) next(rot(-PI/2), 5); } if(clock(1)==2 && clock(2)==0 && clock(3)==1){ // straight next(rot(-PI/2), 5); } if(clock(1)==2 && clock(2)==0 && clock(3)==2){ // right if(IRand.pct(70)) next(rot(-PI/2), 5); } } else if(clock(1)==2 && clock(2)==0 && clock(3)==1){ next(rot(-PI/2), 4); } next(rot(PI/2), 1); } else{ if(clock(0)==2 && clock(1)==0 && clock(3)==2){ next(rot(-PI/2).mul(1), 6).clr(0.5,0,1); } next(0); } } else{ // branch mode if(clock(1)==60){ if(IRand.pct(10)){ next(mul(1), 7).clr(0,0,0.5); } } else{ if(IRand.pct(20)){ next(rot(-PI/4), 1); } if(IRand.pct(20)){ next(rot(PI/4), 1); } if(IRand.pct(90)){ next(1); } } } }
void rules(){ if(clock(3)==1){ if(clock(0)>=2 && IRand.pct(10)){ next(4); } else{ next(0); } } else if(clock(0)==2){ if(clock(1)==0){ next(rot(-PI/3), 3); } if(clock(1)==0){ next(rot(PI/3), 2); } else{ next(rot(PI/3), 1); } } else{ next(0); } }
void rules(){ if(clock(3)==1){ if(clock(0)>=2){ next(4); } else{ next(rot(0.05), 0); } } else if(clock(0)==2){ if(clock(1)==0){ next(rot(-PI/3), 3); } if(clock(1)==0){ next(rot(PI/3), 2); } else{ next(rot(PI/3), 1); } } else{ next(0); } }
void rules(){ // corrugate if(clock(6)==0){ if(clock(3)==1){ if(clock(1)==1){ if(clock(0)==5){ if(clock(4)==12){ next(6); } else{ next(rot(PI/2), 4); } } else{ next(0); } } else{ next(rot(-PI/2), 1); } } else{ if(clock(1)==1){ if(clock(0)==5){ next(rot(-PI/2), 3); if(clock(4)==10){ next(5); } } else{ next(0); } } else{ next(rot(PI/2), 1); } } } // triangles else if(clock(6)==1){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2)==20){ next(rot(-PI/4), 6); } else{ next(rot(-PI/3*2).flipNml(), 2); } } next(rot(PI/3*2), 1); } else{ next(0); } } // squares else{ if(clock(0)==3){ if(clock(1)==1){ if(clock(2)==0){ if(clock(3)==4){ next(rot(-PI/2), 7); } else{ next(rot(-PI/2),3); } } next(2); } next(rot(PI/2), 1); } else{ next(0); } } }
void rules(){ if(clock(0)==10){ if(clock(1)==0){ if(clock(2)==0){ next(pitch(PI/2).roll(-PI/2), 2); } else{ next(pitch(-PI/2).roll(PI/2), 3); } } if(clock(1)==20); // stop else{ if(clock(1)==0 || clock(1)==1){ if(clock(1)==1 && clock(3)==1){ next(4); } next(yaw(PI/2), 1).hsb(clock(1)*0.02,1,1); } else{ next(yaw(PI/2).mul(0.9), 1).hsb(clock(1)*0.02,1,1); } } } else{ next(0); } }
void rules(){ if(clock(0)==10){ if(clock(1)==0){ if(clock(2)==0){ if(clock(3)==2){ next(pitch(PI/2).roll(PI/2).mul(0.9), 2); next(yaw(-PI/2), 4); // new cube } else{ next(pitch(PI/2).roll(PI/2), 2); if(clock(4)==4 && clock(3)==1){ next(yaw(-PI/2), 5); // new cube } } } else{ next(pitch(-PI/2).roll(-PI/2), 3); } } if(clock(1)==20); // stop else{ next(yaw(PI/2).mul(0.9), 1).hsb(clock(1)*0.02,1,1); } } else{ next(0); } }
void rules(){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2)==0){ if(clock(3)>=IRand.get(5,10)){ next(yaw(-PI/2), 4).hsb(sin(IG.time()*.02)*.1+.7,1,1); if(IRand.pct(70)) next(4).hsb(sin(IG.time()*.02)*.1+.7,1,1); } else if(IRand.pct(97)) next(3).hsb(sin(IG.time()*.02)*.1+.7,1,1); next(pitch(PI/2).roll(PI/2), 2).hsb(sin(IG.time()*.02)*.1+.7,1,1); } else if(clock(2)==1){ if(clock(3)>=IRand.get(5,10)){ if(IRand.pct(70)) next(roll(-PI/2), 4).hsb(sin(IG.time()*.02)*.1+.7,1,1); } next(pitch(-PI/2).roll(PI/2), 2).hsb(sin(IG.time()*.02)*.1+.7,1,1); } else if(clock(2)==2){ next(pitch(PI/2).roll(PI/2), 2).hsb(sin(IG.time()*.02)*.1+.7,1,1); } else if(clock(2)==3){ next(pitch(-PI/2).roll(PI/2), 2).hsb(sin(IG.time()*.02)*.1+.7,1,1); } } next(yaw(PI/2),1).hsb(sin(IG.time()*.02)*.1+.7,1,1); } else{ next(0).hsb(sin(IG.time()*.02)*.1+.7,1,1); } }
void rules(){ if(clock(0)==3){ if(clock(1)==0){ if(clock(2)==0){ if(clock(3)>=IRand.get(5,10)){ next(rot(-PI/2), 4).hsb(sin(IG.time()*.02)*.1,1,1); if(IRand.pct(50)) next(4).hsb(sin(IG.time()*.02)*.1,1,1); } else next(3).hsb(sin(IG.time()*.02)*.1,1,1); next(roll(PI/2).yaw(PI/2), 2).hsb(sin(IG.time()*.02)*.1,1,1); } else if(clock(2)==1){ if(clock(3)>=IRand.get(5,10)){ if(IRand.pct(10)) next(roll(-PI/2), 4).hsb(sin(IG.time()*.02)*.1,1,1); } next(roll(-PI/2).yaw(PI/2), 2).hsb(sin(IG.time()*.02)*.1,1,1); } else if(clock(2)==2){ next(roll(PI/2).yaw(PI/2), 2).hsb(sin(IG.time()*.02)*.1,1,1); } else if(clock(2)==3){ next(roll(-PI/2).yaw(PI/2), 2).hsb(sin(IG.time()*.02)*.1,1,1); } } next(rot(PI/2),1).hsb(sin(IG.time()*.02)*.1,1,1); } else{ next(pitch(-0.03).yaw(0.03), 0).hsb(sin(IG.time()*.02)*.1,1,1); } }
void rules(){ if(clock(0)==99){ next(pitch(PI/2).roll(PI/2), 1).clr(0,0,.5-cos(IG.time()*.05)*.5); } if(clock(0)==100){ next(pitch(PI/3).roll(PI/2), 1).clr(0,0,.5-cos(IG.time()*.05)*.5); } next(yaw(PI/3*2).pitch(0.1), 0).clr(0,0,.5-cos(IG.time()*.05)*.5); }
void rules(){ if(clock(0)==99){ next(pitch(PI/2).roll(PI/2), 1); } if(clock(0)==100){ next(pitch(PI/3).roll(PI/2), 1); } if(clock(0)%3==0){ next(roll(0.1).yaw(PI/3*2).pitch(0.1), 0).clr(clock(0)*0.01,0,clock(1)*0.1); } else{ next(yaw(PI/3*2).pitch(0.1), 0).clr(clock(0)*0.01,0,clock(1)*0.1); } }
void rules(){ if(clock(5)==1){ if(clock(0)==6){ next(rot(PI/3).flipNml(),6); } else{ next(rot(PI/3).flipNml(),0); } } else{ if(clock(0)==0){ if(clock(4)==43 && clock(1)==1 && clock(2)==0){ next(rot(PI/3),5); } else{ if(clock(1)==2){ if(clock(2)==1){ if(clock(3)==11){ next(yaw(PI/3-PI/15).pitch(PI/6).roll(PI/6), 4); } else{ next(yaw(PI/3).pitch(PI/6).roll(PI/6), 3); } } else{ next(yaw(PI/3).pitch(PI/6), 2); } } else{ next(yaw(PI/3),1); } } } else{ next(0); } } }
void rules(){ if(clock(7)==2){ // stop return; } if(clock(2)==0){ // segment next(rot(PI/2), 2); } else{ // square if(clock(0)==3){ if(clock(1)==2){ if(clock(3)==3 && clock(5)==0){ next(4); } else if(clock(3)==9 && clock(5)==1){ next(6).clr(0); } else{ next(rot(PI/2), 3); } } next(rot(-PI/2),1); } else{ if(clock(0)==1 && clock(1)==1 && clock(3)==1 && clock(5)==0){ if(clock(6)==1){ next(yaw(PI/2).pitch(PI/2), 7); } else{ next(rot(PI/2), 5).clr(0.5,0,0.5); } } next(0); } } }
import processing.opengl.*; import igeo.*; void setup(){ size(640, 480, IG.GL); IConfig.syncDrawAndDynamics=true; IG.bg(1.0); new ClockStackAgent(new IVec(0,0,0), new IVec(10,0,0), new IVec(0,0,1), null, null); IG.top(); } 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 IVec translate; // just to implement jumping behavior Orientation(IVec d, IVec n, boolean righthandsys){ dir=d; nml=n; righthand=righthandsys; } Orientation(IVec d, IVec n){ this(d,n,true); } Orientation(Orientation o){ dir=o.dir.cp(); nml=o.nml.cp(); righthand=o.righthand; } Orientation cp(){ return new Orientation(this); } 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); }else{ return nml.cross(dir); } } IVec right(){ return side(); } IVec left(){ if(righthand){ return nml.cross(dir); }else{ return dir.cross(nml); } } 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); return this; } Orientation div(double v){ dir.div(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); 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; Attribute(){ super(); } Attribute(IAttribute at){ super(at); } Attribute(Attribute at){ super(at); delay = at.delay; noCollision = at.noCollision; noGeometry = at.noGeometry; } 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; } } class ClockStackAgent extends IAgent{ final double threshold = 1; // collision threshold IVec pos, pos2, prevPos; Orientation orient, prevOrient; int[] clocks; boolean isColliding = false, isStopped = false; ArrayList< IVec > pts; ArrayList< Orientation > nextOrient; ArrayList< int[] > nextClocks; ArrayList< Attribute > nextAttr; IBounds bounds; int delayCount; ClockStackAgent(IVec p, Orientation o, int[] clok, IVec prevP, Orientation prevO){ pos = p; orient = o; clocks = clok; prevPos = prevP; prevOrient = prevO; delayCount=0; } ClockStackAgent(IVec p, IVec d, IVec n, int[] clok, IVec prevP, Orientation prevO){ pos = p; if(d.isParallel(n)){ if(!n.isParallel(IG.zaxis)) n = new IVec(0,0,1); else n = new IVec(0,1,0); } if(d.dot(n)!=0) n = d.cross(n).icross(d); orient = new Orientation(d,n); clocks = clok; prevPos = prevP; prevOrient = prevO; delayCount=0; } ClockStackAgent(IVec p, IVec d, IVec n, IVec prevP, Orientation prevO){ this(p,d,n,new int[0], prevP, prevO); } ClockStackAgent(IVec p, IVec d, IVec prevP, Orientation prevO){ this(p,d,new IVec(0,0,1),null, prevP, prevO); } IVec pos2(){ if(pos2==null) pos2 = pos.cp(orient.dir); return pos2; } IAttribute defaultAttribute(){ return new Attribute(); } 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(); 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; } } } else if(a.delayedTime() >= 0 || !a.isColliding){ // 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(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; } 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; } } } } } } } } 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){ IVec pos2 = pos.cp(orient2.translate); orient2.translate = null; // jump happens only once new ClockStackAgent(pos2, orient2, nextClocks.get(i), null, null).attr(nextAttr.get(i)); if(i==0) isStopped=true; } else{ new ClockStackAgent(pos2(), orient2, nextClocks.get(i), pos.cp(), orient.cp()).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; pos = pos2(); prevOrient = orient; orient = orient2; clocks = nextClocks.get(i); attr(nextAttr.get(i)); bounds.compare(pos2); // next point 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(){ if(prevOrient==null) return null; return prevOrient.dir(); } IVec prevFront(){ if(prevOrient==null) return null; return prevOrient.front(); } IVec prevBack(){ if(prevOrient==null) return null; return prevOrient.back(); } IVec prevNml(){ if(prevOrient==null) return null; return prevOrient.nml(); } IVec prevUp(){ if(prevOrient==null) return null; return prevOrient.up(); } IVec prevDown(){ if(prevOrient==null) return null; return prevOrient.down(); } IVec prevRight(){ if(prevOrient==null) return null; return prevOrient.right(); } IVec prevLeft(){ if(prevOrient==null) return null; return prevOrient.left(); } IVec prevSide(){ if(prevOrient==null) return null; return prevOrient.side(); } // 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; return; } pos2 = pos2(); // make geometry makeGeometry(); rules(); generate(); delayCount=0; } else{ delayCount++; } } IPoint makePoint(){ return new IPoint(pos).attr(attr()); } 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]; double len = orient.dir().len()/2; IVec side = right().cp().len(len); IVec up = up().cp().len(len); pts[0][0][0] = pos.cp().add(side).sub(up); pts[0][1][0] = pos.cp().sub(side).sub(up); pts[1][0][0] = pos2.cp().add(side).sub(up); pts[1][1][0] = pos2.cp().sub(side).sub(up); pts[0][0][1] = pos.cp().add(side).add(up); pts[0][1][1] = pos.cp().sub(side).add(up); pts[1][0][1] = pos2.cp().add(side).add(up); pts[1][1][1] = pos2.cp().sub(side).add(up); 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 && prevOrient!=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 && prevOrient!=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; } void makeGeometry(){ if(attr()!=null && ((Attribute)attr()).noGeometry) return; //makePoint(); makeLine(); //makeSurface(); //makeBox(); //makeSphere(); //makeTangentCurve(); //makeTangentSurface(); } // update rules void rules(){ // describe update rules here // - clock( x ) : // checks the current clock number at the level x // - next( x ) : // creates next instance with incrementing the clock at level x // - next( transformation method, x ) : // creates next instance after the transfomration with incrementing the clock at level x if(clock(1)==11 && clock(0)==3) next(rot(PI).jump(60), 2); if(clock(0) == 7) next(rot(-PI/2), 1); next(rot(PI/12), 0); } }
void setup(){ size(640, 480, IG.GL); IConfig.syncDrawAndDynamics=true; IG.bg(1.0); new ClockStackAgent(new IVec(0,0,0), new IVec(0,0,10), new IVec(0,-1,0), null, null).clr(0.2); } //... void rules(){ double rinc = 0.002; double ginc = 0.02; double ginc2 = 0.05; double binc = 0.004; if(clock(2)==1){ next(pitch(-PI/70), 0).clr(red(),green(),blue()+binc); } else if(clock(3)==1){ next(pitch(-PI/96).rot(-PI/96).roll(PI/6000), 0).clr(red(),green(),blue()+binc); } else if(clock(4)==1){ next(pitch(-PI*0.01475), 0).clr(red(),green(),blue()+binc); } else if(clock(5)==1){ next(rot(-PI*0.01545), 0).clr(red(),green(),blue()+binc); } else if(clock(6)==1){ if(clock(0)==5) next(pitch(-PI/4-PI/10), 1).clr(red(),green()+ginc,blue()); else next(pitch(PI/20), 0).clr(red(),green(),blue()+binc); } else if(clock(7)==1){ next(pitch(-PI/58), 0).clr(red(),green(),blue()+binc); } else if(clock(8)==1){ if(clock(0)==5) next(rot(PI/4).pitch(-PI/58), 1).clr(red(),green()+ginc,blue()); else next(rot(-PI/20).pitch(-PI/58), 0).clr(red(),green(),blue()+binc); } else if(clock(9)==1){ if(clock(1)==0){ if(clock(0)==15) next(1).clr(red(),green(),blue()+binc*2); else next(0).delay(0).clr(red(),green(),blue()+binc*2); } else{ next(pitch(-PI/35).mul(0.99), 0).clr(red(),green(),blue()+binc*2); } next(rot(PI/2), 10).clr(red(),green()+ginc,blue()).clr(red(),green()+ginc2,blue()); next(rot(-PI/2), 10).clr(red(),green()+ginc,blue()).clr(red(),green()+ginc2,blue()); } else if(clock(10)==1){ if(clock(1)==0){ next(pitch(-PI/2), 1).clr(red(),green()+ginc,blue()); } else if(clock(1)==1){ if(clock(0)==4) next(1); else next(0).clr(red(),green(),blue()+binc); } } else{ if(clock(1)==0){ if(clock(0)==2){ next(pitch(-PI/4), 1).clr(red(),green()+ginc,blue()); } else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/120), 2).clr(red(),green()+ginc2,blue()); } else if(clock(1)==1){ if(clock(0)==8) next(pitch(PI/4), 1).clr(red(),green()+ginc,blue()); else next(mul(0.97),0).clr(red()+rinc,green(),blue()); next(rot(PI/2-PI/192).pitch(-PI/192), 3).clr(red(),green()+ginc2,blue()); } else if(clock(1)==2){ if(clock(0)==2){ next(pitch(-PI/2), 0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI*0.01475/2), 4).clr(red(),green()+ginc2,blue()); } else if(clock(0)==3){ next(pitch(PI/2).mul(1.114), 1).clr(red(),green()+ginc,blue()); next(mul(0.954), 0).clr(red()+rinc,green(),blue()); next(rot(PI/2-PI*0.01545/2), 5).clr(red(),green()+ginc2,blue()); } else if(clock(0)==4){ next(rot(PI/2-PI*0.01545/2), 5).clr(red(),green()+ginc2,blue()); } else{ next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI*0.01475/2), 4).clr(red(),green()+ginc2,blue()); } } else if(clock(1)==3){ if(clock(0)==200) next(1).clr(red(),green()+ginc,blue()); else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/6-PI/120), 6).clr(red(),green()+ginc2,blue()); } else if(clock(1)==4){ if(clock(0)==7) next(1).clr(red(),green()+ginc,blue()); else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/120), 7).clr(red(),green()+ginc2,blue()); } else if(clock(1)==5){ next(1).clr(red(),green()+ginc,blue()); } else if(clock(1)==6){ if(clock(0)==6) next(1).clr(red(),green()+ginc,blue()); else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/120).rot(PI/8), 8).clr(red(),green()+ginc2,blue()); } else if(clock(1)==7){ if(clock(0)==4){ next(pitch(-PI/2), 1).clr(red(),green()+ginc,blue()); } else next(0).clr(red()+rinc,green(),blue()); } else if(clock(1)==8){ if(clock(0)<35) next(0).clr(red(),green()+ginc,blue()); next(rot(PI/2).mul(1+0.08*cos(2*PI*clock(0)/35)), 9).clr(red(),green()+ginc2,blue()); next(rot(-PI/2).mul(1+0.08*cos(2*PI*clock(0)/35)), 9).delay(25).clr(red(),green()+ginc2,blue()); } } }
void setup(){ size(640, 480, IG.GL); IConfig.syncDrawAndDynamics=true; IG.bg(1.0); new ClockStackAgent(new IVec(0,0,0), new IVec(0,0,10), new IVec(0,-1,0), null, null).clr(0.2); } //... void rules(){ double rinc = 0.002; double ginc = 0.02; double ginc2 = 0.05; double binc = 0.004; if(clock(2)==1){ next(pitch(-PI/70).mul(1.0003), 0).clr(red(),green(),blue()+binc); } else if(clock(3)==1){ next(pitch(-PI/96).rot(-PI/96).roll(PI/6000*0).mul(1.0003), 0).clr(red(),green(),blue()+binc); } else if(clock(4)==1){ next(pitch(-PI*0.01475), 0).clr(red(),green(),blue()+binc); } else if(clock(5)==1){ next(rot(-PI*0.01545), 0).clr(red(),green(),blue()+binc); } else if(clock(6)==1){ if(clock(0)==5) next(pitch(-PI/4-PI/10), 1).clr(red(),green()+ginc,blue()); else next(pitch(PI/20).mul(1.002), 0).clr(red(),green(),blue()+binc); } else if(clock(7)==1){ next(pitch(-PI/58), 0).clr(red(),green(),blue()+binc); } else if(clock(8)==1){ if(clock(0)==5) next(rot(PI/4).pitch(-PI/58), 1).clr(red(),green()+ginc,blue()); else next(rot(-PI/20).pitch(-PI/58), 0).clr(red(),green(),blue()+binc); } else if(clock(9)==1){ if(clock(1)==0){ if(clock(0)==15) next(1).clr(red(),green(),blue()+binc*2); else next(pitch(PI*0.01).mul(1.01), 0).delay(0).clr(red(),green(),blue()+binc*2); } else{ next(pitch(-PI/35).mul(0.99), 0).clr(red(),green(),blue()+binc*2); } next(rot(PI/2), 10).clr(red(),green()+ginc,blue()).clr(red(),green()+ginc2,blue()); next(rot(-PI/2), 10).clr(red(),green()+ginc,blue()).clr(red(),green()+ginc2,blue()); } else if(clock(10)==1){ if(clock(1)==0){ next(pitch(-PI/2), 1).clr(red(),green()+ginc,blue()); } else if(clock(1)==1){ if(clock(0)==4) next(1); else next(0).clr(red(),green(),blue()+binc); } } else{ if(clock(1)==0){ if(clock(0)==2){ next(pitch(-PI/4), 1).clr(red(),green()+ginc,blue()); } else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/120), 2).clr(red(),green()+ginc2,blue()); } else if(clock(1)==1){ if(clock(0)==8) next(pitch(PI/4), 1).clr(red(),green()+ginc,blue()); else next(mul(0.97),0).clr(red()+rinc,green(),blue()); next(rot(PI/2-PI/192).pitch(-PI/192), 3).clr(red(),green()+ginc2,blue()); } else if(clock(1)==2){ if(clock(0)==2){ next(pitch(-PI/2), 0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI*0.01475/2), 4).clr(red(),green()+ginc2,blue()); } else if(clock(0)==3){ next(pitch(PI/2-PI*0.05).mul(1.114), 1).clr(red(),green()+ginc,blue()); next(mul(0.954), 0).clr(red()+rinc,green(),blue()); next(rot(PI/2-PI*0.01545/2), 5).clr(red(),green()+ginc2,blue()); } else if(clock(0)==4){ next(rot(PI/2-PI*0.01545/2), 5).clr(red(),green()+ginc2,blue()); } else{ next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI*0.01475/2), 4).clr(red(),green()+ginc2,blue()); } } else if(clock(1)==3){ if(clock(0)==200) next(1).clr(red(),green()+ginc,blue()); else next(pitch(PI*0.001).mul(0.998), 0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/6-PI/120), 6).clr(red(),green()+ginc2,blue()); } else if(clock(1)==4){ if(clock(0)==7) next(1).clr(red(),green()+ginc,blue()); else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/120), 7).clr(red(),green()+ginc2,blue()); } else if(clock(1)==5){ next(1).clr(red(),green()+ginc,blue()); } else if(clock(1)==6){ if(clock(0)==6) next(1).clr(red(),green()+ginc,blue()); else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/120).rot(PI/8), 8).clr(red(),green()+ginc2,blue()); } else if(clock(1)==7){ if(clock(0)==4){ next(pitch(-PI/2), 1).clr(red(),green()+ginc,blue()); } else next(0).clr(red()+rinc,green(),blue()); } else if(clock(1)==8){ if(clock(0)<200) next(pitch(PI*0.015).mul(0.995), 0).clr(red(),green()+ginc,blue()); next(rot(PI/2).mul(1+0.08*cos(2*PI*clock(0)/35)), 9).clr(red(),green()+ginc2,blue()); next(rot(-PI/2).mul(1+0.08*cos(2*PI*clock(0)/35)), 9).delay(25).clr(red(),green()+ginc2,blue()); } } }
void setup(){ size(640, 480, IG.GL); IConfig.syncDrawAndDynamics=true; IG.bg(1.0); new ClockStackAgent(new IVec(0,0,0), new IVec(0,0,10), new IVec(0,-1,0), null, null).clr(0.2); } //... void rules(){ double rinc = 0.002; double ginc = 0.02; double ginc2 = 0.05; double binc = 0.004; if(clock(2)==1){ next(pitch(-PI/70), 0).clr(red(),green(),blue()+binc); } else if(clock(3)==1){ next(pitch(-PI/96).rot(-PI/96).roll(PI/6000), 0).clr(red(),green(),blue()+binc); } else if(clock(4)==1){ next(pitch(-PI*0.01475), 0).clr(red(),green(),blue()+binc); } else if(clock(5)==1){ next(rot(-PI*0.01545), 0).clr(red(),green(),blue()+binc); } else if(clock(6)==1){ if(clock(0)==5){ next(pitch(-PI/4-PI/10), 1).clr(red(),green()+ginc,blue()); if(clock(1)%5==0){ if(IRand.pct(1)){ next(pitch(PI/2).mul(0.5), 1).clr(red(),green()+ginc,blue()); } else if(IRand.pct(1)){ next(rot(PI/2).mul(0.5), 12).clr(red(),green()+ginc,blue()); } else if(IRand.pct(1)){ next(rot(-PI/2).mul(0.5), 12).clr(red(),green()+ginc,blue()); } else if(IRand.pct(0.5)){ next(mul(0.5), 11).clr(red(),green()+ginc,blue()); } } } else next(pitch(PI/20), 0).clr(red(),green(),blue()+binc); } else if(clock(7)==1){ next(pitch(-PI/58), 0).clr(red(),green(),blue()+binc); } else if(clock(8)==1){ if(clock(0)==5) next(rot(PI/4).pitch(-PI/58), 1).clr(red(),green()+ginc,blue()); else next(rot(-PI/20).pitch(-PI/58), 0).clr(red(),green(),blue()+binc); } else if(clock(9)==1){ if(clock(1)==0){ if(clock(0)==15) next(1).clr(red(),green(),blue()+binc*2); else next(0).delay(0).clr(red(),green(),blue()+binc*2); } else{ next(pitch(-PI/35).mul(0.99), 0).clr(red(),green(),blue()+binc*2); } } else if(clock(10)==1){ if(clock(1)==0){ next(pitch(-PI/2), 1).clr(red(),green()+ginc,blue()); } } else if(clock(11)==1){ if(clock(0)<35) next(0).clr(red(),green()+ginc,blue()); next(rot(PI/2).mul(1+0.08*cos(2*PI*clock(0)/35)), 9).clr(red(),green()+ginc2,blue()); next(rot(-PI/2).mul(1+0.08*cos(2*PI*clock(0)/35)), 9).delay(25).clr(red(),green()+ginc2,blue()); } else{ if(clock(1)==0){ if(clock(0)==2){ next(pitch(-PI/4), 1).clr(red(),green()+ginc,blue()); } else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/120), 2).clr(red(),green()+ginc2,blue()); } else if(clock(1)==1){ if(clock(0)==8) next(pitch(PI/4), 1).clr(red(),green()+ginc,blue()); else next(mul(0.97),0).clr(red()+rinc,green(),blue()); next(rot(PI/2-PI/192).pitch(-PI/192), 3).clr(red(),green()+ginc2,blue()); } else if(clock(1)==2){ if(clock(0)==2){ next(pitch(-PI/2), 0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI*0.01475/2), 4).clr(red(),green()+ginc2,blue()); } else if(clock(0)==3){ next(pitch(PI/2).mul(1.114), 1).clr(red(),green()+ginc,blue()); next(mul(0.954), 0).clr(red()+rinc,green(),blue()); next(rot(PI/2-PI*0.01545/2), 5).clr(red(),green()+ginc2,blue()); } else if(clock(0)==4){ next(rot(PI/2-PI*0.01545/2), 5).clr(red(),green()+ginc2,blue()); } else{ next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI*0.01475/2), 4).clr(red(),green()+ginc2,blue()); } } else if(clock(1)==3){ if(clock(0)>=260) next(1).clr(red(),green()+ginc,blue()); else{ next(mul(0.997), 0).clr(red()+rinc,green(),blue()); if(IRand.pct(1)){ if(IRand.pct(50)){ next(pitch(PI/4).mul(0.7), 0).clr(red()+rinc,green(),blue()); } else{ next(pitch(-PI/4).mul(0.7), 0).clr(red()+rinc,green(),blue()); } } } if(IRand.pct(80)){ next(rot(PI/2).pitch(-PI/6-PI/120), 6).clr(red(),green()+ginc2,blue()); } else{ next(rot(PI/2).pitch(-PI/6-PI/120).mul(0.5), 6).clr(red(),green()+ginc2,blue()); } } else if(clock(1)==4){ if(clock(0)==7) next(1).clr(red(),green()+ginc,blue()); else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/120), 7).clr(red(),green()+ginc2,blue()); } else if(clock(1)==5){ next(1).clr(red(),green()+ginc,blue()); } else if(clock(1)==6){ if(clock(0)==6) next(1).clr(red(),green()+ginc,blue()); else next(0).clr(red()+rinc,green(),blue()); next(rot(PI/2).pitch(-PI/120).rot(PI/8), 8).clr(red(),green()+ginc2,blue()); } else if(clock(1)==7){ if(clock(0)==4){ next(pitch(-PI/2), 11).clr(red(),green()+ginc,blue()); } else next(0).clr(red()+rinc,green(),blue()); } } }
import processing.opengl.*; import igeo.*; IGeometry[][] modules; void setup(){ size(640, 480, IG.GL); IConfig.syncDrawAndDynamics=true; IG.open("module_geometry1.3dm"); modules = new IGeometry[4][]; modules[0] = IG.layer("m1").geometries(); modules[1] = IG.layer("m2").geometries(); modules[2] = IG.layer("m3").geometries(); modules[3] = IG.layer("m4").geometries(); IG.delAll(); IG.bg(0); new ClockStackAgent(new IVec(0,0,0), new IVec(10,0,0), new IVec(0,0,1), null); } 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 IVec translate; // just to implement jumping behavior Orientation(IVec d, IVec n, boolean righthandsys){ dir=d; nml=n; righthand=righthandsys; } Orientation(IVec d, IVec n){ this(d,n,true); } Orientation(Orientation o){ dir=o.dir.cp(); nml=o.nml.cp(); 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) && (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); }else{ return nml.cross(dir); } } IVec right(){ return side(); } IVec left(){ if(righthand){ return nml.cross(dir); }else{ return dir.cross(nml); } } 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); return this; } Orientation div(double v){ dir.div(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); 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; Attribute(){ super(); } Attribute(IAttribute at){ super(at); } Attribute(Attribute at){ super(at); delay = at.delay; noCollision = at.noCollision; noGeometry = at.noGeometry; } 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 layer(String layerName){ super.layer = new ILayer(layerName); return this; } } class ClockStackAgent extends IAgent{ final double threshold = 5; // collision threshold IVec pos, pos2, prevPos; Orientation orient, prevOrient; int[] clocks; boolean isColliding = false, isStopped = 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)): n.isParallel(IG.zaxis)?new Orientation(d,d.cross(0,1,0).icross(d)):new Orientation(d,d.cross(0,0,1)), 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; } IAttribute defaultAttribute(){ return new Attribute(); } 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(); 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; } } } else if(a.delayedTime() >= 0 || !a.isColliding){ // 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(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; } 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; } } } } } } } } 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 pos = pos2(); orient = orient2; clocks = nextClocks.get(i); attr(nextAttr.get(i)); bounds.compare(pos2); // next point 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(); } IVec prevUp(){ return prevOrient==null?null:prevOrient().up(); } IVec prevDown(){ return prevOrient==null?null:prevOrient().down(); } IVec prevRight(){ return prevOrient==null?null:prevOrient().right(); } IVec prevLeft(){ return prevOrient==null?null:prevOrient().left(); } IVec prevSide(){ return prevOrient==null?null:prevOrient().side(); } // 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; return; } pos2 = pos2(); rules(); makeGeometry(); // make geometry generate(); delayCount=0; } else{ delayCount++; } } IPoint makePoint(){ return new IPoint(pos).attr(attr()); } 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]; double len = orient.dir().len()/2; IVec side = right().cp().len(len); IVec up = up().cp().len(len); pts[0][0][0] = pos.cp().add(side).sub(up); pts[0][1][0] = pos.cp().sub(side).sub(up); pts[1][0][0] = pos2.cp().add(side).sub(up); pts[1][1][0] = pos2.cp().sub(side).sub(up); pts[0][0][1] = pos.cp().add(side).add(up); pts[0][1][1] = pos.cp().sub(side).add(up); pts[1][0][1] = pos2.cp().add(side).add(up); pts[1][1][1] = pos2.cp().sub(side).add(up); 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[] copyModuleGeometry(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().transform(orient.right().cp().unit(), orient.front().cp().unit(), orient.up().unit(), pos2).attr(attr()); } return geom2; } return null; } IGeometry[] makeModule(){ if(layer().name().equals("m1")){ return copyModuleGeometry(2); } else if(layer().name().equals("m2")){ return copyModuleGeometry(2); } else if(layer().name().equals("m3")){ return copyModuleGeometry(1); } else if(layer().name().equals("m4")){ return copyModuleGeometry(0); } else if(layer().name().equals("m5")){ return copyModuleGeometry(1); } else if(layer().name().equals("m6")){ return copyModuleGeometry(1); } else if(layer().name().equals("m7")){ return copyModuleGeometry(3); } return null; } void makeGeometry(){ if(attr()!=null && ((Attribute)attr()).noGeometry) return; //makePoint(); //makeLine(); //makeSurface(); //makeBox(); //makeSphere(); //makeTangentCurve(); //makeTangentSurface(); //makeCSLine(); //makeTetrahedron(); makeModule(); } void rules(){ if(IG.time()>300) return; // stop growth if(clock(2)==4){ if(IRand.pct(10)){ next(pitch(-PI/2), 3); layer("m1").clr(1.,0,0); } else{ next(pitch(PI/2), 3); layer("m2").clr(1.,0.5,0); } } else{ if(clock(0) < 8){ if(clock(0)%2==0 && IRand.pct(20)){ next(0); next(rot(PI/2), 1); layer("m3").clr(0.3); } else{ next(0); layer("m4").clr(0.75); } } else{ if(clock(1)==0){ next(2); next(rot(PI/2), 1); layer("m5").clr(0.5); } else if(IRand.pct(20)){ next(rot(PI/2), 1); next(rot(-PI/2), 3); layer("m6").clr(1.0,0.9,0); } else{ layer("m7").clr(0.4,0.4,0); } } } } }
FYI: If it is too heavy or too slow to generate module geometries in the way above, you can generate only lines which suggest the locations and orientations of modules by using "makeCSLine()" method instead of "makeModule()", and then you can place module geometries inside Rhinoceros using this python script AllocateModuleToGeometryToLine.py.
import processing.opengl.*; import igeo.*; IGeometry[][] modules; void setup(){ size(640, 480, IG.GL); IConfig.syncDrawAndDynamics=true; IG.open("module_geometry1.3dm"); modules = new IGeometry[4][]; modules[0] = IG.layer("m1").geometries(); modules[1] = IG.layer("m2").geometries(); modules[2] = IG.layer("m3").geometries(); modules[3] = IG.layer("m4").geometries(); IG.delAll(); IG.bg(0); new ClockStackAgent(new IVec(0,0,0), new IVec(10,0,0), new IVec(0,0,1), null); } 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 IVec translate; // just to implement jumping behavior Orientation(IVec d, IVec n, boolean righthandsys){ dir=d; nml=n; righthand=righthandsys; } Orientation(IVec d, IVec n){ this(d,n,true); } Orientation(Orientation o){ dir=o.dir.cp(); nml=o.nml.cp(); 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) && (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); }else{ return nml.cross(dir); } } IVec right(){ return side(); } IVec left(){ if(righthand){ return nml.cross(dir); }else{ return dir.cross(nml); } } 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); return this; } Orientation div(double v){ dir.div(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); 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; Attribute(){ super(); } Attribute(IAttribute at){ super(at); } Attribute(Attribute at){ super(at); delay = at.delay; noCollision = at.noCollision; noGeometry = at.noGeometry; } 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 layer(String layerName){ super.layer = new ILayer(layerName); return this; } } class ClockStackAgent extends IAgent{ final double threshold = 5; // collision threshold IVec pos, pos2, prevPos; Orientation orient, prevOrient; ArrayList< Orientation > pastOrient; ArrayList< IVec > pastPos; int[] clocks; boolean isColliding = false, isStopped = false; ArrayList< IVec > pts; ArrayList< Orientation > nextOrient; ArrayList< int[] > nextClocks; ArrayList< Attribute > nextAttr; IBounds bounds; int delayCount; ClockStackAgent parent; int parentBranchTime; CourierManager manager; ModuleCourier courier; ClockStackAgent(IVec p, Orientation o, int[] clok, ClockStackAgent parentAgent){ pos = p; orient = o; clocks = clok; parent = parentAgent; if(parent!=null){ addPrev(parent.pos.cp(), parent.orient.cp()); parentBranchTime = parent.prevNum(); manager = parent.manager; } else{ manager = new CourierManager(); } 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)): n.isParallel(IG.zaxis)?new Orientation(d,d.cross(0,1,0).icross(d)):new Orientation(d,d.cross(0,0,1)), 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; } void addPrev(IVec ppos, Orientation porient){ if(prevPos==null && prevOrient==null){ prevPos=ppos; prevOrient=porient; } else{ if(pastPos==null) pastPos = new ArrayList< IVec >(); if(pastOrient==null) pastOrient=new ArrayList< Orientation >(); pastPos.add(ppos); pastOrient.add(porient); } } IVec prevPos(){ if(pastPos!=null) return pastPos.get(pastPos.size()-1); return prevPos; } IVec prevPos(int i){ if(i < 0) return null; if(i==0) return prevPos; if(pastPos==null) return null; if(i-1 >= pastPos.size()) return null; return pastPos.get(i-1); } Orientation prevOrient(){ if(pastOrient!=null) return pastOrient.get(pastOrient.size()-1); return prevOrient; } Orientation prevOrient(int i){ if(i < 0) return null; if(i==0) return prevOrient; if(pastOrient==null) return null; if(i-1 >= pastOrient.size()) return null; return pastOrient.get(i-1); } int prevNum(){ if(pastPos!=null) return pastPos.size()+1; // pastPos + prevPos if(prevPos!=null) return 1; return 0; // no prevPos } IAttribute defaultAttribute(){ return new Attribute(); } 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(); 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; } } } else if(a.delayedTime() >= 0 || !a.isColliding){ // 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(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; } 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; } } } } } } } } 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 pos = pos2(); orient = orient2; addPrev(pos, orient); clocks = nextClocks.get(i); attr(nextAttr.get(i)); bounds.compare(pos2); // next point 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 prevNum()==0?null:prevOrient().dir(); } IVec prevFront(){ return prevNum()==0?null:prevOrient().front(); } IVec prevBack(){ return prevNum()==0?null:prevOrient().back(); } IVec prevNml(){ return prevNum()==0?null:prevOrient().nml(); } IVec prevUp(){ return prevNum()==0?null:prevOrient().up(); } IVec prevDown(){ return prevNum()==0?null:prevOrient().down(); } IVec prevRight(){ return prevNum()==0?null:prevOrient().right(); } IVec prevLeft(){ return prevNum()==0?null:prevOrient().left(); } IVec prevSide(){ return prevNum()==0?null:prevOrient().side(); } // 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; return; } pos2 = pos2(); rules(); makeGeometry(); // make geometry generate(); delayCount=0; } else{ delayCount++; } } IPoint makePoint(){ return new IPoint(pos).attr(attr()); } 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]; double len = orient.dir().len()/2; IVec side = right().cp().len(len); IVec up = up().cp().len(len); pts[0][0][0] = pos.cp().add(side).sub(up); pts[0][1][0] = pos.cp().sub(side).sub(up); pts[1][0][0] = pos2.cp().add(side).sub(up); pts[1][1][0] = pos2.cp().sub(side).sub(up); pts[0][0][1] = pos.cp().add(side).add(up); pts[0][1][1] = pos.cp().sub(side).add(up); pts[1][0][1] = pos2.cp().add(side).add(up); pts[1][1][1] = pos2.cp().sub(side).add(up); 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(prevNum() > 0){ 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(prevNum() > 0){ 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[] copyModuleGeometry(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().transform(orient.right().cp().unit(), orient.front().cp().unit(), orient.up().unit(), pos2).attr(attr()); } return geom2; } return null; } IGeometry[] makeModule(){ if(layer().name().equals("m1")){ return copyModuleGeometry(2); } else if(layer().name().equals("m2")){ return copyModuleGeometry(2); } else if(layer().name().equals("m3")){ return copyModuleGeometry(1); } else if(layer().name().equals("m4")){ return copyModuleGeometry(0); } else if(layer().name().equals("m5")){ return copyModuleGeometry(1); } else if(layer().name().equals("m6")){ return copyModuleGeometry(1); } else if(layer().name().equals("m7")){ return copyModuleGeometry(3); } return null; } ModuleCourier makeCourier(){ IGeometry[] geom = null; if(layer()==null) { IG.err("layer is null."); return null; } IVec offset = null; Orientation shippingOrient = null; if(layer().name().equals("m1")){ geom = getModuleGeometry(2); offset = new IVec(0,0,8); } else if(layer().name().equals("m2")){ geom = getModuleGeometry(2); offset = new IVec(0,0,8); shippingOrient = new Orientation(new IVec(0,0,1), new IVec(0,-1,0)); } else if(layer().name().equals("m3")){ geom = getModuleGeometry(1); offset = new IVec(0,0,8); } else if(layer().name().equals("m4")){ geom = getModuleGeometry(0); offset = new IVec(0,0,8); } else if(layer().name().equals("m5")){ geom = getModuleGeometry(1); offset = new IVec(0,0,8); } else if(layer().name().equals("m6")){ geom = getModuleGeometry(1); offset = new IVec(0,0,8); } else if(layer().name().equals("m7")){ geom = getModuleGeometry(3); offset = new IVec(0,0,8); } ModuleCourier courier= new ModuleCourier(this, geom, offset, shippingOrient, manager); courier.attr(attr()); return courier; } void makeGeometry(){ if(attr()!=null && ((Attribute)attr()).noGeometry) return; //makePoint(); //makeLine(); //makeSurface(); //makeBox(); //makeSphere(); //makeTangentCurve(); //makeTangentSurface(); //makeModule(); //makeCSLine(); //makeTetrahedron(); makeCourier(); } void rules(){ if(IG.time()>300) return; // stop growth if(clock(2)==4){ if(IRand.pct(10)){ next(pitch(-PI/2), 3); layer("m1").clr(1.,0,0); } else{ next(pitch(PI/2), 3); layer("m2").clr(1.,0.5,0); } } else{ if(clock(0) < 8){ if(clock(0)%2==0 && IRand.pct(20)){ next(0); next(rot(PI/2), 1); layer("m3").clr(0.3); } else{ next(0); layer("m4").clr(0.75); } } else{ if(clock(1)==0){ next(2); next(rot(PI/2), 1); layer("m5").clr(0.5); } else if(IRand.pct(20)){ next(rot(PI/2), 1); next(rot(-PI/2), 3); layer("m6").clr(1.0,0.9,0); } else{ layer("m7").clr(0.4,0.4,0); } } } } } static class ModuleCourier extends IAgent{ CourierManager manager; int curIndex; ArrayList< IVec > pos; ArrayList< Orientation > orient; boolean started=false; IGeometry[] template; IGeometry[] geometry; boolean isNextParallel; IVec offset; Orientation shippingOrient; ModuleCourier(ClockStackAgent agent, IGeometry[] geom, IVec off, CourierManager mngr){ this(agent,geom,off,null,mngr); } ModuleCourier(ClockStackAgent agent, IGeometry[] geom, IVec off, Orientation shipOrient, CourierManager mngr){ manager = mngr; manager.add(this); template = geom; offset = off; shippingOrient = shipOrient; pos = new ArrayList< IVec >(); orient = new ArrayList< Orientation >(); pos.add(agent.pos2().cp()); orient.add(agent.orient.cp()); int branchIndex = agent.prevNum(); while(agent!=null){ for(int i=branchIndex-1; i >= 0; i--){ pos.add(0, agent.prevPos(i)); } for(int i=branchIndex-1; i >= 0; i--){ orient.add(0, agent.prevOrient(i)); } branchIndex = agent.parentBranchTime-1; agent = agent.parent; } } void start(){ curIndex=0; started=true; } void transform(int idx){ if(idx < 0 || idx >= orient.size()+1) return; IMatrix4 trans=null; Orientation or = idx < orient.size()?orient.get(idx):orient.get(idx-1); IVec p = idx < pos.size()?pos.get(idx):pos.get(idx-1); IVec off = or.right().cp().len(offset.x).add(or.front().cp().len(offset.y)).add(or.up().cp().len(offset.z)); IVec off2 = IG.v(); if(idx==0){ trans = IMatrix4.getTransform(or.right().unit(), or.front().unit(), or.up().unit(), p.cp().add(off)); if(shippingOrient!=null){ trans = trans.mul(IMatrix4.getTransform(shippingOrient.right().unit(), shippingOrient.front().unit(), shippingOrient.up().unit(), IG.v())); } } else{ Orientation or2 = orient.get(idx-1); IVec p2 = pos.get(idx-1); off2 = or2.right().cp().len(offset.x).add(or2.front().cp().len(offset.y)).add(or2.up().cp().len(offset.z)); IMatrix4 move1 = IMatrix4.getTransform(IVec.xaxis,IVec.yaxis,IVec.zaxis,off2); IMatrix4 move2 = IMatrix4.getTransform(IVec.xaxis,IVec.yaxis,IVec.zaxis,off); if(idx < orient.size()){ trans = IMatrix4.convertCoordinates(or2.right().cp().unit(), or2.front().cp().unit(), or2.up().cp().unit(), p2.cp(), or.right().cp().unit(), or.front().cp().unit(), or.up().cp().unit(), p.cp()); trans = move2.mul(trans).mul(move1.invert()); } else{ if(shippingOrient!=null){ IMatrix4 trans1 = IMatrix4.getTransform(or2.right().cp().unit(), or2.front().cp().unit(), or2.up().cp().unit(), p2.cp()); IMatrix4 trans2 = IMatrix4.getTransform(shippingOrient.right().unit(), shippingOrient.front().unit(), shippingOrient.up().unit(), IG.v()); IMatrix4 trans3 = IMatrix4.getTransform(or.right().cp().unit(), or.front().cp().unit(), or.up().cp().unit(), p.cp()); trans1.invert(); trans2.invert(); trans = trans3.mul(trans2).mul(trans1).mul(move1.invert()); } else{ trans = IMatrix4.convertCoordinates(or2.right().cp().unit(), or2.front().cp().unit(), or2.up().cp().unit(), p2.cp(), or.right().cp().unit(), or.front().cp().unit(), or.up().cp().unit(), p.cp()); trans.mul(move1.invert()); } } } if(idx==0){ geometry = new IGeometry[template.length]; for(int i=0; i < geometry.length; i++){ geometry[i] = template[i].cp().clr(clr()).layer(layer()); } } for(int i=0; i < geometry.length; i++){ geometry[i].transform(trans).updateGraphic(); } } void update(){ if(started){ transform(curIndex); curIndex++; if(curIndex==orient.size()+1){ started=false; manager.del(this); del(); } } } } class CourierManager extends IAgent{ ArrayList< ModuleCourier > couriers; int cycle=4; CourierManager(){ couriers = new ArrayList< ModuleCourier >(); } void add(ModuleCourier courier){ couriers.add(courier); } void del(ModuleCourier courier){ couriers.remove(courier); } void update(){ if(time()%cycle==0){ for(int i=0; i < couriers.size(); i++){ if(!couriers.get(i).started){ couriers.get(i).start(); // start one at one cycle to avoid collision break; } } } } }