实验12 密闭球

简介

本实验将引入极坐标的速度表示方式来解决物体与任意面碰撞的问题。这种方式在后面会用矢量代替。这种方式虽然可以降一个维度,二维降为一维,三维降为二维,但是这种方式无法方便地解决碰撞等物理问题,而且在使用极坐标时描绘坐标的位置也要相应地改成极坐标的形式,这样才能体现其优势,然而Canvas的所有API都是基于X轴和Y轴的,所以后面会统一使用矢量计算来代替,本实验只是拓展一下知识面。在进行实验模拟之前,先来了解几种数学上的基本概念。

笛卡儿坐标系

笛卡儿坐标系是直角坐标系和斜角坐标系的统称,如图2-15所示。相交于原点的两条数轴构成了平面放射坐标系。如两条数轴上的度量单位相等,则称此放射坐标系为笛卡儿坐标系。两条数轴互相垂直的笛卡儿坐标系称为笛卡儿直角坐标系,否则称为笛卡儿斜角坐标系。笛卡儿坐标表示了点在空间中的位置,但却和直角坐标有区别,两种坐标可以相互转换。

图2-15 笛卡儿坐标系

用一组数(x,y)可以表示平面上的一个点,平面上的一个点也可以用一组两个有顺序的数来表示,这就是坐标系的雏形。

极坐标系

在数学中,极坐标系是一个二维坐标系统。该坐标系统中的点由一个夹角和一段相对于中心点——极点(相当于较为熟知的直角坐标系中的原点)的距离来表示,如图2-16所示。极坐标系的应用领域十分广泛,包括数学、物理、工程、航海及机器人领域。当两点间的关系用夹角和距离很容易表示时,极坐标系便显得尤为有用;而在平面直角坐标系中,这样的关系就只能使用三角函数来表示了。对于很多类型的曲线,极坐标方程是最简单的表达形式,甚至对于某些曲线来说,只有极坐标方程能够表示。

图2-16 极坐标系

极坐标系到直角坐标系的转化

极坐标系中的两个坐标ρθ可以由下面的公式转换为直角坐标系下的坐标值:

x=ρcosθ

y=ρsinθ

由此,可得到从直角坐标系中xy两坐标计算出极坐标系下的坐标:

其中,x不等于0,在x=0的情况下,若y为正数,则θ=90°(π/2 rad);若y为负数,则θ=270° (3π/2 rad)。

极坐标速度

在前几个实验中,使用以下方法定义小球:

        var ball={
                x: 100,
                y: 100,
                r: 15,
                vx: 100,
                vy: 100
            };

定义速度的方法是利用笛卡儿坐标系,分别定义X轴方向的速度和Y轴方向的速度,这样就表示了在二维坐标系当中的速度。利用极坐标的方式同样也可以定义小球的速度:

        var ball={
                x: 100,
                y: 100,
                r: 15,
                v: 100 * Math.sqrt(2),
                direction: Math.PI / 4
            };

代码中,定义了小球的速度v和方向direction。这两种定义方式,达到的效果是完全一样的,vx和vy也同样为100。

密闭球

与任意轴碰撞的实验最适合的场景就是密闭球。图2-17就是该实验场景的描绘。

图2-17 在密闭球中与任意轴碰撞的实验

要让运动的小球不脱离圆形范围,所要做的就是碰撞检测和碰撞之后小球运动方向的改变。这里假设所有碰撞是完全弹性碰撞,没有任何速度损失。只要求出碰撞时,大圆的圆心和小球的圆心连线与水平X轴的夹角,速度方向的变化角度就迎刃而解了。

用极坐标的形式描述速度:

        for (var i=0; i <6; i++) {
        var ball={
                      position: new Vector2(300, 250),
                      r: 10,
                      v: getRandomNumber(50, 200),
                      direction: (getRandomNumber(0, 360) / 360) * 2 * Math.PI
                  };
                  balls.push(ball);
                }

初始化一个大球:

        var bigCircularity={
                  position: new Vector2(300, 250),
                  r: 190
                }

碰撞检测:

        if (Math.round(Math.pow(balls[i].position.x-bigCircularity.position.x, 2)+Math.pow(bigCircularity.
        position.y-balls[i].position.y, 2)) >=Math.round(Math.pow(bigCircularity.r-balls[i].r, 2))) {
        }
                // 大圆圆心到小球圆心的距离大于或等于(R-r)×(R-r)时,代表碰撞。

碰撞后,小球运动方向的改变:

        var cD=balls[i].direction;
        var step=Math.acos((balls[i].position.x-bigCircularity.posit ion.x)/
        distance);
        if (balls[i].position.y > bigCircularity.position.y) {
                balls[i].direction=-cD-Math.PI+2 * step;
              }else {
                balls[i].direction=-cD+Math.PI-2 * step;
              }

其中,step为大圆的圆心到小球的圆心这条线段与水平X轴的夹角。

运行代码,其效果如图2-18所示。

图2-18 Canvas密闭球模拟

当然,可以通过增加小球的数量,减小小球的半径来制作很炫的粒子系统效果。

        for (var i=0; i < 1200; i++) {
        var ball={
                      position: new Vector2(300, 250),
                      r: 1,
                      v: getRandomNumber(50, 200),
                      direction: (getRandomNumber(0, 360) / 360) * 2 * Math.PI
                  };
                  balls.push(ball);
                }

其效果如图2-19所示。

图2-19 粒子系统模拟1

或者改变所有小球(即爆炸点)的起始位置:

        for (var i=0; i < 1200; i++) {
        var ball={
                      position: new Vector2(300, 150),
                      r: 1,
                      v: getRandomNumber(50, 200),
                      direction: (getRandomNumber(0, 360) / 360) * 2 * Math.PI
                  };
                  balls.push(ball);
                }

其效果如图2-20所示。

图2-20 粒子系统模拟2