пятница, 26 октября 2012 г.

Построение графиков функции в Delphi (часть 1)

Доброго времени суток дорогие читатели сегодня мы поговорим о построении графиков функции.

Для работы нам потребуется форма, на которую следует поместить два компонента TImage и один компонент TButton. На одном image разместим изображение формулы, график которой будем строить, а на втором Image будем выводить непосредственно сам график при нажатии на кнопку. Если у вас стоит Windows 7, то для создания изображения формулы проще всего использовать программку «Ножницы», которая находится по адресу: Пуск – Все программы – Стандартные - Ножницы. Думаю, проблем с этим не возникнет.

Кстати, прошу заметить, что у нашей формулы есть два коэффициента a и b, поэтому я сделал два поля TEdit, куда будут вводится значения этих коэффициентов.

Теперь перейдем к самому коду. Главным событием будет нажатие на кнопку «Строить». Дважды щелкнув на нее, перейдем в кодовое окно. Буду расписывать поэтапно:

Очистим рисунок от предыдущих построений, это делается, если наша программа уже строила график:

Image1.Canvas.Pen.Color:=clWhite;
Image1.Canvas.Rectangle(0,0,Clientwidth,clientheight);

Как вы заметили я сначала задал цвет пера белым, это для того чтобы не получилось черного обвода по краю изображения. Чтобы точнее понять этот механизм, советую сначала закомментировать эту строку, а потом раскомментировать. Для очистки используется такой прием как рисование белого прямоугольника на все изображения. Для рисования прямоугольника используется команда Rectangle, у которой в скобках указывается 4 целых числа через запятую, первых два числа – координата левого верхнего угла, следующие два числа – координаты правого нижнего угла:

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

Едем дальше. Теперь нам необходимо считать введенные значения коэффициентов, чтобы не усложнять код пусть коэффициенты будут целыми числами (спросите каким это образом, объясняю: если вводится, будут и вещественные числа, то придется следить за тем, чтобы пользователь не допустил ошибок ввода, ну типа две запятые и т.д.). Итак, считываем:

a:=StrToInt(Edit1.text);
b:=StrToInt(Edit2.text);
// Примечание:
// StrToInt – String to Integer – строку в целое число

Далее нужно построить оси координат, которые будут разбивать наш рисунок на четверти, но для этого нам нужно знать координаты центра рисунка. Объявим две дополнительные переменные x0 и y0 целого типа, которые будут хранить в себе координаты центра рисунка.

И напишем вот что:

x0:=Image1.width div 2;
y0:=Image1.height div 2;

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

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

x:=x+delta; y:=1/(a*x*x+a*x+b);
// Примечание:
// delta – заранее инициализированная константа имеет значение 0,1.

При каждом вычислении пары (x,y) будем рисовать новую линию командой LineTo(x,y). И весь наш график будет представлять собой череду отрезков.

Но так как величина приращения аргумента (delta) относительно мала, то человеческому глазу будет представлена гладкая кривая.

Но вдруг получится так, что функция примет отрицательное значение и уйдет за границу рисунка, ведь у него нет отрицательных координат в поле видимости. Тогда следует к каждой паре (x,y) прибавлять x0 и y0. А также следует произвести масштабирование. Пусть у нас имеется заранее инициализированная константа по имени mash и со значением 100. Тогда каждую пару мы умножаем на масштаб – (x*mash, y*mash). И еще один маленький нюанс так как delta – число не целое и следовательно пара (x,y) будет вещественной, а координаты на рисунке таких чисел не любит. Поэтому после того как мы их отмасштабировали просто отбросим дробную часть числа командой Trunc. Вот что у нас получилось:

x:=x+delta;
y:=1/(a*x*x+a*x+b);
Image1.Canvas.LineTo(x0+trunc(x*mash),y0+trunc(y*mash));

Но это ведь единичное действие, давайте «засунем» его в цикл. Пусть ограничением для цикла будет значение аргумента x, в диапазоне от -10 до 10.

x:=-10;
repeat
x:=x+delta;
y:=1/(a*x*x+a*x+b);
Image1.Canvas.LineTo(x0+trunc(x*mash),y0+trunc(y*mash));
until x>=10;

Вот теперь важно запустить программу и посмотреть что получается...

А получается, что-то не то. Это произошло, потому что при построении первого отрезка программа знает куда вести линию, а от куда, вот в чем вопрос. Как Вы помните, при рисовании осей мы сначала передвигали перо в начало отрезка командой MoveTo. Здесь необходимо проделать то же самое. Нам необходимо определить первую точку графика, ну это просто, и это нужно делать перед циклом:

x:=-10;
y:=1/(a*x*x+a*x+b);
Image1.Canvas.MoveTo(x0+trunc(x*mash),y0+trunc(y*mash));

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

Теперь до совершенства осталось лишь добавить обработку ошибок. К примеру, при возникновении такой ситуации, когда пользователь введет не цифры а буквы в поля ввода коэффициентов. Мы просто элементарно запретим вводить любые символы кроме цифр и знака Backspace (#8).

Выделите Edit1 и перейдите ко списку событий (Events рядом с Properties). Переедите в этом списке на пустое поле рядом с надписью OnKeyPress и дважды щелкните по нему. Таким образом Вы перейдете на окно кода, в котором уже будет шаблон кода для обработчика OnKeyPress. И пропишем следующий код:

Case key of
'0'..'9',#8:;
else key:=chr(0);
end;

Данный код означает: если пользователь нажал на клавиши от 0 до 9 или Backspace, то ничего не делать. Если же будет нажата какая-либо другая кнопка, то заблокировать ее. Такой же код следует поместить для обработчика OnKeyPress у компонента Edit2.

Также следует ограничить длину вводимого числа до двух. Для этого нужно у компонентов Edit1 и Edit2 в свойстве MaxLength написать 2.

Еще может возникнуть такая ситуация что пользователь ввел только один коэффициент, а второе поле оставил пустым и нажал кнопку «Строить». Это также является критическим моментом для программы. Следует блокировать кнопку «Строить» до тех пор, пока хотя бы одно поле является пустым, и только когда оба поля содержат числа, тогда разблокировать ее.

Для этого в обработчике OnChange у компонентов Edit1 и edit2 следует прописать следующее условие:

if (edit1.Text<>'')and(edit2.Text<>'') then button1.Enabled:=true else button1.Enabled:=false;

Также следует поставить свойство Enabled у компонента Button1 в значение false. Так как при запуске наши поля являются пустыми. И у самой формы свойство BorderStyle поставить

Все, дальше усложнять программу не имеет смысла.


Комментариев нет:

Отправить комментарий