Recreating the way I color my Mandelbrot set I'm having a hard time implementing it in JavaScript. I currently use the common "escape time" algorithm:
for(px = 0; px < a; px+=scale){
for(py = 0; py < b; py+=scale){
x0 = panX + px/zm;
y0 = panY + py/zm;
var x = 0;
var y = 0;
var i = 0;
var xtemp;
var xSquare = x*x;
var ySquare = y*y;
while (x*x + y*y <= 4 && i < maxI) {
xtemp = x*x - y*y + x0
y = 2*x*y + y0
x = xtemp
i += 1;
}
//coloring
var shade = pallete.colourAt(i);
c.fillStyle = "#"+shade;
c.fillRect(px,py,scale, scale);
}
}
Here's the full code. I want to implement the part above to this pseudo code found at Wikipedia.
For each pixel (Px, Py) on the screen, do: { x0 = scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1)) y0 = scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1, 1)) x = 0.0 y = 0.0 iteration = 0 max_iteration = 1000 // Here N=2^8 is chosen as a reasonable bailout radius. while ( xx + yy <= (1 << 16) AND iteration < max_iteration ) { xtemp = xx - yy + x0 y = 2*xy + y0 x = xtemp iteration = iteration + 1 } // Used to avoid floating point issues with points inside the set. if ( iteration < max_iteration ) { // sqrt of inner term removed using log simplification rules. log_zn = log( xx + y*y ) / 2 nu = log( log_zn / log(2) ) / log(2) // Rearranging the potential function. // Dividing log_zn by log(2) instead of log(N = 1<<8) // because we want the entire palette to range from the // center to radius 2, NOT our bailout radius. iteration = iteration + 1 - nu } color1 = palette[floor(iteration)] color2 = palette[floor(iteration) + 1] // iteration % 1 = fractional part of iteration. color = linear_interpolate(color1, color2, iteration % 1) plot(Px, Py, color) }
To this:
for(px = 0; px < a; px+=scale){
for(py = 0; py < b; py+=scale){
//zoom factors
x0 = panX + px/zm;
y0 = panY + py/zm;
var x = 0;
var y = 0;
var i = 0;
var xtemp;
var xSquare = x*x;
var ySquare = y*y;
while (x*x + y*y <= 4 && i < maxI) {
/*ticks++
xtemp = x*x - y*y + x0
y = 2*x*y + y0
x = xtemp
i = i + 1*/
y = x*y;
y += y;
y += y0;
x = xSquare - ySquare + x0;
xSquare = Math.pow(x,2);
ySquare = Math.pow(y,2);
i += 1;
}
if ( i < maxI ) {
log_zn = Math.log( x*x + y*y ) / 2
nu = Math.log( log_zn / Math.log(2) ) / Math.log(2)
i += 1 - nu
}
color1 = palette.colourAt(Math.floor(i))
color2 = palette.colourAt(Math.floor(i) + 1)
/*****************
I dont know how to implement this.....
color = linear_interpolate(color1, color2, iteration % 1)
*****************/
c.fillStyle = color
c.fillRect(px,py,scale, scale);
}
}
But I don't know how to implement this part of pseudo-code:
color1 = palette[floor(iteration)]
color2 = palette[floor(iteration) + 1]
// iteration % 1 = fractional part of iteration.
color = linear_interpolate(color1, color2, iteration % 1)
plot(Px, Py, color)
Can someone help me understand and give a way to implement this?
The linear_interpolate function is supposed to calculate a color between two colors, based on the linear function y = mx + b. To apply the linear function to colors, y is the output color, m is the difference between the two colors, b is the start color and x is a value between 0 and 1. When x is 0, this function outputs the start color. When x is 1, this function outputs the end color.
To do this calculation we need the color in the form of three numbers. If you need to use hex strings, you'll have to split them and parse each two characters as a 16 bit number. I'm going to use a palette that is already in number form, because it is easier.
Here's my three color palette. I'm not recommending that you use these colors, it's just for demonstration:
let palette = [{r:255,g:0,b:0},{r:0,g:255,b:0},{r:0,g:0,b:0}]
This first function takes in iteration, which is probably not a whole number and may be larger than 1. It takes the floor of iteration, turning it into a whole number which an array index must be. Then it takes the remainder of iteration divided by 1 to get a number between 0 and 1.
function interpolation(iteration) {
let color1 = palette[Math.floor(iteration)];
let color2 = palette[Math.floor(iteration) + 1];
return linear_interpolate(color1, color2, iteration % 1);
}
Now we need to create the linear interpolation function, which must apply the linear function to each color channel and use floor to turn them into a whole number. I have it returning a css color in rgb(), but you could convert it into hex instead.
function linear_interpolate(color1, color2, ratio) {
let r = Math.floor((color2.r - color1.r) * ratio + color1.r);
let g = Math.floor((color2.g - color1.g) * ratio + color1.g);
let b = Math.floor((color2.b - color1.b) * ratio + color1.b);
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
Here is the code shading rectangles: https://jsfiddle.net/q7kLszud/