<!DOCTYPE html>
<html>
<head>
</head>
<body onload =" setState() ">
<canvas id ="myCanvas" width =" 500 " height =" 500 " style =" border:1px solid #d3d3d3; ">
Your browser does not support the HTML5 canvas tag. </canvas> <br />
<span id ="myTextReduce" type =" button " value =" Reduce " onclick =" clickState='1';setState(); "> Reduce </span>
<span id ="myTextCenter" type =" button " value =" Center " onclick =" clickState='0';setState(); "> Center </span>
<span id ="myTextEnlarge" type =" button " value =" Enlarge " onclick =" clickState='2';setState()"> Enlarge </span>
<span id ="myTextUndo" type =" button " value =" Undo " onclick =" clickState='3';setState()"> Undo </span>
<div id ="calc" style ="position:relative; left:200px; top:-17px; color:red"> Calculating ... </div>
<div id ="realMin" style =" color: blue;"> real minimum: </div>
<div id ="realMax" style =" color: blue;"> real maimum: </div>
<div id ="imaginaryMin" style ="color: blue;"> imaginary minimum: </div>
<div id ="imaginaryMax" style ="color: blue;"> imaginary maximum: </div>
<script>
var canvas=document.getElementById("myCanvas");
var context=canvas.getContext("2d");
var MAX_MAGNITUDE = 4;
var MAX_ITERATIONS = 1000;
var xmax = 500;
var ymax = 500;
var rgba = context.getImageData(0, 0, xmax, ymax);
var realStart = -3.0;
var imaginaryStart = -3;
var realEnd = 3.0;
var imaginaryEnd = 3.0;
var undoRealMin = realStart;
var undoRealMax = realEnd;
var undoImaginaryMin = imaginaryStart;
var undoImaginaryMax = imaginaryEnd;
var isCalculating = 0; // track state of click action
var isMouseDown = 0;
var clickState = 0;
var startx = 100;
var starty = 100;
var endx = 0;
var endy = 0;
var currentx = 0;
var currenty = 0;
//============= Mouse Functions =============
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
currentx = mousePos.x;
currenty = mousePos.y;
if (isMouseDown == 1)
{
drawRectangle(currentx, currenty);
}
setState();
}, false);
canvas.addEventListener('mousedown', function(evt) {
isCalculating = 1;
setState();
if (clickState != 3)
{
undoRealMin = realStart;
undoRealMax = realEnd;
undoImaginaryMin = imaginaryStart;
undoImaginaryMax = imaginaryEnd;
var mousePos = getMousePos(canvas, evt);
startx = mousePos.x;
starty = mousePos.y;
isMouseDown = 1;
}
else
{
realStart = undoRealMin;
realEnd = undoRealMax;
imaginaryStart = undoImaginaryMin;
imaginaryEnd = undoImaginaryMax;
}
}, false);
canvas.addEventListener('mouseup', function(evt) {
isCalculating = 0;
if (clickState != 3)
{
setState();
var mousePos = getMousePos(canvas, evt);
endx = mousePos.x;
endy = mousePos.y;
if ( (clickState == 1) && ((endx == startx) || (endy == starty)) ) // Reduce
{
var xdelta = xmax * 1.1;
var ydelta = ymax * 1.1;
startx = startx - xdelta;
starty = starty - ydelta;
endx = endx + xdelta;
endy = endy + ydelta;
}
else if ( (clickState == 0) && ((endx == startx) || (endy == starty)) ) // Center
{
var xdelta = xmax / 2;
var ydelta = ymax / 2;
startx = startx - xdelta;
starty = starty - ydelta;
endx = endx + xdelta;
endy = endy + ydelta;
}
else if ( (clickState == 2) && ((endx == startx) || (endy == starty)) ) // Enlarge
{
var xdelta = xmax / 4;
var ydelta = ymax / 4;
startx = startx - xdelta;
starty = starty - ydelta;
endx = endx + xdelta;
endy = endy + ydelta;
}
var x = Math.min(startx, endx);
var y = Math.min(starty, endy);
var real1 = ( (realEnd - realStart) / xmax) * x + realStart;
var imaginary1 = imaginaryEnd - ( (imaginaryEnd - imaginaryStart) / ymax) * y;
x = Math.max(startx, endx);
y = Math.max(starty, endy);
var real2 = ( (realEnd - realStart) / xmax) * x + realStart;
var imaginary2 = imaginaryEnd - ( (imaginaryEnd - imaginaryStart) / ymax) * y;
realStart = real1;
realEnd = real2;
imaginaryStart = imaginary2;
imaginaryEnd = imaginary1;
}
isMouseDown = 0;
drawFractal();
}, false);
//============= Drawing Functions =============
function drawRectangle(x, y)
{
updateCanvas();
context.beginPath();
context.globalCompositeOperation="xor";
context.rect(startx, starty, x - startx, y - starty);
context.strokeStyle="000000";
context.stroke();
context.putImageData(canvas, 0, 0);
}
//============= State Functions ===============
function setState()
{
if (clickState == 0)
{
document.getElementById("myTextReduce").style.backgroundColor="#cccccc";
document.getElementById("myTextCenter").style.backgroundColor="#ff0000";
document.getElementById("myTextEnlarge").style.backgroundColor="#cccccc";
document.getElementById("myTextUndo").style.backgroundColor="#cccccc";
}
else if (clickState == 1)
{
document.getElementById("myTextReduce").style.backgroundColor="#ff0000";
document.getElementById("myTextCenter").style.backgroundColor="#cccccc";
document.getElementById("myTextEnlarge").style.backgroundColor="#cccccc";
document.getElementById("myTextUndo").style.backgroundColor="#cccccc";
}
else if (clickState == 2)
{
document.getElementById("myTextReduce").style.backgroundColor="#cccccc";
document.getElementById("myTextCenter").style.backgroundColor="#cccccc";
document.getElementById("myTextEnlarge").style.backgroundColor="#ff0000";
document.getElementById("myTextUndo").style.backgroundColor="#cccccc";
}
else if (clickState == 3)
{
document.getElementById("myTextReduce").style.backgroundColor="#cccccc";
document.getElementById("myTextCenter").style.backgroundColor="#cccccc";
document.getElementById("myTextEnlarge").style.backgroundColor="#cccccc";
document.getElementById("myTextUndo").style.backgroundColor="#ffff00";
}
var temp = "real minimum: " + realStart;
document.getElementById("realMin").innerHTML = temp;
temp = "real maximum: " + realEnd;
document.getElementById("realMax").innerHTML = temp;
temp = "imaginary minimum: " + imaginaryStart;
document.getElementById("imaginaryMin").innerHTML = temp;
temp = "imaginary maximum: " + imaginaryEnd;
document.getElementById("imaginaryMax").innerHTML = temp;
if (isCalculating == 1)
{
document.getElementById("calc").innerHTML="Calculating ...";
}
else
{
document.getElementById("calc").innerHTML=" ";
}
}
function iterate(c)
{
var constant = c;
var comp = c;
for (loop = 1; loop <= MAX_ITERATIONS; loop++)
{
comp = multiply(comp, comp);
//comp = kick(comp, comp);
comp = add(comp, constant);
if ( magnitude(comp) > MAX_MAGNITUDE )
{
return loop;
}
}
return 0;
}
//============= Fractal Functions =============
function drawPixel (x, y, r, g, b, a)
{
var index = (x + y * xmax) * 4;
rgba.data[index + 0] = r;
rgba.data[index + 1] = g;
rgba.data[index + 2] = b;
rgba.data[index + 3] = a;
}
function updateCanvas() {
context.putImageData(rgba, 0, 0);
}
function drawFractal()
{
var real = realStart;
var imaginary = imaginaryStart;
for (xPix = 0; xPix < xmax; xPix++)
{
updateCanvas();
for (yPix = 1; yPix < ymax; yPix++)
{
var reps = iterate( complex(real, imaginary) );
if ( reps == 0 )
{
drawPixel(xPix, yPix, 0, 0, 0, 255);
}
else
{
drawPixel(xPix, yPix, redColor(reps), greenColor(reps), blueColor(reps), 255);
}
imaginary = imaginaryEnd - ( (imaginaryEnd - imaginaryStart) / ymax) * yPix;
}
real = ( (realEnd - realStart) / xmax) * xPix + realStart;
}
isCalculating = 0;
clickSate = 0;
}
//============= Color Functions =============
function redColor(reps)
{
return (reps * 61) % 256;
}
function greenColor(reps)
{
return (reps * reps) % 256;
}
function blueColor(reps)
{
return (reps + 50) % 256;
}
//============= Complex Functions =============
function magnitude(c)
{
return c.real * c.real + c.imaginary * c.imaginary;
}
function complex(r, i)
{
return {
real: r,
imaginary: i
};
}
function multiply(c1, c2)
{
return {
real: c1.real * c2.real - c1.imaginary * c2.imaginary,
imaginary: c1.real * c2.imaginary + c1.imaginary * c2.real
};
}
function kick(c1, c2)
{
return {
real: Math.sin(c1.real * c2.real) + Math.cos(c1.imaginary * c2.imaginary),
imaginary: c1.real * c2.imaginary + c1.imaginary * c2.real
};
}
function add(c1, c2)
{
return {
real: c1.real + c2.real,
imaginary: c1.imaginary + c2.imaginary
};
}
setState(); // initialize display
drawFractal(); // draw initial fractal
</script>
</body>
</html>
<!-- =======================
<!DOCTYPE html>
<html>
<head>
</head>
<body onload="setState()">
<canvas id="myCanvas" width="500" height="500" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas><br />
<div id="calc" style="position:relative; left:200px; top:-17px; color:red;">Calculating ...</div>
<input type="text" id="vertices" size="2" value="3" onfocus="this.select()" />
<span id="numVertices" style="color: blue;"> = Number Of Vertices</span><br />
<input type="text" id="radius" size="2" value="1" onfocus="this.select()" />
<span id="pointRadius" style="color: blue;"> = PointRadius</span><br />
<input type="text" id="orientation" size="4" value="0" onfocus="this.select()" />
<span id="verticesOrientation" style="color: blue;"> = Vertices Orientation (degrees)</span><br />
<br />
<input type="text" id="ratio" size="4" value="0.5" onfocus="this.select()" />
<span id="plotRatio" style="color: blue;"> = Plot Ratio</span><br />
<input type="text" id="mag" size="4" value="0.9" onfocus="this.select()" />
<span id="magnification" style="color: blue;"> = Magnification</span><br />
<input type="text" id="maxIters" size="6" value="10000" />
<span id="maxIterations" style="color: blue;"> = Max Number of Iterations</span><br />
<br />
<script>
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var xmax = document.getElementById('myCanvas').width;
var ymax = document.getElementById('myCanvas').height;
var rgba = context.getImageData(0, 0, xmax, ymax);
var maxIterations = 10000; // number of points plotted
var magnification = 0.9; // fraction of dim / 2 used for plot
var plotRatio = 0.5; // fraction of dist. from curr. pt. to vertex
// .5, .55, .62, .67, .69, .71, .73, .74
var numberOfVertices = 3;
var verticesOrientation = 0; // Nico Suggestion (radians)
var vertsOrient = -2*Math.PI * verticesOrientation/360; // Nico Suggestion (radians)
var isCalculating = 0; // track state of click action
var isMouseDown = 0;
var clickState = 0;
var startx = 100;
var starty = 100;
var endx = 0;
var endy = 0;
var currentx = 0;
var currenty = 0;
var dim = Math.min(xmax, ymax);
var xUnit = magnification * (dim / 2);
var yUnit = xUnit; // number of pixels for 1 unit of distance
var maxNumberOfVertices = 20;
var color = new Array(maxNumberOfVertices); // color for each vertex
for (rep = 0; rep < maxNumberOfVertices; rep++){
color[rep] = new Array(3);
}
color[0] = [255, 0, 0]; // each color is an array of 3 integers
color[1] = [0, 255, 0]; // in [0, 255] for red, green, and blue
color[2] = [0, 0, 255];
color[3] = [255, 111, 255];
color[4] = [138, 111, 226];
color[5] = [100, 55, 180];
color[6] = [0, 155, 255];
color[7] = [0, 255, 255];
color[8] = [155, 155, 155];
color[9] = [255, 155, 255];
color[10] = [200, 055, 155];
color[11] = [55, 55, 255];
color[12] = [55, 255, 155];
color[13] = [0, 0, 55];
color[14] = [0, 0, 55];
color[15] = [0, 0, 255];
color[16] = [0, 255, 0];
color[17] = [255, 0, 155];
color[18] = [55, 155, 155];
color[19] = [55, 55, 255];
//============= Mouse Functions =============
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
currentx = mousePos.x;
currenty = mousePos.y;
setState();
}, false);
canvas.addEventListener('mousedown', function(evt) {
isCalculating = 1;
setState();
}, false);
canvas.addEventListener('mouseup', function(evt) {
isCalculating = 0;
if (clickState != 1)
{
drawFractal();
}
isMouseDown = 0;
setState();
}, false);
//============= Drawing Functions =============
function drawRectangle(x, y)
{
updateCanvas();
context.beginPath();
context.globalCompositeOperation="xor";
context.rect(startx, starty, x - startx, y - starty);
context.strokeStyle="000000";
context.stroke();
context.putImageData(rgba, 0, 0);
}
function drawPixel (x, y, r, g, b, a)
{
var index = (x + y * xmax) * 4;
rgba.data[index + 0] = r;
rgba.data[index + 1] = g;
rgba.data[index + 2] = b;
rgba.data[index + 3] = a;
}
function dotPlot(xPixelCoord, yPixelCoord, radius, red, green, blue)
{
xPixelCoord = Math.trunc(xPixelCoord);
yPixelCoord = Math.trunc(yPixelCoord);
for (xco = xPixelCoord - radius; xco <= xPixelCoord + radius; xco++) {
for (yco = yPixelCoord - radius; yco <= yPixelCoord + radius; yco++) {
if ( inRange(xco, yco) && inCircle(xPixelCoord, yPixelCoord, radius, xco, yco) ){
drawPixel(xco, yco, red, green, blue, 255);
}
}
}
}
function inRange(x, y)
{
return x >= 0 && y >= 0 && x < xmax && y < ymax;
}
function inCircle(cx, cy, r, x, y)
{
return Math.pow( Math.abs(cx - x), 2 ) + Math.pow( Math.abs(cy - y), 2 ) < r*r;
} // bug had r instead of r*r
function updateCanvas() {
context.putImageData(rgba, 0, 0);
}
//============= State Functions ===============
function setState()
{
function setPrecision(num, digits)
{
return Math.round(num / Math.pow(10, digits - 1)) * Math.pow(10, digits - 1);
}
var temp = "Number of Vertices = " + numberOfVertices;
document.getElementById("vertices").innerHTML = temp;
temp = "Plot Ratio = " + plotRatio;
document.getElementById("ratio").innerHTML = temp;
temp = "Magnification = " + magnification;
document.getElementById("mag").innerHTML = temp;
temp = "Max Iterations = " + maxIterations;
document.getElementById("maxIters").innerHTML = temp;
temp = "Vertices Orientation = " + verticesOrientation;
document.getElementById("orientation").innerHTML = temp;
if (isCalculating == 1)
{
document.getElementById("calc").innerHTML="Calculating ...";
}
else
{
document.getElementById("calc").innerHTML=" ";
}
}
//============= Fractal Functions =============
function convertCoordToXPixel( coord )
{ // convert coord to pixel value for creative displays
// return Math.trunc( xmax / 2 + 1.5*Math.cos(coord*coord)*xUnit*coord );
return Math.trunc( xmax / 2 + xUnit * coord ); // traditional
}
function convertCoordToYPixel( coord )
{ // convert coord to pixel value for creative displays
// return Math.trunc( ymax / 2 - 1.1*Math.sin(1.2*Math.PI*coord) * yUnit * coord );
return Math.trunc( ymax / 2 - yUnit * coord ); // traditional
}
function setVerticies(numberOfVertices, xUnit, yUnit) {
vertex = new Array(numberOfVertices); // array for vertex coords
for (rep = 0; rep < numberOfVertices; rep++){
vertex[rep] = new Array(2); // array of 2 coordinates per vertex
}
var tempx = 0.0; // x = x'cosT - y'sinT
var tempy = 1.0; // y = x'sinT + y'cosT
angle = 2 * Math.PI / numberOfVertices;
theta = angle; // rotate vertex[0] theta to obtain other vertices
vertex[0][0] = tempx * Math.cos(vertsOrient) -
tempy * Math.sin(vertsOrient);
vertex[0][1] = tempx * Math.sin(vertsOrient) +
tempy * Math.cos(vertsOrient);
dotPlot(convertCoordToXPixel(vertex[0][0]), convertCoordToYPixel(vertex[0][1]),
3*radius, color[0][0], color[0][1], color[0][2]);
for (rep = 1; rep < numberOfVertices; rep++){
vertex[rep][0] = vertex[0][0] * Math.cos(theta) -
vertex[0][1] * Math.sin(theta);
vertex[rep][1] = vertex[0][0] * Math.sin(theta) +
vertex[0][1] * Math.cos(theta);
theta += angle;
dotPlot(convertCoordToXPixel(vertex[rep][0]), convertCoordToYPixel(vertex[rep][1]),
3*radius, color[rep][0], color[rep][1], color[rep][2]);
}
currVx = vertex[0][0]; // inital point for start of game
currVy = vertex[0][1];
nextVx = currVx;
nextVy = currVy;
return vertex;
}
function getNextXCoordinate(next, vert, plotRatio)
{ // add to the current coordinate the plotRatio
// of the distance to the chosen vertex
// return next + 0.75*Math.tan((vert - next) * plotRatio );
return next + (vert - next) * plotRatio;
}
function getNextYCoordinate(next, vert, plotRatio)
{ // add to the current coordinate the plotRatio
// of the distance to the chosen vertex
// return next + 0.3*Math.tan(vert - next) * plotRatio;
return next + (vert - next) * plotRatio;
}
function errorCheckCorrect(elem, min, max, defaultValue)
{
var tempVal = document.getElementById(elem).value;
if ( tempVal == "" || isNaN(tempVal) )
{
tempVal = defaultValue;
}
else
{
tempVal = eval(tempVal);
if (tempVal < min || tempVal > max)
{
tempVal = defaultValue;
}
}
document.getElementById(elem).value = tempVal;
return tempVal;
}
function drawFractal()
{
updateCanvas();
numberOfVertices = errorCheckCorrect("vertices", 1, 20, 1);
plotRatio = errorCheckCorrect("ratio", 0.0, 3.0, 0.5);
magnification = errorCheckCorrect("mag", 0.0, 2.0, 0.9);
maxIterations = errorCheckCorrect("maxIters", 1, 100000, 10000);
verticesOrientation = errorCheckCorrect("orientation", -360000, 360000, 0);
vertsOrient = -2*Math.PI * verticesOrientation / 360; // rotate counterclockwise
radius = errorCheckCorrect("radius", 0, 10, 1);
vertex = setVerticies(numberOfVertices, xUnit, yUnit);
for (rep = 0; rep < maxIterations; rep++) {
updateCanvas();
vert = Math.floor( Math.random() * numberOfVertices );
currVx = getNextXCoordinate(currVx, vertex[vert][0], plotRatio);
currVy = getNextYCoordinate(currVy, vertex[vert][1], plotRatio);
pixelX = Math.floor( convertCoordToXPixel(currVx) );
pixelY = Math.floor( convertCoordToYPixel(currVy) );
if (pixelX >= 0 && pixelX < xmax &&
pixelY >= 0 && pixelY < ymax )
{
dotPlot(pixelX, pixelY, radius, color[vert][0], color[vert][1], color[vert][2]);
}
}
isCalculating = 0;
clickSate = 0;
}
</script>
</body>
</html>