碰撞后计算 angular 速度

Calculating angular velocity after a collision

我已经相对较好地降低了碰撞分辨率的线性分量,但我不太清楚如何为 angular 做同样的事情。根据我的阅读,它类似于... torque = point of collision x linear velocity。 (叉积)我试图将 example I found 合并到我的代码中,但实际上当对象碰撞时我根本看不到任何旋转。另一个 fiddle 与分离轴定理和 angular 速度计算的基本实现完美配合。这是我想出的...

属性 定义(方向,angular 速度和 angular 加速度):

rotation: 0,
angularVelocity: 0,
angularAcceleration: 0

正在计算碰撞响应中的 angular 速度:

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);

在更新循环中更新方向:

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;

我会 post 我拥有的用于计算惯性矩的代码,但是每个对象都有一个单独的代码,所以这会有点……冗长。尽管如此,还是以圆圈为例:

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

为了显示结果,这是我的 fiddle。如图所示,物体不会在碰撞时旋转。 (圆圈不完全可见,但它应该适用于零和七)

我做错了什么?

编辑:它们根本不旋转的原因是响应函数中的组出错——它现在旋转了,只是不正确。但是,我暂时将其注释掉,因为它把事情搞砸了。

另外,我尝试了另一种旋转方法。这是响应中的代码:

_bodyA.angularVelocity = direction.vCross(_bodyA.velocity) / (isCircle ? _bodyA.radius : boundsA.width);
_bodyB.angularVelocity = direction.vCross(_bodyB.velocity) / (isCircle ? _bodyB.radius : boundsB.width);

注意direction指的是"collision normal"。

Angular 和由力矢量引起的线性加速度

Angular 和由作用力引起的定向加速度是同一事物的两个组成部分,不能分开。要获得一个,您需要同时解决两个问题。

定义计算

从简单的物理学和站在肩膀上我们知道以下内容。

F is force (equivalent to inertia)
Fv is linear force
Fa is angular force
a is acceleration could be linear or rotational depending on where it is used
v is velocity. For angular situations it is the tangential component only
m is mass
r is radius

对于线性力

F = m * v 

我们从中得出

m = F / v
v = F / m

对于旋转力(v 是切向速度)

F = r * r * m * (v / r) and simplify F = r * m * v

我们从中得出

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

因为我们施加的力是瞬时的,所以我们可以交换 a 加速度和 v 速度来给出以下所有公式

线性

F = m * a  
m = F / a
a = F / m

旋转

F = r * m * a
m = F / ( r * a )
a = F / ( r * m )
r = F / ( a * m )

因为我们只对线性和旋转解的速度变化感兴趣

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

其中 a1 是每帧像素的加速度2a2 是每帧弧度的加速度2(方框正方形表示是加速度)

从一维到二维

因为这是一个二维解,而以上都是一维的,我们需要使用向量。对于这个问题,我使用了两种形式的二维向量。具有大小(长度、距离等...)和方向的极坐标。具有 x 和 y 的笛卡尔坐标。矢量表示什么取决于它的使用方式。

以下函数用作解决方案中的助手。它们是用 ES6 编写的,因此对于不兼容的浏览器,您将不得不调整它们,但我永远不会建议您使用它们,因为它们是为了方便而编写的,它们效率很低并且会进行大量冗余计算。

将矢量从极坐标转换为笛卡尔坐标并返回一个新矢量

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;
}

将向量从笛卡尔坐标转换为极坐标并返回一个新坐标

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});
}

创建笛卡尔向量

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

正确的是 arg vec 是极坐标形式的向量

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

Returns 如果 arg vec 是笛卡尔形式的向量,则为真

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

Returns 极坐标形式的新向量也确保 vec.mag 为正数

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 };
}

如果尚未将未知的 vec 复制并转换到购物车

function asCart(vec){
     if(isPolar(vec)){ return polarToCart(vec); }
     return { x : vec.x, y : vec.y};
}

计算可能会导致负数,尽管这对某些计算有效这会导致不正确的向量(反转)这只是验证极向量具有正数它不会仅改变向量的符号和方向

function validatePolar(vec) {
    if (isPolar(vec)) {
        if (vec.mag < 0) {
            vec.mag =  - vec.mag;
            vec.dir += PI;
        }
    }
    return vec;
}

盒子

现在我们可以定义一个我们可以用来玩的对象了。一个具有位置、大小、质量、方向、速度和旋转的简单盒子

function createBox(x,y,w,h){
    var box = {
        x : x,   // pos
        y : y,
        r : 0.1,   // its rotation AKA orientation or direction in radians
        h : h,  // its height
        w : w,  // its 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;
}    

对对象施加力

所以现在我们可以重新定义一些术语

F(力)是矢量力,大小是力,它有方向

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

没有施加位置的力是没有意义的。

Position 是一个只包含 x 和 y 位置的向量

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

方向向量保存位置向量之间的方向和距离

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

direction 现在有从 canvas 中心到点 (100,100) 的方向和距离。

我们需要做的最后一件事是沿方向向量从力向量中提取分量。当你对一个物体施加一个力时,这个力被分成两部分,一个是沿着到物体中心的线的力,并增加了物体的加速度,另一个力是与到物体中心的线成 90 度(切线)这就是改变旋转的力。

要获得这两个分量,您需要获得力矢量与力施加到对象中心的方向矢量之间的方向差异。

var force = polar(100,0);  // the force
var forceLoc = vector(50,50);  // the location the force is applied

var direction2Center = asPolar(vector(box.x - forceLoc.x, box.y - forceLoc.y)); // get the direction as polar vector
var pheta = direction2Center - force.dir; // get the angle between the force and object center   

现在你有了那个角 pheta,力可以用三角法分解成它的旋转和线性分量。

var F = force.mag; // get the force magnitude
var Fv = Math.cos(pheta) * F; // get the linear force
var Fa = Math.sin(pheta) * F; // get the angular force 

现在可以将力转换回线性加速度 a = F/m 和 angular a = F/(m*r)

accelV = Fv / box.mass; // linear acceleration in pixels
accelA = Fa / (box.mass * direction2Center.mag); // angular acceleration in radians

然后将线性力转换回方向指向对象中心的矢量

var forceV = polar(Fv, direction2Center);

Convert 返回到笛卡尔坐标,因此我们可以将其添加到对象 deltaX 和 deltaY

forceV = asCart(forceV);

并将加速度添加到盒子

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

旋转加速度只是一维的,所以只需将其添加到盒子的增量旋转中即可

box.dr += accelA;

就是这样。

对盒子施加力的函数

如果附加到盒子上的函数将在盒子的某个位置应用力矢量。

像这样贴在盒子上

box.applyForce = applyForce; // bind function to the box;

然后您可以通过方框调用函数

box.applyForce(force, locationOfForce);


function applyForce(force, loc){ // force is a vector, loc is a coordinate
    var toCenter = asPolar(vector(this.x - loc.x, this.y - loc.y)); // get the vector to the center
    var pheta = toCenter.dir - force.dir;  // get the angle between the force and the line to center
    var Fv = Math.cos(pheta) * force.mag;  // Split the force into the velocity force along the line to the center
    var Fa = Math.sin(pheta) * force.mag;  // and the angular force at the tangent to the line to the center
    var accel = asPolar(toCenter); // copy the direction to center
    accel.mag = Fv / this.mass; // now use F = m * a in the form a = F/m to get acceleration
    var deltaV = asCart(accel); // convert acceleration to cartesian 
    this.dx += deltaV.x // update the box delta V
    this.dy += deltaV.y //
    var accelA = Fa / (toCenter.mag  * this.mass); // for the angular component get the rotation
                                                   // acceleration from F=m*a*r in the 
                                                   // form a = F/(m*r)
    this.dr += accelA;// now add that to the box delta r
}

演示

该演示只是关于函数 applyForce 与重力和弹跳有关的东西只是非常糟糕的近似值,不应用于任何物理类型的东西,因为它们不守恒。

单击并拖动以在鼠标移动的方向上对对象施加力。

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 the wall normals


var box = createBox(200, 200, 50, 100);
box.applyForce = applyForce; // Add this function to the 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 for setting and 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 i = 0; i < tempVecs.length; i ++ ){
        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,   // its rotation AKA orientation or direction in radians
        h : h,  // its height, and I will assume that its depth is always equal to its height
        w : w,  // its 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,  // only directional
                velocityT : velocityT,  // total
                velocityA :  velocityA, // angular only
                pos : vector(xx, yy),
                radius : details.mag,
            }
        },
    }
    box.mass = box.mass(); // Mass remains the same so just set it with its function
    return box;
}
// calculations can result in a negative magnitude though this is valide for some
// calculations this results in the incorrect vector (reversed)
// this simply validates that the polat vector has a positive magnitude
// it does not change the vector just the sign and direction
function validatePolar(vec){
    if(isPolar(vec)){
        if(vec.mag < 0){
            vec.mag = - vec.mag;
            vec.dir += PI;
        }
    }
    return vec;
}
// converts a vector from polar to cartesian returning a 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 a vector from cartesian to polar returning a 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;
}
function polar (mag = 1, dir = 0) { return validatePolar({dir : dir, mag : mag}); } // create a polar vector
function vector (x= 1, y= 0) { return {x: x, y: y}; } // create a 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 and converts an unknown vec to polar if not already
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 and converts an unknown vec to cart if not already
function asCart(vec){
     if(isPolar(vec)){ return polarToCart(vec); }
     return { x : vec.x, y : vec.y};
}
// normalise makes a vector a unit length and returns it as a 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);
}
// This splits the vector (polar or cartesian) into the components along  dir and the tangent to that dir
function vectorComponentsForDir(vec,dir){
    var v = asPolar(vec); // as 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 the velocity is in cartesian form
    var va = asPolar(pointDetails.velocityA); // Angular V make sure the velocity is 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 that the two equal forces from the wall
    vac.along.mag *= 1.18; // against the box and the box against the wall be summed. 
                          // As the wall can not move the result is that the force is twice 
                          // the force the box applies to the wall (Yes and currently force is in 
                          // velocity form untill the next line)
    vvc.along.mag *= box.mass; // convert to force
    //vac.along.mag/= pointDetails.radius
    vac.along.mag *= box.mass
    vvc.along.dir += PI; // force is in the oppisite direction so turn it 180
    vac.along.dir += PI; // force is in the oppisite direction so turn it 180
    // split the force into components based on the wall normal. One along the norm the 
    // other along the wall


    vvc.tangent.mag *= 0.18;  // add friction along the wall 
    vac.tangent.mag *= 0.18;
    vvc.tangent.mag *= box.mass  //
    vac.tangent.mag *= box.mass
    vvc.tangent.dir += PI; // force is in the oppisite direction so turn it 180
    vac.tangent.dir += PI; // force is in the oppisite direction so turn it 180



    // apply the force out from the wall
    box.applyForce(vvc.along, pointDetails.pos)    
    // apply the force along the wall
    box.applyForce(vvc.tangent, pointDetails.pos)    
    // apply the force out from the wall
    box.applyForce(vac.along, pointDetails.pos)    
    // apply the force along the 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 is a vector, loc is a coordinate
    validatePolar(force); // make sure the force is a valid polar
   // addTempVec(loc, force,"White", LIFE, SCALE_FORCE) // show the force
    var l = asCart(loc); // make sure the location is 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 the direction to center
    accel.mag = Fv / this.mass; // now use F = m * a in the form a = F/m
    var deltaV = asCart(accel); // convert it to cartesian 
    this.dx += deltaV.x // update the box delta V
    this.dy += deltaV.y
    var accelA = Fa / (toCenter.mag  * this.mass); // for the angular component get the rotation
                                                   // acceleration
    this.dr += accelA;// now add that to the box delta r
}

// make a 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; i < 4; i++){
        var p = box.getPoint(i);
        // only do one collision per frame or we will end up 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 to apply force to box",canvas.width/2,FONT_SIZE +17,FONT,14,"black");

    requestAnimationFrame(update)   


}



update();