home processing download documents tutorial python tutorial gallery source about
 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 >();
    }
    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);
  }
}


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


     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 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.


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


(back to the list of tutorials)

HOME
FOR PROCESSING
DOWNLOAD
DOCUMENTS
TUTORIALS (Java / Python)
GALLERY
SOURCE CODE(GitHub)
PRIVACY POLICY
ABOUT/CONTACT