计算碰撞后的角速度

2023-11-26

我已经相对较好地降低了碰撞分辨率的线性部分,但我不太清楚如何对角度部分做同样的事情。从我读到的内容来看,这就像......torque = point of collision x linear velocity。 (交叉产品)我尝试合并我发现的例子进入我的代码,但实际上当物体碰撞时我根本看不到任何旋转。另一个小提琴与分离轴定理和角速度计算的基本实现完美配合。这是我想出的......

属性定义(方向、角速度和角加速度):

rotation: 0,
angularVelocity: 0,
angularAcceleration: 0

计算碰撞响应中的角速度:

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;

我会发布用于计算转动惯量的代码,但是每个对象都有一个单独的代码,所以这会有点……冗长。尽管如此,这里还是以圆形为例:

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指的是“碰撞法线”。


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

由于施加的力而产生的角加速度和方向加速度是同一事物的两个组成部分,不能分开。为了得到一个,你需要同时解决这两个问题。

定义计算

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

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速度给出以下所有公式

Linear

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 ) 

Where a1 is acceleration in pixels per frame2 and a2 is acceleration in radians per frame2 ( the frame squared just denotes it is acceleration)

从一维到二维

因为这是一个 2D 解决方案,而以上都是 1D,所以我们需要使用向量。对于这个问题,我使用了两种形式的二维向量。具有大小(长度、距离等...)和方向的极坐标。有 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};
} 

True 是 arg vec 是极坐标形式的向量

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

如果 arg vec 是笛卡尔形式的向量,则返回 true

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

返回极坐标形式的新向量也确保 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;
}

The Box

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

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现在有从画布中心到点 (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 和角度 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);

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

forceV = asCart(forceV);

并将加速度添加到框中

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

旋转加速度只是一维,因此只需将其添加到盒子的增量旋转中

box.dr += accelA;

就是这样。

对 Box 施加力的函数

如果该函数附加到盒子上,则会在盒子的某个位置施加力矢量。

像这样贴在盒子上

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
}

The Demo

演示仅涉及功能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();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

计算碰撞后的角速度 的相关文章

  • 如何根据另一个动态下拉列表的值创建动态下拉列表?

    我有一个下拉菜单 当我选择一个选项时 它会创建一个动态下拉菜单 到目前为止 一切都很好 但我想创建另一个动态下拉列表 现在基于另一个动态下拉列表的值 我该怎么做 第一个动态下拉列表有效 我猜第二个无效 因为动态变量 div 没有静态 ID
  • 拖放 HTML5 jQuery:带有 JSON 的 e.dataTransfer.setData()

    这是我的拖拽 dragstart function e this css opacity 0 5 e dataTransfer effectAllowed move e dataTransfer setData application js
  • 使用 Charts.js 禁用动画

    我在使用 Charts js 关闭动画时遇到一些问题 这是我的代码 var pieData value 30 color F38630 value 50 color E0E4CC value 100 color 69D2E7 var myP
  • 如何在Javascript中声明静态变量[重复]

    这个问题在这里已经有答案了 在下面的代码中 我希望有一个计数器来跟踪创建的 Person 对象的数量 这段代码没有这样做 我该如何实现呢 function Person this name Peter this counter this c
  • 强制执行 show.bind

    我有一个包含数据的表 当从另一个视图触发事件时 我希望视图检查 show bind 语句 问题是该事件没有更改当前视图中的任何数据 foo html tr p canBeRemoved p tr 我正在使用 EventAggregator
  • jQuery 中如何判断 JSON 对象是否为空

    我有以下 JSON meta limit 20 next null offset 0 previous null total count 0 objects 我对对象感兴趣 我想知道对象是否为空并显示警报 像这样的东西 success fu
  • 使用 Javascript 对象模型在 SharePoint 任务上设置“分配给”

    我想创建一个共享点任务并将其分配给我自己 当前用户 在 javascript 对象模型中 我有下面的代码 但我认为我需要设置 spusercollection 对象 而不是设置特定用户 但是 我似乎无法在任何地方找到如何执行此操作的任何示例
  • 我什么时候应该使用内联和外部 Javascript?

    我想知道什么时候应该包含外部脚本或将它们与 html 代码内联编写 就性能和易于维护而言 这方面的一般做法是什么 真实场景 我有几个需要客户端表单验证的 html 页面 为此 我使用了一个包含在所有这些页面上的 jQuery 插件 但问题是
  • Javascript“命名空间”和 jQuery AJAX

    我正在使用此处列出的建议 http www odetocode com articles 473 aspx http www odetocode com articles 473 aspx 使用模拟的JavaScript AJAX网络聊天系
  • React JS“this”没有按预期工作

    我有下面的代码 save function var this this console log this refs itemText this setState isEditing false function console log In
  • 上传前如何检查图片的宽度和高度

    对于图片上传 我编写了以下 html 代码
  • 如何使用 JavaScript 播放任意 MIDI 音符?

    澄清一下 我不想生成 MIDI 文件 也不想播放 MIDI 文件 我希望即时播放 MIDI 音符 我尝试使用https github com mudcube MIDI js https github com mudcube MIDI js作
  • 单击时突出显示文本(javascript jquery html)

    当您在所有浏览器中双击某个单词时 它们会自动突出显示单击下的单词 但是否有可能找到一种方法exact单击一下就会发生同样的事情吗 我想这涉及到的事情可能是 TextRange 的东西 对所有段落 或整个正文或 div 的 onclick 做
  • 调用不带括号的 javascript 函数

    以下 renderChat 函数用于将消息和图像渲染到聊天板上 该函数内部还有另一个函数 var onComplete function 它完成创建列表元素并将其附加到聊天列表的所有工作 onComplete函数之后就只有这三行代码 img
  • 加载 angularjs 路由后运行 javascript 代码

    我需要在 angularjs 加载路线后显示警报 显示警报的代码位于 angularjs 异步加载的视图中 视图加载后 我希望它能够运行 但它没有 我知道我可以广播并告诉它稍后运行等 但我需要一个更通用的解决方案 假设您正在谈论基于以下内容
  • 如何从 HTML 中的列数据而不是行数据创建表格?

    根据这篇文章W3学校 http www w3schools com html html tables asp 可以像这样在 HTML 中创建一个基本表格 table border 1 tr td row 1 cell 1 td td row
  • Bootstrap - 为反向行模式创建移动自适应

    我想用 Bootstrap 创建一个反向效果 第一行 左边是文字 右边是图像 第二行 左边是图片 右边是文字 第三行 左边是文字 右边是图片 第四行 左边是图片 右边是文字 而且这种情况一直持续下去 它在大型设备上看起来非常漂亮 但当它在设
  • 具有相等宽度和高度 TD 的响应式表格

    如何创建每个单元格 TD 具有相同宽度和高度的响应式 HTML 表格 因此 当我调整浏览器窗口的大小或调整表格容器的大小时 表格将调整大小 但每个单元格将具有相同的高度和宽度 基金会不关心这个 当我用固定宽度和高度 以像素为单位 初始化 T
  • 使用 BASH 和 AWK 创建 HTML 表

    我在创建 html 表来显示文本文件中的统计信息时遇到问题 我确信有 100 种方法可以做得更好 但这里是 以下脚本中的注释显示了输出 bin bash function getapistats curl s http api exampl
  • Chrome 中的 addEventListener

    我正在关注 Lynda com 上有关新 DOM 事件模型的教程 这是我正在使用的代码 function addEventHandler oNode sEvt fFunc bCapture if typeof window event un

随机推荐

  • 如何包含另一个php文件?

    我有一个 php 文件 我想包含另一个具有 css 链接标签和 javascript 源标签的 php 文件 但是当我尝试包含它们时 它不会添加到页面中 我的 php 页面 通用 php 有谁知道出了什么问题吗 谢谢 PHP s inclu
  • 合并两个数据框的所有组合

    我遇到了一个复杂的问题 我有两个数据框 其中有一种编号和规格 颜色 零件 的组合 每个数据框适用于 4 台不同的机器 每台机器总共有 5 个规格 实际上甚至更多的机器和规格 以下是两个示例数据框 df1 lt data frame nr c
  • 弧形画廊的可能性

    是否可以更改 Android Gallery 的默认排列 我的意思是我们能否将图库制作成一条弯曲的路径 其中图像将沿着弯曲的路径移动 同时它具有Android图库的所有属性 如果可以的话 请告诉我你的想法 欢迎所有想法 感谢和问候 森 扩展
  • 为什么cudaMalloc()使用指针到指针?

    例如 cudaMalloc void device array num bytes 这个问题已经被asked之前 回复是 因为cudaMalloc返回错误代码 但我不明白 双指针与返回错误代码有什么关系 为什么简单的指针不能完成这项工作 如
  • 仅更改 NSAttributedString 的字体大小

    我有一个NSAttributedString它是从 RTF 文件加载的 因此它已经保存了不同范围的多个字体属性 现在我想让字体大小适应设备的屏幕大小 但是当我添加具有新大小的全新字体属性时 其他字体消失了 有办法改变吗only整个字符串的字
  • 在任何兼容版本下都无法启动WiredTiger。这可能是由于不支持的升级或降级造成的

    在 Ubuntu 20 04 上出现此错误 我最初在 ubuntu 上安装了默认版本 3 0 6 我已经清除它并安装了5 0 9 但现在我在日志中收到此错误 并且 mongod 无法启动 Failed to start up WiredTi
  • Pandas 按年份透视时间序列

    您好 提前感谢您的帮助 我有一个包含两列的简单数据框 我没有明确设置索引 但我相信数据帧会获得一个整数索引 我在输出的左侧看到该索引 问题如下 df pandas DataFrame res df columns date pb df da
  • 何时使用查询或代码[关闭]

    Closed 这个问题是基于意见的 目前不接受答案 我要求提供 Java JPA Hibernate Mysql 的具体案例 但我认为您可以将这个问题应用于很多语言 有时我必须对数据库执行查询才能获取某些实体 例如员工 假设您需要一些特定的
  • 有没有人在 IViewPart 中完全实现 XText 编辑器

    基本上我需要在一个完整的 XText 编辑器中实现IViewPart 我的大部分工作都是基于XText 表单集成项目 不幸的是 这是notXText 编辑器上的完整实现 我想知道是否有人知道更好的起点 甚至完整的实现 从 2 2 开始就可以
  • 在 Chrome 控制台中一次突出显示多个元素

    当您将鼠标悬停在 Chrome 控制台的 元素 选项卡中的某个元素上时 它会在视口中突出显示该元素 有没有办法同时突出显示多个元素 None
  • C 中较大整数到较小无符号类型转换

    我经历了 k r 我在理解第 197 页 A6 部分 的以下几行时遇到问题 积分转换 任何整数都是 转换为给定的无符号类型 找到最小的非负数 与此一致的值 整数 模一以上 可以表示的最大值 在无符号类型中 任何机构都可以详细解释一下吗 谢谢
  • 计算字符串中模式匹配的数量

    例如 我有一个字符串 AAAAAAACGAAAAAACGAAADGCGEDCG 我想数数有多少次 CG 被重复 我怎么做 您可以使用gregexpr找到的位置 CG in vec 我们必须检查是否没有匹配项 1 功能sum计算匹配的数量 g
  • 以编程方式调整 DataGridView 大小以删除滚动条

    我有一个 DataGridView 其中包含用户可定义的数值数据列数 从 6 60 的任何位置 在高端 网格中的数据量超过了屏幕上可以立即显示的数据量 我有一个与数据相关的图表 我想让两者保持同步 以便图表上的特定时间 T 与网格中的同一时
  • 未弃用的相当于

    我想达到同样的目标 window open lalala php lalala 但我想发送 HTTP POST 请求而不是 HTTP GET 请求 因此 我使用以下内容
  • 收到错误“超出可用参数键空间”?

    在我的 Rails 应用程序中 表单发布后出现错误exceeded available parameter key space 表单内的表单字段太多 这是错误的原因吗 有什么看法吗 如果您确实非常需要它 请尝试增加key space lim
  • `if [-e file.txt]` 在 bash 中不起作用

    我正在尝试使用 bash 检查文件是否存在 这是我的代码 if e file txt then echo file exists else echo file doesn t exist fi 但是当我运行它时我得到 test sh lin
  • 更改 rmarkdown 保存 r 代码生成的图像的位置

    我有一个 rmarkdown 文档 我正在将此文件转换为 md 文档 我的问题是我希望将绘图创建的图片放置在文件同一目录中名为 Images 的文件夹中 假设我有这个文档 title my test author daniel date 1
  • MapView 在触摸时添加图钉

    我设法让地图显示在屏幕上 现在 用户将在地图上移动并按下它 按下一个点后 我需要在屏幕上按下的位置添加一个图钉 如果用户决定去另一个点 当按下所选点时 第一个图钉将消失 并且将在新位置绘制一个新图钉 我确实是这样的 public class
  • 是否可以在 symfony2 中动态设置路由的默认参数值?

    我使用注释在 symfony2 控制器中定义了一条路由 例如 Route year name show list for user defaults year 2012 是否可以使默认年份动态化 也许从服务对象中读取年份 您可以在Reque
  • 计算碰撞后的角速度

    我已经相对较好地降低了碰撞分辨率的线性部分 但我不太清楚如何对角度部分做同样的事情 从我读到的内容来看 这就像 torque point of collision x linear velocity 交叉产品 我尝试合并我发现的例子进入我的