2025, Sep 14 07:50
Как согласовать координаты для множества Мандельброта в Python и PIL
Почему фрактал Мандельброта в Python/PIL выглядит разрезанным на зеркальные квадранты и как это исправить: согласование координат, центр, масштаб и смещения.
При рисовании множества Мандельброта на Python с PIL распространённым первым симптомом ошибки отображения координат становится картинка, будто разрезанная на четыре части, где каждый квадрант зеркально отражён или перевёрнут. С формулами всё в порядке, с цветами тоже — проблема в том, что комплексная плоскость и буфер изображения по‑разному понимают, где находится начало координат.
Пример кода
from PIL import Image
from PIL import ImageShow
from PIL import ImageColor as ColorMap
palette = ["navy","darkblue","blue","cornflowerblue","lightsteelblue","lightskyblue","turquoise","palegreen","lawngreen"]
def iterate_point(c, scale):
z = 0.0
iter_count = 0
iter_cap = 5*scale
while z.real == z.real and iter_count < iter_cap:
z = (z*z) + c
iter_count = iter_count + 1
if z.real == z.real:
return -1
else:
return iter_count
def run_app():
scale = int(input("Set Zoom Level. (Default: 10) ") or "10")*10
cx = int(input("Input centre x value. (Default: -50) ") or "-50")
cy = int(input("Input centre y value. (Default: 0) ") or "0")
w2 = canvas.width // 2
h2 = canvas.height // 2
for py in range((cy - h2),(cy + h2)):
for px in range((cx - w2),(cx + w2)):
c = complex((px/scale),(py/scale))
steps = iterate_point(c,scale)
if steps == -1:
rgb = (0,0,0)
else:
rgb = ColorMap.getrgb(palette[steps % len(palette)])
canvas.putpixel(((px + w2 - cx) % canvas.width,
(py + h2 - cy) % canvas.height),
rgb)
print("line",(py+h2),"done")
ImageShow.show(canvas)
run_app()
canvas = Image.new("RGB",(1000,500),"Black")
run_app()
Суть проблемы
Функция, определяющая цвет для каждой точки на комплексной плоскости, работает с множеством Мандельброта, центрированным в complex(0, 0). Так обычно и задаётся математика: начало координат — в центре комплексной плоскости, X — это действительная ось, а мнимая идёт вертикально. Буфер изображения, напротив, считает началом координат верхний левый пиксель. Если напрямую переводить комплексные координаты в индексы пикселей, не согласовав эти две системы, получится картинка как будто разрезанная и зеркальная — каждый квадрант окажется в «чужой» части холста.
Есть и второй слой: вы можете смещать область просмотра с помощью пользовательского ввода центра. Это смещение нужно применять последовательно и при обходе комплексной плоскости, и при записи пикселей в изображение, иначе появятся сдвиги или «заворот» по краям.
Решение
Нужно совместить математический центр с визуальным. Перенесите начало координат пиксельного пространства из верхнего левого угла в середину изображения, используя половины ширины и высоты. Затем учтите заданное пользователем смещение центра как при итерации, так и при записи на холст. На практике это значит: проходить диапазоны, центрированные на выбранных смещениях, переводить переменные цикла в комплексные координаты с учётом текущего масштаба и ставить пиксели по позициям, сдвинутым на половину размеров изображения с вычитанием того же смещения. Приведённый выше код делает именно это: вычисляет полу-ширину и полу-высоту по фактическому размеру изображения, получает комплексные координаты через scale и размещает пиксели по скорректированным индексам.
Попутно две прагматичные правки делают поведение программы предсказуемее. Поля ввода действительно уважают значения по умолчанию: берётся то, что ввёл пользователь, или строковый дефолт, который безопасно приводится к int. Размер изображения больше не зашит в логику, поскольку половинные размеры берутся с холста — менять разрешение можно где угодно, не ломая координатную математику.
Есть и небольшое усиление надёжности внутри итерации. Вместо разбора строк для поиска некорректных значений код опирается на простое свойство: NaN не равен сам себе. Проверка z.real == z.real — компактный способ продолжать цикл, пока значение нормальное, и остановиться, когда это уже не так, избегая лишних преобразований строк.
Почему это важно
Любой алгоритм, переводящий математическую область в пиксели, держится на корректном преобразовании координат. Множество Мандельброта особенно чувствительно: симметрия мгновенно выдаёт ошибки — сдвинутое или отражённое начало координат сразу «ломает» знакомые кардиоиду и «пузыри». Точный центр, последовательное применение смещений и выбор окон итерации по реальному размеру изображения — малые шаги, которые предотвращают артефакты и упрощают дальнейшую работу, будь то навигация или настройка палитры.
На комплексной плоскости ось X — это действительные числа, а вертикальная ось — мнимые. Держась этой модели, проще не запутаться в отображении при панорамировании и масштабировании.
Итоги
Если картинка множества Мандельброта выглядит как зеркальные квадранты, согласуйте системы координат. Совместите начало комплексной плоскости с центром изображения, последовательно вычитайте заданные пользователем смещения и вычисляйте границы из размеров изображения, а не жёстко прописывайте их. Сделайте значения по умолчанию действительно значениями по умолчанию и предпочитайте прямые числовые проверки на NaN. С этими элементами фрактал проявится как задумано, а у вас появится чистая база для дальнейших улучшений. Вдобавок, если вертикальное смещение не нужно, вертикальную симметрию множества можно использовать, чтобы сократить работу, просчитывая лишь половину строк. Ключевое — правильно настроить отображение; дальше всё становится значительно проще.