14. 我们直觉上习惯以度数来考虑角。但Processing却要求我们用弧度来制作一个角。
角度、弧度转换公式:
弧度 = 2 * PI * (角度/360)
幸运的是,若我们习惯了以角度来考虑角而又必须以弧度写代码,Processing为我们提供了radians()函数自动将角度值转化为弧度值。另外,常数PI和TWO_PI也可以现成使用(分别等于180 ° 和 360 ° )。以下代码将使形状旋转60 ° 。
rotate(angle);
15. 复习一下,数学常数PI (或 π )是圆周与其直径的比率(围绕圆周的距离)。它的值约等于3.14159。
16. Sohcahtoa,貌似无意义而且奇怪的词,确实巨大部分计算机图形工作的基础。当你要计算一个角,决定点间距,除了圆形或弧形等等等等的时候,你会发现三角学的重要性。sohcahtoa是记忆三角学基础,正弦、余弦和正切的助记符。
◎ soh : sine = 对边/斜边
◎ cah : cosine = 邻边/斜边
◎ toa : tangent = 对边/邻边
17. 我们要在Processing内绘制图形,需要给出其x,y坐标值,这种坐标称为笛卡尔坐标。另外一种有用的坐标称为极坐标,以空间内围绕原点旋转(以角度计)的以及距离原点的半径定义的一个点。我们可以将其作为p5内一个函数的引数。三角函数公式允许我们将这些坐标转换为笛卡尔,然后被用于绘制形状。
sine(theta) = y/r → y = r * sine(theta)
cosine(theta) = x/r → x = r * cosine(theta)
18. 比如,如果r为75,角度为45° (或 PI/4 弧度) ,我们可以如下计算x和y:
float theta = PI / 4; // 我们同样可以说: float theta = radians(45);
float x = r * cos(theta);
float y = r * sin(theta);
19。 这样的转换对我们来说是非常有用的,例如说,你如何用笛卡尔坐标使一个形状延圆形轨迹运动?那将非常困难。不过使用极坐标也很简单,只需增加角度即可!
float r = 75;
float theta = 0;
void setup() {
size(200,200);
background(255);
smooth();
}
void draw() {
// 极坐标到笛卡尔坐标的转换
float x = r * cos(theta);
float y = r * sin(theta);
// 在x,y绘制圆形
noStroke();
fill(0);
ellipse(x + width/2, y + height/2, 16, 16); // 为窗口中央进行调整
// 增加角度
theta += 0.01;
}
20. Daniel出题,来,我们一起来画一盘七彩蚊香吧。。注意,只需在上例代码基础上修改一行、增加一行即可。我做出来了,你呢?
21. 正弦曲线是平滑的,并且总在-1和1间变化。这种行为类型称为振动,在两点间的周期运动,例如钟摆。在p5中,我们可以通过将正弦函数的值赋给一个对象的位置来模拟这种振动。下例为一个摇动的钟摆的代码:
void setup() {
size(200,200);
smooth();
}
void draw() {
background(255);
// sin()函数的输出在-1到1间平滑振动.
// 通过加1,我们得到0~2间的振动.
// 通过乘以100(width/2),我们得到0~200的值,这可以用于x位置。
float x = (sin(theta) + 1) * width/2;
// 对于每次循环,增加theta。
theta += 0.05;
// 用正弦值绘制圆形
fill(0);
stroke(0);
line(width/2,0,x,height/2);
ellipse(x,height/2,16,16);
}
22. Daniel出题:将以上功能封装入Oscillator对象,获取一个Oscillators数组,每个都围绕x和y轴以不同的比率运动。哦,和你一样,我还不大会用类,因此最后在这里看一下答案。看完还是很有帮助的,一遍遍的看这些类似代码,差不多也能记个七八成了吧。。
23. 同样的,我们可以在正弦函数的路径上绘制一序列形状以实现有趣的效果。
void setup() {
size(200,200);
smooth();
}
void draw() {
background(255);
// 增加theta
theta += 0.02;
noStroke();
fill(0);
float x = theta;
// 一个for循环被用来绘制依附正弦波路径的所有点 (使其放大到窗口像素尺寸).
for (int i = 0; i <= 20; i++) {
// 基于正弦函数计算y
float y = sin(x)*height/2;
// 绘制一个圆
ellipse(i*10,y + height/2,16,16);
// 顺x轴移动
x += 0.2;
}
}
24. 1975年,Benoit Mandelbrot创造了分形(fractal)体系用以形容自然界中那些自相似的形状。产生这些形状的一个程序被称为递归或循环(recursion)。
25. 我们知道在draw()内,一个函数可以呼叫另一个函数。但它们可以呼叫它们本身吗?draw()可以呼叫draw()吗?实际上,它可以(虽然这会造成死循环的惨烈局面..)。函数呼叫自身便是递归。在数学中,最普遍的例子便是阶乘。n个数的阶乘:
n! = n * n – 1 * . . . . * 3 * 2 * 1
0! = 1
在p5内用for循环写:
int f = 1;
for (int i = 0; i < n; i++ ){
f = f * (i + 1);
}
return f;
}
再进一步观察阶乘:
4! = 4 * 3 * 2 * 1
3! = 3 * 2 * 1
因此. . . 4! = 4 * 3!
再推:
n! = n * ( n – 1)!
1! = 1
26. 阶乘的定义包括阶乘吗?!这有点像说“疲倦”被定义为“当你疲倦时的感受”。这种在函数内自参照的概念被称为递归。我们可以利用递归写一个呼叫自身阶乘的函数。
if (n == 1) {
return 1;
} else {
return n * factorial(n-1);
}
}
27. 下图为fractorial(4)时的情况
28. 同样的原理可以被用来绘制有趣的图形。看看接下来的递归函数。
ellipse(x, y, radius, radius);
if(radius > 2) {
radius *= 0.75f;
drawCircle(x, y, radius);
}
}
drawCircle()干了什么?它绘制一个圆,然后以相同的参数(微微调整)呼叫自身。结果就是一系列在自身内绘制的圆形。注意以上函数仅在半径大于2时递归呼叫自身。这是一个关键点。所有递归函数必须拥有一个推出的条件(exit condition)!像for和while循环一样,如果没有停止的条件,则很可能会出现死循环最后使程序崩溃。
29. 让我们试着将drawCircle()搞得更复杂一点点。绘制一个圆形,并在其左右分别绘制两个大小为其一半的圆,如此反复:
size(200,200);
smooth();
}
void draw() {
background(255);
stroke(0);
noFill();
drawCircle(width/2,height/2,100);
}
void drawCircle(float x, float y, float radius) {
ellipse(x, y, radius, radius);
if(radius > 2) {
// drawCircle()呼叫自己两次,制造一个分支效果
// 对每个圆来说,两个更小的圆分别在其一左一右绘制.
drawCircle(x + radius/2, y, radius/2);
drawCircle(x – radius/2, y, radius/2);
}
}
30. 同样的,我们可以基于上例分别再在每个圆的一上一下各增加一个圆,效果很美:
ellipse(x, y, radius, radius);
if(radius > 8) {
drawCircle(x + radius/2, y, radius/2);
drawCircle(x – radius/2, y, radius/2);
drawCircle(x, y + radius/2, radius/2);
drawCircle(x, y – radius/2, radius/2);
}
}
31. 这道练习搞得我很纠结(哦,看来注释真的很重要,hey,Daniel,别说填你留给我的空了,光看懂你答案的代码都很难啊- -),还是属于对象没学好…:
size(400,200);
smooth();
}
void draw() {
background(255);
stroke(0);
branch(width/2,height,100);
}
void branch(float x, float y, float h) {
line(x,y,x-h,y-h);
line(x,y,x+h,y-h);
if (h > 2) {
branch(x-h,y-h,h/2);
branch(x+h,y-h,h/2);
}
}
恩,经过半天一夜的思考和草稿,我终于弄明白这题了。。还有想不明白的可以问我。
32. 之前我们学习的数组是一维的,比如:
其实它也可以多维,比如二维的数组看起来就像这样:
可以理解为,数组的数组。而三维数组便可理解为数组的数组的数组。
33. 出于我们的目的,我们最好将二维数组看做一个矩阵,写做:
{ 3, 2, 1, 0},
{ 3, 5, 6, 1},
{ 3, 8, 3, 4} };
34. 我们可以利用这种数据结构编码一个图片的信息。例如,一个不同颜色的网格可由如下代码实现(每个值代表一个颜色):
{ 236, 80, 189, 189},
{ 236, 0, 189, 80},
{ 236, 189, 189, 80} };
35. 顺序阅遍一维数组,我们用for循环:
for (int i = 0; i < myArray.length; i++ ) {
myArray[i] = 0;
}
而对于二维数组,如果想要关照每一个元素,我们必须要使用两个嵌套的循环。这给了我们一个对于矩阵内每一行每一列的计数器变量。
int rows = 10;
int[][] myArray = new int[cols][rows];
// 两个嵌套的循环使得我们可以访问二维数组中的每一点。使每个列i,方位每个行j。
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
myArray[i][j] = 0;
}
}
36. 一个例子:
size(200,200);
int cols = width;
int rows = height;
// 申明2D数组
int[][] myArray = new int[cols][rows];
// 初始化2D数组值
for (int i = 0; i < cols; i ++ ) {
for (int j = 0; j < rows; j ++ ) {
myArray[i][j] = int(random(255));
}
}
// 画点
for (int i = 0; i < cols; i ++ ) {
for (int j = 0; j < rows; j ++ ) {
stroke(myArray[i][j]);
point(i,j);
}
}
37. 一个二维数组同样可以用来存储对象,这尤其适用于那些包括某种网格或板子的程序。下例显示的正是将一堆Cell对象存储于一个二维数组。每个cell的亮度由一个正弦函数引发由0~255的振动。
Cell[][] grid;
// 网格的行数和列数
int cols = 10;
int rows = 10;
void setup() {
size(200,200);
grid = new Cell[cols][rows];
// 计数器变量i和j同样是行和列的数目
// 在本例中, 它们被用于格子对象构造器的引数
for (int i = 0; i < cols; i ++ ) {
for (int j = 0; j < rows; j ++ ) {
// Initialize each object
grid[i][j] = new Cell(i*20,j*20,20,20,i + j);
}
}
}
void draw() {
background(0);
for (int i = 0; i < cols; i ++ ) {
for (int j = 0; j < rows; j ++ ) {
// 振动并显示每个对象
grid[i][j].oscillate();
grid[i][j].display();
}
}
}
// 一个Cell对象
class Cell {
// 一个cell对象通过变量x,y,w,h获取其在网格内的位置和大小
float x,y; // x,y位置
float w,h; // 宽、高
float angle; // 振动亮度的角度
// Cell构造器
Cell(float tempX, float tempY, float tempW, float tempH, float tempAngle) {
x = tempX;
y = tempY;
w = tempW;
h = tempH;
angle = tempAngle;
}
// 振动意味着增加角度
void oscillate() {
angle += 0.02;
}
void display() {
stroke(255);
// 用正弦波计算颜色
fill(127 + 127*sin(angle));
rect(x,y,w,h);
}
}
38. 最后这个练习貌似很有意思。但学到这里我已经很想死了,血槽还有血的慢慢玩吧。。
请问那个七彩蚊香加的是哪一行?
无论用“李李”还是“eva”作为id,都可以显示你的急于求成,静下心来,好好想想,然后再提问
豆瓣跟过来的。这东西不错
@秋凉如心, :)