相连的系统I:绳子
2022-06-20 本文已影响0人
大龙10
书名:代码本色:用编程模拟自然系统
作者:Daniel Shiffman
译者:周晗彬
ISBN:978-7-115-36947-5
目录
5.18 相连的系统I:绳子
1、模拟柔软物体
- 在上例中,两个粒子对象通过一根弹簧相连。
- toxiclibs物理库尤其适用于模拟柔软物体,
- 比如可以用连接成一条线的粒子模拟绳子,
- 可以用连接在一起的粒子网格模拟毯子。
-
下面这个可爱的卡通模型也可以用相连的粒子进行模拟,这些粒子都通过弹簧相连。
图5-14
2、模拟一个“柔软的钟摆”模型
- 下面我们要模拟一个“柔软的钟摆”模型——将摆球挂在绳子的底端,这里的摆臂不再是第3章里使用的刚性摆臂,而是图5-14所示的“绳子”。
1)首先,我们需要一个粒子列表
(使用上例的Particle类)
ArrayList<Particle> particles = new ArrayList<Particle>();
-
假如我们需要20个粒子,它们之间的间隔是10个像素。
图5-15
float len = 10;
float numParticles = 20;
- 我们可以将下标i从0递增到20,将每个粒子的y坐标设置成i * 10,这样一来,第1个粒子位于坐标(0,10),第2个粒子位于(0,20),第3个粒子位于(0,30)……
for (int i=0; i < numPoints; i++) {
Particle particle=new Particle(i*len, 10); 沿着x轴摆放粒子
physics.addParticle(particle); 将粒子加入列表
particles.add(particle); 将粒子加入物理世界
}
-
除了将粒子对象加入toxiclibs的物理世界,我们还将它放入自己的列表中。尽管这有些多余,但后面可能会有很多条绳子,到时候我们可以方便地获知粒子被连在哪一条绳子上。
-
下面要做一件有趣的事:将所有的粒子连接在一起。粒子1和粒子0相连,粒子2和粒子1相连,粒子3和粒子2相连……
图5-16 -
也就是:粒子i和粒子i - 1相连(除去i等于0的情况)。
if (i != 0) {
Particle previous = particles.get(i-1); 首先,我们需要前一个粒子的引用
VerletSpring2D spring = new VerletSpring2D(particle,previous,len,strength);
之后,我们需要在两个粒子之间创建弹簧连接,并指定弹簧的静止长度和强度(都是浮点数)
physics.addSpring(spring); 不要忘记将弹簧加入物理世界
}
- 如果我们想让绳子挂在某个定点上,该怎么做?可以将其中一个粒子锁定——比如第一个粒子、最后一个粒子或者最中间的粒子等。以下代码的作用就是将第一个粒子的位置锁定。
Particle head=particles.get(0);
head.lock();
- 如果想要绘制绳子上的所有粒子,我们可以从ArrayList获取所有的粒子位置,再调用beginShape()函数、endShape()函数和vertex()函数绘制它们。
3、示例
示例代码5-11 柔软的钟摆
import toxi.physics2d.*;
import toxi.physics2d.behaviors.*;
import toxi.geom.*;
// Reference to physics "world" (2D)
VerletPhysics2D physics;
// Our "Chain" object
Chain chain;
void setup() {
size(640, 360);
// Initialize the physics world
physics=new VerletPhysics2D();
physics.addBehavior(new GravityBehavior(new Vec2D(0, 0.1)));
physics.setWorldBounds(new Rect(0, 0, width, height));
// Initialize the chain
chain = new Chain(180, 20, 16, 0.2);
}
void draw() {
background(255);
// Update physics
physics.update();
// Update chain's tail according to mouse position
chain.updateTail(mouseX, mouseY);
// Display chain
chain.display();
}
void mousePressed() {
// Check to see if we're grabbing the chain
chain.contains(mouseX, mouseY);
}
void mouseReleased() {
// Release the chain
chain.release();
}
Chain.pde
class Chain {
// Chain properties
float totalLength; // How long
int numPoints; // How many points
float strength; // Strength of springs
float radius; // Radius of ball at tail
// This list is redundant since we can ask for physics.particles, but in case we have many of these
// it's a convenient to keep track of our own list
ArrayList<Particle> particles;
// Let's keep an extra reference to the tail particle
// This is just the last particle in the ArrayList
Particle tail;
// Some variables for mouse dragging
PVector offset = new PVector();
boolean dragged = false;
// Chain constructor
Chain(float l, int n, float r, float s) {
particles = new ArrayList<Particle>();
totalLength = l;
numPoints = n;
radius = r;
strength = s;
float len = totalLength / numPoints;
// Here is the real work, go through and add particles to the chain itself
for(int i=0; i < numPoints; i++) {
// Make a new particle with an initial starting position
Particle particle=new Particle(width/2,i*len);
// Redundancy, we put the particles both in physics and in our own ArrayList
physics.addParticle(particle);
particles.add(particle);
// Connect the particles with a Spring (except for the head)
if (i != 0) {
Particle previous = particles.get(i-1);
VerletSpring2D spring = new VerletSpring2D(particle,previous,len,strength);
// Add the spring to the physics world
physics.addSpring(spring);
}
}
// Keep the top fixed
Particle head=particles.get(0);
head.lock();
// Store reference to the tail
tail = particles.get(numPoints-1);
tail.radius = radius;
}
// Check if a point is within the ball at the end of the chain
// If so, set dragged = true;
void contains(int x, int y) {
float d = dist(x,y,tail.x,tail.y);
if (d < radius) {
offset.x = tail.x - x;
offset.y = tail.y - y;
tail.lock();
dragged = true;
}
}
// Release the ball
void release() {
tail.unlock();
dragged = false;
}
// Update tail position if being dragged
void updateTail(int x, int y) {
if (dragged) {
tail.set(x+offset.x,y+offset.y);
}
}
// Draw the chain
void display() {
// Draw line connecting all points
beginShape();
stroke(0);
strokeWeight(2);
noFill();
for (Particle p : particles) {
vertex(p.x,p.y);
}
endShape();
tail.display();
}
}
Particle.pde
class Particle extends VerletParticle2D {
float radius = 4; // Adding a radius for each particle
Particle(float x, float y) {
super(x,y);
}
// All we're doing really is adding a display() function to a VerletParticle
void display() {
fill(127);
stroke(0);
strokeWeight(2);
ellipse(x,y,radius*2,radius*2);
}
}