Mandelbrot 在 C 中设置缩放失真

Mandelbrot Set Zoom Distortion in C

我正在编写一个 C 程序来渲染 Mandelbrot 集,目前,我一直在尝试弄清楚如何正确放大。

我希望缩放能够跟随屏幕上的鼠标指针 - 以便分形放大到光标位置。

我有一个 window 定义为:

# define WIDTH 800
# define HEIGHT 600

我的Re_max, Re_min, Im_Max, Im_Min定义和初始化如下:

man->re_max = 2.0;
man->re_min = -2.0;
man->im_max = 2.0;
man->im_min = -2.0;

插值值(稍后详细介绍)定义和初始化如下:

pos->interp = 1.0;

为了将像素坐标映射到屏幕中心,我使用了位置函数:

void        position(int x, int y, t_mandel *man)
{
    double  *s_x;
    double  *s_y;

    s_x = &man->pos->shift_x;
    s_y = &man->pos->shift_y;
    man->c_re = (x / (WIDTH / (man->re_max - man->re_min)) + man->re_min) + *s_x;
    man->c_im =(y / (HEIGHT / (man->im_max - man->re_min)) + man->im_min) + *s_y;
    man->c_im *= 0.8;
}

为了放大,我首先获取鼠标指针的坐标并使用此函数将它们映射到由(Re_Max, Re_Min, Im_Max, Im_Min)定义的矩形给出的可见区域,其中x和y是坐标屏幕上的指针:

int                     mouse_move(int x, int y, void *p)
{
    t_fract         *fract;
    t_mandel        *man;

    fract = (t_fract *)p;
    man = fract->mandel;
    fract->mouse->Re = x / (WIDTH / (man->re_max - man->re_min)) + man->re_min;
    fract->mouse->Im = y / (HEIGHT / (man->im_max - man->re_min)) + man->im_min;
    return (0);
}

注册鼠标滚轮时调用此函数。实际缩放是通过这个函数实现的:

void        zoom_control(int key, t_fract *fract)
{
    double      *interp;

    interp = &fract->mandel->pos->interp;
    if (key == 5)    // zoom in
    {
        *interp = 1.0 / 1.03;
        apply_zoom(fract->mandel, fract->mouse->Re, fract->mouse->Im, *interp);
    }
    else if (key == 4)    // zoom out
    {
        *interp = 1.0 * 1.03;
        apply_zoom(fract->mandel, fract->mouse->Re, fract->mouse->Im, *interp);
    }
}

这叫做:

void        apply_zoom(t_mandel *man, double m_re, double m_im, double interp)
{
    man->re_min = interpolate(m_re, man->re_min, interp);
    man->im_min = interpolate(m_im, man->im_min, interp);
    man->re_max = interpolate(m_re, man->re_max, interp);
    man->im_max = interpolate(m_im, man->im_max, interp);
}

我有一个简单的插值函数来重新定义区域边界矩形:

double      interpolate(double start, double end, double interp)
{
    return (start + ((end - start) * interp));
}

所以问题是:

我的代码像这样呈现分形 - Mandelbrot set

但是当我尝试按照鼠标的描述进行放大时,效果并不好 "in",它只是像 this 一样扭曲进入分形。

我真的很感激这方面的帮助,因为我已经坚持了一段时间了。

如果您也能解释一下您的解决方案背后的实际数学原理,我将非常高兴!

谢谢!

如果我没理解错的话,您想将鼠标所在的点作为图像的新中心,并将图像的比例更改为 1.03 倍。我会尝试这样的事情:

  • 您的 position() 和 mouse_move() 函数保持不变。

  • 在zoom_control()中只是改变了插值新值的设置方式,它不应该是一个固定的常数,而应该是基于它的当前值。此外,将新的比例因子传递给 apply_zoom():

void zoom_control(int key, t_fract *fract)
{
    double *interp;
    interp = &fract->mandel->pos->interp;
    double zoom_factor = 1.03;

    if (key == 5)    // zoom in
    {
        *interp /=  zoom_factor;
        apply_zoom(fract->mandel, fract->mouse->Re, fract->mouse->Im, 1.0 / zoom_factor);
    }
    else if (key == 4)    // zoom out
    {
        *interp *= zoom_factor;
        apply_zoom(fract->mandel, fract->mouse->Re, fract->mouse->Im, zoom_factor);
    }
}
  • 修改应用缩放功能:
void        apply_zoom(t_mandel *man, double m_re, double m_im, double zoom_factor)
{
    // Calculate the new ranges along the real and imaginary axes.
    // They are equal to the current ranges multiplied by the zoom_factor.
    double re_range = (man->re_max - man->re_min) * zoom_factor;
    double im_range = (man->im_max - man->im_min) * zoom_factor;

    // Set the new min/max values for real and imaginary axes with the center at 
    // mouse coordinates m_re and m_im.
    man->re_min = m_re - re_range / 2;
    man->re_max = m_re + re_range / 2;
    man->im_min = m_im - im_range / 2;
    man->im_max = m_im + im_range / 2;
}

在经历了相当大的头痛并在重新计算插值方法上浪费了大量论文之后,我意识到我在屏幕上映射复数的方式从一开始就是不正确的。修改我的映射方法解决了我的问题,所以我将分享我所做的。

---------------------------------老办法-- ----------------------------------

我已经初始化了 Re_max, Re_min, Im_Max, Im_Min 值,这些值按以下方式定义可见区域:

re_max = 2.0;
re_min = -2.0;
im_max = 2.0;
im_min = -2.0;

然后,我使用这种方法将我的屏幕坐标转换为用于计算分形的复数(请注意,用于映射鼠标位置以进行缩放插值的坐标和用于计算分形本身的坐标使用同样的方法):

Re = x / (WIDTH / (re_max - re_min)) + re_min;
Im = y / (HEIGHT / (im_max - re_min)) + im_min;

但是,这样我就没有考虑屏占比,忽略了一个事实(由于缺乏知识)y 屏幕上的坐标是 inverse(至少在我的程序中是这样)——负方向是 up,正方向是 向下.

这样,当我尝试使用插值放大时,图像自然会失真。

-----------------------------正确的方法--- ------------------------------

定义集合的外接矩形时,根据屏幕比例计算最大虚数im_max)部分,避免显示时图像失真window 不是正方形:

re_max = 2.0;
re_min = -2.0;
im_min = -2.0;
im_max = im_min + (re_max - re_min) * HEIGHT / WIDTH;

为了将屏幕坐标映射到复数,我首先找到了“坐标与数字*比率”,它等于 *rectangle length / screen width*:

re_factor = (re_max - re_min) / (WIDTH - 1);
im_factor = (im_max - im_min) / (HEIGHT - 1);

然后,我将我的像素坐标映射到计算中使用的复数的 实数虚数 部分,如下所示:

c_re = re_min + x * re_factor;
c_im = im_max - y * im_factor;

实施这些更改后,我终于能够平滑地放大到鼠标位置而没有任何失真或图像 "jumps"。