Tutorials (back to the list of tutorials)

## Clock Stack Agent Algorithm

### Template Code

```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 >();
}
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]++;
IAttribute attr = null;
if(attr()==null) attr = new IAttribute();
else attr = attr().cp(); // attribute (color) for next agent
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);
}
}```

### Rule 1

To run the code, replace the rule method in the template code with the following.

```  void rules(){
if(clock(0)==9){
if(clock(1)==0){
next(2);
}
next(rot(PI/2), 1);
}
else{
next(0);
}
}```

### Rule 2

```  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); }
}```

### Rule 3

```  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); }
}```

### Rule 4

```  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); }
}```

### Rule 5

```  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); }
}```

### Rule 6

```  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); }
}```

### Rule 7

```  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); }
}```

### Rule 8

```  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); }
}```

### Rule 9

```  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); }
}```

### Rule 10

```  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); }
}```

### Rule 11

```  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); }
}```

### Rule 12

```  void rules(){
if(clock(0)==1){
next(rot(-PI/6), 2);
next(rot(PI/3*2), 1);
}
else{ next(0); }
}```

### Rule 13

```  void rules(){
if(clock(0)==1){
next(rot(PI/3).flipNml(), 2);
next(rot(PI/3*2), 1);
}
else{ next(0); }
}```

### Rule 14

```  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); }
}
}```

### Rule 15

```  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); }
}
}```

### Rule 16

```  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); }
}
}```

### Rule 17

```  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); }
}
}```

### Rule 18

```  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);
}
}
}```

### Rule 19

```  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); }
}
}```

### Rule 20

```  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); }
}
}```

### Rule 21

```  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);
}
}```

### Rule 22

```  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); }
}```

### Rule 23

```  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); }
}```

### Rule 24

```  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); }
}```

### Rule 25

```  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);
}
}
}
}```

### Rule 26

```  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); }
}```

### Rule 27

```  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); }
}```

### Rule 28

```  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); }
}
}```

### Rule 29

```  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); }
}```

### Rule 30

```  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); }
}```

### Rule 31

```  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); }
}```

### Rule 32

```  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); }
}```

### Rule 33

```  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);
}```

### Rule 34

```  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);
}
}```

### Rule 35

```  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); }
}
}```

### Rule 36

```  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);
}
}
}```

### Template Code2

```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 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 >();
}
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]++;
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);
}
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][1] = pos.cp().sub(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][1][0] = pos.cp().sub(side).sub(up);
pts[1][1][0] = pos2.cp().sub(side).sub(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);
}
}
```

### Rule 37

```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());
}
}
}```

### Rule 38

```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());
}
}
}```

### Rule 39

```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());
}
}
}```

### Template Code3: Imported Module Geometry

```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 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 >();
}
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]++;
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);
}
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][1] = pos.cp().sub(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][1][0] = pos.cp().sub(side).sub(up);
pts[1][1][0] = pos2.cp().sub(side).sub(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[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 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.

### Template Code4: Module Delivdery Simulation

module_geometry1.3dm

```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 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){
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;
}

if(prevPos==null && prevOrient==null){
prevPos=ppos; prevOrient=porient;
}
else{
if(pastPos==null) pastPos = new ArrayList< IVec >();
if(pastOrient==null) pastOrient=new ArrayList< Orientation >();
}
}

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 >();
}
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]++;
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);
}
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 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][1] = pos.cp().sub(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][1][0] = pos.cp().sub(side).sub(up);
pts[1][1][0] = pos2.cp().sub(side).sub(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[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 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;
template = geom;
offset = off;
shippingOrient = shipOrient;

pos = new ArrayList< IVec >();
orient = new ArrayList< Orientation >();

int branchIndex = agent.prevNum();
while(agent!=null){
for(int i=branchIndex-1; i >= 0; i--){
}
for(int i=branchIndex-1; i >= 0; 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 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);
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 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;
}
}
}
}
}
```

(back to the list of tutorials)