Step 8:Processing分形之三——Diffusion-Limited Aggregation

来源:互联网 发布:简历管理系统知乎 编辑:程序博客网 时间:2024/06/08 05:32

人类一思考,上帝就发笑。
——米兰·昆德拉 《生命中不能承受之轻》

大自然创造出瑰丽奇特的珊瑚,Witten和Sander共同提出了DLA。唐代冯延巳有诗云:“年少王孙有俊才,登高欢醉夜忘回。歌阑赏尽珊瑚树,情厚重斟琥珀杯。但愿千千岁,金菊年年秋解开。”可世人皆知珊瑚美,哪知DLA也奇妙。

Tree Corals
They look fabulous, like underwater bonsai trees that have been painted by Salvador Dali.

这里写图片描述

3D DLA
For many years now the ultimate inspiration in DLA for me has been Andy Lomas.
More Works: http://www.andylomas.com/aggregationImages.html

这里写图片描述

8.1 Diffusion-Limited Aggregation

8.1.1 Basic Idea

时光易逝,容颜易老?且看度娘怎么说

首先置一初始粒子作为种子,在远离种子的任意位置随机产生一个粒子使其做无规行走,直至与种子接触,成为集团的一部分;然后再随机产生一个粒子,重复上述过程,这样就可以得到足够大的DLA团簇(cluster)。

君未尝与吾相知,亦无所怨。你该怎么看

故事发生在一个被小酒馆包围的城市广场上。醉鬼们离开酒馆,开始在广场上游荡。直到他们终于遇到了一个没有任何意义的同伴,而在那时候,他们躺下睡着了,传来平静的打鼾声。你是否想亲眼看看第二天早晨睡觉人群的鸟瞰图?

这里写图片描述

Learn More: 我的博客 DLA - Diffusion Limited Aggregation

In a word, DLA即颗粒通过随机移动接触并粘附到现有固定颗粒而产生支化和珊瑚状的结构

8.1.2 Real Life Experiments

在电沉积池中加入硫酸铜溶液, you can get results like the following images:

这里写图片描述

8.1.3 You Should Know

创始人之一Sander曾经总结过DLA 的研究意义:

  1. 模型用极其简单的算法抓住了广泛的自然现象的关键成分却没有明确的物理机制;
  2. 通过简单的运动学和动力学过程就可以产生具有标度不变性的自相似的分形结构,从而建立分形理论和实验观察之间的桥梁,在一定程度上揭示出实际体系中分形生长的机理
  3. 界面具有复杂的形状和不稳定性的性质,生长过程是一个远离平衡的动力学过程,但集团的结构却有稳定且确定的分形维数

8.2 The Story of the Drunkards

Story Outlines:

这里写图片描述

① 夜黑风高,在一个被小酒馆包围的城市广场上;

 size(800, 800);//地点 background(0);//时间

② “半斤不是酒,一斤扶墙走,斤半墙走我不走。”不知何时,冒出来一个醉鬼,躺卧在广场的中央;

sleepers.add(new Drunkard(width/2, height/2));  //广场中央的醉鬼

③ 接着,四周纷纷又走出许多醉鬼;

//四周随机冒出醉鬼PVector randomDrunkard() {    float i = random(4);  //4种可能性    PVector p = new PVector();    float x = random(width);    float y = random(height);    if (i <= 1) {  //顶部      p = new PVector(x, 0);    } else if (i <= 2) {  //底部      p = new PVector(x, height);    } else if (i <= 3) {  //右边缘       p = new PVector(0, y);    } else if (i <= 4) {  //左边缘       p = new PVector(width, y);    }    return p;  }

④ 他们在广场四周游荡,毫无目的可言;

void walk() {    vel = PVector.random2D();  //毫无目的地游荡    pos.add(vel);    //只在广场上活动    pos.x = constrain(pos.x, 0, width);    pos.y = constrain(pos.y, 0, height);  }

⑤ 可一旦他们瞧见脚下有睡觉的人,他们自己也会情不自禁地倒下睡觉;

//是否倒下睡觉boolean checkStuck(Drunkard dr) {    float d = dist(pos.x, pos.y, dr.pos.x, dr.pos.y);    if (d < sqrt(radius * dr.radius * 2 * 2)) {       if (random(1) < 0.1) {        return true;      }    }    return false;  }

⑥ 第二天清晨,自广场上空飞过的无人机就拍下了这戏剧性的一幕。

//显示操作void draw() {  background(0);  //显示移动的醉鬼和睡觉的醉鬼    for (Drunkard m : movers) {    m.show();  }  for (Drunkard s : sleepers) {    s.show();    //限制醉鬼在广场内    if (s.pos.y >= height || s.pos.y <= 0 || s.pos.x >= width || s.pos.x <= 0) {      limit = true;    }  }  for (int n = 0; n < iterations; n++) {    for (int i = movers.size() - 1; i > 0; i--) {      Drunkard m = movers.get(i);      m.walk();      for (Drunkard s : sleepers) {        if (s.checkStuck(m)) {          m.stuck = true;          sleepers.add(m);                    break;        }      }    }  }  //醉鬼人数的增长  while (movers.size() < maxDrunkards && limit == false) {    movers.add(new Drunkard());  }}

Source Code:

Let’s make up this story——

/** * The Story of the Drunkards * @微信公众号:维度模态 * @author:Hewes * @date 2017/08/26 *///存储移动的醉鬼和睡觉的醉鬼们ArrayList<Drunkard> movers = new ArrayList<Drunkard>(); ArrayList<Drunkard> sleepers = new ArrayList<Drunkard>();int maxDrunkards = 500; //醉鬼的数目int iterations = 500;   //决定图形生长速度boolean limit = false;void setup() {  size(800, 800);  sleepers.add(new Drunkard(width/2, height/2)); //中央的醉鬼    //初始其余的醉鬼  for (int i = 0; i < maxDrunkards; i++) {    movers.add(new Drunkard());  }}//显示操作void draw() {  background(0);  for (Drunkard m : movers) {    m.show();  }  for (Drunkard s : sleepers) {    s.show();    //限制静止醉鬼在广场内    if (s.pos.y >= height || s.pos.y <= 0 || s.pos.x >= width || s.pos.x <= 0) {      limit = true;    }  }  for (int n = 0; n < iterations; n++) {    for (int i = movers.size() - 1; i > 0; i--) {      Drunkard m = movers.get(i);      m.walk();      for (Drunkard s : sleepers) {        if (s.checkStuck(m)) {          m.stuck = true;          sleepers.add(m);          movers.remove(i);           break;        }      }    }  }  //醉鬼人数的增长  while (movers.size() < maxDrunkards && limit == false) {    movers.add(new Drunkard());  }}//醉鬼类class Drunkard {  PVector pos;  PVector vel;  boolean stuck;  int radius;  //移动的醉鬼  Drunkard() {    pos = randomDrunkard();  //随机初始位置    stuck = false;  //初始状态    radius = 6;  //粒子半径  }  //睡觉的醉鬼  Drunkard(int x, int y) {    pos = new PVector(x, y);    stuck = true;    radius = 6;  }    //四周随机出现的醉鬼    PVector randomDrunkard() {    float i = random(4);  //4种可能性    PVector p = new PVector();    float x = random(width);    float y = random(height);    if (i <= 1) {  //顶部      p = new PVector(x, 0);    } else if (i <= 2) {  //底部      p = new PVector(x, height);    } else if (i <= 3) {  //右边缘       p = new PVector(0, y);    } else if (i <= 4) {  //左边缘       p = new PVector(width, y);    }    return p;  }  //醉鬼的移动  void walk() {    vel = PVector.random2D();  //随机移动速度    pos.add(vel);    pos.x = constrain(pos.x, 0, width);    pos.y = constrain(pos.y, 0, height);  }  //以圆代表醉鬼  void show() {    noStroke();    if (stuck) {      fill(0, 200, 0);    } else {      fill(300, 100, 100);    }    ellipse(pos.x, pos.y, radius * 2, radius * 2);  }  //是否睡觉  boolean checkStuck(Drunkard dr) {    float d = dist(pos.x, pos.y, dr.pos.x, dr.pos.y);    if (d < sqrt(radius * dr.radius * 2 * 2)) {       if (random(1) < 0.1) {        return true;      }    }    return false;  }}

8.3 Processing与DLA

以黑色像素点作为醉鬼形象:

这里写图片描述

将屏幕边缘设置为睡觉的醉鬼:

这里写图片描述

以圆周作为目标区域,从广场中央走出醉鬼:

这里写图片描述

嫌像素点太小?试试这个吧:

这里写图片描述

太单调?加点颜色:

这里写图片描述

导入Box2d:

这里写图片描述

See more, try more:

这里写图片描述

这里写图片描述

8.4 3D DLA

分享几个DLA神级作品:

Karsten Schmidt’s Works:

这里写图片描述

这里写图片描述

这里写图片描述

Jason’s Works:

列表内容

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

列表内容

五花马,千金裘,呼儿将出换美酒,与尔同消万古愁。The story of the drunkards
 is over, I will definitely come back!

阅读全文
1 0
原创粉丝点击