Verlet physics glitches

edited July 2016 in Questions about Code

Hi, I'm not sure if this is the best spot to ask this, but I thought it might be worth a shot to see if the glitches in my code could be fixed.

I've been making a verlet physics program, but, instead of having actual shapes programmed, I built all the shapes out of constraints and have only point - constraint collisions. Right now I have a way to detect collisions:

void shapeShapeCollision(Atom a, Link b) {//!!!!!!!!!!!!!!!
  if(b.bonk){
    PVector futL = PVector.sub(b.b.pos.copy(),b.a.pos.copy());
    PVector pasL = PVector.sub(b.b.ppos.copy(),b.a.ppos.copy());
    PVector ppoint = PVector.sub(a.ppos.copy(),b.a.ppos.copy());
    PVector point = PVector.sub(a.pos.copy(),b.a.pos.copy());
//this is translating,
//   rotating and rescaling where the constraint was to where it will
 //be and checks if the translated old point and the new point 
//crossed the where the constraint is.
    float mx = futL.x*pasL.x+futL.y*pasL.y;
    float my = futL.y*pasL.x-futL.x*pasL.y;
    float q = pasL.x*pasL.x+pasL.y*pasL.y;
    PVector newppoint = new PVector(ppoint.x*mx-ppoint.y*my,ppoint.x*my+ppoint.y*mx);
    newppoint.div(q);
    if(acrossLine(newppoint,point,new PVector(0,0),futL)) {
      if(acrossLine(new PVector(0,0),futL,newppoint,point)) { 
        b.hit(true);
        collide(a,b);
      }
    }
  }
}

void collide(Atom a, Link b) {
  PVector aToPos = a.pos.copy();//this is from the constraint's left side to the point
  aToPos.sub(b.a.pos);
  PVector aTob = b.b.pos.copy();this is from the line's left to its right side
  aTob.sub(b.a.pos);
  float z = aTob.mag();
  PVector substitute = aTob.copy();
  PVector notNorm = PVector.mult(substitute.normalize(), (aTob.dot(aToPos))/aTob.mag());
  float x = notNorm.mag();
  PVector norm = PVector.sub(aToPos, notNorm);
  float y = norm.mag();
  float t = (z*z)/(2*(z*z-x*z+x*x))+.2;
  a.move(PVector.mult(norm, -t));
  b.moveA(PVector.mult(norm, (1-x/z)*t));
  b.moveB(PVector.mult(norm, (x/z)*t));
}

These work well with a few shapes. Unfortunately, for more than three shapes composed of points and lines if one shape collides with another it could push that one into a third without detection, and, since I'm only checking collisions with constraints, the point becomes stuck in the square.

Additionally, every once in a while if two shapes collide too hard a 'black hole' forms, sucking everything into it. Finally, is it possible to make the squares less bouncy? dropping one onto the other really squishes the one on the bottom.

Thanks for your help!

If you need to see the sketch I can post everything later.

Tagged:

Answers

  • Hmm. Maybe if I clarify what my guess for the problem is. Is it possible, using verlet physics, to guarantee that every collision has been solved for and no objects are intersecting each frame?

  • don't expect us to be able to help you debug a program that we cannot run.

  • edited July 2016
    int simDepth;
    boolean[] keys = new boolean[255];
    ArrayList<Pointmass> points = new ArrayList<Pointmass>();
    ArrayList<Link> links = new ArrayList<Link>();
    Screen scr;
    Clock clock;
    void setup() {
      size(1000,600);
      scr = new Screen(width,height);
      simDepth = 16;
      clock = new Clock(simDepth);
    }
    
    
    void draw() {
      //setup
      background(255);
      int itNum = clock.run();
      //physics loop
      for (int i = 0; i < itNum; i++) {
        for (Pointmass p : points) {
          p.run();                 }//closed
        for (Link l : links) {
          l.run();           }//closed
    
        for (Pointmass p : points) {//has to come after everything
          for (Link l : links) {
            if ((!(l.a==p))&&(!(l.b==p))){
              shapeShapeCollision(p,l);  }}}//closed
      }
      for (Pointmass p : points) {
          p.display();           }
      for (Link l : links) {
          l.display();     }//closed
    
      border();
    }
    
    
    void mouseClicked() {
      mouseClickedRect(new PVector(mouseX,mouseY),new PVector(100,100));
    }
    
     //void mouseDragged(){
        //scr.move(new PVector(pmouseX-mouseX,pmouseY-mouseY));
     //}
    
    void keyPressed() {
      keys[keyCode] = true; }
    
    void keyReleased() {
      keys[keyCode] = false; }
    
    
    class Clock {
    
      int elapsedTime;
      int lastTime;
      int leftOverTime;
      int timesteps;
      int magicNumPerS;
    
      Clock(int magicNumPerS_) {
        timesteps = 0;
        elapsedTime = 0;
        lastTime = 0;
        leftOverTime = 0;
        magicNumPerS = magicNumPerS_;}//closed
    
      int run() {
        int mil = millis();
        elapsedTime = mil - lastTime;
        lastTime = mil;
        elapsedTime += leftOverTime;
        timesteps = floor(elapsedTime/magicNumPerS);
        leftOverTime = elapsedTime - timesteps*magicNumPerS;
        return timesteps;}//closed
    }
    
    class Link {
      Pointmass a;
      Pointmass b;
      float lnth;
      float breakingDistance;
      PVector aloc;
      PVector bloc;
      float d;
      float elasticity;
      boolean intact;
      boolean hit=false;
      boolean bonk;
    
      Link(Pointmass a_,Pointmass b_, float length_, float elasticity_,boolean bonk_) {
        a = a_;
        b = b_;
        lnth = length_;
        aloc = new PVector();
        bloc = new PVector();
        elasticity = elasticity_;
        intact = true;
        bonk = bonk_;
      }
    
    
    
      void run() {
        aloc = a.pos.copy();
        bloc = b.pos.copy();
        bloc.sub(aloc);//now bloc is the vector pointing from a to b
        d = bloc.mag();
        bloc.mult((lnth - d) / (d*2));
        bloc.mult(elasticity);
        a.pos.sub(bloc);
        b.pos.add(bloc);
        hit(false);
      }
    
      void display() {
        if(hit==true){strokeWeight(6);}
        if(hit==false){strokeWeight(1);}
        scr.lines(a.pos,b.pos);
        //line(a.pos.x,a.pos.y,b.pos.x,b.pos.y);
      }
      void moveA(PVector dis) {
        a.move(dis);
      }
      void moveB(PVector dis) {
        b.move(dis);
      }
      void hit(boolean l) {
        hit=l;
      }
    }
    
    
    class Pointmass {
    
      PVector pos = new PVector();
      PVector ppos = new PVector();
      PVector acc = new PVector();
      PVector fpos = new PVector();
      PVector screen = new PVector();
      float r;
    
      Pointmass(PVector pos_, PVector ppos_, PVector acc_,float radius_, int simDepth_) {
        fpos = new PVector(0,0);
        pos = pos_.copy();
        ppos = ppos_.copy();
        acc = acc_.copy();
        acc.mult(simDepth_).mult(simDepth_);
        r = radius_;
      }
    
      void run() {
        move();
        screenCheck();
      }
    
      void display() {
        fill(0);
        strokeWeight(1);
        scr.oval(pos,r,r);
        //ellipse(pos.x,pos.y,r,r);
      }
    
      void screenCheck() {
        pos.x = constrain(pos.x, 0, width);
        pos.y = constrain(pos.y, 0, height);
      }
    
      void move() {
        fpos.x = 2*pos.x - ppos.x + acc.x;
        fpos.y = 2*pos.y - ppos.y + acc.y;
        ppos = pos.copy();
        pos = fpos.copy();
      }
    
      void move(PVector dis) {
        pos.add(dis);
      }
    }
    
    
    class Screen {
    
      PVector dimensions;
      PVector location;
      PVector shapeStorage;
    
      Screen(int x, int y) {
       dimensions = new PVector(x,y);
       location = new PVector(0,0);
       shapeStorage = new PVector(0,0);
      }
    
      void shapeStuffAndPushMatrix(PVector absloc, float rotation) {
        shapeStorage = PVector.sub(absloc,location);
        pushMatrix();
        translate(shapeStorage.x,shapeStorage.y);
        rotate(radians(rotation)); //You need a popMatrix after this!
      }
    
      void rectangle(PVector absloc, int x, int y, float rotation) {
        shapeStuffAndPushMatrix(absloc,rotation);
        rect(0,0,x,y);
        popMatrix();
      }
      void rectangle(PVector absloc, int x, int y) {
        shapeStuffAndPushMatrix(absloc, 0);
        rect(0,0,x,y);
        popMatrix();
      }
      void rectangle(PVector absloc, float x, float y, float rotation) {
        shapeStuffAndPushMatrix(absloc,rotation);
        rect(0,0,x,y);
        popMatrix();
      }
      void rectangle(PVector absloc, float x, float y) {
        shapeStuffAndPushMatrix(absloc, 0);
        rect(0,0,x,y);
        popMatrix();
      }
    
      void oval(PVector absloc, int x, int y, float rotation) {
        shapeStuffAndPushMatrix(absloc, rotation);
        ellipse(0,0,x,y);
        popMatrix();
      }
      void oval(PVector absloc, int x, int y) {
        shapeStuffAndPushMatrix(absloc, 0);
        ellipse(0,0,x,y);
        popMatrix();
      }
      void oval(PVector absloc, float x, float y, float rotation) {
        shapeStuffAndPushMatrix(absloc, rotation);
        ellipse(0,0,x,y);
        popMatrix();
      }
      void oval(PVector absloc, float x, float y) {
        shapeStuffAndPushMatrix(absloc, 0);
        ellipse(0,0,x,y);
        popMatrix();
      }
    
      void triangles(PVector absloc, PVector point1, PVector point2, float rotation) {
        shapeStuffAndPushMatrix(absloc, rotation);
        triangle(0,0,point1.x,point1.y,point2.x,point2.y);
        popMatrix();
      }
      void triangles(PVector absloc, PVector point1, PVector point2) {
        shapeStuffAndPushMatrix(absloc, 0);
        triangle(0,0,point1.x,point1.y,point2.x,point2.y);
        popMatrix();
      }
    
      void lines(PVector absloc, PVector point1,float rotation) {
        shapeStuffAndPushMatrix(absloc, rotation);
        line(0,0,point1.x,point1.y);
        popMatrix();
      }
    
      void lines(PVector absloc, PVector point1) {
        PVector relpoint = PVector.sub(point1,absloc);
        shapeStuffAndPushMatrix(absloc, 0);
        line(0,0,relpoint.x,relpoint.y);
        popMatrix();
      }
    
      void move(PVector vel) {
       location.add(vel); 
      }
    
      void centerabs(PVector loc) {
        location = PVector.add(loc,new PVector(width/2,height/2));
      }
      void centerrel(PVector loc) {
        location.add(PVector.sub(loc,new PVector(width/2,height/2)));
      }
    }
    
    
    void shapeShapeCollision(Pointmass a, Link b) {//!!!!!!!!!!!!!!!
      if(b.bonk){
        PVector futL = PVector.sub(b.b.pos.copy(),b.a.pos.copy());
        PVector pasL = PVector.sub(b.b.ppos.copy(),b.a.ppos.copy());
        PVector ppoint = PVector.sub(a.ppos.copy(),b.a.ppos.copy());
        PVector point = PVector.sub(a.pos.copy(),b.a.pos.copy());
    
        float mx = futL.x*pasL.x+futL.y*pasL.y;
        float my = futL.y*pasL.x-futL.x*pasL.y;
        float q = pasL.x*pasL.x+pasL.y*pasL.y;
        PVector newppoint = new PVector(ppoint.x*mx-ppoint.y*my,ppoint.x*my+ppoint.y*mx);
        newppoint.div(q);
        if(acrossLine(newppoint,point,new PVector(0,0),futL)) {
          if(acrossLine(new PVector(0,0),futL,newppoint,point)) {
            b.hit(true);
            collide(a,b);
          }
        }
      }
    }
    
    
    boolean acrossLine(PVector a, PVector b, PVector linePoint1, PVector linePoint2) {//title says it
     if(linePoint1.x == linePoint2.x) {
       if((a.x <= linePoint1.x && linePoint1.x <= b.x) || (a.x >= linePoint1.x && linePoint1.x >= b.x)) {
         return true;
       }
     } else {
       float m = (linePoint2.y-linePoint1.y)/(linePoint2.x-linePoint1.x);
         if((a.y >= m*(a.x-linePoint1.x) +linePoint1.y) && (b.y <= m*(b.x-linePoint1.x) +linePoint1.y)) {
         return true;
       } else {
         if((a.y <= m*(a.x-linePoint1.x) +linePoint1.y) && (b.y >= m*(b.x-linePoint1.x) +linePoint1.y)) {
         return true;
       }
       }
     }
      return false;
    }
    
    void collide(Pointmass a, Link b) {
      PVector aToPos = a.pos.copy();
      aToPos.sub(b.a.pos);
      PVector aTob = b.b.pos.copy();
      aTob.sub(b.a.pos);
      float z = aTob.mag();
      PVector substitute = aTob.copy();
      PVector notNorm = PVector.mult(substitute.normalize(), (aTob.dot(aToPos))/aTob.mag());
      float x = notNorm.mag();
      PVector norm = PVector.sub(aToPos, notNorm);
      float y = norm.mag();
      float t = (z*z)/(2*(z*z-x*z+x*x))+.2;
      a.move(PVector.mult(norm, -t));
      b.moveA(PVector.mult(norm, (1-x/z)*t));
      b.moveB(PVector.mult(norm, (x/z)*t));
    }
    
    void ballBallCollision(Pointmass a,Pointmass b) {
      PVector dist = PVector.sub(a.pos,b.pos);
      float p = dist.mag();
      if(p==0){dist = new PVector(0,a.r+b.r);}
      p = a.r + b.r - p;
      if(0<p) {
        a.move(PVector.mult(dist, .5));//a.r*a.r/(a.r*a.r + b.r*b.r)
        b.move(PVector.mult(dist,-.5));//-b.r*b.r/(a.r*a.r + b.r*b.r)
      }
    }
    
    
    void border() {
      scr.lines(new PVector(0,0),new PVector(0,height));
      scr.lines(new PVector(0,0),new PVector(width,0));
      scr.lines(new PVector(width,height),new PVector(width,0));
      scr.lines(new PVector(width,height),new PVector(0,height));  }//closed
    
    void mouseClickedRect(PVector mouse,PVector sub) {
      mouse.sub(PVector.div(sub,2));
      PVector acc = new PVector(0,.009);
      points.add(new Pointmass(mouse,mouse,acc,3,simDepth));
      points.add(new Pointmass(PVector.add(mouse,new PVector(sub.x,0)),PVector.add(mouse,new PVector(sub.x,0)),acc,3,simDepth));
      points.add(new Pointmass(PVector.add(mouse,new PVector(0,sub.y)),PVector.add(mouse,new PVector(0,sub.y)),acc,3,simDepth));
      points.add(new Pointmass(PVector.add(mouse,new PVector(sub.x,sub.y)),PVector.add(mouse,new PVector(sub.x,sub.y)),acc,3,simDepth));
      int w = points.size()-1;
      int x = points.size()-2;
      int y = points.size()-3;
      int z = points.size()-4;
      links.add(new Link(points.get(x),points.get(y),sub.x*sqrt(2),1,false));
      links.add(new Link(points.get(y),points.get(z),sub.x,1,true));
      links.add(new Link(points.get(z),points.get(w),sub.x*sqrt(2),1,false));
      links.add(new Link(points.get(w),points.get(x),sub.x,1,true));
      links.add(new Link(points.get(x),points.get(z),sub.x,1,true));
      links.add(new Link(points.get(w),points.get(y),sub.x,1,true));    }//closed
    

    This is everything. I'm not sure if the forum will format it correctly, but, if you copy paste the above into processing, it should run.

    If you know of a better way to give you it please tell me.

    Edit: it won't format correctly, but, if I leave it as plain text, when you paste it into Processing everything will reformat correctly.

  • Thanks, it's formatted now.

  • Well, since I still don't have any suggestions, I deleted some of the less consequential parts:

    boolean[] keys = new boolean[255];
    ArrayList<Pointmass> points = new ArrayList<Pointmass>();
    ArrayList<Link> links = new ArrayList<Link>();
    void setup() {
      size(1000,600);
    }
    
    
    void draw() {
      background(255);
      for (Pointmass p : points) {
        p.run();                 }//closed
      for (Link l : links) {
        l.run();           }//closed
      for (Pointmass p : points) {//has to come after everything
        for (Link l : links) {
          if ((!(l.a==p))&&(!(l.b==p))){
              shapeShapeCollision(p,l);  }}}//closed
      for (Pointmass p : points) {
          p.display();           }
      for (Link l : links) {
          l.display();     }//closed
    }
    
    
    void mouseClicked() {
      mouseClickedRect(new PVector(mouseX,mouseY),new PVector(100,100));
    }
    
    class Link {
      Pointmass a;
      Pointmass b;
      float lnth;
      PVector aloc;
      PVector bloc;
      float d;
      float elasticity;
      boolean intact;
      boolean hit=false;
      boolean bonk;
    
      Link(Pointmass a_,Pointmass b_, float length_, float elasticity_,boolean bonk_) {
        a = a_;
        b = b_;
        lnth = length_;
        aloc = new PVector();
        bloc = new PVector();
        elasticity = elasticity_;
        intact = true;
        bonk = bonk_;
      }
    
    
    
      void run() {
        aloc = a.pos.copy();
        bloc = b.pos.copy();
        bloc.sub(aloc);//now bloc is the vector pointing from a to b
        d = bloc.mag();
        bloc.mult((lnth - d) / (d*2));
        bloc.mult(elasticity);
        a.pos.sub(bloc);
        b.pos.add(bloc);
        hit(false);
      }
    
      void display() {
        if(hit==true){strokeWeight(6);}
        if(hit==false){strokeWeight(1);}
        line(a.pos.x,a.pos.y,b.pos.x,b.pos.y);
      }
      void moveA(PVector dis) {
        a.move(dis);
      }
      void moveB(PVector dis) {
        b.move(dis);
      }
      void hit(boolean l) {
        hit=l;
      }
    }
    
    
    class Pointmass {
    
      PVector pos = new PVector();
      PVector ppos = new PVector();
      PVector acc = new PVector();
      PVector fpos = new PVector();
      PVector screen = new PVector();
      float r;
    
      Pointmass(PVector pos_, PVector ppos_, PVector acc_,float radius_) {
        fpos = new PVector(0,0);
        pos = pos_.copy();
        ppos = ppos_.copy();
        acc = acc_.copy();
        acc.mult(16).mult(16);
        r = radius_;
      }
    
      void run() {
        move();
        screenCheck();
      }
    
      void display() {
        point(pos.x,pos.y);
      }
    
      void screenCheck() {
        pos.x = constrain(pos.x, 0, width);
        pos.y = constrain(pos.y, 0, height);
      }
    
      void move() {
        fpos.x = 2*pos.x - ppos.x + acc.x;
        fpos.y = 2*pos.y - ppos.y + acc.y;
        ppos = pos.copy();
        pos = fpos.copy();
      }
    
      void move(PVector dis) {
        pos.add(dis);
      }
    }
    
    
    void shapeShapeCollision(Pointmass a, Link b) {//!!!!!!!!!!!!!!!
      if(b.bonk){
        PVector futL = PVector.sub(b.b.pos.copy(),b.a.pos.copy());
        PVector pasL = PVector.sub(b.b.ppos.copy(),b.a.ppos.copy());
        PVector ppoint = PVector.sub(a.ppos.copy(),b.a.ppos.copy());
        PVector point = PVector.sub(a.pos.copy(),b.a.pos.copy());
    
        float mx = futL.x*pasL.x+futL.y*pasL.y;
        float my = futL.y*pasL.x-futL.x*pasL.y;
        float q = pasL.x*pasL.x+pasL.y*pasL.y;
        PVector newppoint = new PVector(ppoint.x*mx-ppoint.y*my,ppoint.x*my+ppoint.y*mx);
        newppoint.div(q);
        if(acrossLine(newppoint,point,new PVector(0,0),futL)) {
          if(acrossLine(new PVector(0,0),futL,newppoint,point)) {
            b.hit(true);
            collide(a,b);
          }
        }
      }
    }
    
    
    boolean acrossLine(PVector a, PVector b, PVector linePoint1, PVector linePoint2) {//title says it
     if(linePoint1.x == linePoint2.x) {
       if((a.x <= linePoint1.x && linePoint1.x <= b.x) || (a.x >= linePoint1.x && linePoint1.x >= b.x)) {
         return true;
       }
     } else {
       float m = (linePoint2.y-linePoint1.y)/(linePoint2.x-linePoint1.x);
         if((a.y >= m*(a.x-linePoint1.x) +linePoint1.y) && (b.y <= m*(b.x-linePoint1.x) +linePoint1.y)) {
         return true;
       } else {
         if((a.y <= m*(a.x-linePoint1.x) +linePoint1.y) && (b.y >= m*(b.x-linePoint1.x) +linePoint1.y)) {
         return true;
       }
       }
     }
      return false;
    }
    
    void collide(Pointmass a, Link b) {
      PVector aToPos = a.pos.copy();
      aToPos.sub(b.a.pos);
      PVector aTob = b.b.pos.copy();
      aTob.sub(b.a.pos);
      float z = aTob.mag();
      PVector substitute = aTob.copy();
      PVector notNorm = PVector.mult(substitute.normalize(), (aTob.dot(aToPos))/aTob.mag());
      float x = notNorm.mag();
      PVector norm = PVector.sub(aToPos, notNorm);
      float y = norm.mag();
      float t = (z*z)/(2*(z*z-x*z+x*x))+.2;
      a.move(PVector.mult(norm, -t));
      b.moveA(PVector.mult(norm, (1-x/z)*t));
      b.moveB(PVector.mult(norm, (x/z)*t));
    }
    
    void mouseClickedRect(PVector mouse,PVector sub) {
      mouse.sub(PVector.div(sub,2));
      PVector acc = new PVector(0,.009);
      points.add(new Pointmass(mouse,mouse,acc,3));
      points.add(new Pointmass(PVector.add(mouse,new PVector(sub.x,0)),PVector.add(mouse,new PVector(sub.x,0)),acc,3));
      points.add(new Pointmass(PVector.add(mouse,new PVector(0,sub.y)),PVector.add(mouse,new PVector(0,sub.y)),acc,3));
      points.add(new Pointmass(PVector.add(mouse,new PVector(sub.x,sub.y)),PVector.add(mouse,new PVector(sub.x,sub.y)),acc,3));
      int w = points.size()-1;
      int x = points.size()-2;
      int y = points.size()-3;
      int z = points.size()-4;
      links.add(new Link(points.get(x),points.get(y),sub.x*sqrt(2),1,false));
      links.add(new Link(points.get(y),points.get(z),sub.x,1,true));
      links.add(new Link(points.get(z),points.get(w),sub.x*sqrt(2),1,false));
      links.add(new Link(points.get(w),points.get(x),sub.x,1,true));
      links.add(new Link(points.get(x),points.get(z),sub.x,1,true));
      links.add(new Link(points.get(w),points.get(y),sub.x,1,true));    }//closed
    
Sign In or Register to comment.