Студопедия

КАТЕГОРИИ:

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника



Алгоритм построения множества Мандельброта




Читайте также:
  1. A) Словесный, графический, формально - словесный, алгоритмический язык
  2. Cent; Операции над множествами
  3. Cent; Основные законы над множествами
  4. Cent; Понятие множества. Способы задания множества
  5. II.2. Методика построения напорной и пьезометрической линий
  6. Админ теории менед-та(А Файоль) и теория бюрократического построения орг-и(М Вебер)
  7. Аксиоматический способ построения теории
  8. Алгоритм
  9. Алгоритм
  10. Алгоритм 2.

Выполнять для каждой точки выбранного прямоугольника:

  1. Записать координаты точки в p и q, обнулить x и y, обнулить счетчик. Перейти к пункту 2.
  2. Увеличить счетчик, реализовать преобразования x и y. Перейти к 3.
  3. Если модуль числа превысил L или счетчик превысил определенное время Time, остановить преобразования и перейти к 4, иначе перейти к 2.
  4. Если счетчик превысил Time, окрасить точку в черный цвет, иначе (модуль превысил L) окрасить точку в цвет тем более темный, чем больше счетчик в момент выхода. Перейти к следующей точке.

Преобразования в пункте 2 имеют вид:

xt+1 = xt2 – yt2+p

yt+1 = 2xtyt + q

Модуль комплексного числа z = x + iy вычисляется по формуле: |z|2 = x2 + y2

Первая задача, с которой вы сталкиваетесь при разработке приложения, – это оформление, создание интерфейса вашей программы.

Начните новый проект, поместите на форму Image кнопку Button, четыре поля ввода Edit, четыре надписи Label и линейку прогресса ProgressBar.

Установите свойства компонентов.

Значения этих полей ввода будут задавать прямоугольную часть комплексной плоскости, которая будет отображаться на компоненте Image.

При кажущейся простоте данного этапа – просто добавить нужные компоненты и расставить их – оформлению нужно уделить надлежащее внимание. Интерфейс должен быть удобным, дружественным пользователю. Он должен быть интуитивно понятным: в идеале пользователь должен догадаться, что куда вводить и на какие кнопки нажимать, без чужой подсказки, не читая учебников и руководства пользователя. Надо стремиться к тому, чтобы для достижения нужного ему эффекта пользователь производил минимум действий – как мышкой, так и с помощью клавиатуры. Если возможно, клавиатурный ввод лучше вообще свести на нет, однако не забыть продублировать все действия горячими клавишами (согласитесь, что для запуска программы в Delphi иногда проще нажать F9, чем целиться мышкой на кнопку в панели инструментов).

Конечно, это идеал, но стремиться к нему нужно. Пользователь всегда чувствует, позаботились ли вы о нем, или слепили форму, особо не утруждаясь, предоставив ему, бедному, мучиться со всеми неудобствами.

Мы реализовали описанный ранее алгоритм построения фрактала Мандельброта:

procedure TForm1.Button1Click(Sender: TObject);
var i, j, t, Time, Size, L: integer;
p0, q0, p1, q1, p, q, x, y, xx: double;
begin
Time := 500;
Size := 250;
L := 100;
p0 := StrToFloat(Edit1.Text);
p1 := StrToFloat(Edit2.Text);
q0 := StrToFloat(Edit3.Text);
q1 := StrToFloat(Edit4.Text);
for i := 0 to Size - 1 do
begin
p := p0 + i * (p1 - p0) / (Size - 1);
for j := 0 toSize - 1 do
begin
q := q0 + j * (q1 - q0) / (Size - 1);
x := 0;
y := 0;
t := 0;
repeat inc(t);
xx := x;
x := sqr(x) - sqr(y) + p;
y := 2 * xx * y + q;
until (x * x + y * y > L) or (t = Time);
if t = TimethenImage1.Canvas.Pixels[i, j] := clNavy
else Image1.Canvas.Pixels[i, j] := RGB(250 - t div 2,
250 - t div 2, 250);
end;
ProgressBar1.Position := round(i * 100 / Size);
end;
end;



При реализации этого алгоритма мы столкнулись с некоторыми новыми для нас функциями. Остановимся подробнее на следующих моментах:

  • Функция round служит для перевода чисел с плавающей точкой в целые числа. Делает она это, округляя число до ближайшего целого. Таким образом round(1.4) = 1, a round(8.7) = 9. Мы используем эту функцию для того, чтобы установить свойство Position у полоски прогресса, которое имеет целый тип. Есть и другая функция, переводящая числа с плавающей точкой в целые. Функция trunc округляет до наименьшего целого, то есть trunc(1.4) = 1 и trunc(8.7) = 8.
  • функия StrToFloat(const S: string): Extended служит для перевода текстовых строк в действительные числа. Поскольку не всякую строку можно перевести в число, то при вызове этой процедуры нужно быть уверенным, что строка описана верно. Иначе ее вызов приведет к возникновению исключительной ситуации (exception). Об исключительных ситуациях мы поговорим на следующем уроке, пока же, если вдруг возникнет исключительная ситуация, – нажмите Ok и прервите выполнение программы (Ctrl-F2). Обратите внимание, что в строках string десятичная точка должна быть записана в виде запятой: StrToFloat(‘5.5’) вызовет исключительную ситуацию, а StrToFloat(‘5,5’) выдаст число, равное 5.5. Для обратного перевода используется функция FloatToStr(), а для работы с целыми числами используются StrToInt() и IntToStr() соответственно.
  • Напоминаем тем, кто работал с Turbo Pascal, что функция inc() увеличивает целое число на единицу, sqr() выдает квадрат действительного числа, а div – это деление целых чисел нацело.
  • Введенная нами переменная xx нужна для хранения значения x на прошлом шаге. Если бы ее не было, то y рассчитывался бы с учетом уже измененного x, что некорректно.
  • Мы уже знакомы с холстом Canvas, но еще не рассматривали его свойство Pixels[X, Y: Integer]: TColor. В этом доступном для чтения и записи свойстве содержится цвет точки в формате TColor.

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



Увеличивать картинку, задавая каждый раз с клавиатуры четыре числа и нажимая кнопку, не очень удобно. Лучше добиться этого одним нажатием мыши.



Сделаем так, чтобы при нажатии на Image картина увеличивалась в два раза, с центром в точке, на которую мы нажали.

Выделите Image и в Инспекторе Объектов дважды щелкните справа от события OnMouseDown.

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

Эти границы записываем в поля ввода.

Затем вызываем метод Click кнопки Button1. Фактически мы имитируем нажатие на кнопку и, следовательно, вызываем обработчик этого нажатия, перерисовывая картинку.

В результате теперь нам не нужно задавать размеры с клавиатуры, а можно увеличивать и уменьшать картинку, нажимая левую и правую кнопку мыши.

Запустите программу и попробуйте сами.

 

procedure TForm1.Image1MouseDown(Sender: TObject;
Button: TMouseButton;
Shift: TShiftState;
X, Y: Integer);
var p0, q0, p1, q1, x0, y0, x1, y1: double;
begin
p0 := StrToFloat(Edit1.Text);
p1 := StrToFloat(Edit2.Text);
q0 := StrToFloat(Edit3.Text);
q1 := StrToFloat(Edit4.Text);
if Button = mbLeft then
begin
x0 := p0 + (p1 - p0) * X / Image1.Width - (p1 - p0) / 4;
y0 := q0 + (q1 - q0) * Y / Image1.Height - (q1 - q0) / 4;
x1 := p0 + (p1 - p0) * X / Image1.Width + (p1 - p0) / 4;
y1 := q0 + (q1 - q0) * Y / Image1.Height + (q1 - q0) / 4;
Image1.Repaint;
end
else
begin
x0 := p0 + (p1 - p0) * X / Image1.Width - (p1 - p0);
y0 := q0 + (q1 - q0) * Y / Image1.Height - (q1 - q0);
x1 := p0 + (p1 - p0) * X / Image1.Width + (p1 - p0);
y1 := q0 + (q1 - q0) * Y / Image1.Height + (q1 - q0);
end;
Edit1.Text := FloatToStr(x0);
Edit2.Text := FloatToStr(x1);
Edit3.Text := FloatToStr(y0);
Edit4.Text := FloatToStr(y1);
Button1.Click;
end;

Метод OnMouseDown вкратце был описан во втором уроке. Здесь вы можете наблюдать, как реально используются его параметры.

Параметр Button содержит информацию о нажатой кнопке. В данном случае мы проверяем, равен ли он mbLeft, то есть была ли нажата левая кнопка. Если да, то, опираясь на параметры X и Y, в которых хранятся координаты нажатия мыши, мы переопределяем границы картинки.

Затем мы записываем новые границы в поля ввода Edit, используя описанную выше в этом уроке функцию FloatToStr.

И, наконец, вызов метода Click кнопки Button1 имитирует нажатие кнопки. Вызывается обработчик Button1.Click(), перерисовывается картинка (при перерисовке берутся уже измененные границы, содержащиеся в полях ввода Edit и, следовательно, картинка рисуется увеличившейся или уменьшившейся).

Интересный факт: начальная картинка на экране вашего монитора – около 10 см в ширину. Если вы будете увеличивать ее, щелкая мышкой, то где-то щелчков через 50 картинка «испортится» – разобьется на прямоугольники. Это связано не с тем, что фрактал исчерпал себя (он бесконечен), а с тем, что компьютер уже не может обеспечить необходимую точность. А во сколько раз при этом увеличилась начальная десятисантиметровая картинка? В это трудно поверить, но ее размер при подобном увеличении составит около 100.000.000.000 км! (расстояние от Земли до Солнца – "всего" 150.000.000 км) Вот вам и фрактал…

Последние штрихи к программе…

Для того чтобы наша программа приобрела еще более привлекательный вид, добавим несколько оформительских штрихов.

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

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

В-третьих, предоставим пользователю возможность сохранять полученные картинки. Для этого добавим на форму еще один Edit, еще один Button и настроим их соответствующим образом.

В Edit пользователь должен указать имя файла без расширения, в который будет сохранена картинка в формате bmp. В обработчике нажатия на кнопку вызовем процедуру сохранения картинки.

В-четвертых, заметим, что раскраску фрактала можно производить каким-то другим способом, отличным от того, который предложили мы. Попробуйте на досуге сами поэкспериментировать.

Теперь ваша первая полноценная программа приобрела вполне привлекательный и профессиональный вид. Не забудьте сохранить и продемонстрировать ее вашим родителям, друзьям и знакомым.

 

При рисовании рамки обратим внимание на то, что у кисти (Brush) стиль устанавливается в значение bsClear. В результате рисуется рамка без закрашивания (с прозрачной закраской).

Image1.Canvas.Pen.Color := clRed;
Image1.Canvas.Brush.Style := bsClear;
Image1.Canvas.Rectangle(X - Image1.Width div 4, Y -
Image1.Height div 4, X + Image1.Width div 4, Y +
Image1.Height div 4);
Image1.Repaint;

Необходимость вызова метода Repaint для Image1 связана с тем, что перерисовка происходит, когда обработчик события заканчивает работу. А он закончит работу только тогда, когда отработают все вызываемые внутри него процедуры, и в том числе Button1.Click, которая изменяет картинку. То есть перерисовка Image произойдет уже после всех вычислений, а нам нужно, чтобы рамка была видна во время вычислений. Поэтому необходимо самим перерисовать Image, вызвав метод Repaint.

Сохранение изображения, хранящегося в Image, происходит с помощью вызова метода SaveToFile(const FileName: string), принадлежащего свойству Picture (если помните, в прошлых уроках именно в Picture вы загружали картинки из файла в Image через Инспектор Объекта). С помощью метода SaveToFile картинки сохраняются в файлы в формате bmp. В качестве параметра FileName нужно указать имя файла, например:

SaveToFile(‘фрактал.bmp’)

или

SaveToFile(‘.\Pictures\’ + Edit1.Text + ‘.bmp‘)

Напоследок отметим, что при увеличении фрактала качество изображения (как вы, наверное, заметили) несколько ухудшается. Это происходит из-за того, что ограничитель L и время расчета Time малы. Увеличив Time, можно получить более детальный фрактал. Однако это замедлит расчеты, и тогда нужно будет откорректировать функцию раскраски фрактала – нужно сделать, чтобы компоненты цвета были в диапазоне от 0 до 255, то есть при Time = 1000 нужно будет t делить уже на 4 а не на 2:

RGB(250 – t div 4, 250 – t div 4, 250)

Раскрашивать фрактал можно вообще по другому принципу, например, опираясь не на t, а на компоненты x и y при выходе преобразований из цикла.

То есть вместо

if t = Time thenImage1.Canvas.Pixels[i, j] := clNavy
else Image1.Canvas.Pixels[i, j] := RGB(250 - t div 2, 250 - t div 2, 250);

написать

Image1.Canvas.Pixels[i,j] := $00007f + $000201 * round(127 * (x * x / (x * x + y * y)));

Замечание. Цвета, кроме известных вам констант clRed, clBlue и функции RGB, можно задавать в шестнадцатеричном формате: $ff7f00. Первый байт $ff = 255 – компонента синего цвета, второй $7f = 127 – компонента зеленого и третий $00 = 0 – красная компонента.

Можете придумать и свою раскраску фрактала …

 


 


Дата добавления: 2015-07-26; просмотров: 29; Нарушение авторских прав







lektsii.com - Лекции.Ком - 2014-2021 год. (0.008 сек.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав
Главная страница Случайная страница Контакты