计算碰撞后的角速度 [英] Calculating angular velocity after a collision

查看:248
本文介绍了计算碰撞后的角速度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经得到了冲突分辨率的线性分量相对较好,但我不能完全弄清楚如何做相同的角度。从我所读到的,就像... torque = 碰撞点 x 线速度。 (交叉产品)我试图在我的代码中加入示例,但我实际上看不到当物体碰撞时任何旋转。另一个小提琴与分离轴定理和角速度计算的基本实现完美地工作。



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

  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;

我将发布我用于计算惯性矩的代码,但是每一个都有一个单独的对象,所以会有点...长。虽然如此,这里是以圆圈为例:

 返回this.mass * this.radius * this.radius / 2 ; 

只是为了显示结果,这里是我的小提琴。如图所示,对象在碰撞时不旋转。



我做错了什么?



EDIT:他们没有旋转的原因是因为响应函数中的组出现错误 - 它现在旋转,只是不正确。但是,我现在已经评论过,因为它弄乱了事情。



此外,我试过另一种旋转方法。以下是响应中的代码:

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

请注意, direction 正常。

解决方案

由于力矢量的角度和线性加速度



由于施加的力的方向加速度是相同事物的两个分量,并且不能被分离。



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

  F是力(相当于惯性)
Fv线性力
Fa是角力
a是加速度可以是线性的或旋转的,取决于使用的位置
v是速度。对于有角度的情况,只有切向分量
m是质量
r是半径

对于线性力

  F = m * v 

我们从中衍生出来

  m = F / v 
v = F / m

旋转力(v为切向速度)

  F = r * r * m *(v / r)并简化F = r * m * v 

我们从中导出

  m = F / 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 2 和 a2 是每帧以弧度表示的加速度 2 (帧平方只是表示它是加速度)



从1D到2D



因为这是一个2D解决方案,载体。我对于这个问题使用两种形式的2D向量。极性具有大小(长度,距离,等等...)和方向。笛卡尔具有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 .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});
}

创建一个向量作为笛卡尔

 函数向量(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

 函数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.dir + = PI;
}
}
return vec;
}

盒子



现在我们可以定义一个我们可以使用的对象。一个简单的框,其位置,大小,质量,方向,速度和旋转

  function createBox(x,y,w,h ){
var box = {
x:x,// pos
y:y,
r:0.1,//其旋转AKA方向或弧度方向
h:h ,//它的高度
w:w,//它的宽度
dx:0,// delta像素每帧第1/60秒
dy:0,// delta y
dr:0.0,// deltat旋转以弧度为单位每帧1/60秒
mass:w * h,//物体在物体
update:function(){
this.x + = this.dx;
this.y + = this.dy;
this.r + = this.dr;
},
}
return box;
}

对对象施加力 >

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



F(force)是一个矢量力,大小是力,方向

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




位置是一个向量,只有x和y位置

var location = vector(canvas.width / 2,canvas.height / 2); //在画布的中间定义一个点

方向向量保持方向和距离向量

  var l1 = vector(canvas.width / 2,canvas.height / 2); //在canvas的中间定义一个点
var l2 = vector(100,100);
var direction = asPolar(vector(l2.x-l1.x,l2.y-l1.y)); //获取方向作为极向量

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



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



为了得到两个分量,你得到力矢量和力的方向矢量之间的方向差异对象中心。

  var force = polar(100,0); // force 
var forceLoc = vector(50,50); //施加力的位置

var direction2Center = asPolar(vector(box.x - forceLoc.x,box.y - forceLoc.y)); //获取方向作为极向量
var pheta = direction2Center - force.dir; //获取力和对象中心之间的角度

现在你有了角度pheta

  var F = force.mag; //获得力量值
var Fv = Math.cos(pheta)* F; //得到线性力
var Fa = Math.sin(pheta)* F; //获得角力

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

  accelV = Fv / box.mass; // linear acceleration in pixels 
accelA = Fa /(box.mass * direction2Center.mag); //以弧度表示的角加速度

然后将线性力转换回一个向量,对象的中心

  var forceV = polar(Fv,direction2Center); 

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

  forceV = asCart(forceV); 

并将加速度添加到框中

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

旋转加速度只是一维,所以只需将它加到框的增量旋转

  box.dr + = accelA; 

就是这样。



strong>对Box应用力的函数



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



附加到盒子上

  box.applyForce = applyForce; //绑定函数到框; 

然后您可以通过

框调用该函数

  box.applyForce(force,locationOfForce); 


function applyForce(force,loc){// force是向量,loc是坐标
var toCenter = asPolar(vector(this.x - loc.x, this.y-loc.y)); //获取向量到中心
var pheta = toCenter.dir - force.dir; //获取力和线到中心之间的角度
var Fv = Math.cos(pheta)* force.mag; //将力分解成沿着线到中心的速度力
var Fa = Math.sin(pheta)* force.mag; // and the angle force at the tangent to the line to the center
var accel = asPolar(toCenter); //将方向复制到中心
accel.mag = Fv / this.mass; //现在使用F = m * a形式a = F / m以获得加速度
var deltaV = asCart(accel); //将加速度转换为笛卡尔
this.dx + = deltaV.x //更新框delta V
this.dy + = deltaV.y //
var accelA = Fa /(toCenter .mag * this.mass); //为角度分量获取旋转
//从
//中的F = m * a * r加速a = F /(m * r)
this.dr + = accelA; //现在将其添加到框中delta r
}

演示



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



单击并拖动以将力应用于



  const PI90 = Math.PI / 2; const PI = Math.PI; const PI2 = Math.PI * 2; const INSET = 10; // playfeild insetconst ARROW_SIZE = 6const 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 normalsvar box = createBox(200,200,50,100); box.applyForce = applyForce; //将此函数添加到框// render / update functionvar mouse =(function(){function preventDefault(e){e.preventDefault();} var i; var mouse = {x:0,y:0,buttonRaw :0,bm:[1,2,4,6,5,3],//用于设置和清除按钮原始位的掩码; mouseEvents:mousemove,mousedown,mouseup.split(,)}; function mouseMove (e){var t = e.type,m = mouse; mx = e.offsetX; my = e.offsetY; if(mx === undefined){mx = e.clientX; my = e.clientY;} if (t ===mousedown){m.buttonRaw | = m.bm [e.which-1];} else if(t ===mouseup){m.buttonRaw& = m.bm [e .h4 + 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  


I've got the linear component of collision resolution down relatively well, but I can't quite figure out how to do the same for the angular one. From what I've read, it's something like... torque = point of collision x linear velocity. (cross product) I tried to incorporate an example I found into my code but I actually don't see any rotation at all when objects collide. The other fiddle works perfectly with a rudimentary implementation of the seperating axis theorem and the angular velocity calculations. Here's what I've come up with...

Property definitions (orientation, angular velocity, and angular acceleration):

rotation: 0,
angularVelocity: 0,
angularAcceleration: 0

Calculating the angular velocity in the 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 the orientation in the 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 would post the code that I have for calculating the moments of inertia but there's a seperate one for every object so that would be a bit... lengthy. Nonetheless, here's the one for a circle as an example:

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

Just to show the result, here's my fiddle. As shown, objects do not rotate on collision. (not exactly visible with the circles, but it should work for the zero and seven)

What am I doing wrong?

EDIT: Reason they weren't rotating at all was because of an error with groups in the response function -- it rotates now, just not correctly. However, I've commented that out for now as it messes things up.

Also, I've tried another method for rotation. Here's the code in the response:

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

Note that direction refers to the "collision normal".

解决方案

Angular and linear acceleration due to force vector

Angular and directional accelerations due to an applied force are two components of the same thing and can not be separated. To get one you need to solve for both.

Define the calculations

From simple physics and standing on shoulders we know the following.

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

For linear forces

F = m * v 

From which we derive

m = F / v
v = F / m

For rotational force (v is tangential velocity)

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

From which we derive

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

Because the forces we apply are instantaneous we can interchange a acceleration and v velocity to give all the following formulas

Linear

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

Rotational

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

As we are only interested in the change in velocity for both linear and rotation solutions

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)

From 1D to 2D

Because this is a 2D solution and all above are 1D we need to use vectors. I for this problem use two forms of the 2D vector. Polar that has a magnitude (length, distance, the like...) and direction. Cartesian which has x and y. What a vector represents depends on how it is used.

The following functions are used as helpers in the solution. They are written in ES6 so for non compliant browsers you will have to adapt them, though I would not ever suggest you use these as they are written for convenience, they are very inefficient and do a lot of redundant calculations.

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

Creates a polar vector

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

Create a vector as a cartesian

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

True is the arg vec is a vector in polar form

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

Returns true if arg vec is a vector in cartesian form

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

Returns a new vector in polar form also ensures that vec.mag is 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 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};
}

Calculations can result in a negative magnitude though this is valid for some calculations this results in the incorrect vector (reversed) this simply validates that the polar 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;
}

The Box

Now we can define an object that we can use to play with. A simple box that has position, size, mass, orientation, velocity and rotation

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

Applying a force to an object

So now we can redefine some terms

F (force) is a vector force the magnitude is the force and it has a direction

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

The force is meaningless without a position where it is applied.

Position is a vector that just holds and x and y location

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

Directional vector holds the direction and distance between to positional vectors

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 now has the direction from canvas center to point (100,100) and the distance.

The last thing we need to do is extract the components from a force vector along a directional vector. When you apply a force to an object the force is split into two, one is the force along the line to the object center and adds to the object acceleration, the other force is at 90deg to the line to the object center (the tangent) and that is the force that changes rotation.

To get the two components you get the difference in direction between the force vector and the directional vector from where the force is applied to the object center.

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   

Now that you have that angle pheta the force can be split into its rotational and linear components with trig.

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 

Now the forces can be converted back to accelerations for linear a = F/m and angular a = F/(m*r)

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

You then convert the linear force back to a vector that has a direction to the center of the object

var forceV = polar(Fv, direction2Center);

Convert is back to the cartesian so we can add it to the object deltaX and deltaY

forceV = asCart(forceV);

And add the acceleration to the box

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

Rotational acceleration is just one dimensional so just add it to the delta rotation of the box

box.dr += accelA;

And that is it.

Function to apply force to Box

The function if attached to the box will apply a force vector at a location to the box.

Attach to the box like so

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

You can then call the function via 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

The demo is only about the function applyForce the stuff to do with gravity and bouncing are only very bad approximations and should not be used for any physic type of stuff as they do not conserve energy.

Click and drag to apply a force to the object in the direction that the mouse is 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 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();

这篇关于计算碰撞后的角速度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆