有一类重要的分形称为L-system。大致的想法是不要过多地考虑整个结构,而是只考虑其中的一小部分以及如何将同一事物的较小版本附加到自身上。
一个很好的例子是一棵树。也许直到现在,当您描述一棵树时,您会说“嗯,它是一棵树。它有根。从根上长出树枝。从那里长出叶子”。但是还有另一种方法可以通过规则来描述一棵树:
首先定义一根棍子。一根棍子在空间中具有某个位置、一个长度并沿某个方向旋转。现在规则:拿一根棍子。附上 5 到 10 根棍子,左右交替旋转。对你的新棍子重复这个过程。继续这样做,直到你累了。令人难以置信的是,您最终会得到一些看起来像某种植物的东西。
不同的参数会影响数字工厂的视觉外观。一根棍子连接到另一根棍子上的旋转可能会使树看起来更浓密或更窄。附加的棍子的位置会改变你得到什么样的植物。将所有“儿童棒”连接到远端会使其看起来像一棵普通的树。将事物间隔更均匀会使它看起来更像蕨类植物(如图所示)。等等。好的是你不受现实的限制。
无论如何,所有这些都是理论。我已经编写了一堆代码并向其中添加了一些 cmets,但希望它是我上面用盒子而不是棍子解释的内容的一个非常简单的实现。
click here to play with a jsfiddle of this code。点击运行几次以查看它产生的不同结果。
// a ton of colors. always handy!
var colors = ["AliceBlue","AntiqueWhite","Aqua","Aquamarine","Azure","Beige","Bisque","Black","BlanchedAlmond","Blue","BlueViolet","Brown","BurlyWood","CadetBlue","Chartreuse","Chocolate","Coral","CornflowerBlue","Cornsilk","Crimson","Cyan","DarkBlue","DarkCyan","DarkGoldenRod","DarkGray","DarkGreen","DarkKhaki","DarkMagenta","DarkOliveGreen","DarkOrange","DarkOrchid","DarkRed","DarkSalmon","DarkSeaGreen","DarkSlateBlue","DarkSlateGray","DarkTurquoise","DarkViolet"];
// store base structure here
// in the end this will contain a nested representation of your village
base = {
x: 0,
y: 0,
width: 400,
height: 400,
children: []
};
// and a flat structure here, that's always handy too
var boxes = [base];
// add some children to the base recursively.
addChildren( base, 0 );
// now create a div for each
for( var i in boxes ){
var box = boxes[i];
var el = document.createElement("div");
el.className = "box";
el.style.left = box.x + "px";
el.style.top = box.y + "px";
el.style.width = box.width + "px";
el.style.height = box.height + "px";
el.style.backgroundColor = colors[i%colors.length];
document.body.appendChild( el );
}
// randomly add children to a box recursively
function addChildren( box, level ){
// maybe... split vertically? (two next to each other)
if( Math.random() < 0.5 ){
// maybe... nest further in the top?
if( Math.random() < 1-level/10.0 ){
box.children.push( {
x: box.x,
y: box.y,
width: box.width/2,
height: box.height,
children: []
} );
}
// maybe bottom too?
if( Math.random() < 1-level/10.0 ){
box.children.push( {
x: box.x + box.width/2,
y: box.y,
width: box.width/2,
height: box.height,
children: []
} );
}
}
// ah. maybe we split horizontally instead
else{
// maybe... nest further in the top?
if( Math.random() < 1-level/10.0 ){
box.children.push( {
x: box.x,
y: box.y,
width: box.width,
height: box.height/2,
children: []
} );
}
// maybe bottom too?
if( Math.random() < 1-level/10.0 ){
box.children.push( {
x: box.x,
y: box.y + box.height/2,
width: box.width,
height: box.height/2,
children: []
} );
}
}
// also add all the children to our
// flat list of boxes
for( var i in box.children ){
boxes.push( box.children[i] );
}
// unless we reach level 5 subdivide further!
if( level < 5 ){
for( var i in box.children ){
// nest deeper!
addChildren( box.children[i], level+1 );
}
}
}
addChildren 函数是神奇发生的地方。它随机决定是将盒子水平还是垂直一分为二。然后它随机决定是否在顶部/左侧和/或底部/右侧添加一个框。这是您想要进行一些修改并使用不同标准的地方。例如,如果你总是在level 是偶数时水平分割,如果它是奇数则垂直分割,你可能会得到有趣的结果。等等等等。
无底的奇迹源于简单的规则,不断重复。
正如 benoit mandelbrot 所说:)
祝你好运,分形很棒!
ps。我知道这段代码不会产生你图片中的结果。您可能会花费数小时来调整 if 条件以使其执行您想要的操作,并且最好让自己成为一个带有几个滑块的界面来尝试不同的设置。或者你有时想分成三个而不是一两个盒子。对于已经很长的答案来说,可能性太无穷了:)