基于processing的《代码本色》应用拓展——表现随机行为及牛顿运动学


动图3

前言

任务主题

创作一组编程习作,体现随机行为及牛顿运动学

任务要求

编程语言与工具

编程工具不限;

作品范围

参考《代码本色》的第0~4章内容及其实例程序(自行在processing内下载),针对这5章分别编写1个习作(一共5个),每个习作都有不少于2个案例参考,且必须有一定的拓展;

报告要求

写一篇文章(也可以多篇文章,但最好有一个总的导航文章),发表为博文/推文等形式,描述运用的规律,若用到了数学/物理/化学等学科中的知识,要用平实易懂的语言介绍原理,尝试运用凝练的数学语言表达(公式、方程、推导等),特别要描述出这些原理如何在作品中呈现的。

评价指标

技术性:技术难度
综合性:通过编程融汇其他学科知识、方法
拓展性:对原作的变化与组合
表现性:艺术表现水平
展示质量:报告+视频+演示程序的直观呈现水平

第0章:Perlin噪声

知识点

本章介绍了随机函数和perlin噪声。通过本章内容的学习可以认识到,随机函数和perlin噪声可以对图像、色彩、纹理进行美观表现,可以进一步实现数据可视化。
noise函数:得到一个无规律的小范围变化的随机值
map函数:线性映射

实例应用

①perlin一维自定义

int myperlin(int t,int newmin,int newmax)
  {
    float n = noise(t);
    float pp = map(n,0,1,newmin,newmax); 
    return (int)pp;
  }

②perlin二维自定义

float myperlintwo(float x,float y,int newmin,int newmax)
  {
    float n = noise(x,y);
    float pppp = map(n,0,1,newmin,newmax); 
    return pppp;//pppp to zhengshu
  }

③定位使用了random随机函数

void draw() {
    
      display();
  
      ellipse(xx,yy,20,20);
    
      float cha=random(-10,10);
      
      if(cha >-10&&cha<-5) {
      xx+=random(0,60); 
    } else if(cha >-5&&cha<0) {
      xx-=random(0,60); 
    } else if(cha >0&&cha<5) {
      yy+=random(0,60); 
    } else {
      yy-=random(0,60); 
    }
    
    if(xx<0||xx>1000||yy<0||yy>600)
    {
      xx=random(0,1000);
      yy=random(0,600);
    }
  }

④颜色的选择采用了perlin一维噪声算法

void display()
{
    float time1=random(1000);
    float time2=random(1000);
    float time3=random(1000);
    float time4=random(1000);
    int r=myperlin(int(time1),50,200);
    int g=myperlin(int(time2),100,150);
    int b=myperlin(int(time3),200,255);
    int alpha=myperlin(int(time4),20,80);
    stroke(r,g,b,alpha);
    fill(r,g,b,alpha);
  }

⑤定位也可以采用perlin二维噪声算法(此处没有用该算法,因为效果图是小圆圈几乎沿直线运动,不够美观)

void move()
{
    float timex=random(1000);
    float timey=random(1000);
    float delta=myperlintwo(timex,timey,0,50);
    
      xx+=delta;
      yy+=delta;
    
}

效果图

第0章

第1章:向量

知识点

①向量单位化处理
向量单位化
fill()stroke()函数fill和stroke
③Processing中的向量

对每一帧动画:新位置 = 当前位置在速度作用下的位置

如果速度是一个向量(两点之间的差异),那位置是否也是一个向量?从概念上来说,有人会争论说位置并不是向量,因为它没有描述从某个点到另一个点的移动,它只描述了空间中的一个点而已.

然而,对于位置这个概念,另一种描述是从原点到该位置的移动路径.因此位置也可以用一个向量表示,它代表原点与该位置的差异

④向量的定义(套用格式)

class PVector {
  float x;
  float y;
  
  PVector(float x_,float y_) {
    x = x_;
    y = y_;
  }
  
  PVector get() {
    PVector newVector = new PVector(x,y);
    return newVector;
  }
  
  void add(PVector v) {
    x = x + v.x; 
    y = y + v.y;
  }
  
  void sub(PVector v) {
    x = x - v.x;
    y = y - v.y;
  }
  
  void mult(float n) {
    x = x * n;
    y = y * n;
  }
  
  void div(float n) {
    x = x / n;
    y = y / n;
  }
  
  float mag() {
     return sqrt(x * x + y * y); 
  }
  
  void normalize() {
    float m = mag();
    if(m != 0) {
      div(m);  
    }
  }
  
  void limit(float max) {
    if(mag() > max) {
      normalize();
      mult(max);
      
      println(mag());
    }
  }
}

实例应用

void setup() {
  size(1000,600);
  background(255);
  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].checkEdges();
    //movers[i].display();
  }
}

②设置速度velocity和加速度acceleration

void update() {
    PVector mouse = new PVector(mouseX,mouseY);
    mouse.sub(location);
    PVector dir = mouse;
    
    dir.normalize();//dan wei hua
    dir.mult(0.5);
    acceleration = dir;
   
    velocity.add(acceleration);
    velocity.limit(topspeed);
    location.add(velocity);
  }

③绘制小鱼:色彩使用一维perlin,坐标实现交互效果

void display(int i) {
    float time1=random(1000);
    float time2=random(1000);
    float time3=random(1000);
    int r=myperlin(int(time1),0,100);
    int g=myperlin(int(time2),100,150);
    int b=myperlin(int(time3),150,200);
    int alpha=255;
    stroke(r,g,b,alpha);
    fill(r,g,b,alpha);
    triangle(location.x,location.y,location.x+i*20,location.y+20,location.x+i*20,location.y-20);
    triangle(location.x+i*20,location.y,location.x+i*26,location.y+6,location.x+i*26,location.y-6);
    
    fill(255);
    ellipse(location.x+i*8,location.y,6,6);
    
    stroke(r,g,b,alpha);
    fill(r,g,b,alpha);
    triangle(mouseX,mouseY,mouseX+i*40,mouseY+40,mouseX+i*40,mouseY-40);
    triangle(mouseX+i*40,mouseY,mouseX+i*55,mouseY+10,mouseX+i*55,mouseY-10);
    
    fill(255);
    ellipse(mouseX+i*14,mouseY,10,10);

  }

④当鼠标在小小鱼的左边时,鱼儿们面向左边;当鼠标在小小鱼的右边时,鱼儿们面向右边。

 if(mouseX<=location.x){
      i=1;
    }
    else
    i=-1;
    
    display(i);

效果图

第1章

第2章:力

知识点

2.1 力和牛顿运动定律

力是一个向量,它使有质量的物体产生加速

2.1.1 牛顿第一运动定律

牛顿第一运动定律简述为:物体有保持禁止或运动的趋势

加上外力的作用,牛顿第一运动定律可以扩展为:除非有不均衡外力的作用,否则物体始终保持静止或匀速直线运动状态.

在Processing中,我们可以这样表述牛顿第一运动定律:在平衡状态下,对象的速度向量(PVector对象)始终都是常量

2.1.2 牛顿第三运动定律

牛顿第三运动定律通常表述为:每个作用力都有一个大小相等,方向相反的反作用力

牛顿第三运动定律的更好表述是:力总是成对出现,且这两个力大小相等,方向相反

这个表述仍然会引起误解,因为它看起来像是说:成对出现的力总是会相互取消,事实并不是这样,成对出现的力并不是作用在同一个物体上.这两个力的大小相等,但并不意味着它们产生的运动效果也一样(或者物体会停止运动).

2.2 力和Processing的结合:将牛顿第二运动定律作为一个函数

牛顿的第二运动定律被表述为:力等于质量乘以加速度.用公式表示为:F = M * A; 另外一种写法: A = F /
M;加速度和力成正比,和质量成反比

重量和质量

质量是物质量的度量(以千克为单位)

重量,通常被误认为质量,实际上指的是物体所受重力的大小.根据牛顿第二运动定律,重量等于质量乘以重力加速度(w = m *
g).重量以"牛顿"为单位.

密度等于质量除以物体的体积(例如以"克/立方厘米"为单位)

2.3 力的累加

更准确的牛顿第二运动定律:合力等于质量乘以加速度,或者可以说成:加速度等于所有力的和除以质量.

我们不需要在程序中记录加速度,因为它是根据当时的外力计算出来的.在这一点上,加速度和位置截然不同,为了能在下一帧移动到正确的位置,我们必须记录物体上一帧的位置

阻力公式:
阻力公式
简化版的阻力公式:
简化版的阻力公式
引力:
引力

实例应用

①绘图

void draw() {
  background(255);
  
  for(int i = 0; i < movers.length; i++) {
    for(int j = 0; j < movers.length; j++) {
      if(i != j) {
        PVector force = movers[j].attract(movers[i]);
        movers[i].applyForce(force);
      }
    }
    
    movers[i].update();
    movers[i].display();
  }
}

②引力

 PVector attract(Mover m) {
    PVector force = location.get();
    force.sub(m.location);
    float distance = force.mag();
    distance = constrain(distance,5.0,25.0);
    force.normalize();
    float strength = (G * mass * m.mass) / (distance * distance);
    force.mult(strength);
    return force;
  }

效果图

第2章

第3章:振荡

知识点

atanatan2
atan:
atan
atan2:
不存在正反问题
②极坐标系和笛卡儿坐标系

float r = 0;
float theta = 0;

void setup() {
  size(200,200);
  background(255);
  smooth();
}

void draw() {
  float x = r * cos(theta);
  float y = r * sin(theta);
  
  noStroke();
  fill(0);
  ellipse(x + width / 2,y + height / 2,4,4);
  
  
  r += 0.03;
  theta += 0.01;
}

③带有角速度的振荡
振荡(角速度)

实例应用

定义100个小球:

Oscillator[] oscillators = new Oscillator[100];

主要类:

class Oscillator {
  PVector angle;
  PVector velocity;
  PVector amplitude;
  
  color c;
  
  Oscillator() {
    angle = new PVector();
    velocity = new PVector(random(-0.05,0.05),random(-0.05,0.05));
    amplitude = new PVector(random(width / 2),random(height / 2));
    
    c = color(random(255),random(255),random(255));
  }
  
  
  
  int myperlin(int t,int newmin,int newmax)
  {
    float n = noise(t);
    float pp = map(n,0,1,newmin,newmax); 
    return (int)pp;//x to zhengshu
  }
  
  
  
  void oscillate() {
    angle.add(velocity); 
  }
  
  void display() {
    float x = sin(angle.x) * amplitude.x;
    float y = sin(angle.y) * amplitude.y;
    
    float jr=0;
    float jtheta = 0;
    float jx = jr * cos(jtheta);
    float jy = jr * sin(jtheta);
    
    
    pushMatrix();
    translate(width / 2,height / 2);
    
    
    
    float time1=random(1000);
    float time2=random(1000);
    float time3=random(1000);
    int r=myperlin(int(time1),0,100);
    int g=myperlin(int(time2),100,150);
    int b=myperlin(int(time3),150,200);
    int alpha=100;
    stroke(r,g,b,alpha);
    fill(r,g,b,alpha);
    //stroke(0);
    //fill(c);
    line(jx,jy,x,y);
    ellipse(x,y,16,16);
    
    jr+=300;
    jtheta+=10;
    
    popMatrix();
  }
}

效果图

振荡

第4章:粒子系统及综合练习

知识点

①与unity的粒子系统有一定的共通之处
②ArrayList
使用new ArrayList<类型>([容量可省略])
以构造由自定义类型作为元素的列表。

ArrayList<Particle> particles;

相关知识点:@万能的百度

实例应用

①设置寿命:体现在透明度的渐变上,fill(R,G,B,transparent)

 void update() {
    velocity.add(acceleration);
    location.add(velocity);
    lifespan -= 2.0;
  }
  
  void display() {
    stroke(0,lifespan);
    fill(175,lifespan);
    ellipse(location.x,location.y,8,8);
  }

②当“寿命终结”,使其消失

while(it.hasNext()) {
    Particle p = it.next();
    p.run();
    if(p.isDead()) {
      it.remove(); 
    }
  }

③同样存在速度、加速度

class Particle {
  PVector location;
  PVector velocity;
  PVector acceleration;
  
  float lifespan;
  
  Particle(PVector l) {
    location = l.get();
    acceleration = new PVector();
    velocity = new PVector();
    lifespan = 255;    
  }
  
  void run() {
    update();
    display();
  }
  
  void update() {
    velocity.add(acceleration);
    location.add(velocity);
    lifespan -= 2.0;
  }
  
  void display() {
    stroke(0,lifespan);
    fill(175,lifespan);
    ellipse(location.x,location.y,8,8);
  }
  
  boolean isDead() {
    if(lifespan < 0.0) {
      return true; 
    } else {
      return false; 
    }
  }
}

效果图

动图1
动图2

动图3

番外

配色参考:一些比较舒服的rgb配色
鱼戏水中完整代码:
导入

import java.util.Iterator;

定义

ParticleSystem ps;
ParticleSystem ps2;
ParticleSystem ps3;
ParticleSystem ps4;
ParticleSystem ps5;
ParticleSystem ps6;
ParticleSystem ps7;
Mover[] movers = new Mover[10];

setup和draw
setup函数的前半部分后半部分分别是粒子系统的初始化和小鱼向量的初始化;
draw函数前半部分是调用绘制粒子系统,后面的for循环是为了控制并刷新小鱼的移动。

void setup() {
  size(1000,600);
  
  ps = new ParticleSystem(new PVector(width / 2,700));
  ps2=new ParticleSystem(new PVector(width / 6,700));
  ps3=new ParticleSystem(new PVector(width / 3,700));
  ps4=new ParticleSystem(new PVector(width / 6*4,700));
  ps5=new ParticleSystem(new PVector(width / 6*5,700));
  ps6=new ParticleSystem(new PVector(0,700));
  ps7=new ParticleSystem(new PVector(width,700));
 
   for(int i = 0; i < movers.length; i++) {
    movers[i] = new Mover(); 
  }
  
}

void draw() {
  background(255);
  ps.addParticle();
  ps.run();
  ps2.addParticle();
  ps2.run();
  ps3.addParticle();
  ps3.run();
  ps4.addParticle();
  ps4.run();
  ps5.addParticle();
  ps5.run();
  ps6.addParticle();
  ps6.run();
  ps7.addParticle();
  ps7.run();
  
  
  for(int i = 0; i < movers.length; i++) {
    movers[i].update();
    movers[i].checkEdges();
  }
}

粒子系统:

class ParticleSystem {
  ArrayList<Particle> particles;
  PVector origin;
  
  ParticleSystem(PVector location) {
    origin = location.get();
    particles = new ArrayList<Particle>();
  }
  
  void addParticle() {
    float r = random(1);
    if(r < 0.5) {
      particles.add(new Particle(origin)); 
    } else {
      particles.add(new Confetti(origin));
    }
  }
  
  void run() {
    Iterator<Particle> it = particles.iterator();
    while(it.hasNext()) {
      Particle p = it.next();
      p.run();
      if(p.isDead()) {
        it.remove(); 
      }
    }
  }
}

class Particle {
  PVector location;
  PVector velocity;
  PVector acceleration;
  
  float lifespan;
  
  Particle(PVector l) {
    acceleration = new PVector(0,0.05);
    velocity = new PVector(random(-1,1),random(0,2));
    location = l.get();
    lifespan = 255.0;
  }
  
  void run() {
    update();
    display();
  }
  
  void update() {
    velocity.add(acceleration);
    location.sub(velocity);
    lifespan -= 2.0;
  }
  
  void display() {
    stroke(30,80,200,lifespan);
    strokeWeight(2);
    fill(30,80,200,lifespan);
    ellipse(location.x,location.y,12,12);
  }
  
  boolean isDead() {
    if(lifespan < 0.0) {
      return true; 
    } else {
      return false; 
    }
  }
}

class Confetti extends Particle {
  Confetti(PVector l) {
    super(l); 
  }
  
  void display() {
    rectMode(CENTER);
    fill(55,155,200,lifespan);
    stroke(55,155,200,lifespan);
    strokeWeight(2);
    pushMatrix();
    translate(location.x,location.y);
    float theta = map(location.x,0,width,0,TWO_PI * 2);
    rotate(theta);
    rect(0,0,12,12);///
    popMatrix();
  }
}

鱼儿:

class Mover {
  PVector location;
  PVector velocity;
  PVector acceleration;
  float topspeed;
  
  Mover() {
    location = new PVector(random(width),random(height));
    velocity = new PVector(0,0);
    topspeed = 4;
  }
  int myperlin(int t,int newmin,int newmax)
  {
    float n = noise(t);
    float pp = map(n,0,1,newmin,newmax); 
    return (int)pp;//x to zhengshu
  }
  
  
  void update() {
    PVector mouse = new PVector(mouseX,mouseY);
    mouse.sub(location);
    PVector dir = mouse;
    
    dir.normalize();//dan wei hua
    dir.mult(0.5);
    acceleration = dir;
   
    velocity.add(acceleration);
    velocity.limit(topspeed);
    location.add(velocity);
  }
  
  void display(int i) {
    float time1=random(1000);
    float time2=random(1000);
    float time3=random(1000);
    int r=myperlin(int(time1),200,255);
    int g=myperlin(int(time2),0,50);
    int b=myperlin(int(time3),50,100);
    //int alpha=myperlin(int(time4),20,80);
    int alpha=255;
    stroke(r,g,b,alpha);
    fill(r,g,b,alpha);
    //stroke(255);
    //fill(175,75,233,60);
    
    triangle(location.x,location.y,location.x+i*20,location.y+20,location.x+i*20,location.y-20);
    triangle(location.x+i*20,location.y,location.x+i*26,location.y+6,location.x+i*26,location.y-6);
    
    fill(255);
    ellipse(location.x+i*8,location.y,6,6);
    
    stroke(r,g,b,alpha);
    fill(r,g,b,alpha);
    triangle(mouseX,mouseY,mouseX+i*40,mouseY+40,mouseX+i*40,mouseY-40);
    triangle(mouseX+i*40,mouseY,mouseX+i*55,mouseY+10,mouseX+i*55,mouseY-10);
    
    fill(255);
    ellipse(mouseX+i*14,mouseY,10,10);
    
    
  }
  
  void checkEdges() {
    int i=1;
    if(location.x >= width-5 ) {
      location.x = random(width); 
      location.y = random(height);
     
      
    } else if(location.x <= 0+5) {
      location.x = random(width); 
      location.y = random(height);
      
    }
    
    if(location.y >= height-5) {
      location.x = random(width); 
      location.y = random(height); 
    } else if(location.y <= 0+5) {
      location.x = random(width); 
      location.y = random(height); 
    }
    
    if(mouseX<=location.x){
      i=1;
    }
    else
    i=-1;
    
    display(i);
    
  }
}

总结:

《代码本色》一书中,前五章的知识点都是通俗易懂,或者已经接触过的,所以上手比较快。原理也基本上都理解并且掌握了。我比较注重视觉的传达,所以包括最后做的小鱼儿戏水都比较侧重色彩和比例表达。
希望在下学期的交互课程中,更深一步地理解掌握这其中的奥秘。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值