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

Agent-Based Panelization Algorithm

     1

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60);
  
  IPoint[] pts = IG.points();
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 60;
  int vdiv = 30;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 5; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; //1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    double ratio = shiftRatio();
    
    IVec p1 = pt(0, 0);
    IVec p2 = pt(ratio, 0);
    IVec p3 = pt(ratio, 1);
    IVec p4 = pt(0, 1);
    
    ISurface rect = new ISurface(p1,p2,p3,p4);
    
    addGeom(rect);
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}

static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
}


     2

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 60;
  int vdiv = 30;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 5; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}

class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; //1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    double ratio = shiftRatio();
    
    IVec p1 = pt(0, 0);
    IVec p2 = pt(ratio, 0);
    IVec p3 = pt(ratio, 1-ratio);
    IVec p4 = pt(0, 1-ratio);
    
    ISurface rect = new ISurface(p1,p2,p3,p4);
    
    addGeom(rect);
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
}


     3

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 20;
  int vdiv = 30;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 5; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
}



class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; //1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    double ratio = shiftRatio();
    
    IVec p1 = pt(0, 0);
    IVec p2 = pt(1, 0);
    IVec p3 = pt(1, 1);
    IVec p4 = pt(0, 1);
    
    IVec axis = uvec();
    
    p1.rot(p3, axis, ratio * -PI/2);
    p2.rot(p3, axis, ratio * -PI/2);
    
    ISurface rect = new ISurface(p1,p2,p3,p4);
    rect.clr(0, 0, ratio);
    addGeom(rect);
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     4

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  //IG.open("srf1.3dm");
  //ISurface s = IG.surface(0);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 20;
  int vdiv = 30;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 5; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
}



class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; //1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    double ratio = shiftRatio();
    
    IVec p1 = pt(0, 0);
    IVec p2 = pt(1, 0);
    IVec p3 = pt(1, 1);
    IVec p4 = pt(0, 1);
    
    IVec axis = uvec();
    
    p1.rot(p3, axis, ratio * -PI/2);
    p2.rot(p3, axis, ratio * -PI/2);
    
    ISurface rect = new ISurface(p1,p2,p3,p4);
    rect.clr(0, 0, ratio);
    addGeom(rect);
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     5

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 20;
  int vdiv = 30;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 5; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}



class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; // 1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    double ratio = shiftRatio();

    IVec[][] pts = new IVec[4][2];
     
    pts[0][0] = pt(0, 0); 
    pts[0][1] = pt(0, 1);
    pts[1][0] = pt(1.0/3, 0);
    pts[1][1] = pt(1.0/3, 1); 
    pts[2][0] = pt(2.0/3, 0);
    pts[2][1] = pt(2.0/3, 1);
    pts[3][0] = pt(1, 0);
    pts[3][1] = pt(1, 1);
    
    IVec axis = uvec();
        
    // rotating around the center pts[1][2] and the axis rotAxis
    pts[2][0].rot(pts[0][1], axis, ratio * -PI/2);
    // rotating around the center pts[1][3] and the axis rotAxis
    pts[3][0].rot(pts[0][1], axis, ratio * -PI/2);
    
    ISurface rect = new ISurface(pts, 3, 1);
    rect.clr(0, ratio*0.5 + 0.5, 0);
    addGeom(rect);
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry > ();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
}


     6

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); 
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 40;
  int vdiv = 20;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 5; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec p1 = pt(0, 0);
    IVec p2 = pt(1, 0);
    IVec p3 = pt(1, 1);
    IVec p4 = pt(0, 1);
    
    shiftScale(0.1);
    
    double ratio = shiftRatio()*0.9 + 0.1; // 0 - 1 + 0.1
    
    IVec center = center();
    
    IVec[] pts = new IVec[]{ p1, p2, p3, p4 };
    ICurve rect = new ICurve(pts, true);
    
    ICurve rect2 = rect.cp();
    rect2.scale(center, ratio);
    
    ISurface srf = IG.loft(rect2, rect);
    
    addGeom(srf);
    
    rect.del();
    rect2.del();
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r>1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot>ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot>vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot>maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
}


     7

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  //IG.open("srf1.3dm");
  //ISurface s = IG.surface(0);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); 
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 40;
  int vdiv = 20;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 10; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; // 1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec center = pt(0.5, 0.5);
    IVec shift = shift();
    ICurve line = new ICurve(center, center.cp().add(shift));
    line.clr(1.0,0,0);
    
    panelGeom.add(line);
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r>1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot>ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot>vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){ return nshift(-1,-1); } 
  
  IVec nshift(double maxLen){ return nshift(maxLen,-1); } 
  
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot>maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot>0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot>0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     8

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 40;
  int vdiv = 20;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 10; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; // 1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec p1 = pt(0,0);
    IVec p2 = pt(1,0);
    IVec p3 = pt(1,1);
    IVec p4 = pt(0,1);
    
    double u = uratio();
    double v = vratio();
    double n = nratio();
    IVec center = pt(0.5 + u, 0.5 + v, n);
    
    ISurface s1 = new ISurface(p1,p2,center).clr(u+0.5, v+0.5, n);
    ISurface s2 = new ISurface(p2,p3,center).clr(u+0.5, v+0.5, n);
    ISurface s3 = new ISurface(p3,p4,center).clr(u+0.5, v+0.5, n);
    ISurface s4 = new ISurface(p4,p1,center).clr(u+0.5, v+0.5, n);
    
    panelGeom.add(s1);
    panelGeom.add(s2);
    panelGeom.add(s3);
    panelGeom.add(s4);
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){ return nshift(-1,-1); } 
  
  IVec nshift(double maxLen){ return nshift(maxLen,-1); } 
  
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
}


     9

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); 
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 40;
  int vdiv = 20;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 10; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; // 1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    double u = uratio();
    double v = vratio();
    
    if(Math.abs(u) > 0.01 && Math.abs(v) > 0.01){  
      IVec p1 = pt(0.5 + u, 0.5 - v);
      IVec p2 = pt(0.5 + u, 0.5 + v);
      IVec p3 = pt(0.5 - u, 0.5 + v);
      IVec p4 = pt(0.5 - u, 0.5);
      IVec p5 = pt(0.5, 0.5);
      IVec p6 = pt(0.5, 0.5 - v);
      
      ICurve outline = new ICurve(new IVec[]{ p1,p2,p3,p4,p5,p6 }, true);
      
      panelGeom.add(new ISurface(outline));
      outline.del();
    }

  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot<-ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){ return nshift(-1,-1); } 
  
  IVec nshift(double maxLen){ return nshift(maxLen,-1); } 
  
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i<0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     10

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);

  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); 
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 40;
  int vdiv = 20;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      //nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int j=0; j < vdiv; j++){
    if(j%2==0){
      for(int i=0; i < udiv-1; i+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+2][j], nodes[i+2][j+1], nodes[i][j+1] });
      }
    }
    else{
      for(int i=1; i < udiv-1; i+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+2][j], nodes[i+2][j+1], nodes[i][j+1] });
      }
    }
  }

  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 10; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; // 1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec p1 = pt(0,0);
    IVec p2 = pt(1,0);
    IVec p3 = pt(1,1);
    IVec p4 = pt(0,1);
    double u = uratio();
    if(u==0){
      panelGeom.add(new ISurface(p1,p2,p3,p4));
    }
    else if(u>0){
      
      IVec q1 = pt(0.5, 0.6, -2);
      IVec q2 = pt(0.5+u, 0.6, -2);
      IVec q3 = pt(0.5+u, 0.8, -2);
      IVec q4 = pt(0.5, 0.8, -2);
      
      ICurve rect1 = new ICurve(new IVec[]{ p1,p2,p3,p4 }, true);
      ICurve rect2 = new ICurve(new IVec[]{ q1,q2,q3,q4 }, true); 
      ISurface srf = IG.loft(rect2,rect1).clr(u+0.5,0.5,0.5);
      rect1.del();
      rect2.del();
      addGeom(srf);
    }
    else{ // u<0
      IVec q1 = pt(0.5+u, 0.6, -2);
      IVec q2 = pt(0.5, 0.6, -2);
      IVec q3 = pt(0.5, 0.8, -2);
      IVec q4 = pt(0.5+u, 0.8, -2);

      ICurve rect1 = new ICurve(new IVec[]{ p1,p2,p3,p4 }, true);
      ICurve rect2 = new ICurve(new IVec[]{ q1,q2,q3,q4 }, true); 
      ISurface srf = IG.loft(rect2,rect1).clr(0.5,0.5,-u+0.5);
      rect1.del();
      rect2.del();
      addGeom(srf);
    }
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r>1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot>ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot>vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){ return nshift(-1,-1); } 
  
  IVec nshift(double maxLen){ return nshift(maxLen,-1); } 
  
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot>maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot>0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     11


srf3.3dm

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  IG.open("srf3.3dm");
  ISurface s = IG.surface(0);
  
  //ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 80;
  int vdiv = 16;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    if(i%2==0){
      for(int j=0; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+2], nodes[i][j+2] });
      }
    }
    else{
      for(int j=1; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+2], nodes[i][j+2] });
      }
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 20; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; //1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec p1 = pt(0,0);
    IVec p2 = pt(1,0);
    IVec p3 = pt(1,1);
    IVec p4 = pt(0,1);
    
    double u = uratio();
    double v = vratio();
    double n = nratio();
    
    if(n < 0.5) n = 0.5;
    
    panelGeom.add(new ISurface(p1,p2,p3,p4).clr(0.5,1,1,0.5));
    
    IVec[][] pts = new IVec[3][2];
    pts[0][1] = pt(0,0);
    pts[0][0] = pt(1,0);
    pts[1][1] = pt(0.4 + u, 0.5 + v, n);  
    pts[1][0] = pt(0.6 + u, 0.5 + v, n);
    pts[2][1] = pt(0.5,1);
    pts[2][0] = pt(1, 1);
    panelGeom.add(new ISurface(pts));
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r>1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot>ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot>vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot>maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot>0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     12


srf2.3dm

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  IG.open("srf2.3dm");
  ISurface s = IG.surface(0);
  
  //ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 60;
  int vdiv = 24;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      //nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    if(i%2==0){
      for(int j=0; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+2], nodes[i][j+2] });
      }
    }
    else{
      for(int j=1; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+2], nodes[i][j+2] });
      }
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 20; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; //1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec p1 = pt(0,0);
    IVec p2 = pt(1,0);
    IVec p3 = pt(1,1);
    IVec p4 = pt(0,1);
    
    double u = uratio();
    double v = vratio();
    double n = nratio() * 2;
    
    addGeom(new ISurface(p1,p2,p3,p4).clr(0.5,1,1,0.5));
    
    if(n < 1) n = 1;
    IVec p5 = pt(0.5+u, 0.5+v, n);
    
    ISurface srf1 = new ISurface(p1,p5,p4).hsb(u*0.2, 1-v, 1.0);
    ISurface srf2 = new ISurface(p3,p4,p5).hsb(u*0.2, 1-v, 1.0);
    addGeom(srf1);
    addGeom(srf2);
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r>1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot>ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot>vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot>maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot>0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot>0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     13


srf2.3dm

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  IG.open("srf2.3dm");
  ISurface s = IG.surface(0);
  
  //ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 30;
  int vdiv = 24;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[(i+3)%udiv][j+1], nodes[(i+2)%udiv][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 20; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; //1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec p1 = pt(0,0);
    IVec p2 = pt(0,1);
    
    double u = uratio();
    
    IVec axis = vvec();
    IVec widthVec = axis.cross(nml());
    widthVec.unit();
    widthVec.mul(widthVec.dot(uvec()));
    widthVec.rot(axis, -(-u+0.5)*PI);
    ISurface srf = new ISurface(p1,p1.cp().add(widthVec),p2.cp().add(widthVec),p2);
    srf.hsb(u, u, 1); 
    addGeom(srf);
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     14


srf2.3dm

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  //IG.open("srf2.3dm");
  //ISurface s = IG.surface(0);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 70;
  int vdiv = 20;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      //nodes[i][j].fixU();
      //nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv-2; i++){
    for(int j=0; j < vdiv; j++){
      new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[(i+2)%udiv][j+1], nodes[(i+1)%udiv][j+1] });
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 5; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; //1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec p1 = pt(0,0);
    IVec p2 = pt(0,1);
    
    double u = uratio();
    
    IVec axis = vvec();
    IVec widthVec = axis.cross(nml());
    widthVec.unit();
    widthVec.mul(widthVec.dot(uvec()) * 0.5);
    widthVec.rot(axis, -(-u+0.5)*PI);
    
    addGeom(new ISurface(p1,p1.cp().add(widthVec),p2.cp().add(widthVec),p2));
  }
  
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     15

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  //IG.open("srf1.3dm");
  //ISurface s = IG.surface(0);

  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 30;
  int vdiv = 40;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      nodes[i][j] = new PanelNode(s, i*uinc, j*vinc );
      nodes[i][j].fixU();
      //nodes[i][j].fixV();
    }
  }
  for(int i=0; i < udiv; i++){
    if(i%2==0){
      for(int j=0; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+2], nodes[i][j+2] });
      }
    }
    else{
      for(int j=1; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+2], nodes[i][j+2] });
      }
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 10; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; //1.0;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    double v = vratio() * 1.5;
    double n = nratio();
    if(n < 0) n=0;
    
    IVec p1 = pt(0, 0.15);
    IVec p2 = pt(1, 0.35 + v);
    IVec p3 = pt(1, 0.65 + v);
    IVec p4 = pt(0, 0.85);
    IVec p5 = pt(1, 0.5 + v, n);
    
    ISurface srf1 = new ISurface(p1,p2,p5);
    ISurface srf2 = new ISurface(p1,p5,p4);
    ISurface srf3 = new ISurface(p4,p5,p3);
    srf1.clr(v*0.25+0.5);
    srf2.clr(v*0.25+0.5);
    srf3.clr(v*0.25+0.5);
    
    addGeom(srf1);
    addGeom(srf2);
    addGeom(srf3);
    
  }
    
  IVec pt(double u, double v){
    IVec upt1 = nodes[0].pos().sum(nodes[1], u);
    IVec upt2 = nodes[3].pos().sum(nodes[2], u);
    return upt1.sum(upt2, v);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[1].pos().mid(nodes[2]).dif(nodes[0].pos().mid(nodes[3]));
  }
  
  IVec vvec(){
    return nodes[2].pos().mid(nodes[3]).dif(nodes[0].pos().mid(nodes[1]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r>1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot>ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot>vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot>maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot>0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot>0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     16

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  //IG.open("srf1.3dm");
  //ISurface s = IG.surface(0);
  
  ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 70;
  int vdiv = 20;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      double ushift = ((i+j)%2 - 0.5)*2*0.2;
      nodes[i][j] = new PanelNode(s, (i-ushift)*uinc, j*vinc );
      //nodes[i][j].fixU();
      //nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    if(i%2==0){
      for(int j=0; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i+1][j+2], nodes[i][j+2], nodes[i][j+1] });
      }
    }
    else{
      for(int j=1; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i+1][j+2], nodes[i][j+2], nodes[i][j+1] });
        
      }
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 10; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1;
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec p1 = node(0);
    IVec p2 = node(1);
    IVec p3 = node(2);
    IVec p4 = node(3);
    IVec p5 = node(4);
    IVec p6 = node(5);
    
    double u = uratio() * 0.8;
    double v = uratio() * 0.8;
    double n = nratio() * -2;
    double ratio = shiftRatio() * 0.8;
    
    if(n>0) n=0;
    
    IVec center = pt(u + 0.5, v + 0.5);
    
    p1.scale(center, 1 - ratio);
    p3.scale(center, 1 - ratio);
    p5.scale(center, 1 - ratio);
    
    IVec c2 = pt(u + 0.5, v + 0.5, n );
    
    IVec[][] pts = new IVec[][]{ new IVec[]{ p1,p2,p3,p4,p5,p6 }, new IVec[]{ c2,c2,c2,c2,c2,c2 } }; 
    ISurface srf = new ISurface(pts, false, true);
    srf.clr(u+0.5,v+0.5,1.0); 
    addGeom(srf);
  }
  
  IVec pt(double u, double v){
    IVec center = center();
    IVec uvec = uvec().mul(u-0.5);
    IVec vvec = vvec().mul(v-0.5);
    return center.add(uvec).add(vvec);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[2].pos().dif(nodes[5]);
  }
  
  IVec vvec(){
    return nodes[0].pos().mid(nodes[1]).dif(nodes[3].pos().mid(nodes[4]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r>1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


     17


srf1.3dm

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

void setup(){
  IConfig.syncDrawAndDynamics = true;
  size(480,360,IG.GL);
  IG.open("srf1.3dm");
  ISurface s = IG.surface(0);
  
  //ISurface s = new ISurface(0,0,0, 200,0,0, 200,0,60, 0,0,60); //
  
  IPoint[] pts = IG.points();
  
  for(int i=0; i < pts.length; i++){
    new IAttractor(pts[i], 1).target(Swarm.class).clr(1.0,0,0).gauss(100);
  }
  new ISurfaceAttractorField(s).target(Swarm.class).intensity(100);
  
  int udiv = 70;
  int vdiv = 20;
  double uinc = 1./udiv;
  double vinc = 1./vdiv;
  PanelNode[][] nodes = new PanelNode[udiv+1][vdiv+1];
  for(int i=0; i <= udiv; i++){
    for(int j=0; j <= vdiv; j++){
      double ushift = ((i+j)%2 - 0.5)*2*0.2;
      nodes[i][j] = new PanelNode(s, (i-ushift)*uinc, j*vinc );
      nodes[i][j].fixU();
      nodes[i][j].fixV();
    }
  }
  
  for(int i=0; i < udiv; i++){
    if(i%2==0){
      for(int j=0; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i+1][j+2], nodes[i][j+2], nodes[i][j+1] });
      }
    }
    else{
      for(int j=1; j < vdiv-1; j+=2){
        new PanelAgent(new PanelNode[]{ nodes[i][j], nodes[i+1][j], nodes[i+1][j+1], nodes[i+1][j+2], nodes[i][j+2], nodes[i][j+1] });
        
      }
    }
  }
  
  // distance threshold for swarm to influence panels
  Swarm.panelReachDist = 10; 
  
  for(int i=0; i < 50; i++){
    new Swarm(s.pt(IRand.pt(0,0,-5,1,1,5)), IRand.dir(10)).clr(1.0);
  }
  
  s.del();
  IG.pers();
}


class PanelAgent extends IAgent{
  PanelNode[] nodes;
  IVec pos;
  IVec dir;
  double dirScale = 0.1; // 1.0
  boolean needUpdate;
  ArrayList < IGeometry > panelGeom;
  
  PanelAgent(PanelNode[] n){
    nodes = n;
    dir = new IVec();
    needUpdate = true;
  }
  
  void makePanel(){
    IVec p1 = node(0);
    IVec p2 = node(1);
    IVec p3 = node(2);
    IVec p4 = node(3);
    IVec p5 = node(4);
    IVec p6 = node(5);
    
    IVec center = center();
    
    ICurve hex1 = new ICurve(new IVec[]{ p1,p2,p3,p4,p5,p6 }, true);
    ICurve hex2 = hex1.cp().scale(center, 0.66);
    ICurve hex3 = hex1.cp().scale(center, 0.33);
    
    IVec shift = shift();
    double max = 2;
    if(shift.len() > max) shift.len(max); 
    
    hex3.add(shift);
    ISurface srf = IG.loft(new ICurve[]{ hex3, hex2, hex1 } );
    srf.clr(uratio()*0.5, vratio()*0.5+0.5, 0.5); 
    addGeom(srf);
    
    hex1.del();
    hex2.del();
    hex3.del();
  }
  
  IVec pt(double u, double v){
    IVec center = center();
    IVec uvec = uvec().mul(u-0.5);
    IVec vvec = vvec().mul(v-0.5);
    return center.add(uvec).add(vvec);
  }
  
  IVec pt(double u, double v, double n){
    return pt(u,v).add(nml().mul(n));
  }
  
  void addGeom(IGeometry g){
    panelGeom.add(g);
  }
  
  void addForce(IVec frc){
    needUpdate = true;
    dir.add(frc, 0.1);
  }
  
  void interact(ArrayList < IDynamics > agents){
    pos = center();
    for(int i=0;i < agents.size(); i++){
      if(agents.get(i) instanceof PanelNode){
        PanelNode n = (PanelNode)agents.get(i);
        double threshold = 10;
        double dist = n.pos().dist(pos);
        if(dist < threshold){ 
          n.push(dir.cp().mul((threshold-dist)/threshold*0.1));
        }
      }
    }
  }
  
  IVec uvec(){
    return nodes[2].pos().dif(nodes[5]);
  }
  
  IVec vvec(){
    return nodes[0].pos().mid(nodes[1]).dif(nodes[3].pos().mid(nodes[4]));
  }
  
  IVec nml(){
    return uvec().cross(vvec()).unit();
  }
  
  IVec center(){
    IVec cnt = nodes[0].pos().cp();
    for(int i=1; i < nodes.length; i++){
      cnt.add(nodes[i].pos());
    }
    return cnt.div(nodes.length);
  }
  
  double shiftAmount(){
    return dir.len()*dirScale;
  }
  
  double shiftRatio(){ // returns 0 - 1
    double r = shiftAmount();
    if(r > 1) r=1;
    return r;
  }
  
  void shiftScale(double ratio){
    dirScale = ratio;
  }
  
  IVec shift(){
    return dir.cp().mul(dirScale); 
  }
  
  IVec ushift(){
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/ulen;
    if(dot > ulen/2) dot = ulen/2; // half is limit
    else if(dot < -ulen/2) dot = -ulen/2;
    return uv.mul(dot/ulen);
  }
  IVec vshift(){
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/vlen;
    if(dot > vlen/2) dot = vlen/2; // half is limit
    else if(dot < -vlen/2) dot = -vlen/2;
    return vv.mul(dot/vlen);
  }
  
  IVec nshift(){
    return nshift(-1,-1); 
  }
  IVec nshift(double maxLen){
    return nshift(maxLen,-1); 
  }
  IVec nshift(double maxLen, double minLen){
    IVec n = nml();
    double dot = dir.dot(n)*dirScale;
    if(maxLen!=-1){
      if(dot > maxLen) dot = maxLen;
      if(minLen!=-1){
        if(dot < minLen) dot = minLen;
      }
    }
    return n.mul(dot);
  }
  
  double uratio(){ // returns -0.5 to 0.5
    IVec uv = uvec();
    double ulen = uv.len(); 
    double dot = dir.dot(uv)*dirScale/(ulen*ulen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  } 
  double vratio(){ // returns -0.5 to 0.5
    IVec vv = vvec();
    double vlen = vv.len(); 
    double dot = dir.dot(vv)*dirScale/(vlen*vlen);
    if(dot > 0.5) dot = 0.5; // half is limit
    else if(dot < -0.5) dot = -0.5;
    return dot;
  }
  double nratio(){ // no limit
    return dir.dot(nml())*dirScale;
  }
  
  IVec node(int i){
    if(i < 0 || i >= nodes.length) return new IVec();
    return nodes[i].pos().cp();
  }
  
  
  void update(){
    needUpdate = true; // every time
    if(needUpdate){
      if(panelGeom!=null){
        for(int i=0; i < panelGeom.size(); i++){
          panelGeom.get(i).del();
        }
      }
      panelGeom = new ArrayList < IGeometry >();
      makePanel();
      needUpdate = false;
    }
  }
}

class PanelNode extends IParticleOnSurface{
  ISurface surf;
  PanelNode(ISurface s, double u, double v){
    super(s,u,v);
    fric(0.1);
    
    IParticle origPt = new IParticle(s.pt(u,v)).fix();
    new ITension(origPt, this).tension(4);
    origPt.hide();
    hide();
  }
}


static class Swarm extends IBoid{
  static double panelReachDist = 10;
  
  Swarm(IVec p, IVec v){ 
    super(p,v);
    cohesionRatio(10);
    cohesionDist(10);
    separationRatio(10);
    separationDist(10);
    alignmentRatio(0);
    alignmentDist(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof PanelAgent){
        PanelAgent panel = (PanelAgent)agents.get(i);
        double dist = panel.center().dist(pos()); 
        if(dist < panelReachDist){
          panel.addForce(vel().cp().mul((panelReachDist-dist)/panelReachDist));
        }
      }
    }
  }
   
}


(back to the list of tutorials)

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