При выполнении данной работы, как и ранее, надо разбить задачу на подзадачи. При этом надо учесть, что мы работаем с математическими функциями, а построение графиков происходит в экранных координатах, то есть, нужны масштабирующие преобразования. В примере выполнения предлагается заполнить таблицы со значениями математических функций, найти минимальные и максимальные значения этих математических значений, чтобы потом можно было вычислить масштабирующие коэффициенты по обеим координатным осям, зная которые просто вычислить координаты расположения точек на экране монитора и заполнить соответствующие таблицы. По таблицам для вывода на экран вычисляется начало координат для прорисовки осей координат. Поиск точек пересечения – это особая задача, но она также относится к подготовке исходных для рисования всех составляющих графика. Далее будет сама прорисовка на экране монитора в графическом режиме. В примере отдельные части программы выделены в модули. Часть модулей следует разработать самостоятельно.
{ ПАРАМЕТРЫ РИСУНКА МОЖНО ИЗМЕНИТЬ В МОДУЛЕ Consts }
uses
Crt,
Graph, { модуль графической библиотеки }
Consts, { модуль с константами }
Types, { модуль описания типов данных }
Mathem1, { модуль математических преобразований }
Draw1, { модуль процедур и функций для рисования }
{ МОДУЛИ ДЛЯ САМОСТОЯТЕЛЬНОЙ РАБОТЫ }
Mathem2, { модуль математических преобразований }
Draw2; { модуль процедур и функций для рисования }
{ Первая математическая функция }
function F1(x:real):real; far; { Дальняя ссылка }
begin
F1:=9*x*x-11;
end;
{ Вторая математическая функция }
function F2(x:real):real; far; { Дальняя ссылка }
begin
F2:=x*x*x+5;
end;
{ **************** Основная часть **************** }
var
M1,M2:TMatMas; { Таблицы математических функций }
Y1min,Y2min, { Минимумы и... }
Y1max,Y2max, {... максимумы математических функций }
Ymin,Ymax:real; { Абсолютный минимум и максимум функций }
Kx,Ky:real; { Масштабирующие коэффициенты }
Ms1,Ms2:TScrMas; { "Экранные" таблицы }
Xo,Yo:word; { Координаты "экранного" нуля }
RootMas:TRoot; { Массив точек пересечения }
K:word; { Количество точек пересечения }
Rect:TRect; { Прямоугольник для вывода графика }
begin
{ ********* Подготовка данных для рисования *********** }
with Rect do
begin
Origin.X:=OriginX;
Origin.Y:=OriginY;
Size.X:=Nx;
Size.Y:=Ny;
end;
{ Заполнение таблиц математических функций }
EnterMatMas (M1,Nx,@F1,Xmin,Xmax);
EnterMatMas (M2,Nx,@F2,Xmin,Xmax);
{ Вычисление минимума и максимума математических функций }
Y1min:=Min(M1,Nx); Y1max:=Max(M1,Nx);
Y2min:=Min(M2,Nx); Y2max:=Max(M2,Nx);
Ymin:=Minimum(Y1min,Y2min);
Ymax:=Maximum(Y1max,Y2max);
{ Вычисление масштабирующих коэффициентов }
Kx:=Nx/(Xmax-Xmin);
Ky:=Ny/(Ymax-Ymin);
{ Заполнение "экранных" таблиц }
EnterScrMas(Ms1,M1,Ymin,Ky,Rect);
EnterScrMas(Ms2,M2,Ymin,Ky,Rect);
{ Вычислить координаты "экранного" нуля }
Zero(Xmin,Xmax,Ymin,Ymax,Rect,Kx,Ky,Xo,Yo);
{ Найти точки пересечения }
K:=Solution(Xmin,Xmax,@F1,@F2,RootMas);
{ ********************** Рисование ********************* }
GraphInit;
{ Нарисовать рамку }
Ramka(Rect,Width,Cyan);
{ Рисование граничных значений по углам рамки }
DrawBounds(Xmin,Xmax,Ymin,Ymax,Rect,Red);
{ Рисование координатной сетки }
DrawGridLines(NgrX,NgrY,Rect,DarkGray);
{ Нарисовать оси }
DrawAxis(Rect,Xo,Yo,Red);
{ Нарисовать функции }
DrawFunction(Ms1,Nx,LightGreen);
DrawFunction(Ms2,Nx,Yellow);
{ Вывести координаты точек пересечения }
WriteCoord(RootMas,K,Rect,LightGreen);
ReadKey;
CloseGraph;
end.
Разбиение текста программы на отдельные модули позволяет сделать программу более «читабельной». Пусть даже эти модули очень небольшие.
unit Consts;
interface
const
Nx=440; { Размер графика по оси X }
Ny=480; { Размер графика оси Y }
OriginX=180; OriginY=40; { Левый верхний угол рамки }
Xmin=-2; Xmax=2; { Начальное и конечное значения аргумента }
NgrX=6; NgrY=23; { Количество линий координатной сетки }
Epsilon=0.001; { Точность решения }
R=10; { Количество поддиапазонов }
const
Width=3; { Ширина рамки }
implementation
end.
Модуль используемых типов.
unit Types;
interface
uses Consts;
type
TMatMas=array[1..Nx] of real; { Тип математической таблицы }
TScrMas=array[1..Nx] of word; { Тип "экранной" таблицы }
TRealCoord=record x,y:real; end; { Тип координата в действит. числах }
TRoot=array[1..Nx div 10] of TRealCoord; { Тип массив точек пересечения }
TIntCoord=record x,y:word; end; { Тип координата в целых числах }
TRect=record { Координаты прямоугольника }
Origin:TIntCoord; { Левый верхний угол }
Size:TIntCoord; { Размер }
end;
implementation
end.
Модули математической обработки исходных данных и прорисовки графика разбиты на две части, чтобы дать возможность самостоятельной разработки вторых частей этих модулей.
Приведенный модуль Mathem1 содержит две особенности, не рассматриваемые ранее. Это передача функции в процедуру в качестве параметра. Функции передаются в виде нетипизированного указателя, а внутри процедуры преобразуются к типу математической функции.
unit Mathem1;
interface
uses Types, Consts;
procedure EnterMatMas (
var M: array of real; { Таблица математической функции }
Nx: word; { Количество точек в таблице }
Func: Pointer; { Указатель на математическую функцию }
Xmin,Xmax: real { Начальное и конечное значения аргумента }
);
{ Вычисление точек пересечения }
function Solution(
Xmin,Xmax:real; { Минимум и максимум аргумента }
F1,F2:pointer; { Математические функции }
var RootMas:TRoot): { Массив координат пересечения }
byte; { Количество точек пересечения }
implementation
procedure EnterMatMas(
var M: array of real; { Таблица математической функции }
Nx: word; { Количество точек в таблице }
Func: Pointer; { Указатель на математическую функцию }
Xmin,Xmax: real { Начальное и конечное значения аргумента }
);
type
TFunc = function (x:real):real; { Описание типа функции }
var
F: TFunc; { Математическая функция }
x: real; { Значение аргумента }
dx: real; { Шаг вычисления функции }
i: word; { Счетчик вычисляемых точек }
begin
F:=TFunc(Func); { Сделать ссылку на математическую функцию }
dx:=(Xmax-Xmin)/(Nx-1); { Вычислить шаг вычисления функции }
x:=Xmin; { Начальное значение аргумента }
for i:=0 to Nx-1 do { Цикл заполнения таблицы }
begin
M[i]:=F(x); { занести значение в массив }
x:=x+dx; { следующее значение аргумента }
end;
end;
{ Вычисление точек пересечения }
function Solution(
Xmin,Xmax:real; { Минимум и максимум аргумента }
F1,F2:pointer; { Математические функции }
var RootMas:TRoot): { Массив координат пересечения }
byte; { Количество точек пересечения }
type
TFunc = function (x:real):real; { Описание типа функции }
var
Fu1,Fu2: TFunc; { Математические функции }
{ Для вычисления точек пересечения задана новая функция,
как разность двух исходных }
function Fu(x:real):real;
begin
Fu:=Fu1(x)-Fu2(x);
end;
{ Определить, есть ли решения в поддиапазоне }
function SubRange (var FirstX, LastX, Step: real): boolean;
begin
{ Найти поддиапазон, на котором есть решение.
Из условия, что при прохождении через ось X функция меняет знак }
while (Fu(FirstX)*Fu(FirstX+Step)>0)and((FirstX+Step)<=Xmax) do
FirstX:= FirstX+Step;
if ((FirstX+Step)<=Xmax)
then
begin
LastX:= FirstX+Step;
SubRange:= True;
end
else SubRange:= False;
end;
{ Вычисление корня }
function Root (FirstX, LastX, NewStep: real): real;
begin
repeat
{ Вычислить новое значение шага }
NewStep:= NewStep/R;
{ Найти новый (уточненный) поддиапазон }
SubRange (FirstX,LastX,NewStep);
{ Определение правой границы поддиапазона }
LastX:= FirstX + NewStep;
until abs(NewStep)<=Epsilon/R; { Условие достижения заданной точности }
Root:= FirstX; { Возвращение корня }
end;
var
Step: real; { Величина подиапазона }
CurLeft,CurRight:real; { Границы отрезка, на котором есть решение }
k:word; { Номер точки пересечения }
begin
k:=0;
{ Задать функции }
Fu1:=TFunc(F1); Fu2:=TFunc(F2);
{ Вычислить начальный размер поддиапазона }
Step:=(Xmax-Xmin)/R;
{ Поиск решения с левой крайней границы отрезка }
CurLeft:= Xmin;
{ Цикл поиска всех решений }
while SubRange(CurLeft,CurRight,Step) do
begin
inc(k);
RootMas[k].X:=Root(CurLeft,CurRight,Step);
RootMas[k].Y:=Fu1(RootMas[k].X);
CurLeft:= CurRight;
end;
Solution:=K;
end;
end.
Задание: самостоятельно реализуйте реализацию (implementation) модуля Mathem2. Интерфейсная часть данного модуля имеет вид:
unit Mathem2;
interface
{ Функция вычисления минимума в массиве (таблице) точек }
function Min(
M: array of real; { Таблица математической функции }
N: word { Количество точек в таблице }
):real;
{ Функция вычисления минимума в массиве (таблице) точек }
function Max(
M: array of real; { Таблица математической функции }
N: word { Количество точек в таблице }
):real;
{ Выбор минимального из двух значений }
function Minimum(x,y:real):real;
{ Выбор максимального из двух значений }
function Maximum(x,y:real):real;
{ Процедура заполнения "экранной" таблицы }
procedure EnterScrMas(
var Ms: array of word; { "Экранная" таблица }
Mm: array of real; { Таблица математической функции }
Ymin: real; { Минимум математической функции }
K: real; { Коэффициент масштабирования }
var Rect { Прямоугольник вывода }
);
{ Вычислить координаты "экранного" нуля }
procedure Zero (
Xmin,Xmax,Ymin,Ymax:real; { Математические пределы изменения функций }
var Rect; { "Экранные" пределы изменения функций }
Kx,Ky:real; { Коэффициенты масштабирования }
var Xo,Yo:word); { Координаты "экранного" нуля }
implementation
end.
Теперь осталось рассмотреть последний модуль – модуль работы в графическом режиме. Каких-либо особенностей в этом модуле нет. Самое главное дело состоит в том, что здесь сгруппированы процедуры и функции для работы только с графикой. Все подготовительные операции были сделаны раньше.
{*********** Процедуры и функции для работы с графикой *************}
unit Draw1;
interface
uses Types;
{ Инициализация графики }
procedure GraphInit;
{ Рисование функции по точкам из массива }
procedure DrawFunction(M:array of word;N:word;Color:byte);
{ Рисование рамки }
procedure Ramka(Rect:TRect;Width,Color:word);
implementation
uses Graph,Consts;
{ Инициализация графики }
procedure GraphInit;
var
Driver,Mode:integer;
Res:integer;
begin
Driver:=Detect;
InitGraph(Driver,Mode,'');
Res:=GraphResult;
if Res<>0 then
begin
WriteLn(GraphErrorMsg(Res));
WriteLn('Press <Enter> for exit');
ReadLn;
Halt(1);
end;
end;
{ Рисование функции по точкам из массива }
procedure DrawFunction(M:array of word;N:word;Color:byte);
var
i:word;
begin
SetColor(Color);
MoveTo(OriginX,M[0]);
for i:=1 to N-1 do
begin
LineTo(OriginX+i,M[i]);
{ PutPixel(OriginX+i,M[i],Color);}
end;
end;
{ Рисование рамки }
procedure Ramka(Rect:TRect;Width,Color:word);
var
i,OldColor:byte;
begin
OldColor:=GetColor;
SetColor(Color);
with Rect do
for i:=1 to Width do
Rectangle(Origin.X-i, Origin.Y-i,
Origin.X+Size.X+i, Origin.Y+Size.Y+i);
SetColor(OldColor);
end;
end.
Задание: самостоятельно реализуйте реализацию (implementation) модуля Draw2. Интерфейсная часть данного модуля имеет вид:
{*********** Процедуры и функции для работы с графикой *************}
unit Draw2;
interface
{ Нарисовать оси }
procedure DrawAxis(var Rect;Xo,Yo,Color:word);
{ Рисование граничных значений по углам рамки }
procedure DrawBounds(Xmin,Xmax,Ymin,Ymax:real;var Rect;Color:word);
{ Рисование координатной сетки }
procedure DrawGridLines(NgrX,NgrY:word;var Rect;Color:word);
{ Вывод координат точек пересечения }
procedure WriteCoord(var RootMas;K:word;var Rect;Color:word);
implementation
end.
Контрольные вопросы по теме № 8
1. Библиотечные функции графической библиотеки в языке Turbo Pascal.
2. Масштабирование функции для вывода в виде графика.
3. Тип процедура и тип функция. Преобразование типов.
4. Решение нелинейного алгебраического уравнения (метод деления отрезка пополам, метод простых итераций, метод пошаговых приближений).