Comments 85
Немного не в тему. Но могу добавить восторженных впечатлений от журнала "В мире науке". Фантастически интересный журнал 80-х годов. Когда-то прочитала там про redcode. Настолько зацепило, что уже позже в 90-х написала собственный redcode c интерпретатором и средой выполнения. Дело было ещё на PC XT под DOS на С. Жаль, что с тех пор не осталось ни журналов, ни дискет. Всё как-то и куда-то складывалось, а потом перестало быть в неизвестном направлении.
А я помню статьи про бои в памяти! Целый цикл был, но для меня он оказался сложноват.
Журналы можно найти в электронном виде: https://flibusta.site/sequence/30589
Вот тут еще масса идей и картинок
Я еще в универе случайно открыл похожую формулу в виде
x := Frac(x+sin(y));
y := Frac(y+cos(x));
и все эти годы не мог найти упоминаний о ней и объяснения получающихся картинок. Спасибо за статью!
Frac - что за функция? Возвращает дробную часть?
да. Ну и координаты соответственно получаются от 0 до 1, для рендера надо умножить на размер окна.
А x и y в каком диапазоне должны быть?
Со второй итерации они попадут в диапазон [0;1]
А есть примеры картинок, полученных именно по Вашей формуле?
У меня так получилось.
Прикольная абстракция! Здесь цвет отображает плотность аттрактора при 100 млн итерациях. Интересно, что от начальной точки итерации изображение не зависит -- оно является инвариантом указанного преобразования.
Непонятно, по какой формуле цвет из плотности рассчитывается? В ч/б варианте (каждая точка просто черным цветом рисуется) у меня совершенно другая картинка получилась (см. ниже).
Можете полный псевдокод процедуры дать?
как я написал ниже, изначально я сделал с "ошибочной" формулой, и как раз с ней получаются резонансы похожие на картинки из статьи.
Попробовал сделать несколько картинок по этой формуле. Начальные значения: x = 0.5, y = 0.3. При изменении количества итераций (в тысячах) 50 => 100 => 200 => 500 => 1000 картинка меняется так:
Видно, что поле всё больше заполняется точками, но остаются пустые "дырки". Интересно, это следствие конечной точности вычислений? Я все вычисления делал в double (для получения координат, понятно, после умножения x на ширину и y на высоту приводил значения к int).
У нас разница в том, что в моём случае новые значения x
и y
вычисляются одновременно, а в вашем — последовательно. Мой алгоритм будет выглядеть так:
z = x
x = frac(z - sin(y))
y = frac(y - cos(z))
При этом старое значение x
сохраняется после вычисления строки 2.
А если просто вычислять
x = frac(z - sin(y))
y = frac(y - cos(z))
То после выполнения строки 1 значение x
уже изменится и это изменение пойдёт в строку 2
Я уже понял свою ошибку. В случае с формулой Мартина, если не сохранять x в промежуточную переменную, x и y очень быстро расходятся в бесконечность.
На мой взгляд, важное замечание: тому, кто решит реализовать подобный код сам, особенно на других языках программирования, стоит иметь ввиду, что здесь для рисования точек используется не просто setPixel(x, y, color). Я посмотрел Ваш код скрипта, и заметил следующие моменты:
ctx.globalAlpha = 0.1
Я не силен в JS - здесь устанавливается глобальная константа смешивания цветов точек при рисовании?
Ну и сама точка рисуется как прямоугольник:
ctx.fillRect(W/2+x*W/S,W/2+y*W/S,0.5,0.5)
Правильно ли я понимаю, что это прямоугольник с высотой и шириной 0.5 пиксела? Или с такой толщиной линии? Поясните, пожалуйста.
Я это к тому, что если такое делать на C++, как пытаюсь делать я, то нужно предпринимать какие-то ухищрения, чтобы так же рисовать. Например, формировать массив точек в памяти и там же смешивать цвета точек, если они попадают в одни и те же координаты.
Совершенно верно, на C++ надо делать тот или иной вид субпиксельного сглаживания. Именно такое смешивание, как вы описали, я и сделал для картинки выше: подсчитывал число попаданий точек в ячейку массива и отображал таким образом плотность.
в общем вот на паскале код:
RandSeed := 1;
for i := 1 to 100 do
begin
x := random;
y := random;
color := Random(MaxLongint);
for j := 1 to 1000 do
begin
pr2d_Pixel(x*1000,y*1000,color);
x := Frac(x+cos(y));
y := Frac(y+sin(x));
end;
end;
т.е. если стартовая точка попадает за пределы этих циклов - она не попадет внутрь них а заполняет фон. А если попадает внутрь - образует серию кругов (если посмотреть подробнее, видно что она прыгает из одного круга в другой).
У вас как и выше в треде x,y последовательно друг друга портят... Надо z=x;x=...,y=...(z);
нене, надо как раз как у меня, ведь эту ветку начал я. Ну т.е. изначально это было опечаткой но с ней получаются эти необычные круги. Дальше если добавить в формулу коэффициенты эти круги смещаются и могут образовывать другие структуры.
вот наугад добавил коэффициенты -
x := Frac(x+cos(1.5*y)-0.2);
y := Frac(y+sin(x));
Интересно, надо будет попробовать.
Попробовал Ваш код, только увеличил число шагов во внешнем цикле до 500, а также цвет беру не совсем случайный, а случайный из фиксированного набора в несколько цветов. Также, при добавлении пиксела в точку смешиваю цвет с тем, что там уже есть, с весом 0.5.
ИМХО, довольно красиво получается:
Попробовал вариант и с дополнительными коэффициентами. В формуле Frac(x+cos(1.5*y)-0.2)
вместо 1.5 генерирую случайное число в диапазоне от 0 до 1 и прибавляю 1, вместо 0.2 - еще одно случайное число от 0 до 1. Иногда интересные результаты получаются, например:
Надо еще какие-нибудь функции попробовать вместо sin/cos. В общем, бездна для творчества :-)
из функций я еще пробовал в свое время заменить синус и косинус на первые члены ряда тейлора (sin на x, cos на 1-x*x/2) и на (tan, 1-tan). Ну и да, коэффициенты. Моей мечтой было как-то сделать этот мир интерактивным, чтоб можно было "исследовать" отдельные области (как с фракталами), но каждая картинка на тогдашнем компе рисовалась секунды, так что далеко от этих картинок не ушел.
Из этой статьи я примерно понял хотя бы идею - должен быть поворот на 90 градусов+нелинейное искажение.
Анализируя вашу схему я бы тоже сначала рассмотрел первые члены рядов для синуса и косинуса.
Поворот не обязателен. В вашем случае роль складывания после искажения играет вычисление дробной части, то есть отображение всей плоскости на квадрат со стороной 1. Именно сочетание искажения и складывания обеспечивает перемешивание. А будут при этом неподвижные точки простых типов или странные аттракторы, конечно, зависит от конкретного преобразования.
Что-то мне подсказывает, что здесь периодичность функций ещё имеет значение.
Например, если заменить sin(x) на sin(x*x), картинка сразу в кашу превращается.
Ещё я попробовал заменить sin на функцию Бесселя, т.е. затухающую - то же самое. Явно должны быть периодически повторяющиеся значения, чтобы при отображении на квадрат возникали круги, а не просто каша из точек.
А какие ещё есть периодические функции, отличные от sin/cos?
аргумент в синус приходит от 0 до 1, т.е. периодичность не провляется. И можно вообще заменить sin(x) на x - картинка принципиально не поменяется (станет чуть более скучной).
Хотелось бы понять математические причины, почему все-таки синус и косинус дают такую картинку с фоном и кругами, внутри которых окружности возникают.
Попробую объяснить в двух словах. У отображения двумерного пространства на себя, сохраняющего площади окрестностей любой точки могут быть неподвижные точки, то есть, не изменяющиеся под действием отображения. У гладких отображений неподвижные точки бывают принципиально двух видов: эллиптические и гиперболические (вырожденные случаи тоже могут быть, но они нетипичны).
Вокруг эллиптических точек происходит локальное вращение пространства, а вокруг гиперболических — растяжение в одном направлении и сжатие в другом. При многократном повторении отображений в окрестностях эллиптических точек будут формироваться упорядоченные овальные орбиты. Гиперболические неподвижные точки порождают хаотические орбиты (через разрушение инвариантных гетероклинических многообразий), заполняющие целые площади пространства.
Переход от упорядоченных орбит к хаотическим может быть постепенным (через каскад удвоения периода) либо резким в случае развитого хаоса. Более или менее подробно я описывал это в статьях про прыгающий шарик (https://habr.com/ru/articles/750380/). В вашей схеме некоторые гиперболические точки выдают себя на картинках высокого разрешения крестообразными сгущениями орбит.
Не скажу, что я много что понял :) статью по ссылке почитаю, спасибо. А какая это область математики?
Теория динамических систем и теория хаоса.
А есть какие-то "жизненные" применения этих областей? Моделирование взрывов?
Я как-то уже отвечал на этот вопрос: https://habr.com/ru/articles/746706/#comment_25731380
Моей мечтой было как-то сделать этот мир интерактивным
Тут есть ссылки на две реализации отрисовки в браузере на JS - подозреваю, если переписать отрисовку по sin/cos, должно работать и быстро, и интерактивно, как во втором примере с бегунками для параметров.
Попробовал с кое-какими изменениями, см. выше (сначала ответил не туда).
Как в демке осуществлён переход от двумерной формулы к 3D? Какой-то параметр взят за третью координату?
Что-то вроде b=z
Я точно не скажу, это не моя программа, но думаю, что один или даже два параметра образуют пространство в котором "движется" наблюдатель, и на него уже проецируются фрагменты соответствующего этим параметрам аттрактора.
Интересно, как это всё реализовано. Во время "полёта" заранее просчитывает сколько-то картинок? На каждую нужны десятки, а то и сотни тысяч итераций.
Ну мой не то что бы оптимизированный вариант на JS при определенных значениях генерит вполне похожую картинку за 20к итераций что занимает 11мс, при том что для 60фпс надо уложится в 16... Это если по картике на кадр, а там куда реже генерация, так что если еще и в веб поркере делать...
Я так понимаю, алгоритм распараллелить не получится. Зато у современных процессоров есть много ядер. Можно в фоне несколько картинок генерировать в отдельных потоках, а показывать их, когда готовы.
Ну потому и сказал про WebWorker... Плюс даже на максимальной скорости полета там вроде не более 5ки картинок в секунду... Тут даже без многопоточности ресурсов достаточно.
Глядя на то, с какой скоростью работает JS в браузере, сдается мне, что отрисовка использует аппаратные возможности GPU. Программно в памяти смешивание цветов с весом явно медленнее работает. Возможно, у меня руки кривые, но я реально тут вижу случай, когда что-то по-быстрому сделать на JS получается и проще, и быстрее, чем на C++.
А еще мне приходит в голову генерить это на шейдере, в том смысле что есть зависимость от сгенеренных, но в то же время сгенерив пару тысяч, мы можем шейдером каждым прогоном удваивать результат. А видеокарты щас быстрые.
На самом деле это будет не полностью идентичный последовательной генерации результат, ибо погрешности попадения в пиксель будут, но близко думаю.
Я проверил, если округлять границу пикселя, то все становится скучно, но если добавлять рандом после округления, то картинки визуально не отличимы от оригинала, не смотря на постоянное округление. (Тоже самое было бы с шейдером, но его я щас писать не готов)
Вообще может быть интересно рисовать на хосте, на который натравлять шейдер с инвертированной формулой. Те мы как бы будем своим рисованиям предоставлять x,y исходные...
Спасибо за отличную статью и воспоминание о прекрасном журнале! В одном номере "В мире науки" я познакомился с петлевой квантовой гравитацией... Шикарный журнал)
Похоже на множество Мандельброта. Там даже соревнования проводятся по рисованию :-)
Чуть более удобная для игры с параметрами версия. ПыСы сделал на коленке.
https://codepen.io/findoff/pen/rNgjVKY?editors=0010
Жаль, значения параметров ограничены значением 10. В журнале, о котором идет речь, приведены интересные картинки, для получения некоторых используются значения a = 200, с = 80. А еще там есть пример с отрицательными значениями параметров.
Увеличил лимит до 200, но имхо тогда уже менее удобно прицеливатся ползунками.
Ну и на отрицательных не вижу особого смысла, так как там в основном повторение, а прицеливатся еще сложнее...
Upd: сделал таки отрицательные, но имхо интересный результат с ними только на c...
В журнале пример a=-1000, b=0,1 и с=-10. Правда, картинка у меня получилась почему-то совсем не такая, как в журнале.
Я думаю, вряд ли можно подобрать какой-то диапазон "на все случаи жизни".
У меня аватарка похожим образом построена.
Hidden text
Код там правда был ужасный. Написано еще в студенчестве на редком диалекте паскаля Алго. А формулу вообще не помню откуда взял.
Красивый пледик, без шуток.
Hidden text
Я тут немного не в тему, вспомнился старый пост <<Логотип за три доллара>>. И там был пёс-арбуз с COM портом вместа рта. Что-то найти не могу.
sign(x) - что за функция?
Есть где-нибудь более подробные статьи про данную тему? Попытался загуглить "итерационная схема Барри Мартина", ничего вразумительного не нашел.
Боюсь, что я создал нетленку -- то есть единственный на русском языке материал, посвящённый именно этой схеме. Если поискать в англоязычном сегменте (hopalong attractor, Martin's hopalong), то будет много этюдов по программированию на разных языках, демок или записей в блогах, а также несколько статей (https://jolinton.co.uk/Mathematics/Hopalong_Fractals/Text.pdf), посвящённых генеративному искусству, но не разбору динамики.
Множество интересных схем есть здесь: https://paulbourke.net/fractals/
Всё это очень интересно, спасибо. Помню, в 90-е годы на одном местном телеканале была заставка с подобными анимированными картинками.
При многократном применении (итерациях)
Сколько итераций обычно требуется на построение одной картинки? Есть ли какой-то вычисляемый критерий, по которому программа сама может понять, что можно остановиться?
Нашел вот такой материал, скрипт для рисования тоже имеется. Но информация очень краткая. Там же есть целый большой раздел про фракталы.
Я мимо проходил, но кажется это фракталы.
А по фракталам есть вузовские учебники. Как минимум нам откуда-то курс по ним читали
Если я правильно помню, фракталы состоят из элементов, каждая часть которого рекурсивно заменяется на вложенный такой же элемент, и так до бесконечности. Здесь, вроде бы, не совсем такое. Если я не прав, пусть меня поправят.
Интересно, с какими значениями параметров удается получить картинки с "пледиками", что в начале статьи? Или как на обложке журнала.
Нашел скан журнала в PDF. Кое-какие интересные примеры значений параметров в нем приведены. Например, такие:
Однако, по картинке пришлось изрядно покликать мышкой, чтобы такое получить.
У меня такие круги получились как то, типа дифракционных колец Френеля )
Каким алгоритмом такое получили?
procedure Fresnel;
const
window_size = 10.0;
var
x, y: integer;
dx, hw, hh, m, n, f: double;
lens: array of array of double;
begin
hw := Width * 0.5;
hh := Height * 0.5;
dx := window_size / Width;
SetLength(lens, Width, Height);
for x := 0 to Width - 1 do
for y := 0 to Height - 1 do
lens[x, y] := cos(Density * (sqr(dx * (x - hw)) + sqr(dx * (y - hh))));
m := -MaxInt;
n := MaxInt;
for x := 0 to Width - 1 do
for y := 0 to Height - 1 do
begin
m := Max(m, lens[x, y]);
n := Min(n, lens[x, y]);
end;
m := (m - n + 1e-10);
for x := 0 to Width - 1 do
for y := 0 to Height - 1 do
begin
f := (lens[x, y] - n) / m * 100;
Bmp.Pixel[x, y] := ColorMap.IterToColor(f);
end;
end;
sqr(dx * (x - hw)) + sqr(dx * (y - hh))
Когда x превысит половину ширины или y превысит половину высоты, под аргументом квадратного корня окажется отрицательная величина. Нужно взять её абсолютное значение?
Понял, sqr - это квадрат, а не квадратный корень :-)
Какая у Вас таблица цветов используется? Я использовал палитру {'black', 'yellow', 'red', 'orange', 'blue', 'cyan', 'magenta', 'green'}, картинка больше синяя получается, чем зеленая:
А если вместо квадрата использовать квадратный корень, характер картинки меняется (тут и другие значения window_size и density подбирать приходится):
Еще можно квадраты на кубы заменить:
Круто ! Почему-то не могу плюсануть статью, хотя один голос за статью есть сегодня :) Напомнило кучки песчаные: https://math.stackexchange.com/q/2143728/383320
Попробовал алгоритм CIRCLE2 из статьи.
Использовал вариант с несколькими фиксированными цветами, а не с двумя. Довольно симпатично, но картинки не столь же интересные, на мой взгляд.
Про происхождение этих кругов я когда-то упоминал в небольшой статье о таблице умножения: https://dzen.ru/a/Y6KF_eLgZkAebaW9
На основе формулы, предложенной kipar, сделал программу и опубликовал статью:
Сложная красота в простой формуле