девичья грудь

Паскаль - Уроки - Создаем мыльные пузыри

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

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

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

Увеличив количество пузырей до 100 или 200, получим пену, похожую на легкие или какие-то другие объекты явно биологического происхождения. Продолжая увеличивать количество пузырей, мы заставим их тесниться и уплотняться, в связи с чем возникают интересные вопросы. Сколько соседей в среднем у каждого пузыря и как эта величина зависит от плотности размещения? Влияют ли скорость уменьшения яркости и рост радиуса на вид пены (на количество соседних пузырей)? Нет ли здесь каких-нибудь фрактальных закономерностей, связанных с размерностью объектов, и если да, то как определить фрактальную размерность пузырей? Или еще - интересно, мыльная пена такая же, как пивная, или ее параметры зависят от коэффициента поверхностного натяжения?

А при чем здесь, спросите вы, сотовая связь? Очень просто. Мобильный телефон с помощью компьютера выбирает антенну, от которой идет самый сильный сигнал, обычно она оказывается ближайшей. При перемещении телефона его сопровождение передается от одной антенны к другой, с самым сильным сигналом для данной точки. Лучшей иллюстрации этого процесса, чем наша картинка с пузырями, не найти.

Но приступим к делу. Приведенная ниже программа создана в TMT Pascal 4, хотя вы можете реализовать этот нехитрый алгоритм на любом доступном языке. Я, например, свои первые пузыри рисовал на Visual FoxPro 6.0 и Visual Basic 6. Скопируйте приведенный ниже текст в окно редактора TMT Pascal и запустите программу.

Program Bubbles;
(***********************************************)
(* Пузыри *)
(* Автор алгоритма: Евгений Скляревский (VB) *)
(* ------------------------------------------- *)
(* Адаптация для TMT Pascal: *)
(* Валерий Вотинцев *)
(* MS-DOS 32-bit protected mode *)
(***********************************************)
uses CRT, Math, Graph;
Type
 dArray = Array[0..0] of LongInt;
 pArray = ^dArray;
var
 i, j, k,
 x, y, q, step,
 xmax, ymax,
 v, col, uu: longint;
 ColDepth: word;
 red, green, blue: LongInt;
 xc, yc, r: pArray;
begin
 xmax := 800; // Разрешение по горизонтали
 ymax := 600; // Разрешение по вертикали
 ColDepth := 16; // Максимальная глубина цвета (бит на пиксел)
 v := 8; // Количество пузырей
 step := 4; // Шаг движения по x и y ("мозаичность" картины):
 // step = 1 -> Рисование по 1 пикселу
 // step > 1 -> Рисование квадратами step*step
 Randomize;
 // Установим графический режим
 SetSVGAMode(xmax,ymax,ColDepth,LFBorBanked);
 if GraphResult<>grOk then begin // Проверка результата
 Writeln('Mode not supported..');
 Halt(0);
 end;
 // Выделяем память для трех массивов
 GetMem(xc,v*SizeOf(LongInt));
 GetMem(yc,v*SizeOf(LongInt));
 GetMem(r, v*SizeOf(LongInt));
 // Задаем случайные координаты пузырькам
 For i := 0 To pred(v) do begin
 xc^[i] := Random(xmax);
 yc^[i] := Random(ymax);
 end;
 // В цикле проходим все поле по X и Y
 x := 0;
 While (x < xmax) do begin
 y := 0;
 While (y < ymax) do begin
 For q := 0 To pred(v) do begin
{1} r^[q] := Trunc(Power(IntPower((xc^[q] - x),2) +
 IntPower((yc^[q] - y),2),0.5));
{1a} {r^[q] := Trunc(Power(IntPower((xc^[q] - x),2) +
 IntPower((yc^[q] - y),2),0.8));}
{1b} {r^[q] := Trunc(Power(Abs(IntPower((xc^[q] - x),2) -
 IntPower((yc^[q] - y),2)),0.5));}
 end;
 // Сортируем пузырьки методом пузырька по возрастанию радиуса
 For k := 0 To v - 2 do begin
 For j := 0 To v - 2 do begin
 If r^[j] > r^[j + 1] Then begin
 uu := r^[j];
 r^[j] := r^[j + 1];
 r^[j + 1] := uu;
 End;
 end;
 end;
 // Задаем цвет
{2} red := 255 - r^[0];
 green := 255 - Trunc(r^[0] * 1.3);
 blue := 255 - Trunc(r^[0] * 1.1);
{2a} {red := 100 - r^[0];
 green := 100 - Trunc(r^[0] * 1.3);
 blue := 100 - Trunc(r^[0] * 1.1);}
{2b} {red := 255 - Trunc(r^[0] * 2.5);
 green := 255 - Trunc(r^[0] * 1.1);
 blue := 255 - Trunc(r^[0] * 1.1);}
 If red < 0 Then red := 0;
 If green < 0 Then green := 0;
 If blue < 0 Then blue := 0;
 Col := RGBColor(red, green, blue);
 // Рисуем очередную точку или квадрат
 If (step = 1) Then PutPixel(x, y, Col)
 else begin
 SetFillColor(Col);
 Bar (x, y, x + step, y + step);
 end;
 Inc(y,step);
 end;
 Inc(x,step);
 end;
 // Освобождаем выделенную память
 FreeMem(xc,v*SizeOf(LongInt));
 FreeMem(yc,v*SizeOf(LongInt));
 FreeMem(r, v*SizeOf(LongInt));
 // Ждем нажатия Esc
 Repeat until ReadKey=#27;
 CloseGraph;
end.

Количество пузырей, точнее, размерность массивов, в которых расположены координаты центров и радиуса, задается переменной v. Причем память под массивы выделяется динамически, в зависимости от заданного значения v. Цикл с переменной i задает центры пузырей случайным образом. Для того, чтобы при каждом запуске расположение пузырей было разным, мы используем функцию Random. В цикле с переменной q по теореме Пифагора вычисляем для каждой точки с координатами x и y расстояние до каждого из центров пузырей r^[q], чтобы выбрать наименьший из них. Выбор происходит в циклах с переменными k и j. Разберетесь, как они работают - получите удовольствие от приобщения к классике программирования: сортировке элементов массива методом пузырьков. (Это не каламбур, название метода не имеет отношения к теме статьи, просто с каждым проходом цикла меньшие числа <всплывают вверх>. Вообще, переменная uu и присвоение ей значения r^[j+1] нужны только в случае сохранения всего массива для дальнейшей работы, нас же интересует только наименьшее значение r^[0], поэтому цикл можно сократить на две строки, имеющие, скорее, методический смысл.)

Получив минимальный радиус для текущей точки, можно приступать к формированию цвета - это и есть изюминка всей затеи. Каждая составляющая rgb-функции цвета уменьшается пропорционально радиусу. Меняя коэффициенты и максимальное значение, равное в примере 255, на меньшее, можно получать различные оттенки пены и регулировать контрастность рисунка. Попробуйте убрать комментарии с блоков, помеченных как {2a} и {2b}.

Программа предоставляет безграничный простор для экспериментов. Попробуйте сделать так, чтобы яркость не уменьшалась с удалением от центра пузыря, а увеличивалась, и вы будете поражены получившейся картиной. Попробуйте при вычислении радиуса поменять показатель степени с 0,5 на 0,8 или 0,3 (кстати, именно для этого мы и воспользовались функцией Power, а не Sqrt). Уберите комментарии со строки {1a}.

Попробуйте также вместо формулы r2=x2+y2 применить r2=x2-y2, чтобы получить нечто гиперболическое. Для этого раскройте комментарий вокруг строки {1b}.

А бесконечные игры с цветами, шагом и количеством пузырей... Естественно, что с уменьшением шага рисования (переменная step) и увеличением количества пузырей (v) время рисования возрастает, т.к. в каждой точке рисунка проверяются расстояния до всех центров пузырей, а любые попытки оптимизировать алгоритм лишь замедлят работу программы.

А что если разукрасить пузыри - каждый в свой цвет? Тут придется поработать, ведь нужно не только задать массив цветов для каждого пузыря и заполнить его случайным образом, но и, отслеживая соответствие каждой точки плоскости ближайшему центру, запоминать еще его цвет. Это непросто, потому что при выборе наименьшего радиуса его индекс в массиве теряется. Задача, конечно, решаемая, и если вы с ней справитесь, то будете сторицей вознаграждены получившейся картиной. Экран заполняется разноцветными шариками, как в моделях сложных молекул, или одинокими фонариками - в зависимости от ваших настроек.

     

Вот и все. Как ни странно, нам удалось связать вместе пену, TMT Pascal и мобильники. Отладка программы и попутные опыты доставят вам массу удовольствия. Задание на дом: научите пену бурлить, чтобы некоторые пузыри лопались, а соседи занимали их место. Да, и не забудьте выложить изображения на свой сайт: Созерцание пузырей рождает прекрасные мысли, недоступные в обыденной суете. Может, в этом и состоит их предназначение? Поразмышляйте об этом на досуге.

Автор: Евгений Скляревский
Copyright © 2006-09.