Processing开发实战
上QQ阅读APP看书,第一时间看更新

第3章 数学基础

在绘制图形和动画时往往需要计算点、线、面之间的关系,这其中包含了很多数学运算。Processing提供了很多数学函数,方便开发者使用。

3.1 数学计算

对于数学计算中常用到的运算,Processing给出了一系列函数,如表3-1所示。

表3-1 数学计算函数表

❑abs(a)返回a的绝对值。

                                                                            3-1
int a = abs(153);                           //a赋值为153
int b = abs(-15);                           //b赋值为15
float c = abs(12.234);                      //c赋值为12.234
float d = abs(-9.23);                       //d赋值为9.23

❑ceil(a)向上取整。

                                                                            3-2
float x = 8.22;
int a = ceil(x);                            //a赋值为9

❑max()取最大值,有两种重载(min类似)。

                                                                            3-3
int a = max(5, 9);                          //a赋值为9
int b = max(-4, -12);                       //b赋值为-4
float c = max(12.3, 230.24);                //c赋值为230.24
int[] values = { 9, -4, 362, 21 };          //创建一个整型数组
int d = max(values);                        //d赋值为362

❑log(a)返回以e为底,a为真数的对数lna。

                                                                            3-4
// 求lg
float a = log(5)/log(10);                  //a赋值为0.69897

❑dist(x1, y1, x2, y2)返回两点间的距离。

                                                                            3-5
// 求点(1,1)到点(4,5)间的距离
float d = dist(1,1,4,5);                   //d赋值为5.0

3.2 三角函数

对于有关三角的常用运算,Processing给出了一系列函数,如表3-2所示。

表3-2 三角函数表

与数学中的不一样,Processing中默认以顺时针方向旋转为正方向,另外用PI表示π。

                                                                            3-6
float a = PI/6;
println(sin(a));                            //输出0.5
println(degrees(a));                        //输出30.0
println(a);                                 //输出0.5235988

atan和atan2都是反正切函数,它们之间有以下两点不同:

1)参数的填写方式不同。

例如:有两个点p1(x1, y1)和p2(x2, y2),这两个点形成的斜率的弧度计算方法分别是:

                                                                            3-7
float radian = atan( (y2-y1)/(x2-x1) );

                                                                            3-8
float radian = atan2( y2-y1, x2-x1 );

2)在二、三象限函数的返回结果不同。

                                                                            3-9
// 在第二象限时
float x = -1.7320508;
float y = 1;
float tan = atan(y/x);
float tan2 = atan2(y/x);
println(degrees(tan));                     //输出:-30.0
println(degrees(tan2));                    //输出:149.9999
                                                                            3-10
// 在第三象限时
float x = -1.7320508;
float y = -1;
float tan = atan(y/x);
float tan2 = atan2(y/x);
println(degrees(tan));                     //输出:30.0
println(degrees(tan2));                    //输出:-149.9999

结论:为了根据x、y坐标求得正确的角度,建议使用atan2函数。

3.3 功能映射函数

对于有关映射的运算,Processing给出了一系列函数,见表3-3。

表3-3 映射函数表

❑constrain()通过最大值和最小值来约束指定的数。

constrain(amt, low, high)
amt: int或float
low:最小值
high:最大值

原理:如果amt≥high为真则返回high,如果amt≤low为真则返回low,否则返回amt。

                                                                            3-11
println(constrain(10,20,80));    //输出20
println(constrain(50,20,80));    //输出50
println(constrain(90,20,80));    //输出90

❑lerp()用于在指定的两数之间线性插值。

lerp(start, stop, amt)
start:开始值
stop:结束值
amt:int或float值

插值原理:start+(stop-start)*amt

                                                                            3-12
float x=lerp(50, 100, 0.1);       //将55.0赋值给x
float y=lerp(50, 100, 0.5) ;     //将75.0赋值给y

❑norm()用于把指定数从指定范围映射到0.0~1.0。

norm(value, start, stop)
start:开始值
stop:结束值
value:int或float值

原理:(value-start)/(stop-start)

                                                                            3-13
float x=norm(60, 50, 100);                //将0.2赋值给x
float y=norm(45, 50, 100) ;               //将-0.1赋值给y
float z=norm(110, 50, 100) ;              //将1.2赋值给z

❑map()用于把指定数从指定范围映射到另一个范围。

map (value, start1, stop1, start2, stop2)
start1:第一个范围开始值
stop1:第一个范围结束值
start2:第二个范围开始值
stop2:第二个范围结束值
value:int或float值

原理:start2+(stop2-start2)*norm(value, start1, stop1)

                                                                            3-14
float x=map(55, 50, 100,500,1000) ;      //将550.0赋值给x
float y=map(40, 50, 100,500,1000) ;      //将400.0赋值给y
float z=map(110, 50, 100,500,1000) ;     //将1100赋值给z

3.4 随机数

1.随机数

在Processing中,如果想要获取随机的动画效果,就要用到和随机数相关的函数。

random()函数返回一个从0到指定数值范围内的随机数。

random(float x)
x为float类型数值

示例:

                                                                            3-15
float a=random(20);
// a为0~20的一个随机数

重载函数random(float x, float y)可以返回两个指定数值之间的随机数。

random(float x, float y)
x为float类型数值
y为float类型数值

示例:

                                                                            3-16
float a=random(10,20);                     //a为10~20的一个随机数

当编程人员想获取随机整数时,需要对随机数进行类型转换。

示例:

                                                                            3-17
int a=(int)random(10,20);                  //a为10~20的一个随机整数

示例:在大小为400×400的窗口下绘制16个相等的正方形,使它们的颜色呈随机灰度,如图3-1所示。

                                                                            3-18
void setup()
{
    size(400,400);                          //窗口大小设置为400×400
}
void draw()
{
    for(int i=0; i<4; i++)                   //第i行
    {
        for(int j=0; j<4; j++)              //第j列
        {
            fill(random(255));              //填充颜色为随机灰度
            rect(i*100, j*100,100,100);   //画出宽和高都为100的矩形
        }
    }
    delay(100);                             //延迟100毫秒
}

2.随机数种子

随机数又分为两种,一种是真随机数,另一种是伪随机数。

真随机数是计算机通过对外界信息的采集而获取的。例如,用户敲击键盘的时间,原子的放射性衰变的时间。这些信息都是不可预测的,于是就能获取真随机数。

在Processing中,通过random()函数产生的随机数并不是真正的随机数,它们是“伪随机数”。所获取的伪随机数都是通过向计算机发送一个种子值,然后计算机通过算法计算并返回一个看上去像是随机值的数。实际上,通过这种方法获得的随机数都是可以预测的。

图3-1 随机灰度格子

通过设置随机数种子值就能每次都获取相同的随机数序列。

randomSeed(long x);
x为long类型数值

示例:在窗口大小为400×400下绘制16个相等的正方形,使它们同一行灰度相同,不同行呈随机灰度,如图3-2所示。

                                                                            3-19
void setup()
{
    size(400,400);                 //窗口大小设置为400×400
}
void draw()
{
    for(int i=0; i<4; i++)         //第i行
    {
        randomSeed(5);             //设置随机数种子
        for(int j=0; j<4; j++)     //第j列
        {
            fill(random(255));     //填充颜色为随机灰度
    rect(i*100, j*100,100,100);  //画出宽和高都为100的矩形
    }
    delay(100);                    //延迟100毫秒
}

图3-2 随机数种子生成相同随机数序列

示例:随机产生几个数,为圆的半径,放入数组,每隔一秒调用一个数组的半径。

float[] a=new float[10];          //定义一个长度为10的浮点型数组
int j=0;
void setup()
{
    frameRate(1);                  //每秒刷新一次画面
    for(int i=0; i<10; i++)
    a[i]=random(100);              //把随机生成的数放入数组
}
void draw()
{
    background(204);               //背景设置为灰色
    if(j<10)
    ellipse(50,50, a[j], a[j]);
    j++;
}