lambdaway
::
mandelbrot
6
|
list
|
login
|
load
|
|
_h1 mandelbrot {sup {sup interactive}} | [[mandel|?view=mandel_canvas]] {sup {sup made easy}} _p The amazing {b Mandelbrot Set} is built on a very simple expression: {code z=z{sup 2}+c}, where {code z} and {code c} are the so-called complex numbers {code z=x+iy} and {code c=a+ib}. When {code c=-1}, this expression reduces to {code z=z{sup 2}-1} which can be written: {code z{sup 2}-z-1=0}. It's a second degree equation with two real roots, {code 1.618.. and 0.618..}, known as the {b golden ratio, Ø}! {center {canvas {@ id="mycanvas" width="420"; height="420" style="background:#ffe; border:0; box-shadow:0 0 8px black;"}}} {pre {@ style="position:absolute; top:220px; right:-100px; width:250px; padding:2px; background:#fff; opacity:0.9; box-shadow:0 0 4px #000; "} {drag} {input {@ id="reset" type="submit" value="reset" onclick="MANDEL.reset()"}} {input {@ type="range" min="0" max="100" value="10" step="1" oninput ="getId('iter').value = this.value; MANDEL.update()"}} iterations: {input {@ id="iter" type="text" value="10" style="height:1.0em; background:transparent; border:0; width:30px; padding:0;" onkeyup="MANDEL.update()"}} zoom: {input {@ id="zoom" type="text" value="4" style="height:1.0em; background:transparent; border:0; width:130px; padding:0;" onkeyup="MANDEL.update()"}} x0: {span {@ id="x0"}0} y0: {span {@ id="y0"}0} {span {@ id="info"}time:} } _h2 using the mandelbrot set editor _ul {b 1)} mouse-draw a rect around any point in the canvas to define a zoom around it, or click any point to get a predefined zoom x2, _ul {b 2)} edit fields "iterations" and "zoom", _ul {b 3)} click on "reset" to re-initialize the mandelbrot set. _p See some more powerful mandelbrot set editors. _ul [[https://mandelbrot-set.io/|https://mandelbrot-set.io/]] _ul [[https://mandelbrot.site/|https://mandelbrot.site/]] _ul [[https://mandelbrot.page/|https://mandelbrot.page]] _ul [[https://tsmeets.itch.io/mandelbrot|https://tsmeets.itch.io/mandelbrot]] _ul [[https://mandelbrot.silversky.dev/|https://mandelbrot.silversky.dev/]] _ul [[https://icefractal.com/mandelbrot/|https://icefractal.com/mandelbrot/]] _ul ... {iframe {@ width="580" height="315" src="https://www.youtube.com/embed/Ed1gsyxxwM0?si=tzR10LhW6UwcbzLE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen}} {script ;; var getId = function(id) { return document.getElementById(id) }; var MANDEL = (function () { // the only one global var var ctx, // global 2D context iter, // can be edited by field zoom, // can be edited by field and by mouse x0, y0; // modified by mouse function init () { var canvas = getId( "mycanvas" ); ctx = canvas.getContext( "2d" ); add_zoom_to_mouse( canvas ); getId("iter").value = iter = 10; getId("zoom").value = zoom = 4; x0 = 0; y0 = 0; compute_imageData(); } function reset () { getId("iter").value = iter = 10; getId("zoom").value = zoom = 4; getId("x0").innerHTML = x0 = 0; getId("y0").innerHTML = y0 = 0; compute_imageData(); } function update () { iter = getId("iter").value; iter = (iter < 1)? 1 : (iter > 1000)? 1000 : iter; zoom = getId("zoom").value; compute_imageData(); } function add_zoom_to_mouse ( can ) { var context = can.getContext( "2d" ); var flag = false, coords, z, zmin = 50; can.getMouse = function (event) { var totalOffsetX = 0, totalOffsetY = 0, canvasX = 0, canvasY = 0; var currentElement = this; // recursive search do { totalOffsetX += currentElement.offsetLeft; totalOffsetY += currentElement.offsetTop; } while(currentElement = currentElement.offsetParent) canvasX = event.pageX - totalOffsetX; canvasY = event.pageY - totalOffsetY; return {x:canvasX, y:canvasY}; }; can.onmouseover = function(e) { this.style.cursor = "crosshair"; }; can.onmouseout = function(e) { this.style.cursor = "default"; }; can.onmousedown = function (e) { iter = parseInt(getId("iter").value); var zoom_prev = zoom; zoom = parseFloat(getId("zoom").value); x0 = x0*zoom_prev/zoom; y0 = y0*zoom_prev/zoom; coords = this.getMouse( e ); x0 = coords.x/this.width - 0.5 + x0; y0 = -(coords.y/this.height - 0.5) + y0; getId("x0").innerHTML = x0; getId("y0").innerHTML = y0; context.strokeStyle = "#888"; context.lineWidth = 1; z = zmin; flag = true; }; can.onmousemove = function (e) { if (!flag) return; var coords_2 = this.getMouse( e ); var w = Math.abs( coords_2.x - coords.x ); var h = Math.abs( coords_2.y - coords.y ); z = Math.max( w, h ); context.strokeRect( coords.x - z/2, coords.y - z/2, z, z ); }; can.onmouseup = function (e) { var zoom_prev = zoom; z = (z < zmin)? zmin : z; zoom = zoom*z/this.width; getId("zoom").value = zoom; x0 = x0*zoom_prev/zoom; y0 = y0*zoom_prev/zoom; flag = false; compute_imageData(); }; } // end add_mouse_zoom_to ( canvas ) ///////////////////////////// // MANDEL ///////////////////////////// function compute_imageData() { var width = ctx.canvas.width, height = ctx.canvas.height, imageData = ctx.createImageData( width, height ), data = imageData.data, rgba = {r:0, g:0, b:0, a:255}, index; var t0 = new Date().getTime(); for (var i=0; i< width; i++) { var x = zoom*( i/width - 0.5 + x0); for (var j=0; j< height; j++) { var y = zoom*(-j/height + 0.5 + y0); mandel_geo( x, y ); // compute count and norme; mandel_col( rgba ); index = (i + j * width) * 4; data[index] = rgba.r; data[index+1] = rgba.g; data[index+2] = rgba.b; data[index+3] = rgba.a; } } var t1 = new Date().getTime(); getId( "info" ).innerHTML = "time: " + (t1-t0) + "ms"; ctx.putImageData( imageData, 0, 0 ); } var count, norme; // shared by mandel_geo and mandel_col function mandel_geo( cx, cy ) { // compute k at point C var x = 0.0, y = 0.0; // initialyze z = x + iy = 0 for (count = 0; count < iter; count++) { var X = x*x - y*y + cx; // compute z = z^2 + c var Y = 2*x*y + cy; norme = X*X + Y*Y; if ( norme > 4.0 ) break; // exit if norme > 2 x = X; // update z = x +iy y = Y; } } function mandel_col( rgba ) { var u = 255*(norme*0.25 - 1); if ( count < iter ) { rgba.r = u; rgba.g = u; rgba.b = u; } else { rgba.r = -u; rgba.g = 0; rgba.b = 0; } } return {init, update, reset}; })(); setTimeout( MANDEL.init, 1 ) }
lambdaway v.20211111