前言
近日拜讀了Daniel Shiffman先生的著作——《代碼本色:用編程模擬自然系統(tǒng)》陕壹,決定做一組習(xí)作拼卵,來對書中提到的隨機行為及牛頓運動學(xué)進行理解,并對一些實例進行拓展學(xué)習(xí)袱讹,從而提升自己相關(guān)方面的知識水平和實踐能力闪湾。
《代碼本色》第3章概述
在第3章中,作者向我們介紹了三角函數(shù)尊搬。三角函數(shù)會給我們帶來很多新工具叁鉴。本章我們會學(xué)習(xí)角、角速度以及角加速度佛寿,期間還會涉及正弦函數(shù)和余弦函數(shù)幌墓,它們可以用來制作平滑的波形曲線。有了這些知識,我們就能計算更復(fù)雜的力克锣,而這些力往往都涉及角度,比如鐘擺的擺動和盒子從斜坡滑下時所受的力腔长。作者通過一些實例深入淺出地講解了這些力袭祟,并對這些力進行了分析和實踐。
在這一章中捞附,我印象比較深的點是對簡諧運動的模擬巾乳,此外,還有對鐘擺鸟召、彈簧的模擬胆绊。下面,我將介紹我根據(jù)本章內(nèi)容而創(chuàng)作的Processing編程習(xí)作欧募。
習(xí)作
可以拉拽綠色點压状,每兩個點之間都有一個彈簧效果的力支撐。此外跟继,在其他地方點擊鼠標還可以添加新的點种冬。
創(chuàng)作過程
本次創(chuàng)作的關(guān)鍵在于——如何實現(xiàn)彈簧的效果。對此舔糖,書上花了很大篇幅進行介紹娱两。
首先介紹下彈簧的彈力。彈簧的彈力可以根據(jù)胡克定律計算得到金吗,胡克定律以英國物理學(xué)家羅伯特·胡克命名十兢,他在1660年發(fā)明了這個公式。胡克最初是用拉丁文描述這個公式的——“Ut tensio,sic vis”, 這句話的意思是“力如伸長(那樣變化)”摇庙。我們可以這么理解它:彈簧的彈力與彈簧的伸長量成正比旱物。
換個說法,就是我們大家都熟悉的公式:F=-kx跟匆。這里的k就是彈性系數(shù)异袄,是一個常數(shù)。而x是當(dāng)前長度與靜止長度的差值玛臂。
接下來烤蜕,我開始編寫Spring類(彈簧類)。
首先定義了這三個變量:
PVector anchor; // 起始位置
float len;// 靜止長度
float k = 0.05;//彈性系數(shù)
然后迹冤,在update()函數(shù)里計算彈力:
// 計算彈力
void update() {
PVector force = PVector.sub(a.position, b.position);//矢量指向從a點到b點
float d = force.mag();//計算向量長度
float stretch = d - len;//形變長度
force.normalize();
force.mult(-1 * k * stretch); //F=-kx
a.applyForce(force);
force.mult(-1);//牛頓第三定律
b.applyForce(force);
}
代碼中有詳細的注釋讽营,就不一一解釋了。
此外值得一提的是泡徙,在Circle類中添加了這樣一個變量:
//削減系數(shù)橱鹏,模擬摩擦力
float damping = 0.9;
這個變量的作用是為了模擬摩擦力和空氣阻力的效果,如果沒有這個變量,或者這個變量為1莉兰,那么這個運動將不會停止挑围,這不符合我們對現(xiàn)實生活中運動的認知。在Circle類的update函數(shù)中糖荒,我們將速度向量乘上這個系數(shù):
void update() {
...
velocity.mult(damping);
...
}
在主程序中杉辙,我定義了兩個ArrayList,分別用來存儲Circle類變量和Spring類變量:
ArrayList<Circle> circles=new ArrayList<Circle>();
ArrayList<Spring> springs=new ArrayList<Spring>();
然后在setup函數(shù)里編寫了如下代碼:
Circle Circle1 = new Circle(width/2, height/2);
Circle Circle2 = new Circle(width/2+50, height/2+50);
circles.add(Circle1);
circles.add(Circle2);
Spring Spring = new Spring(circles.get(0), circles.get(1),200);
springs.add(Spring);
簡單來說就是兩個Circle連接了一個彈簧捶朵,很好懂蜘矢,這就是面向?qū)ο缶幊趟枷氲膬?yōu)點。隨后再進行一些力的應(yīng)用综看,添加了鼠標拖拽事件品腹,就可以跑了,我們看看效果:
其實一開始的效果不是那么理想红碑,然后我對一些參數(shù)進行了調(diào)整舞吭,比如彈性系數(shù)k,削減系數(shù)damping 等句喷,最后達成了很“Q彈”的效果镣典。
然后我覺得兩個球一根彈簧過于單調(diào)了,于是做了多個球唾琼,以及每個球都會與除自己之外的所有球連接一根彈簧兄春。這樣一來就會產(chǎn)生一個很神奇的效果,不管怎么拖拽锡溯,最終很可能會回到一個穩(wěn)定的狀態(tài)赶舆,比如五個的情況:
看起來就像一個魔法陣一樣,如果用圖論的知識來說祭饭,這是一個完全圖芜茵。
最后,我添加了鼠標點擊增加點的效果倡蝙,這個和之前第0章做的相似:
void mouseClicked()
{
Circle newCircle = new Circle(mouseX, mouseY);
for(int i=0;i<circles.size();i++)
{
Spring newSpring = new Spring(newCircle,circles.get(i),200);
springs.add(newSpring);
}
circles.add(newCircle);
}
以及添加了漸變軌跡的效果:
fill(255,255,255,50);
rect(0,0,width,height);
最終效果如下:
最后附上所有代碼九串。
Circle類:
class Circle {
PVector position;
PVector velocity;
PVector acceleration;
float mass = 5;
//削減系數(shù),模擬摩擦力
float damping = 0.9;
PVector dragOffset;//拉動的距離向量
boolean dragging = false; // 鼠標是否按下
Circle(float x, float y) {
position = new PVector(x,y);
velocity = new PVector();
acceleration = new PVector();
dragOffset = new PVector();
}
void update() {
velocity.add(acceleration);
velocity.mult(damping);
position.add(velocity);
acceleration.mult(0);
}
void applyForce(PVector force) {
PVector f = force.get();//牛頓第二定律F=ma
f.div(mass);
acceleration.add(f);
}
void display() {
stroke(0);
strokeWeight(2);
fill(#98FFCC);
if (dragging) {
fill(#FF9898);
}
ellipse(position.x,position.y,mass*5,mass*5);
}
void clicked(int mx, int my) {
float d = dist(mx,my,position.x,position.y);
if (d < mass) {
dragging = true;
dragOffset.x = position.x-mx;
dragOffset.y = position.y-my;
}
}
void stopDragging() {
dragging = false;
}
void drag(int mx, int my) {
if (dragging) {
position.x = mx + dragOffset.x;
position.y = my + dragOffset.y;
}
}
}
Spring類:
class Spring {
PVector anchor; // 起始位置
float len;// 靜止長度
float k = 0.05;//彈性系數(shù)
Circle a;
Circle b;
Spring(Circle a_, Circle b_, int l) {
a = a_;
b = b_;
len = l;
}
// 計算彈力
void update() {
PVector force = PVector.sub(a.position, b.position);//矢量指向從a點到b點
float d = force.mag();//計算向量長度
float stretch = d - len;//形變長度
force.normalize();
force.mult(-1 * k * stretch); //F=-kx
a.applyForce(force);
force.mult(-1);//牛頓第三定律
b.applyForce(force);
}
void display() {
strokeWeight(2);
stroke(0);
line(a.position.x, a.position.y, b.position.x, b.position.y);
}
}
主程序類:
//Circle[] circle = new Circle[5];
//Spring[] springs = new Spring[20];
ArrayList<Circle> circles=new ArrayList<Circle>();
ArrayList<Spring> springs=new ArrayList<Spring>();
void setup() {
size(600, 600);
for (int i = 0; i < 5; i++) {
Circle newCircle = new Circle(width/2+i*100-200, height/2+i*100-200);
circles.add(newCircle);
}
for (int i = 0; i < circles.size(); i++) {
for (int j = 0; j< circles.size(); j++) {
if(i!=j) {
Spring newSpring = new Spring(circles.get(i), circles.get(j),200);
springs.add(newSpring);
}
}
}
//Circle Circle1 = new Circle(width/2, height/2);
//Circle Circle2 = new Circle(width/2+50, height/2+50);
//circles.add(Circle1);
//circles.add(Circle2);
//Spring Spring = new Spring(circles.get(0), circles.get(1),200);
//springs.add(Spring);
}
void draw() {
fill(255,255,255,50);
rect(0,0,width,height);
for (Spring s : springs) {
s.update();
s.display();
}
for (Circle b : circles) {
b.update();
b.display();
b.drag(mouseX, mouseY);
}
}
void mouseClicked()
{
Circle newCircle = new Circle(mouseX, mouseY);
for(int i=0;i<circles.size();i++)
{
Spring newSpring = new Spring(newCircle,circles.get(i),200);
springs.add(newSpring);
}
circles.add(newCircle);
}
void mousePressed() {
for (Circle b : circles) {
b.clicked(mouseX, mouseY);
}
}
void mouseReleased() {
for (Circle b : circles) {
b.stopDragging();
}
}
總結(jié)
本文介紹了《代碼本色:用編程模擬自然系統(tǒng)》第3章的主要內(nèi)容寺鸥,并在示例的基礎(chǔ)上進行了拓展性的創(chuàng)作猪钮,創(chuàng)作了彈簧的模型并進行了拓展。