Search code examples
cmathfractals

How do I zoom on cursor position in mandelbrot or julia set?


I am writing a fractal explorer at the moment and I got stuck at zooming on a certain point in the set. My drawing function for the Julia set for example looks like this:

void *julia_thread(void *param)
{
    int x, y, temp;
    long double re, aux, im;
    int start = ((int *)param)[0];
    int end = ((int *)param)[1];
    int iterations;


    for (x = start; x < end; x++)
        for (y = 0; y < WIN_SIZE; y++)
        {
            re = range_change(zoom_factor, x, mv_x);
            im = range_change(zoom_factor, y, mv_y);
            iterations = 0;
            while (!blowing_up(re, im) && iterations < max_iter)
            {
                aux = re;
                re = re * re - im * im + re_c;
                im = 2 * aux * im + im_c;
                iterations++;
            }
            put_pixel(img, x, y, color_table[iterations]);
        }
    return NULL;
}

The function that calculates the initial values for the real and imaginary part of Z is this:

long double range_change(long double zoom_factor, int value, long double mv)
{
    long double newmax = 2 / zoom_factor;
    long double newmin = -2 / zoom_factor;

    return ((long double)value * (newmax - newmin)) / WIN_SIZE + newmin + mv;
}

So I get a scaled down value that is part of the interval where the fractal exists and according to the number of iterations I assign a colour to that certain pixel. Zooming works fine by making the real interval (-2, 2) smaller by dividing both of the ends with a factor. This works but I can't seem to be figure out how to zoom on a certain spot other than the centre. I can move around and reach that spot eventually by adding to the real and imaginary part (x, y) a number but I can't zoom on a point determined by the screen (x, y) given to me by the cursor position.


Solution

  • It is a very bad idea to do indiscriminate integer divisions:

    2 / zoom_factor
    

    will return 0 if zoom_factor is larger than 2. Replace 2 by 2.0 to force floating point division, this should be sufficient to repair the code.


    If I interpret this correctly, you want that the screen window represents a square in the coordinate or fractal plane with the width and height 4.0/zoom_factor around the point (mv_x, mv_y).

    mv is situated at WIN_SIZE/2, so that

    coord = mv + ( 4*value/WIN_SIZE - 2 )/zoom_factor
    

    which can be implemented exactly as this

    return mv + ( (4.0*value)/WIN_SIZE - 2.0 )/zoom_factor;
    

    and with the factor 4.0 the denominator gets type double and division is carried out in double.


    Slow derivation

    What the function range_change wants to achieve is a linear change of coordinates

    coord = A*screen + B
    

    where screen is the input screen coordinate and coord is the coordinate in the Cartesian plane playing host for the Julia fractal. The endpoint mapping is

    screen=0         --> coord = center - 2.0/zoom
    screen=WIN_SIZE  --> coord = center + 2.0/zoom
    

    From the first we read B=center - 2.0/zoom and from the second formula

    A*WIN_SIZE + center - 2.0/zoom = center + 2.0/zoom
    A*WIN_SIZE                     =          4.0/zoom
    A = 4.0/(zoom*WIN_SIZE)
    

    which gives the transformation formula

    coord = (4.0*value)/(zoom*WIN_SIZE) + center - 2.0/zoom
          = ( (4.0*value)/WIN_SIZE - 2.0 )/zoom + center