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.
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.
outubro 22nd, 2011 → 16:32
[...] código que utilizo aqui é derivado daquele que mostrei no post anterior. Por isso, nesse post, explico apenas o código [...]