互动码绘——表现随机行为及牛顿运动学
1.第0章 引言
这一章节主要是为第一章的向量做铺垫,讲述了随机游走这一简单的运动模拟模型,重点在于对随机性、概率和Perlin噪声的说明。首先是随机数,生活中总有很多例子无法用均匀分布的随机数模拟,高斯分布有时也无能为力。但是,这把我们限制在了有限的几个选择中。如果我们想要有一般的选择规则(数字越 大,被选到的概率越大),该怎么做?比如,3.145被选中的几率就比3.144高,就算只高 一点点。换言之,以选中的随机数为x轴,被选中的概率为y轴,我们可以建立这样的映 射:y = x。
拓展
float t = 0.007;
PVector p0=new PVector(0,400);
void setup(){
size(800,800);
color(255);
smooth();
}
int value =1;
PVector p = new PVector(400,400);
void draw(){
float sk = random(100);
if (sk>75){
p = new PVector(p.x+sk*t,p.y+(100-sk)*t);
value=0;}
else {
if (sk>50){
p = new PVector(p.x+sk*t,p.y-(100-sk)*t);
//p = new PVector(p.x+s*t,p.y);
value=1;}
else {
if (sk>25){
p = new PVector(p.x-sk*t,p.y+(100-sk)*t);
//p = new PVector(p.x,p.y+(1-s)*t);
}
else {
p = new PVector(p.x-sk*t,p.y-(100-sk)*t);
//p = new PVector(p.x,p.y);
}
}
}
if (p.x>800) p.x-=800;
else {
if (p.x<0) p.x+=800;
}
if (p.y>800) p.y-=800;
else {
if (p.y<0) p.y+=800;
}
ellipse(p.x,p.y,20,20);
fill(#7FFFD4);
}
参考案例:0-3、0-5
2.第1章 向量
向量通常被绘制为一个带箭头的线段,线段的长度代表向量的大小,箭头所指的方向就是 向量的方向。
在上图中,向量被绘制为从A点到B点的带箭头的线段,并说明了物体如何从A点运动到B
点。
拓展
实现物体的匀速水平运动
int radius = 40;
float x = -radius;
float speed = 0.5;
void setup() {
size(240, 120);
smooth();
ellipseMode(RADIUS);
}
void draw() {
background(0);
x += speed; // Increase the value of x
arc(x, 60, radius, radius, 0.52, 5.76);
}
参考案例:1-6、1-7
3.第2章 力
这一章讲述了力的概念以及力和加速度的关系。,“力”这个词也有很多不同的意义。它可以指代力量的强度,比如“她用力地推动 那块大石头”或者“他有力地说出那句话”。但是我们所说的力是更书面化的概念,它 源自牛顿运动定律:力是一个向量,它使有质量的物体产生加速。
拓展
对引力的概念有了一定了解,构建了围绕一个物体做圆周运动的引力模型
float r, theta;
float speed;
void setup() {
size(400, 400);
r = 150.0;
theta = 0.0; // theta = 0 to TWO_PI
speed = 0.05;
}
void draw() {
background(0, 0, 0);
noStroke();
// draw the sun
fill(255, 255, 0);
translate(width/2, height/2);
ellipse(0, 0, 50, 50);
// draw the earth
theta += speed;
float x = r * cos(theta);
float y = r * sin(theta);
fill(0, 255, 255);
translate(x, y);
ellipse(0, 0, 20, 20);
}
参考案例:2-6、2-7
4.第3章 震荡
本章讲述了角、角速度以及角加速度,期间 还会涉及正弦函数和余弦函数,它们可以用来制作平滑的波形曲线。有了这些知识, 我们就能计算更复杂的力,而这些力往往都涉及角度,比如钟摆的摆动和盒子从斜坡 滑下时所受的力。
三角函数原型: y = sin(theta) 。
简单来说,当 theta从 0 变化至 2π,y 会经历0 ~ 1 ~ 0 ~ -1 ~ 0 一整个周期的变化。初中就学过。
单个点的垂直摆动:垂直位置 = 振幅 * sin(时间的流逝)。
点的位置随时间流逝而摆动,2π 秒是一个摆动周期。
多个采样点拟合正弦波:每个点的垂直位置 = 振幅 * sin(每个点自身在时间线上的偏离 + 时间线本身的流逝)
这里还需要体现出3个参数,方便之后能够很直观的去调曲线:振幅、周期、波长;
振幅上面已经体现出来了;周期主要是影响时间本身的流逝速度,使时间在「一个周期内」刚好流逝掉 2π;波长主要是影响每个点自身在时间线上偏离多少,使「一个波长距离内」 所有采样点的时间线偏离量总和刚好是 2π,形成一个完整的曲线周期。
拓展
构建了水面波动的震荡模型
float r=0,r2=0;
float c=0,c2=0;
void setup()
{
size(600,600);
background(255);
}
void draw()
{
noStroke();
frameRate(70);
fill(c);
ellipse(width/2,height/2,r+10,r+10);
fill(255);
ellipse(width/2,height/2,r,r);
c=c+1;
r=r+1;
//while(r>50)
//{
if(r>50)
{fill(c2);
ellipse(width/2,height/2,r2+10,r2+10);
fill(255);
ellipse(width/2,height/2,r2,r2);
c2=c2+0.7;
r2=r2+1;
}
}
参考案例:3-2、3-9
5.第4章 粒子系统
本章讨论了粒子系统的实现策略。探讨了以下问题:在实现粒子系统时,如何组织 代码;如何存放单个粒子及整个系统的相关信息。
拓展
构建了烟花效果的粒子模型
ParticleSystem ps;
void setup() {
size(640, 360);
ps = new ParticleSystem(new PVector(width/2, 50));
}
void draw() {
background(0);
ps.addParticle();
ps.run();
}
class Particle {
PVector location;
PVector velocity;
PVector acceleration;
float lifespan;
Particle(PVector l) {
// The acceleration
acceleration = new PVector(0, 0.05);
// circel's x and y ==> range
velocity = new PVector(random(-1, 1), random(-2, 0));
// apawn's position
location = l.copy();
// the circle life time
lifespan = 255.0;
}
void run() {
update();
display();
}
void update() {
velocity.add(acceleration);
location.add(velocity);
lifespan-=1.0;
}
boolean isDead() {
if (lifespan <= 0) {
return true;
} else {
return false;
}
}
void display() {
// border
stroke(0, lifespan);
// border's weight
strokeWeight(1);
float r = random(0,255);
float g = random(0,255);
float b = random(0,255);
// random the circle's color
fill(r,g,b, lifespan);
// draw circle
ellipse(location.x, location.y, 3, 3);
}
}
class ParticleSystem {
ArrayList<Particle> particles;
PVector origin;
ParticleSystem(PVector position) {
origin = position.copy();
particles = new ArrayList<Particle>();
}
void addParticle() {
particles.add(new Particle(origin));
}
void run() {
for (int i = particles.size()-1; i >= 0; i--) {
Particle p = particles.get(i);
p.run();
if (p.isDead()) {
particles.remove(i);
}
}
}
}
参考案例:4-2、4-6