Мандельброт установил искажение увеличения в C - PullRequest
1 голос
/ 21 марта 2020

Я пишу C программу для рендеринга набора Мандельброта, и в настоящее время я пытаюсь выяснить, как правильно увеличить масштаб.

Я хочу, чтобы масштабирование было в состоянии следовать за указателем мыши на экране - так, чтобы фрактал увеличивался в положение курсора.

У меня есть окно, определяемое как:

# 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;

Чтобы отобразить координаты пикселя в центре экрана, I Я использую функцию позиционирования:

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));
}

Итак, проблема в следующем:

Мой код отображает фрактал следующим образом - Мандельброт установлен

Но когда я пытаюсь увеличить, как описано с помощью мыши, вместо этого из-за того, что вы идете хорошо «внутрь», он просто искажает, как этот , изображение просто как бы сваливается на себя вместо того, чтобы фактически погрузиться во фрактал.

Я был бы очень признателен за помощь с этим, так как Я застрял на нем некоторое время.

Если бы вы могли также объяснить реальную математику ваших решений, я был бы очень рад!

Спасибо!

Ответы [ 2 ]

2 голосов
/ 24 марта 2020

После небольшой головной боли и большого количества бумаги, потраченной на методы интерполяции с пересчетом, я понял, что способ отображения комплексных чисел на экране был неправильным, с самого начала. Переработка моего метода картирования решила мою проблему, поэтому я поделюсь тем, что я сделал.

-------------------------- ----- OLD WAY ------------------------------------- -

Я инициализировал свои значения 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 равна обратная (по крайней мере, в моей программе) - отрицательное направление вверх , положительное вниз .

Таким образом, когда я пытался увеличить масштаб с помощью своей интерполяции, естественно изображение искажено.

- ---------------------------- ПРАВИЛЬНЫЙ ПУТЬ -------------- ---------------------

При определении ограничивающего прямоугольника набора необходимо вычислить максимальное мнимое im_max) часть на основе соотношения сторон экрана, чтобы избежать искажения изображения, когда окно дисплея не является квадратом:

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

Чтобы отобразить экранные координаты на комплексные числа, я сначала обнаружил -number * ratio, которое равно *rectangle length / screen width*:

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

Затем я сопоставил свои пиксельные координаты с real и мнимой частью комплексное число, используемое в вычислениях, например, так:

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

После внесения этих изменений я наконец смог плавно увеличить положение мыши без каких-либо искажений или скачков изображения.

1 голос
/ 23 марта 2020

Если я вас правильно понимаю, вы хотите, чтобы точка, где указатель мыши находился, был новым центром изображения, и изменили масштаб изображения с коэффициентом 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;
}
...