Эмуляция африканских деревенских фракталов с помощью JavaScript

(Вопрос вдохновлен этим TED Talk - больше информации здесь)

Я разрабатываю плагин jQuery для рисования изображений на холсте таким способом, который предполагает фрактальную компоновку некоторых африканских деревень:

Африканская деревня

Учитывая список источников изображений, плагин работает так:

  1. Создайте ортогонально ориентированную, фрактальную структуру, подобную изображению выше.
  2. Для каждой отдельной области (прямоугольника) в структуре нарисуйте изображение из списка в этой области.

Рисовать каждое изображение достаточно просто, но я застрял на том, как создать структуру. Математика, которая входит в описание фракталов, довольно далеко над моей головой.

В идеале получить фрактальное представление было бы так же просто, как передать размеры холста в функцию, которая возвращала бы список прямоугольников. Каждый прямоугольник будет представлен как:

  • Координаты X/Y для верхнего левого угла (относительно холста)
  • ширина
  • рост

Как я могу реализовать такую ​​функцию?

1 ответ

Существует важный класс фракталов, называемый L-системой. грубо говоря, идея состоит не в том, чтобы думать о всей структуре слишком много, а в том, чтобы думать только об одном крошечном элементе и о том, как прикрепить меньшую версию того же самого к себе.

один замечательный пример - дерево. может быть, до сих пор, когда вы описали бы дерево, вы скажете: "Ну, это дерево. У него есть корень. А из корня растут ветви. Из этого растут листья". но есть другой способ описать дерево по правилам:

сначала определите палку. палка имеет некоторое положение в пространстве, длину и вращается в некотором направлении. Теперь к правилу: возьми палку. прикрепите от 5 до 10 палочек поочередно повернутыми влево и вправо. повторите процедуру для ваших новых палочек. продолжай делать это, пока не устанешь. Невероятно, но в итоге получается что-то похожее на растение.

Пример фрактальных сорняков из Википедии

Различные параметры влияют на внешний вид вашего цифрового оборудования. вращение палки, прикрепленной к другой палочке, может сделать дерево более густым или более узким. положение прикрепленных палочек изменит то, какое растение вы получаете. прикрепление всех "детских палочек" к дальнему концу сделает его похожим на обычное дерево. более равномерное расположение элементов сделает его более похожим на папоротник (как на картинке). и так далее. Самое приятное, что ты не ограничен реальностью.

В любом случае, все это теория. я написал кучу кода и добавил к нему несколько комментариев, но, надеюсь, это довольно простая реализация того, что я объяснил выше, с блоками вместо палочек.

нажмите здесь, чтобы поиграть с jsfiddle этого кода. нажмите Run несколько раз, чтобы увидеть разные результаты.

// 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 является четным числом, и по вертикали, если это нечетное. и так и так и так далее.

Бездонные чудеса возникают из простых правил, повторяющихся без конца. как сказал Бенуа Мандельброт:)

удачи, фракталы классные!


пс. я знаю, что этот код не дает результат, как на вашей картинке. вы, вероятно, потратите часы на то, чтобы настроить условия if, чтобы он выполнял то, что вы хотите, и было бы неплохо создать интерфейс с несколькими ползунками, чтобы экспериментировать с различными настройками. или, может быть, вы хотите разделить на три, а не на одну или две коробки иногда. возможности слишком бесконечны для ответа, который уже получил довольно долго:)

Другие вопросы по тегам