Processing表现随机行为及牛顿运动学

简介

参考《代码本色》的第0~4章内容及其实例程序创作一组编程习作,体现随机行为及牛顿运动学。

第0章 随机游走

目标:
实现鼠标指引“画笔”,但“画笔”在向鼠标靠近的同时具有随机运动的特性。同时统计所画路径点横坐标的分布。

探索过程:
1.概率和非均匀分布
有50%概率向鼠标所在方向移动的Walker对象 :

class Walker {
  int x, y;
  float xoff = 0.0f;

  Walker() {
    x = width/2; 
    y = height/2;
  }

  void render() {
    stroke(255);
    strokeWeight(2);
    point(x, y);
  }

  // Randomly move up, down, left, right, or stay in one place
  void step() {
    float r = random(1);
    // A 50% of moving towards the mouse
    if (r < 0.5) {    
      int xdir = (mouseX-x);
      int ydir = (mouseY-y);
      if (xdir != 0) {
        xdir /= abs(xdir);
      } 
      if (ydir != 0) {
        ydir /= abs(ydir);
      }
      x += xdir;
      y += ydir;
    } else {
      int xdir = int(random(-2, 2));
      int ydir = int(random(-2, 2));
      println(xdir);
      x += xdir;
      y += ydir;
    }

    /*int xoffd=int(random(-2,2));
    float n=noise(xoff);
    int c=(int)(xoffd*4*n);
    x += c;
    y += c;
    xoff += 0.01;
    println(c);*/

    x = constrain(x, 0, width-1);
    y = constrain(y, 0, height-1);
  }
}

2.Perlin噪声(一种更平滑的算法)
噪声就是给定一个输入变量,生成一个值在0-1范围内的伪随机变量的函数。在图形学中一般是输入一个坐标得到一个范围在0-1之间的变量,再利用各种颜色计算得到一些比较酷炫的效果,像火焰、云彩、地形等。
这里只是简单应用Perlin噪声使Walker对象移动随机性增加。
在有50%概率向鼠标所在方向移动的基础上对Walker对象增加Perlin噪声(即上述代码中注释部分):

    int xoffd=int(random(-2,2));
    float n=noise(xoff);
    int c=(int)(xoffd*4*n);
    x += c;
    y += c;
    xoff += 0.01;
    println(c);

3.主程序(包括对所画点横坐标的计数统计):

Walker w;
float[] vals;
float[] norms;

void setup() {
  size(640,360);
  // Create a walker object
  w = new Walker();
  vals = new float[width];
  norms = new float[width];
  background(0);
}

void draw() {

  int index = int(w.x);
  vals[index]++;
  stroke(150);
  boolean normalization = false;
  float maxy = 0.0;
  for (int x = 0; x < vals.length; x++) {
    line(x,height,x,height-norms[x]);
    if (vals[x] > height) normalization = true;
    if(vals[x] > maxy) maxy = vals[x];
  }
  for (int x = 0; x < vals.length; x++) {
    if (normalization) norms[x] = (vals[x] / maxy) * (height);
    else norms[x] = vals[x];
  }

  // Run the walker object
  w.step();
  w.render(); 
}

效果:

第1章 向量

目标:
实现一组同时朝着鼠标加速的运动物体,物体与鼠标之间存在牵引线,同时可统计物体位置向量的长度。

探索过程:
1.朝鼠标加速运动的简单实现原理:

PVector mouse = new PVector(mouseX,mouseY);    
PVector dir = PVector.sub(mouse,location);//第一步:计算方向      
dir.normalize();//第二步:单位化    
dir.mult(0.5);//第三步:改变长度    
acceleration = dir;//第四步:得到加速度 
velocity.add(acceleration);  
velocity.limit(topspeed);   
location.add(velocity);

2.实现一组同时朝着鼠标加速的运动物体的Walker对象:

class Mover {
  // The Mover tracks position, velocity, and acceleration 
  PVector position;
  PVector velocity;
  PVector acceleration;
  // The Mover's maximum speed
  float topspeed;

  Mover() {
    // Start in the center
    position = new PVector(random(width),random(height));
    velocity = new PVector(0,0);
    topspeed = 5;
  }

  void update() {    
    // Compute a vector that points from position to mouse
    PVector mouse = new PVector(mouseX,mouseY);
    acceleration = PVector.sub(mouse,position);
    // Set magnitude of acceleration
    //acceleration.setMag(0.2);
    acceleration.normalize();
    acceleration.mult(0.2);

    // Velocity changes according to acceleration
    velocity.add(acceleration);
    // Limit the velocity by topspeed
    velocity.limit(topspeed);
    // position changes by velocity
    position.add(velocity);
  }

  void display() {
    stroke(0);
    strokeWeight(2);
    fill(127,200);
    ellipse(position.x,position.y,48,48);
  }
}

3.主程序(包括牵引线的绘制以及向量长度的统计):

Mover[] movers = new Mover[20];

void setup() {
  size(640,360);
  for (int i = 0; i < movers.length; i++) {
    movers[i] = new Mover(); 
  }
}

void draw() {  
  background(255);

  for (int i = 0; i < movers.length; i++) {
    movers[i].update();
    movers[i].display(); 

    fill(200);
    noStroke();
    rect(0,i*(360/movers.length),movers[i].position.mag()/2,10);

    stroke(150);
    strokeWeight(1);
    line(mouseX,mouseY,movers[i].position.x,movers[i].position.y);
  }
}

其中,向量长度的计算原理为勾股定理:

float mag() {
  return sqrt(x*x + y*y); 
}

效果:

第2章 力

目标:
实现随机生成一组小球,自由落体后进入水面,模拟物体掉入水中的效果。物体穿过窗口底部的灰色区域(代表流体)时,会减速。并且物体越小,速度减小地越快。因为牛顿第二运动定律表明加速度等于力除以质量,在同一个力的作用下,物体的质量越大,加速度就越小,所以该示例中物体的质量越小,减速越快。
通过线段长度表示每个物体所受力以及速度的大小。
鼠标每点击一次,可以对所有物体增加一个瞬时的向上的力。
按下键盘“R"键可以重新开始演示。

探索过程:
1.随机小球:
位置由速度控制,而速度由加速度控制。加速度是一切运动的起因。而根据牛顿第二定律,力变成了运动的起因。
通过applyForce()函数实现对物体施加力。

class Mover {
  // position, velocity, and acceleration 
  PVector position;
  PVector velocity;
  PVector acceleration;

  // Mass is tied to size
  float mass;

  Mover(float m, float x, float y) {
    mass = m;
    position = new PVector(x, y);
    velocity = new PVector(0, 0);
    acceleration = new PVector(0, 0);
  }

  void applyForce(PVector force) {
    // Divide by mass 
    PVector f = PVector.div(force, mass);
    // Accumulate all forces in acceleration
    acceleration.add(f);
  }

  void update() {
    // Velocity changes according to acceleration
    velocity.add(acceleration);   
    // position changes by velocity
    position.add(velocity);
    // We must clear acceleration each frame
    acceleration.mult(0);
  }

  // Draw Mover
  void display() {
    stroke(0);
    strokeWeight(2);
    fill(127, 200);
    ellipse(position.x, position.y, mass*16, mass*16);
  }

  // Bounce off bottom of window
  void checkEdges() {
    if (position.y > height) {
      velocity.y *= -0.9;  // A little dampening when hitting the bottom
      position.y = height;
    }
  }
}

2.液体生成:
根据阻力公式,阻力 = 阻力系数×速度×速度,由此计算小球在液体中所受的阻力。

class Liquid {
  // Liquid is a rectangle
  float x, y, w, h;
  // Coefficient of drag
  float c;

  Liquid(float x_, float y_, float w_, float h_, float c_) {
    x = x_;
    y = y_;
    w = w_;
    h = h_;
    c = c_;
  }

  // Is the Mover in the Liquid?
  boolean contains(Mover m) {
    PVector l = m.position;
    return l.x > x && l.x < x + w && l.y > y && l.y < y + h;
  }

  // Calculate drag force
  PVector drag(Mover m) {
    // Magnitude is coefficient * speed squared
    float speed = m.velocity.mag();
    float dragMagnitude = c * speed * speed;

    // Direction is inverse of velocity
    PVector dragForce = m.velocity.get();
    dragForce.mult(-1);

    // Scale according to magnitude
    // dragForce.setMag(dragMagnitude);
    dragForce.normalize();
    dragForce.mult(dragMagnitude);
    return dragForce;
  }

  void display() {
    noStroke();
    fill(50);
    rect(x, y, w, h);
  }
}

3.主程序(包括受力、速度显示以及交互的实现):

Mover[] movers = new Mover[9];
// Liquid
Liquid liquid;

void setup() {
  size(640, 360);
  reset();
  // Create liquid object
  liquid = new Liquid(0, height/2, width, height/2, 0.1);
}

void draw() {
  background(255);
  // Draw water
  liquid.display();

  PVector dragForce = new PVector(0, 0);

  for (int i = 0; i < movers.length; i++) {
    // Is the Mover in the liquid?
    if (liquid.contains(movers[i])) {
      // Calculate drag force
      dragForce = liquid.drag(movers[i]);
      // Apply drag force to Mover
      movers[i].applyForce(dragForce);
    }

    // Gravity is scaled by mass here!
    PVector gravity = new PVector(0, 0.1*movers[i].mass);
    // Apply gravity
    movers[i].applyForce(gravity);

    // Update and display
    movers[i].update();
    movers[i].display();
    movers[i].checkEdges();

    line(70*(i+1),height/2,70*(i+1)+movers[i].velocity.x*100,height/2+movers[i].velocity.y*30);
    stroke(155);
    line(70*(i+0.5),height/2,70*(i+0.5),height/2+(gravity.y+dragForce.y)*100);
  }

  fill(0);
  text("press 'R' to reset", 10, 30);
  text("click mouse to apply an upward force", 10, 50);

  if (keyPressed) {
    if (key == 'r') {
      reset();
    }
  }
}

void mousePressed() {
  PVector press = new PVector(0,-6);
  for (int i = 0; i < movers.length; i++) {
    movers[i].applyForce(press);
  }
}

// Restart all the Mover objects randomly
void reset() {
  for (int i = 0; i < movers.length; i++) {
    movers[i] = new Mover(random(0.5, 3), 40+i*70, 0);
  }
}

效果:

第3章 振荡

目标:
实现一个可以通过鼠标交互的钟摆模型,并可视化钟摆摆动的角速度。
探索过程:
1.钟摆模型:
a.运动规律:
角度 = 角度 + 角速度
角速度 = 角速度 + 角加速度
牛顿第二运动定律告诉我们力和加速度的关系,也就是F = M* A,或者A = F / M。因此,如果钟摆上物体受到的力等于重力乘以θ的正弦,就可以得到: 钟摆的角加速度 = 重力加速度 * sin(θ)。
钟摆在最高点角速度最小为0,在最低点角速度最大。
b.模型构建:

class Pendulum {    
  float r;//摆臂长度    
  float angle;//摆臂角度    
  float aVelocity;//角速度    
  float aAcceleration;//角加速度 
}

根据上面的公式更新钟摆的角度:

void update() { 
  float gravity = 0.4;//任意常数    
  aAcceleration = (-1 * gravity / r) * sin(angle);//根据公式计算加速度    
  aVelocity += aAcceleration;//增加速度          
  angle += aVelocity;//增加角度 
} 

display()函数用于绘制钟摆。
钟摆模型类(包括鼠标控制交互):

class Pendulum {
  PVector position;    // position of pendulum ball
  PVector origin;      // position of arm origin
  float r;             // Length of arm
  float angle;         // Pendulum arm angle
  float aVelocity;     // Angle velocity
  float aAcceleration; // Angle acceleration

  float ballr;         // Ball radius
  float damping;       // Arbitary damping amount

  boolean dragging = false;
  // This constructor could be improved to allow a greater variety of pendulums
  Pendulum(PVector origin_, float r_) {
    // Fill all variables
    origin = origin_.get();
    position = new PVector();
    r = r_;
    angle = PI/4;
    aVelocity = 0.0;
    aAcceleration = 0.0;
    damping = 0.995;   // Arbitrary damping
    ballr = 48.0;      // Arbitrary ball radius
  }

  void go() {
    update();
    drag();    //for user interaction
    display();
  }

  // Function to update position
  void update() {
    // As long as we aren't dragging the pendulum, let it swing!
    if (!dragging) {
      float gravity = 0.4;                              // Arbitrary constant
      aAcceleration = (-1 * gravity / r) * sin(angle);  // Calculate acceleration (see: http://www.myphysicslab.com/pendulum1.html)
      aVelocity += aAcceleration;                 // Increment velocity
      aVelocity *= damping;                       // Arbitrary damping
      angle += aVelocity;                         // Increment angle
    }
  }

  void display() {
    position.set(r*sin(angle), r*cos(angle), 0);         // Polar to cartesian conversion
    position.add(origin);                              // Make sure the position is relative to the pendulum's origin
    stroke(0);
    strokeWeight(2);
    // Draw the arm
    line(origin.x, origin.y, position.x, position.y);
    ellipseMode(CENTER);
    fill(175);
    if (dragging) fill(0);
    // Draw the ball
    ellipse(position.x, position.y, ballr, ballr);
  }

  // The methods below are for mouse interaction
  // This checks to see if we clicked on the pendulum ball
  void clicked(int mx, int my) {
    float d = dist(mx, my, position.x, position.y);
    if (d < ballr) {
      dragging = true;
    }
  }

  // This tells us we are not longer clicking on the ball
  void stopDragging() {
    aVelocity = 0; // No velocity once you let go
    dragging = false;
  }

  void drag() {
    // If we are draging the ball, we calculate the angle between the 
    // pendulum origin and mouse position
    // we assign that angle to the pendulum
    if (dragging) {
      PVector diff = PVector.sub(origin, new PVector(mouseX, mouseY));      // Difference between 2 points
      angle = atan2(-1*diff.y, diff.x) - radians(90);                      // Angle relative to vertical axis
    }
  }
}

2.主程序:
用画面中间小球左右的位置表示钟摆运动的角速度。

Pendulum p;
void setup() {
  size(640,360);
  // Make a new Pendulum with an origin position and armlength
  p = new Pendulum(new PVector(width/2,0),175);
}

void draw() {
  //background(255);
  noStroke();
  fill(255,5);
  rect(0,0,width,height);
  p.go();  

  fill(0);
  text("c l i c k    o n    b o b    t o    d r a g",10,height-5);

  float x = 3000*p.aVelocity;

  fill(127);
  translate(width/2,height/2);
  line(0,70,x,70);
  ellipse(x,70,16,16);
}

void mousePressed() {
  p.clicked(mouseX,mouseY);
}

void mouseReleased() {
  p.stopDragging();
}

效果:

第4章 粒子系统

目标:
让粒子系统的原点能够动态移动,让粒子从鼠标所在的位置发射出来。鼠标点击时可以固定一个粒子系统的原点。为粒子系统加入排斥对象。
1.粒子系统:
粒子就是在屏幕中移动的对象,它有位置、速度和加速度变量,有构造函数用于内部变量的初始化,有display()函数用于绘制自身,还有update()函数用于更新位置。
典型的粒子系统中都有一个发射器,发射器是粒子的源头,它控制粒子的初始属性,包括位置、速度等。发射器发射的粒子可能是一股粒子,也可能是连续的粒子流,或是同时包含这两种发射方式。有一点非常关键:在一个典型的粒子系统中,粒子在发 射器中诞生,但并不会永远存在。假设粒子永不消亡,系统中的粒子将越积越多,Sketch的运行速度也会越来越慢,最后程序会挂起。新的粒子不断产生,与此同时,旧的粒子应该不断消亡,只有这样,程序的性能才不会受到影响。决定粒子何时消亡的方法很多,比如,粒子可以和另一个粒子结合在一起,或在离开屏幕时消亡。
用数组实现复杂的粒子系统。

class Particle {
  PVector position;
  PVector velocity;
  PVector acceleration;
  float lifespan;

  float mass = 1; 

  Particle(PVector l) {
    acceleration = new PVector(0,0.05);
    velocity = new PVector(random(-1,1),random(-2,0));
    position = l.get();
    lifespan = 255.0;
  }

  void run() {
    update();
    display();
  }

  void applyForce(PVector force){
    PVector f=force.get();
    f.div(mass);
    acceleration.add(f);
  }

  // Method to update position
  void update() {
    velocity.add(acceleration);
    position.add(velocity);
    acceleration.mult(0);
    lifespan -= 2.0;
  }

  // Method to display
  void display() {
    stroke(0,lifespan);
    strokeWeight(2);
    fill(127,lifespan);
    ellipse(position.x,position.y,12,12);
  }

  // Is the particle still useful?
  boolean isDead() {
    if (lifespan < 0.0) {
      return true;
    } else {
      return false;
    }
  }
}
class ParticleSystem {
  ArrayList<Particle> particles;
  PVector origin;

  ParticleSystem(PVector position) {
    origin = position.get();
    particles = new ArrayList<Particle>();
  }

  ParticleSystem(int num, PVector v) {
    particles = new ArrayList<Particle>();   // Initialize the arraylist
    origin = v.get();                        // Store the origin point
    for (int i = 0; i < num; i++) {
      particles.add(new Particle(origin));    // Add "num" amount of particles to the arraylist
    }
  }

  void addParticle() {
    particles.add(new Particle(origin));
  }

  void addParticle(float x, float y) {
    particles.add(new Particle(new PVector(x, y)));
  }

  void applyForce(PVector f) {
    for (Particle p: particles) {
      p.applyForce(f);
    }
  }

 /* void applyRepeller(Repeller r) {
    for (Particle p: particles) {
      PVector force = r.repel(p);        
      p.applyForce(force);
    }
  }*/

  void run() {
    for (int i = particles.size()-1; i >= 0; i--) {
      Particle p = particles.get(i);
      p.run();
      if (p.isDead()) {
        particles.remove(i);
      }
    }
  }

  boolean dead() {
    if (particles.isEmpty()) {
      return true;
    } 
    else {
      return false;
    }
  }
}

2.排斥对象:
排斥对象对其他对象有斥力作用,以防止对方靠近。
在粒子系统中加入一个新函数,该函数以Repeller对象为参数,它的功能是将排 斥力作用在每个粒子上。

  void applyRepeller(Repeller r) {
    for (Particle p: particles) {
      PVector force = r.repel(p);        
      p.applyForce(force);
    }
  }
class Repeller {  
  // Gravitational Constant
  float G = 100;
  // position
  PVector position;
  float r = 10;

  Repeller(float x, float y)  {
    position = new PVector(x,y);
  }

  void display() {
    stroke(0);
    strokeWeight(2);
    fill(175);
    ellipse(position.x,position.y,48,48);
  }

// Calculate a force to push particle away from repeller
  PVector repel(Particle p) {
    PVector dir = PVector.sub(position,p.position);      // Calculate direction of force
    float d = dir.mag();                       // Distance between objects
    dir.normalize();                           // Normalize vector (distance doesn't matter here, we just want this vector for direction)
    d = constrain(d,5,100);                    // Keep distance within a reasonable range
    float force = -1 * G / (d * d);            // Repelling force is inversely proportional to distance
    dir.mult(force);                           // Get force vector --> magnitude * direction
    return dir;
  }  
}

3.主程序:

ParticleSystem ps;
Repeller repeller;
ArrayList<ParticleSystem> systems;

void setup() {
  size(640,360);
  ps = new ParticleSystem(new PVector(width/2,50));
  repeller=new Repeller(width/2-10,height/2);
  systems=new ArrayList<ParticleSystem>();
}

void draw() {
  background(255);
  PVector gravity = new PVector(0,0.1);

  for (ParticleSystem pss: systems) {   
    pss.applyForce(gravity);  
    pss.applyRepeller(repeller);  
    repeller.display();

    pss.run();
    pss.addParticle(); 
  }

  // Option #1 (move the Particle System origin)
  ps.origin.set(mouseX,mouseY,0);

  ps.addParticle();

  // Apply gravity force to all Particles
  ps.applyForce(gravity);  
  ps.applyRepeller(repeller);  
  repeller.display();

  ps.run();

  fill(0);
  text("click mouse to add particle systems",10,height-30);
}

void mousePressed() {
  systems.add(new ParticleSystem(1,new PVector(mouseX,mouseY)));
}

效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值