利用HTML5生成Mandelbrot分形

分享于 

13分钟阅读

Web开发

  繁體 雙語

查看现场演示插件

介绍

HTML5提供了一个新的酷元素- Canvas。 这是SVG的替代。 你可以用JavaScript在上面画。 理论上我们也可以绘制分形? 是的,我们可以。本文是关于绘制Mandelbrot和 HTML5 Canvas 元素。 大多数浏览器都支持 HTML5,IE 9,但是IE8和较低的浏览器不支持 Canvas

为了绘制,我们必须获取 Canvas的上下文: canvas.getContext('2d') 参数" 2d"是上下文的NAME。 大多数浏览器只支持 2D 个上下文,但是 Chrome 为 3D 上下文提供了实验的。 接下来,使用上下文元素,我们可以绘制和获取或者设置图像数据。 在这个解决方案中,我们将使用获取和设置图像数据,因为我们会按像素绘制像素。

Background

我从中学习了 2D 个上下文接口( 参见参考资料 )。

脚本结构

脚本分为两个部分。 有公式和观众。 公式是计算单个像素的单个函数。 输入是( 从我的图书馆 complex ) 坐标,输出是 0和 1 (。迭代次数除以最大迭代次数) 之间的数字。 查看器部分为 Canvas 中所有坐标运行指定的公式,然后用特殊调色板表示它们。 查看者也处理缩放。 由于这样的构造,它是可以扩展的。 我们可以简单地添加新公式,例如 Julia。

Mandelbrot公式

Mandelbrot需要复数,我用JavaScript编写了一个简单的类,用于这些数字。 你可以从文章的顶部下载。 因此,将复杂类添加到页面:

<scripttype="text/javascript"src="complex.js"></script>

现在我们可以定义Mandelbrot的函数了:

function mandelbrot(xy) {
}

xy 将是一个复数。 这个函数将是一个公式。 迭代生成该算法的算法。 我们将在每次迭代中重新计算 z 复数。 它的起始值是 xy。 循环一直持续到 z的绝对值小于 bailout。 通常等于 4. 在Mandelbrot的中心,迭代的次数是无限的。 所以我们必须定义最大的迭代来避免无限循环。 让我们在函数和 i 中定义用于计数迭代的z:

var z = new Complex(xy.x, xy.y);

现在,我们必须定义最大的迭代。援助和其他数字来重新计算 z。 将声明默认偏移量和大小。 他们会在这个函数之后。

// Code of function ends.mandelbrot.maxIter = 32; // Maximal iterations// Power of fractal. For normal Mandelbrot it will be (2,0).mandelbrot.power = new Complex(2.0, 0.0);
mandelbrot.bailout = 4.0; // Bailout value.mandelbrot.offset = new Complex(-3.0, -2.0); // Default offset of fractal.mandelbrot.size = new Complex(0.25, 0.25); // Default size of fractal.

现在,我们可以定义公式的while 循环:

while (i <mandelbrot.maxIter && z.abs() <= mandelbrot.bailout) {
 z = z.pow(mandelbrot.power).add(xy); // Recalculating z. i++; // Iterations + 1.}

现在我们可以计算迭代。 函数的返回值必须介于 0和 1之间。 一个是最大返回值,迭代的最大值是 maxIter。 所以返回值将被 i 除以最大迭代。 我也应用平滑算法。

if (i <mandelbrot.maxIter) {
 // Smoothing algorithm. i -= Math.log(Math.log(z.abs()))/Math.log(mandelbrot.power.abs());
 return i/mandelbrot.maxIter;
}elsereturn0.0;

具有它的值的函数应如下所示:

function mandelbrot(xy) {
 var z = new Complex(xy.x, xy.y);
 var i = 0;
 while (i <mandelbrot.maxIter && z.abs() <= mandelbrot.bailout) {
 z = z.pow(mandelbrot.power).add(xy);
 i++;
 }
 if (i <mandelbrot.maxIter) {
 i -= Math.log(Math.log(z.abs()))/Math.log(mandelbrot.power.abs());
 return i/mandelbrot.maxIter;
 }
 elsereturn0.0;
}
mandelbrot.maxIter = 32;
mandelbrot.power = new Complex(2.0, 0.0);
mandelbrot.bailout = 4.0;
mandelbrot.offset = new Complex(-3.0, -2.0);
mandelbrot.size = new Complex(0.25, 0.25);

查看者

首先,让我们创建页面的主体:

<h1>Mandelbrot</h1><divid="di"style="position: relative;"><canvasid="cv"width="580"height="580"style="border-style: solid; border-width: 1px;"onmousedown="canvas_onmousedown(event);"> Your browser doesn't support canvas.
 </canvas></div>

Div 保留供将来使用。 inside Div,你可以看到名为 cvCanvas。 现在我们来定义 generateFractal 函数。

function generateFractal(formula, resetSize) {
}

formula 将是像我们的Mandelbrot公式一样的函数。 我们将公式函数传递给 generateFractal,而不是它的值。 如果 resetSizetrue,那么偏移量和大小变量将被设置为默认的分形。 现在在加载页面时添加Mandelbrot生成。

onload="generateFractal(mandelbrot);"

定义以下全局变量:

var lastFormula; // Here we will store actual formula.var tim; // In the future we will store here timeout for generating// lines - it simulates asynchronous thread.var offset = new Complex(-3.0, -2.0); // Offset of fractal view.var size = new Complex(0.25, 0.25); // Scale of fractal view.

计算第一行时,我们将设置用于计算第二行的超时( 1ms )。 这里 1ms 用于刷新 Canvastim 将是当前超时的ID。 首先,如果未完成的话,我们必须停止以前的计算。 然后我们必须将新公式设置为 lastFormula

clearTimeout(tim);
lastFormula = formula;

然后我们必须重置 可选地 偏移和大小:

if (resetSize) {
 offset = new Complex(formula.offset.x, formula.offset.y);
 size = new Complex(formula.size.x, formula.size.y);
} 

然后我们得得到 Canvas的宽度和高度。

var w = cv.width;var h = cv.height;

现在我们必须获取 Canvas的上下文和图像数据。 定义 y 它将是实际渲染行的数目。

var g = cv.getContext("2d"); // Image context.var img = g.getImageData(0, 0, w, h); // Image data.var pix = img.data; // Table of canvas image data.

现在在 generateFractal 中声明函数 drawLine

function drawLine() {
}

现在如果 y <h (。Canvas的高度是 GREATER,而不是 0 ):

if (y <h)
tim = setTimeout(drawLine, 1);

嵌套函数 drawLine 将计算一行分形,将呈现的部分设置为 Canvas,然后将超时设置为下一行。 我们先写这段代码。但是首先,我们需要调色板和一个用来绘制像素的函数。 我不解释调色板是如何工作的。 以下是从公式值返回中获取颜色的函数:

function getColor(i) {
 var k = 1.0/3.0;
 var k2 = 2.0/3.0;
 var cr = 0.0;
 var cg = 0.0;
 var cb = 0.0;
 if (i> = k2) {
 cr = i - k2;
 cg = (k - 1) - cr;
 }
 elseif (i> = k) {
 cg = i - k;
 cb = (k - 1) - cg;
 }
 else {
 cb = i;
 }
 var r = parseInt(cr * 3 * 255);
 var g = parseInt(cg * 3 * 255);
 var b = parseInt(cb * 3 * 255);
 return [r, g, b];
}

把它放在 generateFractal 函数里。 现在让我们定义绘制像素的函数。 它必须能够访问 pix - 像素的table。 所以把它作为生成分形的函数。

function drawPixel(x, y, i) {
 var c = getColor(i);
 var off = 4 * (y * w + x);
 pix[off] = c[0];
 pix[off + 1] = c[1];
 pix[off + 2] = c[2];
 pix[off + 3] = 255;
}

第一行通过公式值的返回获得颜色。 在第二行中,我们计算像素到 plot的起始位置。 每个像素在 array 中分配 4字节的( R,G,B,A )。 在最后 4行中,它设置像素的字节。 最后一个字节是 255,因为图像不是半透明的( 这个通道是阿尔法)。 现在你可以编写 drawLine 函数了。

首先,定义循环:

for (var x = 0; x <w; x++) {
 var c = formula(new Complex(x/w/size.x + 
 offset.x, y/h/size.y + offset.y));
 drawPixel(x, y, c);
}

在变量 c 中,我们计算了公式的值。 它将缩放和平移的位置传递给公式。 接下来,它将 Canvas 中指定的像素设置为返回值。 当我们计算了一行之后,我们必须更新 Canvas:

g.putImageData(img, 0, 0);

现在我们必须设置计算下一行( 如果下一行的数字小于 Canvas的高度)的超时时间。

if (++y <h)
tim = setTimeout(drawLine, 1);

基本查看器已经就绪。

Julia公式

Julia公式与Mandelbrot非常相似。 有一个变化:在循环中不要添加像素,而是添加种子。 种子是一个点。它可以是随机的,也可以从Mandelbrot中选择。

function julia(xy) {
 var z = new Complex(xy.x, xy.y);
 var i = 0;
 while (i <julia.maxIter && z.abs() <= julia.bailout) {
 z = z.pow(julia.power).add(julia.seed);
 i++;
 }
 if (i <julia.maxIter) {
 i -= Math.log(Math.log(z.abs()))/Math.log(julia.power.abs());
 return i/julia.maxIter;
 }
 elsereturn0.0;
}
julia.maxIter = 32;
julia.power = new Complex(2.0, 0.0);
julia.bailout = 4.0;
julia.offset = new Complex(-2.0, -2.0);
julia.size = new Complex(0.25, 0.25);
julia.seed = new Complex(0.0, 0.0);

演示中的其他功能

我已经实现了对分形的放大。 它处理 Canvas 和body的鼠标事件。 按下鼠标按钮时,将保存第一个位置,当按住按钮时,将保存第二个位置。 它计算了分形的新偏移和尺度。 在移动鼠标时,它将显示预览- 红色 rectangle ( 具有绝对位置的Div )。

在演示中,也有切换: 你可以选择Mandelbrot上的点,这产生Julia分形。

为了保存分形,我得到了 Canvas的数据URL并将它的设置为图像。 然后用户可以将图像保存到光盘。 数据URL是带有图像数据的路径,如十六进制。

你可以在源代码中看到这些函数的详细信息。

结束语

HTML5 Canvas 是使用JavaScript动态绘图的一个很酷的元素。 我们也可以用它来绘制分形。

历史记录

  • 2011-12-22添加 Julia

相关文章