Espectro suave para la representación del conjunto de Mandelbrot

Actualmente estoy escribiendo un progtwig para generar imágenes de Mandelbrot realmente enormes (65536×65536 píxeles o más), y me gustaría diseñar un esquema de espectro y coloreado que les haga justicia. La imagen de mandelbrot presentada en wikipedia parece un ejemplo excelente, especialmente cómo la paleta sigue siendo variada en todos los niveles de zoom de la secuencia. Sin embargo, no estoy seguro de si está girando la paleta o haciendo algún otro truco para lograr esto.

Estoy familiarizado con el algoritmo de coloración uniforme para el conjunto de mandelbrot, por lo que puedo evitar las bandas, pero aún necesito una forma de asignar colores a los valores de salida de este algoritmo.

Las imágenes que estoy generando son piramidales (por ejemplo, una serie de imágenes, cada una de las cuales tiene la mitad de las dimensiones que la anterior), así que puedo usar una paleta giratoria de algún tipo, siempre que el cambio en la paleta entre las siguientes los niveles de zoom no son demasiado obvios.

Este es el algoritmo de color suave:

Digamos que comienzas con el número complejo z0 e iteras n veces hasta que escapa. Deje que el punto final sea zn .

Un valor sin problemas sería

 nsmooth := n + 1 - Math.log(Math.log(zn.abs()))/Math.log(2) 

Esto solo funciona para mandelbrot, si quieres calcular una función suave para julia sets, usa

 Complex z = new Complex(x,y); double smoothcolor = Math.exp(-z.abs()); for(i=0;i 

Entonces smoothcolor está en el intervalo (0,max_iter) .

Divida smoothcolor con max_iter para obtener un valor entre 0 y 1.

Para obtener un color suave a partir del valor:

Esto se puede llamar, por ejemplo (en Java):

 Color.HSBtoRGB(0.95f + 10 * smoothcolor ,0.6f,1.0f); 

ya que el primer valor en los parámetros de color HSB se usa para definir el color del círculo de color.

Utilice el algoritmo de coloreo uniforme para calcular todos los valores dentro de la ventana gráfica, luego asigne su paleta del valor más bajo al más alto. Por lo tanto, al acercarse y los valores más altos ya no son visibles, la paleta también se reducirá. Con las mismas constantes para ny B, terminará con un rango de 0.0 a 1.0 para un conjunto completamente alejado, pero a un zoom más profundo el rango dynamic se encogerá, para decir 0.0 a 0.1 con un zoom de 200%, 0.0 a 0.0001 en 20000% de zoom, etc.

Aquí hay un bucle interno típico para un ingenuo generador de Mandelbrot. Para obtener un color uniforme, desea pasar las “longitudes” reales y complejas y la iteración en la que rescató. He incluido el código de Mandelbrot para que pueda ver qué vars usar para calcular el color.

 for (ix = 0; ix < panelMain.Width; ix++) { cx = cxMin + (double )ix * pixelWidth; // init this go zx = 0.0; zy = 0.0; zx2 = 0.0; zy2 = 0.0; for (i = 0; i < iterationMax && ((zx2 + zy2) < er2); i++) { zy = zx * zy * 2.0 + cy; zx = zx2 - zy2 + cx; zx2 = zx * zx; zy2 = zy * zy; } if (i == iterationMax) { // interior, part of set, black // set colour to black g.FillRectangle(sbBlack, ix, iy, 1, 1); } else { // outside, set colour proportional to time/distance it took to converge // set colour not black SolidBrush sbNeato = new SolidBrush(MapColor(i, zx2, zy2)); g.FillRectangle(sbNeato, ix, iy, 1, 1); } 

y MapColor a continuación: (vea este enlace para obtener la función ColorFromHSV )

  private Color MapColor(int i, double r, double c) { double di=(double )i; double zn; double hue; zn = Math.Sqrt(r + c); hue = di + 1.0 - Math.Log(Math.Log(Math.Abs(zn))) / Math.Log(2.0); // 2 is escape radius hue = 0.95 + 20.0 * hue; // adjust to make it prettier // the hsv function expects values from 0 to 360 while (hue > 360.0) hue -= 360.0; while (hue < 0.0) hue += 360.0; return ColorFromHSV(hue, 0.8, 1.0); } 

MapColour está "suavizando" los valores de rescate de 0 a 1, que luego se pueden usar para mapear un color sin bandas horribles. Jugar con MapColour y / o la función hsv le permite modificar qué colores se utilizan.

Parece simple de hacer por ensayo y error. Supongamos que puede definir HSV1 y HSV2 (matiz, saturación, valor) de los colores del punto final que desea usar (blanco y negro, azul y amarillo, rojo oscuro y verde claro, etc.), y suponga que tiene un algoritmo para asignar un valor P entre 0.0 y 1.0 para cada uno de sus píxeles. Entonces el color de ese pixel se convierte

 (H2 - H1) * P + H1 = HP (S2 - S1) * P + S1 = SP (V2 - V1) * P + V1 = VP 

Una vez hecho esto, solo observe los resultados y vea cómo le agradan. Si el algoritmo para asignar P es continuo, entonces el gradiente debería ser uniforme también.

Mi solución final fue crear una paleta de aspecto agradable (y bastante grande) y almacenarla como una matriz constante en la fuente, luego interpolar entre los índices utilizando el algoritmo de coloreo uniforme. La paleta se ajusta (y está diseñada para ser continua), pero esto no parece importar mucho.

aquí puedes encontrar una versión con javascript

uso:

 var rgbcol = [] ; var rgbcol = MapColor ( Iteration , Zy2,Zx2 ) ; point ( ctx , iX, iY ,rgbcol[0],rgbcol[1],rgbcol[2] ); 

función

 /* * The Mandelbrot Set, in HTML5 canvas and javascript. * https://github.com/cslarsen/mandelbrot-js * * Copyright (C) 2012 Christian Stigen Larsen */ /* * Convert hue-saturation-value/luminosity to RGB. * * Input ranges: * H = [0, 360] (integer degrees) * S = [0.0, 1.0] (float) * V = [0.0, 1.0] (float) */ function hsv_to_rgb(h, s, v) { if ( v > 1.0 ) v = 1.0; var hp = h/60.0; var c = v * s; var x = c*(1 - Math.abs((hp % 2) - 1)); var rgb = [0,0,0]; if ( 0<=hp && hp<1 ) rgb = [c, x, 0]; if ( 1<=hp && hp<2 ) rgb = [x, c, 0]; if ( 2<=hp && hp<3 ) rgb = [0, c, x]; if ( 3<=hp && hp<4 ) rgb = [0, x, c]; if ( 4<=hp && hp<5 ) rgb = [x, 0, c]; if ( 5<=hp && hp<6 ) rgb = [c, 0, x]; var m = v - c; rgb[0] += m; rgb[1] += m; rgb[2] += m; rgb[0] *= 255; rgb[1] *= 255; rgb[2] *= 255; rgb[0] = parseInt ( rgb[0] ); rgb[1] = parseInt ( rgb[1] ); rgb[2] = parseInt ( rgb[2] ); return rgb; } // http://stackoverflow.com/questions/369438/smooth-spectrum-for-mandelbrot-set-rendering // alex russel : http://stackoverflow.com/users/2146829/alex-russell function MapColor(i,r,c) { var di= i; var zn; var hue; zn = Math.sqrt(r + c); hue = di + 1.0 - Math.log(Math.log(Math.abs(zn))) / Math.log(2.0); // 2 is escape radius hue = 0.95 + 20.0 * hue; // adjust to make it prettier // the hsv function expects values from 0 to 360 while (hue > 360.0) hue -= 360.0; while (hue < 0.0) hue += 360.0; return hsv_to_rgb(hue, 0.8, 1.0); }