javascript - Calculating angular velocity after a collision -


i've got linear component of collision resolution down relatively well, can't quite figure out how same angular one. i've read, it's like... torque = point of collision x linear velocity. (cross product) tried incorporate example found code don't see rotation @ when objects collide. other fiddle works rudimentary implementation of seperating axis theorem , angular velocity calculations. here's i've come with...

property definitions (orientation, angular velocity, , angular acceleration):

rotation: 0, angularvelocity: 0, angularacceleration: 0 

calculating angular velocity in collision response:

var pivota = this.vector(bodya.x, bodya.y); bodya.angularvelocity = 1 * 0.2 * (bodya.angularvelocity / math.abs(bodya.angularvelocity)) * pivota.subtract(iscircle ? pivota.add(bodya.radius) : {   x: pivota.x + boundsa.width,   y: pivota.y + boundsa.height }).vcross(bodya.velocity); var pivotb = this.vector(bodyb.x, bodyb.y); bodyb.angularvelocity = 1 * 0.2 * (bodyb.angularvelocity / math.abs(bodyb.angularvelocity)) * pivotb.subtract(iscircle ? pivotb.add(bodyb.radius) : {   x: pivotb.x + boundsb.width,   y: pivotb.y + boundsb.height }).vcross(bodyb.velocity); 

updating orientation in update loop:

var torque = 0; torque += core.objects[o].angularvelocity * -1; core.objects[o].angularacceleration = torque / core.objects[o].momentofinertia(); core.objects[o].angularvelocity += core.objects[o].angularacceleration; core.objects[o].rotation += core.objects[o].angularvelocity; 

i post code have calculating moments of inertia there's seperate 1 every object bit... lengthy. nonetheless, here's 1 circle example:

return this.mass * this.radius * this.radius / 2; 

just show result, here's fiddle. shown, objects not rotate on collision. (not visible circles, should work 0 , seven)

what doing wrong?

edit: reason weren't rotating @ because of error groups in response function -- rotates now, not correctly. however, i've commented out messes things up.

also, i've tried method rotation. here's code in response:

_bodya.angularvelocity = direction.vcross(_bodya.velocity) / (iscircle ? _bodya.radius : boundsa.width); _bodyb.angularvelocity = direction.vcross(_bodyb.velocity) / (iscircle ? _bodyb.radius : boundsb.width); 

note direction refers "collision normal".

angular , linear acceleration due force vector

angular , directional accelerations due applied force 2 components of same thing , can not separated. 1 need solve both.

define calculations

from simple physics , standing on shoulders know following.

f force (equivalent inertia) fv linear force fa angular force acceleration linear or rotational depending on used v velocity. angular situations tangential component m mass r radius 

for linear forces

f = m * v  

from derive

m = f / v v = f / m 

for rotational force (v tangential velocity)

f = r * r * m * (v / r) , simplify f = r * m * v 

from derive

m = f / ( r * v ) v = f / ( r * m ) r = f / ( v * m ) 

because forces apply instantaneous can interchange a acceleration , v velocity give following formulas

linear

f = m *   m = f / a = f / m 

rotational

f = r * m * m = f / ( r * ) = f / ( r * m ) r = f / ( * m ) 

as interested in change in velocity both linear , rotation solutions

a1 = f / m a2 = f / ( r * m )  

where a1 acceleration in pixels per frame2 , a2 acceleration in radians per frame2 ( frame squared denotes acceleration)

from 1d 2d

because 2d solution , above 1d need use vectors. problem use 2 forms of 2d vector. polar has magnitude (length, distance, like...) , direction. cartesian has x , y. vector represents depends on how used.

the following functions used helpers in solution. written in es6 non compliant browsers have adapt them, though not ever suggest use these written convenience, inefficient , lot of redundant calculations.

converts vector polar cartesian returning new one

function polartocart(pvec, retv = {x : 0, y : 0}) {     retv.x = math.cos(pvec.dir) * pvec.mag;     retv.y = math.sin(pvec.dir) * pvec.mag;     return retv; } 

converts vector cartesian polar returning new one

function carttopolar(vec, retv = {dir : 0, mag : 0}) {     retv.dir = math.atan2(vec.y, vec.x);     retv.mag = math.hypot(vec.x, vec.y);     return retv; } 

creates polar vector

function polar(mag = 1, dir = 0) {     return validatepolar({dir : dir,mag : mag}); } 

create vector cartesian

function vector(x = 1, y = 0) {     return {x : x, y : y}; }  

true arg vec vector in polar form

function ispolar(vec) {     if (vec.mag !== undefined && vec.dir !== undefined) {return true;}     return false; } 

returns true if arg vec vector in cartesian form

function iscart(vec) {     if (vec.x !== undefined && vec.y !== undefined) {return true;}     return false; }  

returns new vector in polar form ensures vec.mag positive

function aspolar(vec){      if(iscart(vec)){ return carttopolar(vec); }      if(vec.mag < 0){          vec.mag = - vec.mag;          vec.dir += pi;      }      return { dir : vec.dir, mag : vec.mag }; } 

copy , converts unknown vec cart if not already

function ascart(vec){      if(ispolar(vec)){ return polartocart(vec); }      return { x : vec.x, y : vec.y}; } 

calculations can result in negative magnitude though valid calculations results in incorrect vector (reversed) validates polar vector has positive magnitude not change vector sign , direction

function validatepolar(vec) {     if (ispolar(vec)) {         if (vec.mag < 0) {             vec.mag =  - vec.mag;             vec.dir += pi;         }     }     return vec; } 

the box

now can define object can use play with. simple box has position, size, mass, orientation, velocity , rotation

function createbox(x,y,w,h){     var box = {         x : x,   // pos         y : y,         r : 0.1,   // rotation aka orientation or direction in radians         h : h,  // height         w : w,  // width         dx : 0, // delta x  in pixels per frame 1/60th second         dy : 0, // delta y         dr : 0.0, // deltat rotation in radians  per frame 1/60th second         mass : w * h, // mass in things         update :function(){             this.x += this.dx;             this.y += this.dy;             this.r += this.dr;         },     }     return box; }     

applying force object

so can redefine terms

f (force) vector force magnitude force , has direction

var force = polar(100,0); // create force 100 units right (0 radians) 

the force meaningless without position applied.

position vector holds , x , y location

var location = vector(canvas.width/2, canvas.height/2);  // defines point in middle of canvas 

directional vector holds direction , distance between positional vectors

var l1 = vector(canvas.width/2, canvas.height/2);  // defines point in middle of canvas var l2 = vector(100,100); var direction = aspolar(vector(l2.x - l1.x, l2.y - l1.y)); // direction polar vector 

direction has direction canvas center point (100,100) , distance.

the last thing need extract components force vector along directional vector. when apply force object force split two, 1 force along line object center , adds object acceleration, other force @ 90deg line object center (the tangent) , force changes rotation.

to 2 components difference in direction between force vector , directional vector force applied object center.

var force = polar(100,0);  // force var forceloc = vector(50,50);  // location force applied  var direction2center = aspolar(vector(box.x - forceloc.x, box.y - forceloc.y)); // direction polar vector var pheta = direction2center - force.dir; // angle between force , object center    

now have angle pheta force can split rotational , linear components trig.

var f = force.mag; // force magnitude var fv = math.cos(pheta) * f; // linear force var fa = math.sin(pheta) * f; // angular force  

now forces can converted accelerations linear = f/m , angular = f/(m*r)

accelv = fv / box.mass; // linear acceleration in pixels accela = fa / (box.mass * direction2center.mag); // angular acceleration in radians 

you convert linear force vector has direction center of object

var forcev = polar(fv, direction2center); 

convert cartesian can add object deltax , deltay

forcev = ascart(forcev); 

and add acceleration box

box.dx += forcev.x;     box.dy += forcev.y;     

rotational acceleration 1 dimensional add delta rotation of box

box.dr += accela; 

and it.

function apply force box

the function if attached box apply force vector @ location box.

attach box so

box.applyforce = applyforce; // bind function box; 

you can call function via box

box.applyforce(force, locationofforce);   function applyforce(force, loc){ // force vector, loc coordinate     var tocenter = aspolar(vector(this.x - loc.x, this.y - loc.y)); // vector center     var pheta = tocenter.dir - force.dir;  // angle between force , line center     var fv = math.cos(pheta) * force.mag;  // split force velocity force along line center     var fa = math.sin(pheta) * force.mag;  // , angular force @ tangent line center     var accel = aspolar(tocenter); // copy direction center     accel.mag = fv / this.mass; // use f = m * in form = f/m acceleration     var deltav = ascart(accel); // convert acceleration cartesian      this.dx += deltav.x // update box delta v     this.dy += deltav.y //     var accela = fa / (tocenter.mag  * this.mass); // angular component rotation                                                    // acceleration f=m*a*r in                                                     // form = f/(m*r)     this.dr += accela;// add box delta r } 

the demo

the demo function applyforce stuff gravity , bouncing bad approximations , should not used physic type of stuff not conserve energy.

click , drag apply force object in direction mouse moved.

const pi90 = math.pi / 2;  const pi = math.pi;  const pi2 = math.pi * 2;    const inset = 10; // playfeild inset    const arrow_size = 6  const scale_vec = 10;  const scale_force = 0.15;  const line_w = 2;  const life = 12;  const font_size = 20;  const font = "arial black";  const wall_norms = [pi90,pi,-pi90,0]; // dirction of wall normals      var box = createbox(200, 200, 50, 100);  box.applyforce = applyforce; // add function box  // render / update function      var mouse = (function(){      function preventdefault(e) { e.preventdefault(); }      var i;      var mouse = {          x : 0, y : 0,buttonraw : 0,          bm : [1, 2, 4, 6, 5, 3], // masks setting , clearing button raw bits;          mouseevents : "mousemove,mousedown,mouseup".split(",")      };      function mousemove(e) {          var t = e.type, m = mouse;          m.x = e.offsetx; m.y = e.offsety;          if (m.x === undefined) { m.x = e.clientx; m.y = e.clienty; }          if (t === "mousedown") { m.buttonraw |= m.bm[e.which-1];          } else if (t === "mouseup") { m.buttonraw &= m.bm[e.which + 2];}          e.preventdefault();      }      mouse.start = function(element = document){          if(mouse.element !== undefined){ mouse.removemouse();}          mouse.element = element;          mouse.mouseevents.foreach(n => { element.addeventlistener(n, mousemove); } );      }      mouse.remove = function(){          if(mouse.element !== undefined){              mouse.mouseevents.foreach(n => { mouse.element.removeeventlistener(n, mousemove); } );              mouse.element = undefined;          }      }      return mouse;  })();      var canvas,ctx;  function createcanvas(){      canvas = document.createelement("canvas");       canvas.style.position = "absolute";      canvas.style.left     = "0px";      canvas.style.top      = "0px";      canvas.style.zindex   = 1000;      document.body.appendchild(canvas);   }  function resizecanvas(){      if(canvas === undefined){          createcanvas();      }      canvas.width          = window.innerwidth;      canvas.height         = window.innerheight;       ctx            = canvas.getcontext("2d");       if(box){        box.w = canvas.width * 0.10;        box.h = box.w * 2;        box.mass = box.w * box.h;      }  }    window.addeventlistener("resize",resizecanvas);  resizecanvas();  mouse.start(canvas)            var tempvecs = [];  function addtempvec(v,vec,col,life = life,scale = scale_vec){tempvecs.push({v:v,vec:vec,col:col,scale:scale,life:life,slife:life});}  function drawtempvecs(){      for(var = 0; < tempvecs.length; ++ ){          var t = tempvecs[i]; t.life -= 1;          if(t.life <= 0){tempvecs.splice(i, 1); i--; continue}          ctx.globalalpha = (t.life / t.slife)*0.25;          drawvec(t.v, t.vec ,t.col, t.scale)      }  }  function drawvec(v,vec,col,scale = scale_vec){      vec = aspolar(vec)      ctx.settransform(1,0,0,1,v.x,v.y);      var d = vec.dir;      var m = vec.mag;      ctx.rotate(d);      ctx.beginpath();      ctx.linewidth = line_w;      ctx.strokestyle = col;      ctx.moveto(0,0);      ctx.lineto(m * scale,0);      ctx.moveto(m * scale-arrow_size,-arrow_size);      ctx.lineto(m * scale,0);      ctx.lineto(m * scale-arrow_size,arrow_size);      ctx.stroke();  }  function drawtext(text,x,y,font,size,col){      ctx.font = size + "px "+font;      ctx.textalign = "center";      ctx.textbaseline = "middle";      ctx.settransform(1,0,0,1,x,y);      ctx.globalalpha = 1;      ctx.fillstyle = col;      ctx.filltext(text,0,0);  }  function createbox(x,y,w,h){      var box = {          x : x,   // pos          y : y,          r : 0.1,   // rotation aka orientation or direction in radians          h : h,  // height, , assume depth equal height          w : w,  // width          dx : 0, // delta x  in pixels per frame 1/60th second          dy : 0, // delta y          dr : 0.0, // deltat rotation in radians  per frame 1/60th second          getdesc : function(){            var vel = math.hypot(this.dx ,this.dy);            var radius = math.hypot(this.w,this.h)/2            var rvel = math.abs(this.dr * radius);            var str = "v " + (vel*60).tofixed(0) + "pps ";            str += math.abs(this.dr * 60 * 60).tofixed(0) + "rpm ";            str += "va " + (rvel*60).tofixed(0) + "pps ";              return str;          },          mass : function(){ return (this.w * this.h * this.h)/1000; }, // mass in k things          draw : function(){              ctx.globalalpha = 1;              ctx.settransform(1,0,0,1,this.x,this.y);              ctx.rotate(this.r);              ctx.fillstyle = "#444";              ctx.fillrect(-this.w/2, -this.h/2, this.w, this.h)              ctx.strokerect(-this.w/2, -this.h/2, this.w, this.h)          },          update :function(){              this.x += this.dx;              this.y += this.dy;              this.dy += 0.061; // alittle gravity              this.r += this.dr;          },          getpoint : function(which){              var dx,dy,x,y,xx,yy,velocitya,velocityt,velocity;              dx = math.cos(this.r);              dy = math.sin(this.r);              switch(which){                  case 0:                      x = -this.w /2;                      y = -this.h /2;                      break;                  case 1:                      x = this.w /2;                      y = -this.h /2;                      break;                  case 2:                      x = this.w /2;                      y = this.h /2;                      break;                  case 3:                      x = -this.w /2;                      y = this.h /2;                      break;                  case 4:                      x = this.x;                      y = this.y;              }              var xx,yy;              xx = x * dx + y * -dy;              yy = x * dy + y * dx;              var details = aspolar(vector(xx, yy))              xx += this.x;              yy += this.y;              velocitya =  polar(details.mag * this.dr, details.dir + pi90);              velocityt = vectoradd(velocity = vector(this.dx, this.dy), velocitya);              return {                  velocity : velocity,  // directional                  velocityt : velocityt,  // total                  velocitya :  velocitya, // angular                  pos : vector(xx, yy),                  radius : details.mag,              }          },      }      box.mass = box.mass(); // mass remains same set function      return box;  }  // calculations can result in negative magnitude though valide  // calculations results in incorrect vector (reversed)  // validates polat vector has positive magnitude  // not change vector sign , direction  function validatepolar(vec){      if(ispolar(vec)){          if(vec.mag < 0){              vec.mag = - vec.mag;              vec.dir += pi;          }      }      return vec;  }  // converts vector polar cartesian returning new 1  function polartocart(pvec, retv = {x : 0, y : 0}){       retv.x = math.cos(pvec.dir) * pvec.mag;       retv.y = math.sin(pvec.dir) * pvec.mag;       return retv;  }  // converts vector cartesian polar returning new 1  function carttopolar(vec, retv  = {dir : 0, mag : 0}){       retv.dir = math.atan2(vec.y,vec.x);       retv.mag = math.hypot(vec.x,vec.y);       return retv;  }  function polar (mag = 1, dir = 0) { return validatepolar({dir : dir, mag : mag}); } // create polar vector  function vector (x= 1, y= 0) { return {x: x, y: y}; } // create cartesian vector  function ispolar (vec) { if(vec.mag !== undefined && vec.dir !== undefined) { return true; } return false; }// returns true if polar  function iscart (vec) { if(vec.x !== undefined && vec.y !== undefined) { return true; } return false; }// returns true if cartesian   // copy , converts unknown vec polar if not  function aspolar(vec){       if(iscart(vec)){ return carttopolar(vec); }       if(vec.mag < 0){           vec.mag = - vec.mag;           vec.dir += pi;       }       return { dir : vec.dir, mag : vec.mag };  }  // copy , converts unknown vec cart if not  function ascart(vec){       if(ispolar(vec)){ return polartocart(vec); }       return { x : vec.x, y : vec.y};  }  // normalise makes vector unit length , returns cartesian   function normalise(vec){       var vp = aspolar(vec);       vap.mag = 1;       return ascart(vp);  }  function vectoradd(vec1, vec2){      var v1 = ascart(vec1);      var v2 = ascart(vec2);      return vector(v1.x + v2.x, v1.y + v2.y);  }  // splits vector (polar or cartesian) components along  dir , tangent dir  function vectorcomponentsfordir(vec,dir){      var v = aspolar(vec); // polar      var pheta = v.dir - dir;      var fv = math.cos(pheta) * v.mag;      var fa = math.sin(pheta) * v.mag;        var d1 = dir;      var d2 = dir + pi90;          if(fv < 0){          d1 += pi;          fv = -fv;      }        if(fa < 0){          d2 += pi;          fa = -fa;      }      return {          along : polar(fv,d1),          tangent : polar(fa,d2)      };  }    function docollision(pointdetails, wallindex){      var vv = aspolar(pointdetails.velocity); // cartesian v make sure velocity in cartesian form      var va = aspolar(pointdetails.velocitya); // angular v make sure velocity in cartesian form      var vvc = vectorcomponentsfordir(vv, wall_norms[wallindex])                  var vac = vectorcomponentsfordir(va, wall_norms[wallindex])                  vvc.along.mag *= 1.18; // elastic collision requiers 2 equal forces wall      vac.along.mag *= 1.18; // against box , box against wall summed.                             // wall can not move result force twice                             // force box applies wall (yes , force in                             // velocity form untill next line)      vvc.along.mag *= box.mass; // convert force      //vac.along.mag/= pointdetails.radius      vac.along.mag *= box.mass      vvc.along.dir += pi; // force in oppisite direction turn 180      vac.along.dir += pi; // force in oppisite direction turn 180      // split force components based on wall normal. 1 along norm       // other along wall          vvc.tangent.mag *= 0.18;  // add friction along wall       vac.tangent.mag *= 0.18;      vvc.tangent.mag *= box.mass  //      vac.tangent.mag *= box.mass      vvc.tangent.dir += pi; // force in oppisite direction turn 180      vac.tangent.dir += pi; // force in oppisite direction turn 180            // apply force out wall      box.applyforce(vvc.along, pointdetails.pos)          // apply force along wall      box.applyforce(vvc.tangent, pointdetails.pos)          // apply force out wall      box.applyforce(vac.along, pointdetails.pos)          // apply force along wall      box.applyforce(vac.tangent, pointdetails.pos)          //addtempvec(pointdetails.pos, vvc.tangent, "red", life, 10)      //addtempvec(pointdetails.pos, vac.tangent, "red", life, 10)    }      function applyforce(force, loc){ // force vector, loc coordinate      validatepolar(force); // make sure force valid polar     // addtempvec(loc, force,"white", life, scale_force) // show force      var l = ascart(loc); // make sure location in cartesian form      var  tocenter = aspolar(vector(this.x - l.x, this.y - l.y));      var pheta = tocenter.dir - force.dir;      var fv = math.cos(pheta) * force.mag;      var fa = math.sin(pheta) * force.mag;      var accel = aspolar(tocenter); // copy direction center      accel.mag = fv / this.mass; // use f = m * in form = f/m      var deltav = ascart(accel); // convert cartesian       this.dx += deltav.x // update box delta v      this.dy += deltav.y      var accela = fa / (tocenter.mag  * this.mass); // angular component rotation                                                     // acceleration      this.dr += accela;// add box delta r  }    // make box    ctx.globalalpha = 1;  var lx,ly;  function update(){     // clearlog();      ctx.settransform(1, 0, 0, 1, 0, 0);      ctx.clearrect(0, 0, canvas.width, canvas.height);        ctx.settransform(1, 0, 0, 1, 0, 0);      ctx.linewidth = 1;      ctx.strokestyle = "black";      ctx.fillstyle = "#888";      ctx.fillrect(inset, inset, canvas.width - inset * 2, canvas.height - inset * 2);      ctx.strokerect(inset, inset, canvas.width - inset * 2, canvas.height - inset * 2);                  ctx.linewidth = 2;      ctx.strokestyle = "black";        box.update();      box.draw();          if(mouse.buttonraw & 1){          var force = aspolar(vector(mouse.x - lx, mouse.y - ly));          force.mag *= box.mass * 0.1;          box.applyforce(force,vector(mouse.x, mouse.y))          addtempvec(vector(mouse.x, mouse.y), aspolar(vector(mouse.x - lx, mouse.y - ly)), "cyan", life, 5);      }      lx = mouse.x;      ly = mouse.y;      for(i = 0; < 4; i++){          var p = box.getpoint(i);          // 1 collision per frame or end adding energy          if(p.pos.x < inset){              box.x += (inset) - p.pos.x;              docollision(p,3)          }else           if( p.pos.x > canvas.width-inset){              box.x += (canvas.width - inset) - p.pos.x;              docollision(p,1)          }else           if(p.pos.y < inset){              box.y += (inset) -p.pos.y;              docollision(p,0)          }else            if( p.pos.y > canvas.height-inset){              box.y += (canvas.height - inset) -p.pos.y;              docollision(p,2)          }              drawvec(p.pos,p.velocity,"blue")        }            drawtempvecs();      ctx.globalalpha = 1;      drawtext(box.getdesc(),canvas.width/2,font_size,font,font_size,"black");      drawtext("click drag apply force box",canvas.width/2,font_size +17,font,14,"black");        requestanimationframe(update)         }        update();


Comments

Popular posts from this blog

html - Styling progress bar with inline style -

java - Oracle Sql developer error: could not install some modules -

How to use autoclose brackets in Jupyter notebook? -