Fractals and Tiles (com html5) – Parte 5 – Sierpinski triangle via Chaos Game

Posted on 23/10/2011

2


Olá pessoal, tudo certo?!

No último post, mostrei uma técnica para geração do triângulo de Sierpinski através de uma curva. Agora, mostro como fazer isso com uma técnica chamada “chaos game”.

Código-fonte completo: https://gist.github.com/1306848

Live demo: http://users.cjb.net/livedemoelemarjr/sierpinski_chaos.htm

Como falei no início dessa série, explicar a fundamentação matemática para fractais está fora de meus objetivos. No lugar disso, apresento apenas códigos que desenham fractais. O código de hoje, por exemplo, funciona de forma aparentemente “misteriosa” e/ou “mágica”.. Mas funciona.

Explorando o demo

Quando você iniciar a execução da demo, irá perceber que serão destacados três pontos (cruz) na tela. Sendo que, um deles estará em destaque (em vermelho).

image

Além disso, você perceberá que o triângulo vai se “materializando” aos poucos. Depois de algum tempo, temos algo “mais definido”.

image

Para mudar o ponto em destaque, basta clicar sobre o ponto desejado (abaixo, mostro o ponto inferior direito selecionado).

image

Clicando em qualquer área do canvas, que não corresponda a posição de um ponto, o ponto selecionado é deslocado e o desenho é reiniciado.

image

.. outra vez

image

Qual é a lógica do “Chaos Game”

Chaos game começa pela seleção de um ponto aleatório no canvas. Depois, repetidamente, um ponto de referência (anchor) é selecionado aleatoriamente e é calculada uma aproximação para esse ponto que servirá como base para uma nova iteração.

O código que implementa essa lógica é simples. Observe:


              
var currentX = 0;
var currentY = 0;
function update() {
    context.clearRect(0, 0, canvasWidth, canvasHeight);
    currentX = Math.random() * canvasWidth;
    currentY = Math.random() * canvasHeight;
    playGame();
}
update();

function playGame() {

    context.strokeStyle = "#ccc";
    for (i = 0; i < 100; i++) {
        var anchor = Math.floor(points.length * Math.abs(Math.random()));
        currentX = (currentX + points[anchor].x) / 2;
        currentY = (currentY + points[anchor].y) / 2;
        drawLine(currentX, currentY, currentX + 1, currentY);
    }

    for (var i = 0; i < points.length; i++) {
        context.strokeStyle = i == moving ? "#f00" : "#000";
        drawMarker(points[i].x, points[i].y);
    }
    setTimeout(playGame, 33);
}

            

Gostaria de chamar a atenção para o fato de que são desenhados apenas 100 pontos a cada frame. Isso permite que ocorra a interação com o usuário.

Repare, também, como desenho novamente os “pontos” de referência a cada frame.

Como ocorre a inicialização

Como já fiz em outros posts, mantenho o canvas ocupando todo o espaço do browser. Em função disso, recrio a lista dos âncoras toda vez que a janela é redimensionada. Observe:


              
var points = [
    { x: 240, y: 450 },
    { x: 40, y: 40 },
    { x: 440, y: 40 }
];

// -----------------------------------------------------
var canvas = $('#target');
var context = canvas.get(0).getContext('2d');
context.fillStyle = "#ccc";

var moving = 0;

            
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;

$(window).resize(resizeCanvas);

function resizeCanvas() {
    canvas.attr("width", $(window).get(0).innerWidth);
    canvas.attr("height", $(window).get(0).innerHeight);
    canvasWidth = canvas.width();
    canvasHeight = canvas.height();

    points[0].x = canvasWidth / 2;
    points[0].y = canvasHeight - 40;

    points[1].x = 40;
    points[1].y = 40;

    points[2].x = canvasWidth - 40;
    points[2].y = 40;
    update();
};

resizeCanvas();

            

Como foi implementada a “interface” que permite ao usuário reposicionar as âncoras

Sempre que o usuário clica no canvas, verifico se um ponto foi “selecionado” (colocando uma distância mínima para o clique). Se nenhum ponto foi “selecionado”, entendo que deverá ocorrer um deslocamento. Observe:


              
$("#target").click(function (e) {
    var nx = e.pageX;
    var ny = canvasHeight - e.pageY;

    var current = 10;

    for (var i = 0; i < points.length; i++) {
        var d = Math.sqrt(
                Math.pow((nx - points[i].x), 2) +
                Math.pow((ny - points[i].y), 2)
                );

        if (d < current) {
            current = d;
            moving = i;
        }
    }

    if (current == 10) {
        points[moving].x = nx;
        points[moving].y = ny;
        update();
    }
});

            

Entendido?!

Era isso.

Smiley piscando

Posted in: Post