home processing download documents tutorial python tutorial gallery source about
 Tutorials (back to the list of tutorials)

Clock Stack Agent Algorithm for Building

     Template Code with Box

import processing.opengl.*;
import igeo.*;

void setup(){
  size(960, 540, IG.GL); // set the window size
  IConfig.syncDrawAndDynamics=true;
  IG.bg(1.0);  // setting background color. to set RGB color, put 3 numbers like IG.bg(1.0,0.0,0.0)
  IG.fillWire(); // solid shading mode with wireframe. Solid shading without wireframe is IG.fill(). transparent with wireframe is IG.transWire(). transparent without wireframe is IG.trans().
  IRand.init(3); // initial seed of random number

  // First agent is generated here. The first argument is position, the second is front direction and size, the third is upward normal direction and size, the fourth is a parent agent which needs to be null in the first agent
  new ClockStackAgent(new IVec(0,0,0), new IVec(1,0,0), new IVec(0,0,1), null).clr(0.6);
}

class Orientation{
  IVec dir, nml; // as xyz coordinates system, dir corresponds to Y and nml corresponds to Z
  boolean righthand; // right hand coordinates system or not
  double sideLen; // length of side vector. Front length is length of dir. Up length is length of nml.
  IVec translate; // just to implement jumping behavior
  Orientation(IVec d, IVec n, double sideLength, boolean righthandsys){ dir=d; nml=n; sideLen=sideLength; righthand=righthandsys; }
  Orientation(IVec d, IVec n, boolean righthandsys){ dir=d; nml=n; sideLen=dir.len(); righthand=righthandsys; }
  Orientation(IVec d, IVec n, IVec side){ dir=d; nml=n; sideLen=side.len(); righthand= dir.cross(nml).dot(side)>0; } // only length and righthand is set by side, not direction.
  Orientation(IVec d, IVec n, double sideLength){ this(d,n,sideLength,true); }
  Orientation(IVec d, IVec n){ this(d,n,true); }
  Orientation(Orientation o){ dir=o.dir.cp(); nml=o.nml.cp(); sideLen=o.sideLen; righthand=o.righthand; }
  Orientation cp(){ return new Orientation(this); }
  boolean eq(Orientation o){
    return o.righthand==righthand && o.dir.eq(dir) && o.nml.eq(nml) && Math.abs(sideLen-o.sideLen) < IConfig.tolerance &&
            (o.translate==null && translate==null || o.translate!=null&&translate!=null&&o.translate.eq(translate));
  }
  boolean isParallel(Orientation o){
    return o.righthand==righthand && o.dir.isParallel(dir) && o.nml.isParallel(nml);
  }
  IVec dir(){ return dir.cp(); }
  IVec front(){ return dir(); }
  IVec back(){ return dir().neg(); }
  IVec nml(){ return nml.cp(); }
  IVec up(){ return nml(); }
  IVec down(){ return nml().neg(); }
  IVec side(){ if(righthand){ return dir.cross(nml).len(sideLen); }else{ return nml.cross(dir).len(sideLen); } }
  IVec right(){ return side(); }
  IVec left(){ return side().neg(); }
  double sideLen(){ return sideLen; }
  double frontLen(){ return dir.len(); }
  double upLen(){ return nml.len(); }
  Orientation setSideLen(double len){ sideLen=len; return this; }
  Orientation setFrontLen(double len){ dir.len(len); return this; }
  Orientation setUpLen(double len){ nml.len(len); return this; }

  Orientation rot(double ang){ dir.rot(nml, ang); return this; }
  Orientation rot(IVec ax, double ang){ dir.rot(ax,ang); nml.rot(ax,ang); return this; }
  Orientation pitch(double ang){
    IVec ax = dir.cross(nml);
    dir.rot(ax, ang); nml.rot(ax, ang);
    return this;
  }
  Orientation yaw(double ang){ dir.rot(nml, ang); return this; }
  Orientation roll(double ang){ nml.rot(dir, ang); return this; }
  Orientation ref(IVec refNml){
    dir.ref(refNml); nml.ref(refNml); righthand = !righthand;
    return this;
  }
  Orientation flip(){ dir.flip(); righthand = !righthand; return this; }// flip front/back
  Orientation flipNml(){ nml.flip(); righthand = !righthand; return this; }// flip up/down
  Orientation flipSide(){ righthand = !righthand; return this; }// flip left/right
  Orientation mul(double v){ dir.mul(v); nml.mul(v); sideLen*=v; return this; }
  Orientation div(double v){ dir.div(v); nml.div(v); sideLen/=v; return this; }

  Orientation scaleFront(double v){ dir.mul(v); return this; }
  Orientation scaleUp(double v){ nml.mul(v); return this; }
  Orientation scaleSide(double v){ sideLen*=v; return this; }

  Orientation add(Orientation o){ dir.add(o.dir); nml.add(o.nml()); return this; }
  Orientation add(Orientation o, double f){ dir.add(o.dir, f); nml.add(o.nml(), f); return this; }
  Orientation sum(Orientation o, double f){ dir.mul(1-f).add(o.dir, f); nml.mul(1-f).add(o.nml(), f); sideLen=sideLen*(1-f)+o.sideLen*f; return this; }
  Orientation mid(Orientation o){ return sum(o,0.5); }

  Orientation translate(IVec t){ return jump(t); }
  Orientation jump(IVec move){ translate=move; return this; }
  Orientation jump(double x, double y, double z){ return jump(new IVec(x,y,z)); }
  Orientation jump(Orientation or){ return jump(or.dir.cp()); }
  Orientation jump(double factor){ return jump(dir.cp().mul(factor)); }
  Orientation jump(){ return jump(dir.cp()); }
}

class Attribute extends IAttribute{
  int delay=0;
  boolean noCollision=false;
  boolean noGeometry=false;
  boolean noDeform=true;
  int moduleIndex=-1; // default is no module

  Attribute(){ super(); }
  Attribute(IAttribute at){ super(at); }
  Attribute(Attribute at){
    super(at);
    delay = at.delay;
    noCollision = at.noCollision;
    noGeometry = at.noGeometry;
    moduleIndex=at.moduleIndex;
  }
  Attribute cp(){
    return new Attribute(this);
  }
  Attribute delay(int d){ delay = d; return this; }
  Attribute noCollision(){ noCollision=true; return this; }
  Attribute collision(){ noCollision=false; return this; }
  Attribute noGeometry(){ noGeometry=true; return this; }
  Attribute geometry(){ noGeometry=false; return this; }
  Attribute noDeform(){ noDeform=true; return this; }
  Attribute deform(){ noDeform=false; return this; }
  Attribute module(int module){ moduleIndex=module; return this; }
  int module(){ return moduleIndex; }
  Attribute layer(String layerName){ super.layer = IG.layer(layerName); return this; }
}

class ClockStackAgent extends IAgent{
  final double threshold = 0.5; //collision threshold

  IVec pos, pos2, prevPos;
  Orientation orient, prevOrient;

  int[] clocks;

  boolean isColliding = false, isStopped = false;
  boolean addGeometry=false;
  ArrayList< IVec > pts;
  ArrayList< Orientation > nextOrient;
  ArrayList< int[] > nextClocks;
  ArrayList< Attribute > nextAttr;
  IBounds bounds;
  int delayCount;

  ClockStackAgent parent;

  ClockStackAgent(IVec p, Orientation o, int[] clok, ClockStackAgent parentAgent){
    pos = p;
    orient = o;
    clocks = clok;
    parent = parentAgent;
    if(parent!=null){
      prevPos=parent.pos.cp();
      prevOrient=parent.orient.cp();
    }
    delayCount=0;
  }

  ClockStackAgent(IVec p, IVec d, IVec n, int[] clok, ClockStackAgent parentAgent){
    this(p, !d.isParallel(n)?new Orientation(d,d.cross(n).icross(d).len(n.len())):
                             n.isParallel(IG.zaxis)?new Orientation(d,d.cross(0,1,0).icross(d).len(n.len())):new Orientation(d,d.cross(0,0,1).len(n.len())),
         clok, parentAgent);
  }

  ClockStackAgent(IVec p, IVec d, IVec n, ClockStackAgent parentAgent){
    this(p,d,n,new int[0], parentAgent);
  }

  ClockStackAgent(IVec p, IVec d, ClockStackAgent parentAgent){
    this(p,d,new IVec(0,0,1),null, parentAgent);
  }

  IVec pos2(){
    if(pos2==null) pos2 = pos.cp(orient.dir);
    return pos2;
  }

  IVec prevPos(){ return prevPos; }
  Orientation prevOrient(){ return prevOrient; }

  Attribute defaultAttribute(){ return new Attribute(); }

  ClockStackAgent module(int module){
    IAttribute attr = attr();
    if(attr==null){ attr = defaultAttribute(); attr(attr); }
    ((Attribute)attr).module(module);
    return this;
  }

  ClockStackAgent delay(int d){
    IAttribute attr = attr();
    if(attr==null){ attr = defaultAttribute(); attr(attr); }
    ((Attribute)attr).delay(d);
    return this;
  }

  ClockStackAgent noCollision(){
    IAttribute attr = attr();
    if(attr==null){ attr = defaultAttribute(); attr(attr); }
    ((Attribute)attr).noCollision();
    return this;
  }

  ClockStackAgent collision(){
    IAttribute attr = attr();
    if(attr==null){ attr = defaultAttribute(); attr(attr); }
    ((Attribute)attr).collision();
    return this;
  }

  boolean isDelayed(){
    if(attr()==null) return false;
    if(((Attribute)attr()).delay <= delayCount) return true;
    return false;
  }

  int delayedTime(){
    if(attr()==null) return time();
    return delayCount - ((Attribute)attr()).delay;
  }

  boolean isCollidable(){
    if(attr()==null) return true;
    if(((Attribute)attr()).noCollision) return false;
    if(((Attribute)attr()).delay <= delayCount) return true;
    return false;
  }

  void interact(ArrayList< IDynamics > agents){
    if(threshold > 0 && !isStopped && isCollidable()){
      IVec pt2 = pos2();
      // collision to the ground
      for(int i=0; i < agents.size() && !isColliding; i++){
        if(agents.get(i) instanceof ClockStackAgent){
          ClockStackAgent a = (ClockStackAgent)agents.get(i);
          if(a==this){ // check self collision
            for(int j=0; pts!=null && j < pts.size()-2 && !isColliding; j++){ // exclude last segment
              if(IVec.isSegCloserThan(pos, pt2, pts.get(j), pts.get(j+1), threshold)){
                isColliding = true;
                if(pos.distToSegment(pts.get(j), pts.get(j+1))>threshold){
                  addGeometry=true;
                }
              }
            }
          }
          else if(a.delayedTime() >= 0){ // a!=this
            if(a.bounds!=null && bounds!=null){
              IBounds newbounds = bounds.cp();
              newbounds.compare(pt2);
              if(!newbounds.isCloserThan(a.bounds,threshold)){
                continue;
              }
            }
            IVec apt2 = a.pos2();
            if(!a.isColliding && IVec.isSegCloserThan(pos, pt2, a.pos, apt2, threshold) && !(pos.eq(a.pos) && !pt2.eq(apt2))/*not sibling*/ && !(a.time() > 0&&apt2.eq(pos))/*not parent*/ ){
              isColliding = true;
              if(pos.distToSegment(a.pos, apt2)>threshold){
                addGeometry=true;
              }
            }
            for(int j=0; a.pts!=null && j < a.pts.size() && !isColliding; j++){
              IVec apt3 = a.pos;
              if(j < a.pts.size()-1) apt3 = a.pts.get(j+1);
              if(IVec.isSegCloserThan(pos, pt2, a.pts.get(j), apt3, threshold)
                && (!pos.eq(a.pos) || pt2.eq(apt2) || j < a.pts.size()-1) ){
                if(delayedTime() > 0 || !pos.isOnSegment(a.pts.get(j),apt3)){ // exclude if it came from that line
                  isColliding = true;
                  if(pos.distToSegment(a.pts.get(j), apt3)>threshold){
                    addGeometry=true;
                  }
                }
              }
            }
          }
        }
      }
    }
  }

  int clock(int i){
    if(i >= clocks.length) return 0;
    return clocks[i];
  }

  Attribute next(int incrementClock){
    return next(orient, incrementClock);
  }

  Attribute next(Orientation o, int incrementClock){
    if(nextOrient==null){
      nextOrient = new ArrayList< Orientation >();
      nextClocks = new ArrayList< int[] >();
      nextAttr = new ArrayList< Attribute >();
    }
    nextOrient.add(o);
    int[] clocks2 = new int[incrementClock+1 > clocks.length?incrementClock+1:clocks.length];
    for(int i=0; i < clocks2.length; i++){
      if(i < incrementClock) clocks2[i] = 0;
      else if(i < clocks.length) clocks2[i] = clocks[i];
    }
    clocks2[incrementClock]++;
    nextClocks.add(clocks2);
    Attribute attr = null;
    if(attr()==null) attr = new Attribute();
    else{
      IAttribute at = attr();
      if(at instanceof Attribute) attr = ((Attribute)at).cp();
      else attr = new Attribute(at);
    }
    nextAttr.add(attr);
    return attr;
  }

  void generate(){
    if(nextOrient==null || nextOrient.size()==0){
      isStopped=true;
      return;
    }
    for(int i=0; i < nextOrient.size(); i++){
      Orientation orient2 = nextOrient.get(i);
      if(i > 0 || orient2.translate!=null){
        if(orient2.translate!=null){ // jump
          IVec pos2 = pos.cp(orient2.translate);
          orient2.translate = null; // jump happens only once
          new ClockStackAgent(pos2, orient2, nextClocks.get(i), null).attr(nextAttr.get(i));
          if(i==0) isStopped=true;
        }
        else{
          new ClockStackAgent(pos2(), orient2, nextClocks.get(i), this).attr(nextAttr.get(i));
        }
      }
      else{
        if(pts==null){
          pts = new ArrayList< IVec >();
          bounds = new IBounds(pos);
          bounds.compare(pos2);
        }
        if(pts.size() > 1 && pts.get(pts.size()-1).isOnLine(pos, pts.get(pts.size()-2))){
          pts.set(pts.size()-1, pos);
        }
        else{ pts.add(pos); } // past points
        prevPos = pos.cp();
        pos = pos2();
        prevOrient = orient.cp();
        orient = orient2;
        clocks = nextClocks.get(i);
        attr(nextAttr.get(i));
        bounds.compare(pos2); // next pointy
        delayCount=0;
      }
    }
    pos2 = null; // reset pos2
    nextOrient=null;
    nextClocks=null;
    nextAttr=null;
  }

  IVec dir(){ return orient.dir(); }
  IVec front(){ return orient.front(); }
  IVec back(){ return orient.back(); }
  IVec nml(){ return orient.nml(); }
  IVec up(){ return orient.up(); }
  IVec down(){ return orient.down(); }
  IVec right(){ return orient.right(); }
  IVec left(){ return orient.left(); }
  IVec side(){ return orient.side(); }

  IVec prevDir(){ return prevOrient==null?null:prevOrient().dir(); }
  IVec prevFront(){ return prevOrient==null?null:prevOrient().front(); }
  IVec prevBack(){ return prevOrient==null?null:prevOrient().back(); }
  IVec prevNml(){ return prevOrient==null?null:prevOrient().nml().len(prevOrient.dir().len()); }
  IVec prevUp(){ return prevOrient==null?null:prevOrient().up().len(prevOrient.dir().len()); }
  IVec prevDown(){ return prevOrient==null?null:prevOrient().down().len(prevOrient.dir().len()); }
  IVec prevRight(){ return prevOrient==null?null:prevOrient().right().len(prevOrient.dir().len()); }
  IVec prevLeft(){ return prevOrient==null?null:prevOrient().left().len(prevOrient.dir().len()); }
  IVec prevSide(){ return prevOrient==null?null:prevOrient().side().len(prevOrient.dir().len()); }

  // transformation methods
  Orientation rot(double angle){ return orient.cp().rot(angle); }
  Orientation rot(IVec axis, double angle){ return orient.cp().rot(axis,angle); }
  Orientation pitch(double angle){ return orient.cp().pitch(angle); }
  Orientation yaw(double angle){ return orient.cp().yaw(angle); }
  Orientation roll(double angle){ return orient.cp().roll(angle); }
  Orientation mul(double factor){ return orient.cp().mul(factor); }
  Orientation div(double factor){ return orient.cp().div(factor); }
  Orientation ref(IVec axis){ return orient.cp().ref(axis); }
  Orientation flip(){ return orient.cp().flip(); }
  Orientation flipNml(){ return orient.cp().flipNml(); }
  Orientation flipSide(){ return orient.cp().flipSide(); }

  Orientation jump(IVec move){ return orient.cp().jump(move); }
  Orientation jump(double x, double y, double z){ return orient.cp().jump(new IVec(x,y,z)); }
  Orientation jump(Orientation or){ return orient.cp().jump(or.dir); }
  Orientation jump(double factor){return orient.cp().jump(orient.dir.cp().mul(factor)); }
  Orientation jump(){ return orient.cp().jump(orient.dir); }

  void update(){
    if(isStopped){ return; }
    if(attr()==null || ((Attribute)attr()).delay <= delayCount){
      if(isColliding){
        if(attr()==null && time()==0 ||
          ((Attribute)attr()).delay==time()){ del(); }
        else isStopped=true;
        if(addGeometry) makeGeometry(); // last one before disappearing
        return;
      }
      pos2 = pos2();
      rules();
      makeGeometry(); // make geometry
      generate();
      delayCount=0;
    }
    else{
      delayCount++;
    }
  }

  IPoint makePoint(){ return new IPoint(pos.cp()).attr(attr().cp()); }

  ICurve makeLine(){
    return new ICurve(pos, pos2).attr(attr());
  }

  ISurface makeSurface(){
    IVec[][] pts = new IVec[2][2];
    double len = orient.dir().len()/2;
    IVec side = right().cp().len(len);
    pts[0][0] = pos.cp().add(side);
    pts[0][1] = pos.cp().sub(side);
    pts[1][0] = pos2.cp().add(side);
    pts[1][1] = pos2.cp().sub(side);
    return new ISurface(pts).attr(attr());
  }

  IBox makeBox(){
    IVec[][][] pts = new IVec[2][2][2];
    IVec p2 = pos.cp();
    IVec p1 = p2.dif(orient.front());
    IVec uvec2 = orient.right();;
    IVec vvec2 = orient.front();
    IVec wvec2 = orient.up();
    IVec uvec1 = uvec2;
    IVec vvec1 = vvec2;
    IVec wvec1 = wvec2;
    if(prevOrient != null && !((Attribute)attr()).noDeform){
      p1 = prevPos;
      uvec1 = prevOrient.right();
      vvec1 = prevOrient.front();
      wvec1 = prevOrient.up();
    }
    pts[0][0][1] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5);
    pts[1][0][1] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5);
    pts[0][0][0] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5).add(wvec1);
    pts[1][0][0] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5).add(wvec1);
    pts[0][1][1] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5);
    pts[1][1][1] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5);
    pts[0][1][0] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5).add(wvec2);
    pts[1][1][0] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5).add(wvec2);
    return (IBox)new IBox(pts).attr(attr());
  }



  void makeGeometry(){
    if(attr()!=null && ((Attribute)attr()).noGeometry) return;
    //makePoint();
    //makeLine();
    //makeSurface();
    makeBox();
  }

  void rules(){

    if(clock(1) < 1){
      if(clock(0) < 8){
        next(0).module(0);
        if(clock(0)==0 && clock(2)==0 && clock(3) < clock(4)){
          next(jump(up().add(back())).rot(PI/40), 3).module(1);
          if(clock(3)==0){
            if(clock(5)==0){
              if(IRand.pct(20) && clock(6) < 3 && clock(4) > 3){
                next(jump(side()).rot(-PI/2), 5).module(1);
              }
            }
            else if(clock(5)==1){
              if(IRand.pct(20) && clock(6) < 2 && clock(4) > 3){
                next(jump(left()).rot(PI/2), 6).module(1);
              }
            }
          }
        }
      }
      else{
        next(rot(PI/2),1).module(2);
        if(clock(3)==0 && clock(2)==0){
          if(clock(5)==0){
            if(IRand.pct(95) || clock(6)==0){
              next(jump(right()).rot(PI/10), 4).module(1);
            }
          }
          else{
            if(IRand.pct(95) || clock(6)==0){
              next(jump(right()).rot(-PI/6), 4).module(1);
            }
          }
        }
      }
    }
    else{
      if(clock(0) < 4){
        next(0).module(0);
      }
      else{
        next(rot(PI/2),2).module(2);
      }
    }

  }
}


     Rule 1

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

    void rules(){

      if(clock(1) < 1){
        if(clock(0) < 8){
          next(0);
        }
        else{
          next(rot(PI/2),1);
        }
      }
      else{
        if(clock(0) < 4){
          next(0);
        }
        else{
          next(rot(PI/2),2);
        }
      }

    }


     Rule 2

    void rules(){

      if(clock(1) < 1){
        if(clock(0) < 8){
          next(0);
          if(clock(0)==0 && clock(2)==0 && clock(3) < 8){
            next(jump(up().add(back())), 3);
          }
        }
        else{
          next(rot(PI/2),1);
        }
      }
      else{
        if(clock(0) < 4){
          next(0);
        }
        else{
          next(rot(PI/2),2);
        }
      }

    }


     Rule 3

    void rules(){

      if(clock(1) < 1){
        if(clock(0) < 8){
          next(0);
          if(clock(0)==0 && clock(2)==0 && clock(3) < 8){
            next(jump(up().add(back())), 3);
          }
        }
        else{
          next(rot(PI/2), 1);
          if(clock(3)==0 && clock(2)==0){
            next(4);
          }
        }
      }
      else{
        if(clock(0) < 4){
          next(0);
        }
        else{
          next(rot(PI/2), 2);
        }
      }

    }


     Rule 4

    void rules(){

      if(clock(1) < 1){
        if(clock(0) < 8){
          next(0);
          if(clock(0)==0 && clock(2)==0 && clock(3) < 8){
            next(jump(up().add(back())), 3);
          }
        }
        else{
          next(rot(PI/2), 1);
          if(clock(3)==0 && clock(2)==0){
            next(jump(right()), 4);
          }
        }
      }
      else{
        if(clock(0) < 4){
          next(0);
        }
        else{
          next(rot(PI/2), 2);
        }
      }

    }


     Rule 5

    void rules(){

      if(clock(1) < 1){
        if(clock(0) < 8){
          next(0);
          if(clock(0)==0 && clock(2)==0 && clock(3) < 8){
            next(jump(up().add(back())), 3);
          }
        }
        else{
          next(rot(PI/2), 1);
          if(clock(3)==0 && clock(2)==0){
            next(jump(right()).rot(PI/10), 4);
          }
        }
      }
      else{
        if(clock(0) < 4){
          next(0);
        }
        else{
          next(rot(PI/2), 2);
        }
      }

    }


     Rule 6

    void rules(){

      if(clock(1) < 1){
        if(clock(0) < 8){
          next(0);
          if(clock(0)==0 && clock(2)==0 && clock(3) < 8){
            next(jump(up().add(back())), 3);
            if(clock(3)==0){
              if(clock(5)==0){
                if((clock(4)==15 || clock(4)==6) && clock(6) < 2){
                  next(jump(side()).rot(-PI/2), 5);
                }
              }
              else if(clock(5)==1){
                if(clock(4)==4 && clock(6) < 1){
                  next(jump(left()).rot(PI/2), 6);
                }
              }
            }
          }
        }
        else{
          next(rot(PI/2), 1);
          if(clock(3)==0 && clock(2)==0){
            if(clock(5)==0){
              next(jump(right()).rot(PI/10), 4);
            }
            else{
              next(jump(right()).rot(-PI/6), 4);
            }
          }
        }
      }
      else{
        if(clock(0) < 4){
          next(0);
        }
        else{
          next(rot(PI/2),2);
        }
      }

    }


     Template Code with Imported Geometry

module1.3dm

module2.3dm

ground.3dm

import processing.opengl.*;
import igeo.*;

IGeometry[][] modules;
ISurface ground;
ICurve siteBoundary;
ICurve pondBoundary;

void setup(){
  size(960, 540, IG.GL);
  IConfig.syncDrawAndDynamics=true;

  modules = new IGeometry[2][];

  IG.open("module1.3dm");
  modules[0] = IG.geometries();
  IG.delAll();

  IG.open("module2.3dm");
  modules[1] = IG.geometries();
  IG.delAll();

  IG.open("ground.3dm");
  ground = IG.surface(0);
  siteBoundary = IG.layer("site_boundary").curve(0); // comment out this line if you don't need the site boundary collision detection 
  pondBoundary = IG.layer("pond_boundary").curve(0); // comment out this line if you don't need the pond boundary collision detection 
  if(siteBoundary !=null) siteBoundary.del();
  if(pondBoundary !=null) pondBoundary.del();

  IG.showTime();
  IRand.init(5); // initialize random number seed
  IG.bg(1.0);
  IG.pers();
  //IG.pers(new IVec(200,200,200), new IVec(-1,-1,-1)); // when you need to set camera position and direction
  IG.fill();
  //new FocusAgent(); // to automatically focus on all geometry during animatin capture
  new ClockStackAgent(new IVec(-60,10,58), new IVec(5,0,0), new IVec(0,0,5), null).module(1);
}

/*
// to capture animation frames
int frameCount=0;
void draw(){
  if(frameCount < 1000){
    saveFrame("capture/frame_#####.jpg");
  }
  else{
    System.exit(1);
  }
  frameCount++;
}
// to focus on any generated objects all the time
class FocusAgent extends IAgent{
  void update(){ IG.focus(); }
}
*/

class Orientation{
  IVec dir, nml; // as xyz coordinates system, dir corresponds to Y and nml corresponds to Z
  boolean righthand; // right hand coordinates system or not
  double sideLen; // length of side vector. Front length is length of dir. Up length is length of nml.
  IVec translate; // just to implement jumping behavior
  Orientation(IVec d, IVec n, double sideLength, boolean righthandsys){ dir=d; nml=n; sideLen=sideLength; righthand=righthandsys; }
  Orientation(IVec d, IVec n, boolean righthandsys){ dir=d; nml=n; sideLen=dir.len(); righthand=righthandsys; }
  Orientation(IVec d, IVec n, IVec side){ dir=d; nml=n; sideLen=side.len(); righthand= dir.cross(nml).dot(side)>0; } // only length and righthand is set by side, not direction.
  Orientation(IVec d, IVec n, double sideLength){ this(d,n,sideLength,true); }
  Orientation(IVec d, IVec n){ this(d,n,true); }
  Orientation(Orientation o){ dir=o.dir.cp(); nml=o.nml.cp(); sideLen=o.sideLen; righthand=o.righthand; }
  Orientation cp(){ return new Orientation(this); }
  boolean eq(Orientation o){
    return o.righthand==righthand && o.dir.eq(dir) && o.nml.eq(nml) && Math.abs(sideLen-o.sideLen) < IConfig.tolerance &&
            (o.translate==null && translate==null || o.translate!=null&&translate!=null&&o.translate.eq(translate));
  }
  boolean isParallel(Orientation o){
    return o.righthand==righthand && o.dir.isParallel(dir) && o.nml.isParallel(nml);
  }
  IVec dir(){ return dir.cp(); }
  IVec front(){ return dir(); }
  IVec back(){ return dir().neg(); }
  IVec nml(){ return nml.cp(); }
  IVec up(){ return nml(); }
  IVec down(){ return nml().neg(); }
  IVec side(){ if(righthand){ return dir.cross(nml).len(sideLen); }else{ return nml.cross(dir).len(sideLen); } }
  IVec right(){ return side(); }
  IVec left(){ return side().neg(); }
  double sideLen(){ return sideLen; }
  double frontLen(){ return dir.len(); }
  double upLen(){ return nml.len(); }
  Orientation setSideLen(double len){ sideLen=len; return this; }
  Orientation setFrontLen(double len){ dir.len(len); return this; }
  Orientation setUpLen(double len){ nml.len(len); return this; }

  Orientation rot(double ang){ dir.rot(nml, ang); return this; }
  Orientation rot(IVec ax, double ang){ dir.rot(ax,ang); nml.rot(ax,ang); return this; }
  Orientation pitch(double ang){
    IVec ax = dir.cross(nml);
    dir.rot(ax, ang); nml.rot(ax, ang);
    return this;
  }
  Orientation yaw(double ang){ dir.rot(nml, ang); return this; }
  Orientation roll(double ang){ nml.rot(dir, ang); return this; }
  Orientation ref(IVec refNml){
    dir.ref(refNml); nml.ref(refNml); righthand = !righthand;
    return this;
  }
  Orientation flip(){ dir.flip(); righthand = !righthand; return this; }// flip front/back
  Orientation flipNml(){ nml.flip(); righthand = !righthand; return this; }// flip up/down
  Orientation flipSide(){ righthand = !righthand; return this; }// flip left/right
  Orientation mul(double v){ dir.mul(v); nml.mul(v); sideLen*=v; return this; }
  Orientation div(double v){ dir.div(v); nml.div(v); sideLen/=v; return this; }

  Orientation scaleFront(double v){ dir.mul(v); return this; }
  Orientation scaleUp(double v){ nml.mul(v); return this; }
  Orientation scaleSide(double v){ sideLen*=v; return this; }

  Orientation add(Orientation o){ dir.add(o.dir); nml.add(o.nml()); return this; }
  Orientation add(Orientation o, double f){ dir.add(o.dir, f); nml.add(o.nml(), f); return this; }
  Orientation sum(Orientation o, double f){ dir.mul(1-f).add(o.dir, f); nml.mul(1-f).add(o.nml(), f); sideLen=sideLen*(1-f)+o.sideLen*f; return this; }
  Orientation mid(Orientation o){ return sum(o,0.5); }

  Orientation translate(IVec t){ return jump(t); }
  Orientation jump(IVec move){ translate=move; return this; }
  Orientation jump(double x, double y, double z){ return jump(new IVec(x,y,z)); }
  Orientation jump(Orientation or){ return jump(or.dir.cp()); }
  Orientation jump(double factor){ return jump(dir.cp().mul(factor)); }
  Orientation jump(){ return jump(dir.cp()); }
}

class Attribute extends IAttribute{
  int delay=0;
  boolean noCollision=false;
  boolean noGeometry=false;
  boolean noDeform=true;
  int moduleIndex=-1; // default is no module

  Attribute(){ super(); }
  Attribute(IAttribute at){ super(at); }
  Attribute(Attribute at){
    super(at);
    delay = at.delay;
    noCollision = at.noCollision;
    noGeometry = at.noGeometry;
    moduleIndex=at.moduleIndex;
  }
  Attribute cp(){
    return new Attribute(this);
  }
  Attribute delay(int d){ delay = d; return this; }
  Attribute noCollision(){ noCollision=true; return this; }
  Attribute collision(){ noCollision=false; return this; }
  Attribute noGeometry(){ noGeometry=true; return this; }
  Attribute geometry(){ noGeometry=false; return this; }
  Attribute noDeform(){ noDeform=true; return this; }
  Attribute deform(){ noDeform=false; return this; }
  Attribute module(int module){ moduleIndex=module; return this; }
  int module(){ return moduleIndex; }
  Attribute layer(String layerName){ super.layer = IG.layer(layerName); return this; }
}

class ClockStackAgent extends IAgent{
  final double threshold = 0.5; //collision threshold

  IVec pos, pos2, prevPos;
  Orientation orient, prevOrient;
  int[] clocks;
  boolean isColliding = false, isStopped = false;
  boolean addGeometry=false;
  ArrayList< IVec > pts;
  ArrayList< Orientation > nextOrient;
  ArrayList< int[] > nextClocks;
  ArrayList< Attribute > nextAttr;
  IBounds bounds;
  int moveCount;
  int delayCount;
  ClockStackAgent parent;

  ClockStackAgent(IVec p, Orientation o, int[] clok, ClockStackAgent parentAgent){
    pos = p;
    orient = o;
    clocks = clok;
    parent = parentAgent;
    if(parent!=null){
      prevPos=parent.pos.cp();
      prevOrient=parent.orient.cp();
    }
    moveCount=0;
    delayCount=0;
  }

  ClockStackAgent(IVec p, IVec d, IVec n, int[] clok, ClockStackAgent parentAgent){
    this(p, !d.isParallel(n)?new Orientation(d,d.cross(n).icross(d).len(n.len())):
                             n.isParallel(IG.zaxis)?new Orientation(d,d.cross(0,1,0).icross(d).len(n.len())):new Orientation(d,d.cross(0,0,1).len(n.len())),
         clok, parentAgent);
  }

  ClockStackAgent(IVec p, IVec d, IVec n, ClockStackAgent parentAgent){
    this(p,d,n,new int[0], parentAgent);
  }

  ClockStackAgent(IVec p, IVec d, ClockStackAgent parentAgent){
    this(p,d,new IVec(0,0,1),null, parentAgent);
  }

  IVec pos2(){
    if(pos2==null) pos2 = pos.cp(orient.dir);
    return pos2;
  }

  IVec prevPos(){ return prevPos; }
  Orientation prevOrient(){ return prevOrient; }

  Attribute defaultAttribute(){ return new Attribute(); }

  ClockStackAgent module(int module){
    IAttribute attr = attr();
    if(attr==null){ attr = defaultAttribute(); attr(attr); }
    ((Attribute)attr).module(module);
    return this;
  }

  ClockStackAgent delay(int d){
    IAttribute attr = attr();
    if(attr==null){ attr = defaultAttribute(); attr(attr); }
    ((Attribute)attr).delay(d);
    return this;
  }

  ClockStackAgent noCollision(){
    IAttribute attr = attr();
    if(attr==null){ attr = defaultAttribute(); attr(attr); }
    ((Attribute)attr).noCollision();
    return this;
  }

  ClockStackAgent collision(){
    IAttribute attr = attr();
    if(attr==null){ attr = defaultAttribute(); attr(attr); }
    ((Attribute)attr).collision();
    return this;
  }

  boolean isDelayed(){
    if(attr()==null) return false;
    if(((Attribute)attr()).delay <= delayCount) return true;
    return false;
  }

  int delayedTime(){
    if(attr()==null) return time();
    return delayCount - ((Attribute)attr()).delay;
  }

  boolean isCollidable(){
    if(attr()==null) return true;
    if(((Attribute)attr()).noCollision) return false;
    if(((Attribute)attr()).delay <= delayCount) return true;
    return false;
  }

  void interact(ArrayList< IDynamics > agents){

    if(threshold > 0 && !isStopped && isCollidable()){
      IVec pt2 = pos2();

      if(ground!=null){ // collision to the ground
        IVec gpt = ground.closePt(pt2);
        if(pt2.dif(gpt).z < 0){ // below the ground
          isColliding = true;
        }
      }
      if(!isColliding && siteBoundary!=null){ // collision to the site boundary
        // check it by polyline of the control points
        if(!pos2.isInside2d(siteBoundary.cps())){
          isColliding = true;
        }
      }
      if(!isColliding && pondBoundary!=null){ // collision to the pond boundary
        if(pos2.isInside2d(pondBoundary.cps())){
          isColliding = true;
        }
      }

      for(int i=0; i < agents.size() && !isColliding; i++){
        if(agents.get(i) instanceof ClockStackAgent){
          ClockStackAgent a = (ClockStackAgent)agents.get(i);
          if(a==this){ // check self collision
            for(int j=0; pts!=null && j < pts.size()-2 && !isColliding; j++){ // exclude last segment
              if(IVec.isSegCloserThan(pos, pt2, pts.get(j), pts.get(j+1), threshold)){
                isColliding = true;
                if(pos.distToSegment(pts.get(j), pts.get(j+1))>threshold){
                  addGeometry=true;
                }
              }
            }
          }
          else if(a.delayedTime() >= 0){ // a!=this
            if(a.bounds!=null && bounds!=null){
              IBounds newbounds = bounds.cp();
              newbounds.compare(pt2);
              if(!newbounds.isCloserThan(a.bounds,threshold)){
                continue;
              }
            }
            IVec apt2 = a.pos2();
            if(!a.isColliding && IVec.isSegCloserThan(pos, pt2, a.pos, apt2, threshold) && !(pos.eq(a.pos) && !pt2.eq(apt2))/*not sibling*/ && !(a.time() > 0&&apt2.eq(pos))/*not parent*/ ){
              isColliding = true;
              if(pos.distToSegment(a.pos, apt2)>threshold){
                addGeometry=true;
              }
            }
            for(int j=0; a.pts!=null && j < a.pts.size() && !isColliding; j++){
              IVec apt3 = a.pos;
              if(j < a.pts.size()-1) apt3 = a.pts.get(j+1);
              if(IVec.isSegCloserThan(pos, pt2, a.pts.get(j), apt3, threshold)
                && (!pos.eq(a.pos) || pt2.eq(apt2) || j < a.pts.size()-1) ){
                if(delayedTime() > 0 || !pos.isOnSegment(a.pts.get(j),apt3)){ // exclude if it came from that line
                  isColliding = true;
                  if(pos.distToSegment(a.pts.get(j), apt3)>threshold){
                    addGeometry=true;
                  }
                }
              }
            }
          }
        }
      }
    }
  }

  int clock(int i){
    if(i >= clocks.length) return 0;
    return clocks[i];
  }

  Attribute next(int incrementClock){
    return next(orient, incrementClock);
  }

  Attribute next(Orientation o, int incrementClock){
    if(nextOrient==null){
      nextOrient = new ArrayList< Orientation >();
      nextClocks = new ArrayList< int[] >();
      nextAttr = new ArrayList< Attribute >();
    }
    nextOrient.add(o);
    int[] clocks2 = new int[incrementClock+1 > clocks.length?incrementClock+1:clocks.length];
    for(int i=0; i < clocks2.length; i++){
      if(i < incrementClock) clocks2[i] = 0;
      else if(i < clocks.length) clocks2[i] = clocks[i];
    }
    clocks2[incrementClock]++;
    nextClocks.add(clocks2);
    Attribute attr = null;
    if(attr()==null) attr = new Attribute();
    else{
      IAttribute at = attr();
      if(at instanceof Attribute) attr = ((Attribute)at).cp();
      else attr = new Attribute(at);
    }
    nextAttr.add(attr);
    return attr;
  }

  void generate(){
    if(nextOrient==null || nextOrient.size()==0){
      isStopped=true;
      return;
    }
    for(int i=0; i < nextOrient.size(); i++){
      Orientation orient2 = nextOrient.get(i);
      if(i > 0 || orient2.translate!=null){
        if(orient2.translate!=null){ // jump
          IVec pos2 = pos.cp(orient2.translate);
          orient2.translate = null; // jump happens only once
          new ClockStackAgent(pos2, orient2, nextClocks.get(i), null).attr(nextAttr.get(i));
          if(i==0) isStopped=true;
        }
        else{
          new ClockStackAgent(pos2(), orient2, nextClocks.get(i), this).attr(nextAttr.get(i));
        }
      }
      else{
        if(pts==null){
          pts = new ArrayList< IVec >();
          bounds = new IBounds(pos);
          bounds.compare(pos2);
        }
        if(pts.size() > 1 && pts.get(pts.size()-1).isOnLine(pos, pts.get(pts.size()-2))){
          pts.set(pts.size()-1, pos);
        }
        else{ pts.add(pos); } // past points
        prevPos = pos.cp();
        pos = pos2();
        prevOrient = orient.cp();
        orient = orient2;
        clocks = nextClocks.get(i);
        attr(nextAttr.get(i));
        bounds.compare(pos2); // next point
        moveCount++;
        delayCount=0;
      }
    }
    pos2 = null; // reset pos2
    nextOrient=null;
    nextClocks=null;
    nextAttr=null;
  }

  IVec dir(){ return orient.dir(); }
  IVec front(){ return orient.front(); }
  IVec back(){ return orient.back(); }
  IVec nml(){ return orient.nml(); }
  IVec up(){ return orient.up(); }
  IVec down(){ return orient.down(); }
  IVec right(){ return orient.right(); }
  IVec left(){ return orient.left(); }
  IVec side(){ return orient.side(); }

  IVec prevDir(){ return prevOrient==null?null:prevOrient().dir(); }
  IVec prevFront(){ return prevOrient==null?null:prevOrient().front(); }
  IVec prevBack(){ return prevOrient==null?null:prevOrient().back(); }
  IVec prevNml(){ return prevOrient==null?null:prevOrient().nml().len(prevOrient.dir().len()); }
  IVec prevUp(){ return prevOrient==null?null:prevOrient().up().len(prevOrient.dir().len()); }
  IVec prevDown(){ return prevOrient==null?null:prevOrient().down().len(prevOrient.dir().len()); }
  IVec prevRight(){ return prevOrient==null?null:prevOrient().right().len(prevOrient.dir().len()); }
  IVec prevLeft(){ return prevOrient==null?null:prevOrient().left().len(prevOrient.dir().len()); }
  IVec prevSide(){ return prevOrient==null?null:prevOrient().side().len(prevOrient.dir().len()); }

  // transformation methods
  Orientation rot(double angle){ return orient.cp().rot(angle); }
  Orientation rot(IVec axis, double angle){ return orient.cp().rot(axis,angle); }
  Orientation pitch(double angle){ return orient.cp().pitch(angle); }
  Orientation yaw(double angle){ return orient.cp().yaw(angle); }
  Orientation roll(double angle){ return orient.cp().roll(angle); }
  Orientation mul(double factor){ return orient.cp().mul(factor); }
  Orientation div(double factor){ return orient.cp().div(factor); }
  Orientation ref(IVec axis){ return orient.cp().ref(axis); }
  Orientation flip(){ return orient.cp().flip(); }
  Orientation flipNml(){ return orient.cp().flipNml(); }
  Orientation flipSide(){ return orient.cp().flipSide(); }

  Orientation jump(IVec move){ return orient.cp().jump(move); }
  Orientation jump(double x, double y, double z){ return orient.cp().jump(new IVec(x,y,z)); }
  Orientation jump(Orientation or){ return orient.cp().jump(or.dir); }
  Orientation jump(double factor){return orient.cp().jump(orient.dir.cp().mul(factor)); }
  Orientation jump(){ return orient.cp().jump(orient.dir); }

  void update(){
    if(isStopped){ return; }
    if(attr()==null || ((Attribute)attr()).delay <= delayCount){
      if(isColliding){
        if(attr()==null && time()==0 ||
          ((Attribute)attr()).delay==time()){ del(); }
        else isStopped=true;
        if(addGeometry) makeGeometry(); // last one before disappearing
        return;
      }
      pos2 = pos2();
      rules();
      makeGeometry(); // make geometry
      generate();
      delayCount=0;
    }
    else{
      delayCount++;
    }
  }

  IPoint makePoint(){ return new IPoint(pos.cp()).attr(attr().cp()); }

  ICurve makeLine(){
    return new ICurve(pos, pos2).attr(attr());
  }

  ISurface makeSurface(){
    IVec[][] pts = new IVec[2][2];
    double len = orient.dir().len()/2;
    IVec side = right().cp().len(len);
    pts[0][0] = pos.cp().add(side);
    pts[0][1] = pos.cp().sub(side);
    pts[1][0] = pos2.cp().add(side);
    pts[1][1] = pos2.cp().sub(side);
    return new ISurface(pts).attr(attr());
  }

  IBox makeBox(){
    IVec[][][] pts = new IVec[2][2][2];
    IVec p2 = pos.cp();
    IVec p1 = p2.dif(orient.front());
    IVec uvec2 = orient.right();;
    IVec vvec2 = orient.front();
    IVec wvec2 = orient.up();
    IVec uvec1 = uvec2;
    IVec vvec1 = vvec2;
    IVec wvec1 = wvec2;
    if(prevOrient != null && !((Attribute)attr()).noDeform){
      p1 = prevPos;
      uvec1 = prevOrient.right();
      vvec1 = prevOrient.front();
      wvec1 = prevOrient.up();
    }
    pts[0][0][1] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5);
    pts[1][0][1] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5);
    pts[0][0][0] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5).add(wvec1);
    pts[1][0][0] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5).add(wvec1);
    pts[0][1][1] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5);
    pts[1][1][1] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5);
    pts[0][1][0] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5).add(wvec2);
    pts[1][1][0] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5).add(wvec2);
    return (IBox)new IBox(pts).attr(attr());
  }

  ISphere makeSphere(){
    IVec mid = pos.mid(pos2);
    double len = pos.dist(pos2);
    return (ISphere)new ISphere(mid, len/2).attr(attr());
  }

  ICurve makeTangentCurve(){
    if(prevPos() != null){
      IVec m1 = prevPos().mid(pos);
      IVec m2 = pos.mid(pos2);
      double len = orient.dir().len()/2;
      if(!prevOrient().dir.isParallel(orient.dir) || !prevOrient().nml.isParallel(orient.nml)){
        IVec[] pts = new IVec[3];
        Orientation ori = orient.cp().mid(prevOrient());
        pts[0] = m1;
        pts[1] = pos;
        pts[2] = m2;
        return new ICurve(pts, 2).attr(attr());
      }
      return new ICurve(m1, m2).attr(attr());
    }
    return null;
  }

  ISurface makeTangentSurface(){
    if(prevPos() != null){
      IVec m1 = prevPos().mid(pos);
      IVec m2 = pos.mid(pos2);
      double len = orient.dir().len()/2;
      if(!prevOrient().dir.isParallel(orient.dir) || !prevOrient().nml.isParallel(orient.nml)){
        IVec[][] pts = new IVec[3][2];
        Orientation ori = orient.cp().mid(prevOrient());
        pts[0][0] = m1.cp(prevRight().cp().len(len));
        pts[1][0] = pos.cp(ori.right().cp().len(len));
        pts[2][0] = m2.cp(right().cp().len(len));
        pts[0][1] = m1.cp(prevLeft().cp().len(len));
        pts[1][1] = pos.cp(ori.left().cp().len(len));
        pts[2][1] = m2.cp(left().cp().len(len));
        return new ISurface(pts, 2, 1).attr(attr());
      }
      return new ISurface(m1.cp(prevRight().cp().len(len)),
                        m1.cp(prevLeft().cp().len(len)),
                        m2.cp(left().cp().len(len)),
                        m2.cp(right().cp().len(len))).attr(attr());
    }
    return null;
  }

  ICurve makeCSLine(){
    IVec[] pts = new IVec[3];
    double len = pos2.dist(pos)/2;
    pts[0] = orient.nml().cp().len(len).add(pos2);
    pts[1] = pos2.cp();
    pts[2] = pos2.cp(orient.front().cp().len(len));
    return new ICurve(pts).attr(attr());
  }

  IBrep makeTetrahedron(){
    return makeTetrahedron(pos2, orient.front(), orient.nml(), orient.dir.len()).attr(attr());
  }

  IBrep makeTetrahedron(IVec center, IVec front, IVec nml, double size){
    double len1 = size/4.082*6.124;
    double len2 = size/4.082*2.041;
    double len3 = size/4.082*5.774;
    IVec dir1 = front.cp().len(len1);
    IVec dir2 = dir1.cp().flip().len(len2);
    IVec dir3 = nml.cp().len(len3).add(dir2);
    IVec dir4 = dir3.cp().rot(dir1, PI/3*2);
    IVec dir5 = dir3.cp().rot(dir1, PI/3*4);

    IVec p1 = center.cp(dir1);
    IVec p2 = center.cp(dir3);
    IVec p3 = center.cp(dir4);
    IVec p4 = center.cp(dir5);

    ISurfaceGeo[] srf = new ISurfaceGeo[4];
    srf[0] = new ISurfaceGeo(p1,p2,p3);
    srf[1] = new ISurfaceGeo(p2,p3,p4);
    srf[2] = new ISurfaceGeo(p1,p3,p4);
    srf[3] = new ISurfaceGeo(p1,p2,p4);
    return new IBrep(srf);
  }

  IGeometry[] getModuleGeometry(int idx){
    if(modules==null){
      IG.err("modules is null.");
      return null;
    }
    if(idx < 0 || idx >= modules.length){
      IG.err("module index ("+idx+") is out of boundary. ");
      return null;
    }
    return modules[idx];
  }

  IGeometry[] deformModuleGeometry(int idx){
    IGeometry[] geom = getModuleGeometry(idx);
    if(geom!=null){
      IGeometry[] geom2 = new IGeometry[geom.length];
      for(int i=0; i < geom.length; i++){
        geom2[i] = geom[i].cp();
        IVec[][][] target = new IVec[2][2][2];
        IVec p2 = pos.cp();
        IVec p1 = p2.dif(orient.front());
        IVec uvec2 = orient.right();;
        IVec vvec2 = orient.front();
        IVec wvec2 = orient.up();
        IVec uvec1 = uvec2;
        IVec vvec1 = vvec2;
        IVec wvec1 = wvec2;
        if(prevOrient != null && !((Attribute)attr()).noDeform){
          p1 = prevPos;
          uvec1 = prevOrient.right();
          vvec1 = prevOrient.front();
          wvec1 = prevOrient.up();
        }
        target[0][0][0] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5);
        target[1][0][0] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5);
        target[0][0][1] = p1.cp().add(uvec1, -0.5).add(vvec1, 0.5).add(wvec1);
        target[1][0][1] = p1.cp().add(uvec1, 0.5).add(vvec1, 0.5).add(wvec1);
        target[0][1][0] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5);
        target[1][1][0] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5);
        target[0][1][1] = p2.cp().add(uvec2, -0.5).add(vvec2, 0.5).add(wvec2);
        target[1][1][1] = p2.cp().add(uvec2, 0.5).add(vvec2, 0.5).add(wvec2);
        deform(geom2[i], target);
        geom2[i].layer(geom[i].layer().name());
        String name = String.valueOf(this.hashCode());
        geom2[i].name(String.valueOf(this.hashCode())+"_"+String.valueOf(moveCount)); // set object hash code number and moveCount as module geometry name
      }
      return geom2;
    }
    return null;
  }

  IVec deform(IVec uvw, IVec[][][] targetBox){
    IVec ret = new IVec();
    ret.add(targetBox[0][0][0], (1-uvw.x)*(1-uvw.y)*(1-uvw.z));
    ret.add(targetBox[1][0][0], (uvw.x)*(1-uvw.y)*(1-uvw.z));
    ret.add(targetBox[0][1][0], (1-uvw.x)*(uvw.y)*(1-uvw.z));
    ret.add(targetBox[1][1][0], (uvw.x)*(uvw.y)*(1-uvw.z));
    ret.add(targetBox[0][0][1], (1-uvw.x)*(1-uvw.y)*(uvw.z));
    ret.add(targetBox[1][0][1], (uvw.x)*(1-uvw.y)*(uvw.z));
    ret.add(targetBox[0][1][1], (1-uvw.x)*(uvw.y)*(uvw.z));
    ret.add(targetBox[1][1][1], (uvw.x)*(uvw.y)*(uvw.z));
    return ret;
  }

  IGeometry[] deform(IGeometry[] geom,IVec[][][] targetBox){
    for(int i=0; i < geom.length; i++){
      geom[i] = deform(geom[i], targetBox);
    }
    return geom;
  }

  IGeometry deform(IGeometry geom, IVec[][][] targetBox){
    if(geom instanceof IPoint){
      IPoint point = (IPoint)geom;
      point.pos.set(deform(point.pos, targetBox));
      point.updateGraphic();
    }
    else if(geom instanceof ICurve){
      ICurve curve = (ICurve)geom;
      IVecI[] cps = curve.cps();
      for(int i=0; i < cps.length&&cps[0]!=cps[cps.length-1] || i < cps.length-1; i++){
        cps[i].set(deform(cps[i].get(), targetBox));
      }
      curve.updateGraphic();
    }
    else if(geom instanceof ISurface){
      ISurface surface = (ISurface)geom;
      IVecI[][] cps = surface.cps();
      for(int i=0; i < cps.length; i++){
        for(int j=0; j < cps[i].length; j++){
          cps[i][j].set(deform(cps[i][j].get(), targetBox));
        }
      }
      surface.updateGraphic();
    }
    else if(geom instanceof IMesh){
      IMesh mesh = (IMesh)geom;
      for(int i=0; i < mesh.vertexNum(); i++){
        mesh.vertex(i).pos().set(deform(mesh.vertex(i).pos().get(), targetBox));
      }
      mesh.updateGraphic();
    }
    else if(geom instanceof IBrep){
      IBrep brep = (IBrep)geom;
      for(int i=0; i < brep.surfaceNum(); i++){
        IVecI[][] cps = brep.surface(i).cps();
        for(int j=0; j < cps.length; j++){
          for(int k=0; k < cps[j].length; k++){
            cps[j][k].set(deform(cps[j][k].get(), targetBox));
          }
        }
      }
      brep.updateGraphic();
    }
    return geom;
  }

  IGeometry[] makeModule(){
    int module = ((Attribute)attr()).module();
    if(module>=0){
      return deformModuleGeometry(module);
    }
    return null;
  }


  void makeGeometry(){
    if(attr()!=null && ((Attribute)attr()).noGeometry) return;
    //makePoint();
    //makeLine();
    //makeSurface();
    //makeBox();
    //makeSphere();
    //makeTangentCurve();
    //makeTangentSurface();
    //makeCSLine();
    //makeTetrahedron();
    makeModule();
  }

  void rules(){

    int maxClock6 = 7;
    if(clock(6)==maxClock6) return;

    if(clock(5)==0){ // cube going up
      if(clock(2)==0){
        if(clock(0)==8){
          next(rot(PI/2), 1).module(1);
        }
        else{
          if(clock(0)==0){
            if(clock(4)==0){
              next(jump(up()), 2).module(0);
            }
          }
          next(rot(0),0).module(0).deform();
        }
      }
      else{
        if(clock(2)==7){
          next(jump(up()), 4).module(1);
        }
        else{
          next(jump(up()), 2).module(1);
          if(IRand.pct(10)){
            if(clock(6) < maxClock6){
              next(rot(PI/3), 5).module(1); // branch a new cube going down
            }
          }
        }
      }
    }
    else{ // cube going down
      if(clock(2)==0){
        if(clock(0)==8){
          next(rot(PI/2), 1).module(1);
        }
        else{
          if(clock(0)==0){
            if(clock(4)==0){
              next(jump(down()), 2).module(1);
            }
          }
          next(rot(0), 0).module(0).deform();
        }
      }
      else{
        if(clock(2)==7){
          next(jump(down()), 4).module(0);
        }
        else{
          next(jump(down()), 2).module(1);
          if(IRand.pct(10)){
            if(clock(6) < maxClock6){
              next(rot(PI/3), 6).module(1); // branch a new cube going up
            }
          }
        }
      }
    }

  }
}


(back to the list of tutorials)

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