heerdyes_processing-sketching/ctlsys/zU/ctlsys/bhautazAstrayantra.pde
static class PVector {
  float x;
  float y;

  PVector(float _x, float _y) {
    x=_x;
    y=_y;
  }

  PVector(PVector v1) {
    x=v1.x;
    y=v1.y;
  }

  PVector add(PVector v) {
    x=x+v.x;
    y=y+v.y;
    return this;
  }

  PVector mult(float k) {
    x=x*k;
    y=y*k;
    return this;
  }

  PVector sub(PVector v) {
    x=x-v.x;
    y=y-v.y;
    return this;
  }

  PVector div(float n) {
    if (n==0) {
      println("n=0. will not div vector by 0");
      return this;
    }
    x=x/n;
    y=y/n;
    return this;
  }

  float mag() {
    return sqrt(x*x+y*y);
  }

  PVector normalize() {
    float m=mag();
    if (m!=0) {
      div(m);
    }
    return this;
  }

  PVector limit(float max) {
    if (mag()>max) {
      normalize();
      mult(max);
    }
    return this;
  }

  static PVector add(PVector v1, PVector v2) {
    PVector v3=new PVector(v1.x+v2.x, v1.y+v2.y);
    return v3;
  }

  static PVector sub(PVector v1, PVector v2) {
    PVector v3=new PVector(v1.x-v2.x, v1.y-v2.y);
    return v3;
  }

  static PVector mult(PVector v, float k) {
    PVector w=new PVector(v.x*k, v.y*k);
    return w;
  }

  static PVector div(PVector v, float k) {
    if (k==0) {
      println("[vector_div] cannot divide by zero. not updating vector");
      return v;
    }
    PVector w=new PVector(v.x/k, v.y/k);
    return w;
  }
}

class Mover {
  PVector location;
  PVector velocity;
  PVector acceleration;
  float topspeed;
  float mass;
  float r;

  Mover(float _m, float _r) {
    location=new PVector(random(5, width-5), random(5, height-5));
    velocity=new PVector(0, 0);
    acceleration=new PVector(0, 0);
    mass=_m;
    r=_r;
  }

  void update() {
    velocity.add(acceleration);
    location.add(velocity);
    acceleration.mult(0);
  }

  void render() {
    stroke(0);
    fill(0, 175, 0);
    ellipse(location.x, location.y, r, r);
  }

  void checkedges() {
    if (location.x>width) {
      location.x=width;
      velocity.x*=-0.95;
    } else if (location.x<0) {
      velocity.x*=-0.95;
      location.x=0;
    }

    if (location.y>height) {
      velocity.y*=-0.95;
      location.y=height;
    }
  }

  void applyforce(PVector force) {
    PVector f=PVector.div(force, mass);
    acceleration.add(f);
  }
}

class Atom extends Mover{
  color c;
  
  Atom(color c){
    super(1.0,1.5);
    this.c=c;
  }
}

interface Field2D {
  PVector fxy(float x, float y);
  PVector fxy(PVector v);
}

class CentralGravity implements Field2D {
  String name;
  float scalefactor;

  CentralGravity(String nm,float sf) {
    name=nm;
    scalefactor=sf;
  }

  PVector fxy(float x, float y) {
    PVector loc=new PVector(x, y);
    PVector cxy=new PVector(width/2, height/2);
    return PVector.sub(loc, cxy).normalize().mult(-1*scalefactor);
  }
  
  PVector fxy(PVector v){
    return fxy(v.x,v.y);
  }
}

class MoverSystem {
  ArrayList<Mover> movers;
  CentralGravity cg;
  PVector attractor;
  boolean nearfield;
  boolean attractorengaged;

  MoverSystem() {
    movers=new ArrayList<Mover>();
    cg=new CentralGravity("grav",0.75);
    attractor=new PVector(0, 0);
    nearfield=false;
    attractorengaged=false;
  }

  void addmover(Mover m) {
    movers.add(m);
  }

  void applyforce(PVector force) {
    for (Mover m : movers) {
      m.applyforce(force);
    }
  }

  void computeattractor(Mover m) {
    attractor=cg.fxy(m.location);
  }

  void enliven() {
    for (Mover m : movers) {
      PVector nf=null;
      if (attractorengaged) {
        computeattractor(m);
        nf=PVector.add(attractor, PVector.mult(gravity, m.mass));
      } else {
        nf=PVector.mult(gravity, m.mass);
      }
      m.applyforce(nf);
      m.update();
      m.checkedges();
      m.render();
    }
  }
}
