The Nature of Code:Pvector

向量(Vectors)

The Nature of Code-Vectors

向量是Processing编程中的基本模块,向量又叫矢量(Euler Vector),定义为有方向和大小的实体。向量通常用箭头表示,箭头的指向就是向量的方向,箭头的长度就是向量的大小。

Vectors,You Complete Me.

在详细介绍向量之前,先通过一个例子来论证向量的重要性,以至于我们会从向量开始。

Example 1.1 Bouncing ball with no Vectors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
float x = 100;
float y = 100;
float xspeed = 1;
float yspeed = 3.3;

void setup() {
size(640,360);
background(255);
}

void draw() {
background(255);

//Move the ball according to its speed.
x = x + xspeed;
y = y + yspeed;

//Check for bouncing.
if ((x > width) || (x < 0)) {
xspeed = xspeed * -1;
}
if ((y > height) || (y < 0)) {
yspeed = yspeed * -1;
}

stroke(0);
fill(175);
//Display the ball at the location (x,y).
ellipse(x,y,16,16);
}

上面的例子实现了一个弹跳球。这个球具有一些属性包括:

1
2
Location //x and y
Speed //xspeed and yspeed

当然,可以想象它还可能会有更多的变量:

1
2
3
4
5
6
7
Acceleration //xacceleration and yacceleration

Target location //xtarget and ytarget

Wind //xwind and ywind

Friction //xfriction and yfriction

使用向量能让我们的代码更加简洁,比如二维空间中的位置和速度例子中,一般的实现需要四个变量:

1
2
3
4
float x;
float y;
float xspeed;
float yspeed;

如果使用Vector表示则可以简化成:

1
2
3
Vector location;
Vector speed;

编程中使用vector并不会让草图出现什么神奇的变化,但是它能简化代码,并且能提供一些数学函数供使用。开始介绍向量的时候,将不会涉及到3维空间,但是这些例子能很简单就扩展到三维空间中,因为向量类—PVector 支持三维空间向量。

Vectors for Processing Programmers

理解向量的一种方法是它和两个点的区别。考虑一下如何按照指示从一个点到另外一个点。下面是一些例子:

1
2
3
4
5
6
7
8
(-15, 3)
Walk fifteen steps west; turn and walk three steps north.

(3, 4)
Walk three steps east; turn and walk five steps north.

(2, -1)
Walk two steps east; turn and walk one step south.

在之前的Processing编程中可能也接触过类似的情况,比如指示屏幕上的每个对象水平移动一定数量的像素和垂直移动一定数量的像素。

对于每一帧,有:

new location = velocity applied to current location

如果速度(velocity)是一个向量,那么位置(position)到底是不是向量?有的说不是,因为位置描述的是空间中的一个点,这和之前的向量描述怎样从一个点运动到另一个点不同。尽管这样,从另外一个角度看,位置可以看成是从原点到所处位置的路径,因此位置可以表示所处位置和原点的相对位置。

以弹跳球为例讨论位置(location)和速度(velocity)数据,在上面的代码中:

1
2
3
location x,y

velocity xspeed,yspeed

如果我们自己实现一个vector类来保存速度或者位置中的两个变量:

1
2
3
4
5
6
7
8
9
10
11
12
class PVector {

float x;
float y;

PVector(float x_, float y_) {
x = x_;
y = y_;
}

}

从本质上讲,PVector是一种存储两个变量的很方便的方法(或者三维空间中的三个变量)。

如果使用向量,则原来的代码:

1
2
3
4
5
float x = 100;
float y = 100;
float xspeed = 1;
float yspeed = 3.3;

会变成下面这样:

1
2
PVector location = new PVector(100,100);
PVector velocity = new PVector(1,3.3);

有了位置和速度两个向量对象之后,我们对其应用location = location + velocity,则会从原来的代码:

1
2
x = x + xspeed;
y = y + yspeed;

变成:

1
2
location = location + velocity;

但是在Processing中,”+”号运算符仅能用于原始值(整数、浮点等)的计算,不能直接用作将对两个PVector对象相加,PVector有其自身对应的数学运算函数。

Vector Addition

正式开始使用PVector类的add()函数之前,先从数学角度简单介绍一下向量的加法。向量通常是用粗体或者符号上带有一个箭头符号来表示。为了区分向量(vector)和标量(scale,表示单个数,比如整数或者浮点),书中使用的是带箭头的符号。

  • 向量:$\overrightarrow{x}$
  • 标量:$x$

对于如下两个变量:

每个向量有两个元素,x、y。两个向量相加,只需要简单的讲其各自的x和y相加即可。

换句话说,$\overrightarrow{w}=\overrightarrow{u}+\overrightarrow{v}$,可以写成:

既然已经理解了向量的加法,那么就可以在之前自己实现的PVector类中新增一个add()函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class PVector {

float x;
float y;

PVector(float x_, float y_) {
x = x_;
y = y_;
}

//New! A function to add another PVector to this PVector. Simply add the x components and the y components together.
void add(PVector v) {
y = y + v.y;
x = x + v.x;
}
}

使用向量的思路重新实现例子1.1:

Example 1.2: Bouncing ball with PVectors!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PVector location;
PVector velocity;

void setup() {
size(640,360);
location = new PVector(100,100);
velocity = new PVector(2.5,5);
}

void draw() {
background(255);

location.add(velocity);
if ((location.x > width) || (location.x < 0)) {
velocity.x = velocity.x * -1;
}
if ((location.y > height) || (location.y < 0)) {
velocity.y = velocity.y * -1;
}

stroke(0);
fill(175);
ellipse(location.x,location.y,16,16);
}

用向量实现的的弹跳球,相比较起最初的代码看起来更加复杂,但是随着我们进入一个由多个对象和多个力组成的更复杂的系统(我们将在第 2 章中介绍),PVector 的好处将变得更加明显。

然而,我们应该注意上述过渡到向量编程的一个重要方面。 即使我们使用 PVector()对象来描述两个值——位置的 x 和 y 以及速度的 x 和 y——我们仍然经常需要单独引用每个 PVector()的 x 和 y 分量。

ellipse()函数不允许将 PVector()作为参数。 ellipse()只能用两个标量值绘制,一个 x 坐标和一个 y 坐标。 因此我们必须深入研究 PVector()对象并使用面向对象的点语法提取 x 和 y 分量。

1
ellipse(location.x,location.y,16,16);

同样的问题出现在边界判断中。

1
2
3
if ((location.x > width) || (location.x < 0)) {
velocity.x = velocity.x * -1;
}

More Vector Math

向量自带许多数学计算函数;

  • add() — 向量和;
  • sub() — 向量差;
  • mult() — 向量缩放(乘法);
  • div() — 向量缩放(除法);
  • mag() — 向量幅值;
  • setMag() - 设置向量幅值;
  • normalize() — 归一化向量(单位向量);
  • limit() — 限制向量的幅值;
  • heading() — 2维平面向量方向,输出为角度;
  • rotate() — 2维平面向量旋转;
  • lerp() — 线性插值到另一个向量;
  • dist() — 两个向量之间的欧几里得距离(看作点);
  • angleBetween() — 求两个向量的夹角;
  • dot() — 点乘两个向量;
  • cross() — 两个向量的叉积(仅与三个维度相关);
  • random2D() - 生成二维空间随机向量;
  • random3D() - 生成三维空间随机向量;

Vector subtraction

PVector内部减法实现:

1
2
3
4
void sub(PVector v) {
x = x - v.x;
y = y - v.y;
}

向量减法例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void setup() {
size(640,360);
}

void draw() {
background(255);
//Two PVectors, one for the mouse location and one for the center of the window
PVector mouse = new PVector(mouseX,mouseY);
PVector center = new PVector(width/2,height/2);
//PVector subtraction!
mouse.sub(center);
//Draw a line to represent the vector.
translate(width/2,height/2);
line(0,0,mouse.x,mouse.y);
}
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2020-2023 Wh
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信