Fractals and Tiles (com html5) – Parte 3 – Hilbert curves

Posted on 22/10/2011

1


Olá pessoal, tudo certo?

Depois de um “passeio nas estrelas”, volto a tratar de fractais com HTML5.

Na parte 2, mostrei uma implementação simples para snowflakes. Na parte 1, mostrei como desenhar trees. Hoje, mostro como desenhar Hilbert curves.

image

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

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

Um pouco mais sobre Hilbert “Space-filling” curve

Space-filling fractals, como snowflakes, são desenhados recursivamente. Na medida em que o nível de recursão aumenta, a curva tornasse mais complexa.

Você pode ver a lógica abaixo:

 

Para criar uma curva Hilbert em um determinado nível, desenhamos a curva correspondente ao nível anterior e conectamos com segmentos de reta.

A função principal – onde tudo acontece

Para variar, temos um novo exemplo de recursão. Observe:


              
function hibert(depth, dx, dy) {
    with (context) {
        if (depth > 0) hibert(depth - 1, dy, dx);
        walk(dx, dy);
        if (depth > 0) hibert(depth - 1, dx, dy);
        walk(dy, dx);
        if (depth > 0) hibert(depth - 1, dx, dy);
        walk(-dx, -dy);
        if (depth > 0) hibert(depth - 1, -dy, -dx);
    }
}

            

Como as “curvas menores” são curvas rotacionadas, nossa rotina precisa desenhar curvas em diferentes orientações. A forma mais fácil de fazer isso é passando para a rotina dois parâmetros, dx e dy, que indica a direção em que a primeira linha da curva deverá ser desenhada. Então, se a curva estiver em seu “depth 0”, deverá começar a desenhar a linha a partir da posição atual (função walk).

Se a curva não estiver em “depth 0”, então a rotina deverá se “chamar” recursivamente ajustando a orientação. Perceba que, como estamos usando angulos retos, basta fazer uma inversão nos parâmetros de direção.

Abaixo, apresento a função walk.


              
function drawLine(x1, y1, x2, y2) {
    with (context) {
        beginPath();
        moveTo(getCenterX() + x1, canvasHeight - (getCenterY() + y1));
        lineTo(getCenterX() + x2, canvasHeight - (getCenterY() + y2));
        closePath();
        stroke();
    }
}

currentX = 0;
currentY = 0;
function walk(dx, dy) {
    drawLine(currentX, currentY, currentX + dx, currentY + dy);
    currentX += dx;
    currentY += dy;
}

            

Como pode perceber, drawLine desloca o “ponto zero” para a posição indicada pelo usuário na toolbar. O método walk “lembra” o final da linha anterior e utiliza os argumentos como “offset”.

O método update – onde tudo começa

Agora que já temos a implementação do método de desenho e “auxiliares” para desenhar no canvas, vejamos como tudo começa. Observe:


              
function getSize() {
    return getLength() * (Math.pow(2, getDepth() + 1) - 1);
}

function update() {
    with (context) {
        currentX = -getSize() / 2;
        currentY = -getSize() / 2;
        clearRect(0, 0, canvasWidth, canvasHeight);
        hibert(getDepth(), getLength(), 0);
    }

    drawZero();
}

            

Como pode perceber, desloco o ponto inicial para a base da curva. Para isso, calculo sua a “dimensão final”.

Era isso.

Smiley piscando

Posted in: Post