Fractals and Tiles (com html5) – Parte 1 – Trees

Publicado em 19/10/2011

7


Olá pessoal, tudo certo?!

Nessa série que estamos começando, vou demonstrar como gerar imagens incríveis através de fractais e tiles.

Nos posts que seguem, explicarei um pouco da matemática necessária para trabalhar com fractais e tiles. Entretanto, vou me concentrar mais na “mecânica” do desenho do que na matemática envolvida.

Live demo: http://users.cjb.net/livedemoelemarjr/tree.htm (funciona no IE)

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

O que são fractais?!

De forma simples, sem nenhum rigor matemático, podemos dizer que um fractal é uma curva em dimensão fracional. Curvas simples, como um segmento de linha ou a borda de uma elipse são objetos de uma dimensão. Por outro lado, retângulos e círculos tem duas dimensões. Estranhamente, fractais (muitos) parecem ter mais de uma dimensão e menos de duas. Considere a figura abaixo (nosso demo de hoje):

image

Repare como a “árvore” não cobre, completamente, um espaço de duas dimensões (como um quadrado, por exemplo). Entretanto, quanto maior for a ramificação, mais e mais complicado, chegando a parecer cobrir mais que uma simples dimensão.

Fractais geralmente são “Self-similar”. Ou seja, parte do desenho parece representar completamente o padrão do desenho inteiro. Além disso, costumam ser orginados de equações (algoritmos) muito simples.

Trees

Podemos definir uma tree recursivamente, como um tronco que recebe zero ou mais trees menores. Para ser mais claro, cada tronco recebe “galhos”.

Para “configurar” a árvore, podemos definir um depth maior ou menor. Depth representa o número de “ramificações” (recursões) presentes na árvore. Observe:

image

O que mudou nessa figura, foi, basicamente, o número de recursões (definido no primeiro slider).

Também é possível definir o Length Scale. Cada galho, em um determinado depth, deve ser proporcinalmente menor ao depth anterior. Por default, no exemplo de hoje, o novo nível tem 75% do tamanho do anterior. Veja o que acontece quando “aumentamos a proporção”.

image

Outra propriedade interessante é a base length. Corresponde ao tamanho do primeiro galho. Vou reduzir um pouco essa medida para fazer nossa árvore caber no canvas. Observe:

image

 

Por fim, temos o delta theta. Ele regula a “abertura” dos novos galhos. Vamos configurar uma abertura de 90 graus.

image

Implementando o exemplo de hoje

Não vou ilustrar a marcação para a página do demo. Você pode ver o código-fonte completo (https://gist.github.com/1299650) para saber como ela foi feita. No lugar disso, vou destacar o código Javascript.


              
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

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


$("#depthSlider").slider(
{
    min: 3, max: 15, animate: true,
    change: function (e, ui) { update(); }
});

$("#deltaThetaSlider").slider(
{
    min: -Math.PI / 2, max: Math.PI / 2, step: 0.01, value: Math.PI / 5, animate: true,
    change: function (e, ui) { update(); }
});

$("#lengthScaleSlider").slider(
{
    min: 0.2, max: 0.9, step: 0.01, value: 0.7, animate: true,
    change: function (e, ui) { update(); }
});

$("#baseLengthSlider").slider(
{
    min: 5, max: 200, step: 0.01, value: 100, animate: true,
    change: function (e, ui) { update(); }
});

            

Começo configurando os sliders. Repare que utilizei o componente slider do jqueryUi. Em todos eles, programei o evento change para “atualizar” o desenho.

Depois, crio alguns métodos de apoio para resgatar o valor dos sliders. Observe:


              
function getLengthScale() {
    return $("#lengthScaleSlider").slider("value");
};

function getDeltaTheta() {
    return $("#deltaThetaSlider").slider("value");
}

function getDepth() {
    return $("#depthSlider").slider("value");
}

function getBaseLength() {
    return $("#baseLengthSlider").slider("value");
}

            

Por fim, temos a ação propriamente dita. Observe:


              
function drawBranch(depth, x, y, length, theta) {
    var x1 = x + length * Math.cos(theta);
    var y1 = y + length * Math.sin(theta);

    context.beginPath();
    context.moveTo(x, canvasHeight - y);
    context.lineTo(x1, canvasHeight - y1);
    context.closePath();
    context.stroke();

    if (depth > 1) {
        drawBranch(depth - 1, x1, y1, length * getLengthScale(), theta + getDeltaTheta());
        drawBranch(depth - 1, x1, y1, length * getLengthScale(), theta - getDeltaTheta());
    }
}

function update() {
    context.clearRect(0, 0, canvasWidth, canvasHeight);
    drawBranch(getDepth(), canvasWidth / 2, 0, getBaseLength(), 90 * Math.PI / 180);
}

update();

            

Nada novo aqui. Apenas observações básicas:

  • Inverto o sentido de crescimento de Y com uma subtração simples;
  • Implemento a árvore com uma recursão simples.

Por hoje, era isso.

Smiley piscando

Publicado em: Post