【问题标题】:Emulating African village fractals with JavaScript [closed]用 JavaScript 模拟非洲村庄的分形[关闭]
【发布时间】:2014-07-24 22:07:01
【问题描述】:

(受此TED Talk 启发的问题 - 更多信息here

我正在开发一个 jQuery 插件,用于在画布上绘制图像,以暗示某些非洲村庄的分形布局:


(来源:eglash at homepages.rpi.edu

给定一个图像源列表,该插件的工作方式如下:

  1. 生成类似于上图的正交定向的分形结构。
  2. 对于结构中的每个离散区域(矩形),从该区域的列表中绘制图像。

绘制每张图像很容易,但我不知道如何生成结构。用于描述分形的数学计算远远超出了我的想象。

理想情况下,获得分形表示就像将画布尺寸传递给一个函数一样简单,该函数将返回一个矩形列表。每个矩形将表示为:

  • 左上角的 X/Y 坐标(相对于画布)
  • 宽度
  • 身高

我该如何实现这样的功能?

【问题讨论】:

  • 赞成,因为这是一个有趣的问题。虽然,您不应该真的在此站点上要求软件库推荐。我是否可以建议您改写这个问题,而不是询问执行 X 的库,而是询问如何实现 X 背后的算法。这应该使这个问题更适合 SO,或programmers SE
  • 这个问题似乎离题了,因为作者是在寻求帮助编写代码而不是解决编写代码的问题。
  • 感谢@bguiz 的支持。我已经改写了这个问题。
  • @acobster 我必须同意 Mike 的观点。这是一个有趣的问题,但格式不适合 SO。如果您创建了一个实现,然后对它有一个特定的问题,那可能会奏效。
  • codegolf.stackexchange.com来说似乎是一个很好的问题

标签: javascript jquery math fractals


【解决方案1】:

有一类重要的分形称为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 条件以使其执行您想要的操作,并且最好让自己成为一个带有几个滑块的界面来尝试不同的设置。或者你有时想分成三个而不是一两个盒子。对于已经很长的答案来说,可能性太无穷了:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多