我正在开发一个jQuery插件,用于在画布上绘制图像,这样可以显示某些非洲村庄的分形布局:
给定一个图像源列表,该插件的工作方式如下:
绘制每个图像很容易,但我仍然坚持如何生成结构。描述分形的数学知识远远超出我的想象。
理想情况下,获取分形表示就像将画布尺寸传递给函数一样简单,该函数将返回矩形列表。每个矩形表示为:
我该如何实现这样的功能?
答案 0 :(得分:1)
有一类重要的分形称为L-system。大致的想法是不要过多地考虑整个结构,而是只考虑其中的一小部分,以及如何将同一事物的较小版本附加到自身。
一个很好的例子是一棵树。也许直到现在,当你描述一棵树时,你会说“好吧,它是一棵树。它有一根树根。从根部开始,树枝长出来。然后树叶长出来”。但是还有另一种按规则描述树的方法:
首先定义一根棍子。一根棍子在空间中有一些位置,一个长度并在某个方向上旋转。现在规则:拿一根棍子。连接5到10根木棍交替向左和向右旋转。重复你的新棍棒的程序。继续这样做,直到你累了。令人难以置信的是,你最终得到的东西看起来像某种植物。
不同的参数会影响数字工厂的视觉外观。附着在另一根棍子上的棍子的旋转可能使树看起来更浓密或更窄。附着的枝条的位置将改变你正在获得的植物种类。将所有“儿童棍子”连接到远端将使它看起来像一棵普通的树。将东西间隔得更均匀会使它看起来更像蕨类植物(如图所示)。等等。好的是你不受现实的限制。
无论如何,所有这些都是理论。我已经写了一堆代码并添加了一些注释,但希望我用上面用盒子而不是棒子来解释我所说的内容。
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
为偶数时始终水平分割,则可能会获得有趣的结果,如果是奇数则垂直分割。等等等等。
PS。我知道这段代码不会产生你图片中的结果。你可能花了几个小时来调整if条件,让它做你想做的事情,并且让自己成为一个带有几个滑块的接口来试验不同的设置可能是一个好主意。或者你有时想分成三个而不是一个或两个盒子。对于已经有很长时间的答案,可能性太无穷无尽了:)