• fullscreen
  • GlaucusAtlanticus.pde
  • float springRate = 0.1;
    float damping = 0.2;
    float angularSpringRate = 0.2;
    float angularDamping = 0.2;
    float angularMomentum = 150;
    float globalDamping = 0.0005;
    node[] nodes;
    element[] elements;
    node grippedNode;
    boolean nodeIsGripped = false;
    float mouseSpringRate = 0.1;
    int iterationRate = 8;
    int scaleUpFactor = 3;
    int scaleDownFactor = 8;
    void setup(){
      PImage Glaucus = loadImage("j40_1279988272.jpeg");
      PImage GlaucusMask = loadImage("GlaucusMask.jpg");
      GlaucusMask.filter(INVERT);
      Glaucus.mask(GlaucusMask);
      size(800,600);
    //  Glaucus.resize(Glaucus.width*scaleUpFactor/scaleDownFactor,0);
      Glaucus.resize(Glaucus.width/scaleDownFactor,0);
      GlaucusMask.resize(GlaucusMask.width/scaleDownFactor,0);
      nodes = new node[0];
      elements = new element[0];
      boolean[][] maskData = new boolean[GlaucusMask.width][GlaucusMask.height];
      node[][] tempNodes = new node[GlaucusMask.width][GlaucusMask.height];
      for(int i=0;i<GlaucusMask.width;i++){
        for(int j=0;j<GlaucusMask.height;j++){
          maskData[i][j] = brightness(GlaucusMask.get(i,j)) > 128;
          if(maskData[i][j]){
    //        PImage img = Glaucus.get(
    //        round(i*scaleDownFactor/scaleUpFactor)+25,
    //        round(j*scaleDownFactor/scaleUpFactor)+25,
    //        round(3*scaleUpFactor),round(3*scaleUpFactor));
            node newNode = new node(i,j,Glaucus.get(i,j));
    //        node newNode = new node(i,j,img);
            tempNodes[i][j] = newNode;
            nodes = (node[]) append(nodes,newNode);
          }
        }
      }
      PVector directionX = new PVector(0.5,0);
      directionX.mult(scaleUpFactor);
      for(int i=1;i<GlaucusMask.width;i++){
        for(int j=0;j<GlaucusMask.height;j++){
          if(maskData[i-1][j]&&maskData[i][j]){
            elements = (element[]) append(elements,
            new element(tempNodes[i-1][j],tempNodes[i][j],directionX));
          }
        }
      }
      PVector directionY = new PVector(0,0.5);
      directionY.mult(scaleUpFactor);
      for(int i=0;i<GlaucusMask.width;i++){
        for(int j=1;j<GlaucusMask.height;j++){
          if(maskData[i][j-1]&&maskData[i][j]){
            elements = (element[]) append(elements,
            new element(tempNodes[i][j-1],tempNodes[i][j],directionY));
          }
        }
      }
      cleanUpStrayNodes();
      println(nodes.length+" nodes");
      println(elements.length+" elements");
      strokeWeight(2*scaleUpFactor);
      smooth();
      imageMode(CENTER);
    }
    void draw(){
      background(255);
      for(int m=0;m<iterationRate;m++){
        for(int i=0;i<nodes.length;i++){
          nodes[i].update();
        }
        for(int i=0;i<elements.length;i++){
          elements[i].update();
        }
        if(nodeIsGripped){
          PVector force = PVector.mult(PVector.sub(new PVector(mouseX,mouseY),
          grippedNode.location),mouseSpringRate);
          grippedNode.forceSum.add(force);
        }
      }
      for(int i=0;i<nodes.length;i++){
        stroke(nodes[i].clr);
        point(nodes[i].location.x,nodes[i].location.y);
    
    //    pushMatrix();
    //    translate(nodes[i].location.x,nodes[i].location.y);
    //    rotate(nodes[i].rotation);
    //    image(nodes[i].img,0,0);
    //    popMatrix();
      }
    }
    void mousePressed(){
      PVector mouse = new PVector(mouseX,mouseY);
      for(int i=0;i<nodes.length;i++){
        if(PVector.sub(nodes[i].location,mouse).mag()<scaleUpFactor*1.5){
          nodeIsGripped = true;
          grippedNode = nodes[i];
        }
      }
    }
    void mouseReleased(){
      nodeIsGripped = false;
    }
    class node{
      color clr;
      PImage img;
      PVector location;
      PVector velocity;
      PVector forceSum;
      float rotation = 0;
      float angularVelocity = 0;
      float torqueSum = 0;
      boolean isTouchedByElement = false;
    //  node(int x,int y,PImage img){
      node(int x,int y,color clr){
        this.clr = clr;
    //    this.clr = color(#000000);
    //    this.img = img;
        location = new PVector(x,y);
        location.mult(scaleUpFactor);
        velocity = new PVector();
        forceSum = new PVector();
      }
      void applyForce(PVector force, PVector at){
        torqueSum += PVector.sub(at,location).cross(force).z;
        forceSum.add(force);
      }
      PVector toWorld(PVector at){
        float angle = atan2(at.y,at.x)+rotation;
        float magnitude = mag(at.y,at.x);
        return PVector.add(
        new PVector(magnitude*cos(angle),magnitude*sin(angle))
        ,location);
      }
      PVector totalVelocity(PVector at){
        return PVector.add(velocity,
        new PVector(0,0,angularVelocity).cross(PVector.sub(at,location)));
      }
      void update(){
        location.add(velocity);
        velocity.add(forceSum);
        velocity.mult(1.0-globalDamping);
        forceSum = new PVector();
        rotation += angularVelocity;
        angularVelocity += torqueSum/angularMomentum;
        angularVelocity = constrain(angularVelocity,-PI/4,PI/4);
        torqueSum = 0;
      }
    }
    class element{
      node nodeA;
      node nodeB;
      PVector direction;
      element(node A,node B,PVector dir){
        nodeA = A;
        nodeB = B;
        nodeA.isTouchedByElement = true;
        nodeB.isTouchedByElement = true;
        direction = dir;
      }
      void update(){
        PVector middleA = nodeA.toWorld(direction);
        PVector middleB = nodeB.toWorld(PVector.mult(direction,-1));
        PVector middleAB = PVector.mult(PVector.add(middleB,middleA),0.5);
        PVector force = PVector.mult(PVector.sub(middleB,middleA),springRate);
        PVector velocityA = nodeA.totalVelocity(middleAB);
        PVector velocityB = nodeB.totalVelocity(middleAB);
        force.add(PVector.mult(PVector.sub(velocityB,velocityA),damping));
        float torque = (nodeB.rotation-nodeA.rotation)*angularSpringRate;
        torque += (nodeB.angularVelocity-nodeA.angularVelocity)*angularDamping;
        nodeA.applyForce(force,middleAB);
        nodeB.applyForce(PVector.mult(force,-1),middleAB);
        nodeA.torqueSum += torque;
        nodeB.torqueSum -= torque;
      }
    }
    void cleanUpStrayNodes(){
      for(int i=nodes.length-1;i>=0;i--){
        if(nodes[i].isTouchedByElement==false){
          nodes = 
          (node[]) concat(
          (node[]) subset(nodes,0,i),
          (node[]) subset(nodes,i+1));
        }
      }
    }
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    Asher Salomon
    bitcraft
    10 Sep 2012
    Then the LORD God breathed into the pixels the breath of life, and the JPEG became a living creature.
    Felix Woitzel
    11 Sep 2012
    really cool one! skeleton animation next, or what? - made me think of my experiments with framsticks years ago: http://www.framsticks.com/muscles_and_receptors
    Ashley Brown
    11 Sep 2012
    such great code and great results.
    amazing.
    Nickson Yap
    8 Jun 2013
    Wobbly.
    Pierre MARZIN
    22 Jun 2014
    So good! Bravo!
    You need to login/register to comment.