Работа с графикой. Системы координат. Построение графиков функций |
Содержание | Назад | Вперед |
Система
координат устройства и мировая система
координат
Окно вывода
Построение
графиков
Когда нет
времени...
1. Система координат устройства и мировая система координат
При работе с компьютерной графикой приходится иметь дело с двумя системами координат. Первая система – это система координат устройства (или экранная система координат). Координатами точки в этой системе являются номер пиксела в строке
X и номер строки пикселов Y:0 <= X <= Xmax
0 <= Y <= Ymax
Начало координат расположено в левом верхнем углу экрана. Параметры экранной системы координат (максимальное число пикселов в строке
Xmax и максимальное число строк пикселов Ymax) зависят от типа монитора, видеоадаптера и текущего графического режима. Таким образом, эта система координат определенным образом связана с конкретным графическим устройством и режимами его работы.Вторая система координат – так называемая мировая или математическая. Она представляет собой декартову систему (
x, y), определяемую программистом, и является независимой от конкретного графического устройства:xmin < x < xmax
ymin < y < ymax
Параметры, которыми задаются диапазоны изменения
x и y (xmin, ymin, xmax, ymax), определяют прямоугольную область в математическом двумерном пространстве. Эти параметры зависят только от конкретной задачи.Мировые координаты и координаты устройства связаны между собой простыми соотношениями:
(1)
Формула для “экранной” координаты
Y несколько отличается от формулы для координаты X в силу того, что в экранной системе координат ось OY направлена вниз.Для того, что бы каждый раз, когда возникает необходимость в преобразовании координат из одной системы в другую, не писать эти выражения, удобно оформить вычисление X и Y в виде функций. Например:
VAR
xmin, xmax, ymin, ymax : real; { математические координаты
}
FUNCTION Xs( x : real ) :
integer;
BEGIN
Xs := round( GetMaxX * (x-xmin)/(xmax-xmin) );
END;
FUNCTION
Ys( y : real ) : integer;
BEGIN
Ys := round( GetMaxY * (1-(y-ymin)/(ymax-ymin)) );
END;
Разумеется, что наличие двух систем координат вовсе не означает, что во всех без исключения случаях необходимо их использовать. Просто существуют ситуации, когда гораздо удобнее работать именно с мировыми координатами, не особенно задумываясь при этом о том сколько же на экране пикселов по горизонтали и вертикали.
В качестве примера использования
функций преобразования координат рассмотрим
следующую задачу. В центре экрана необходимо
отобразить некоторую область (см. рис.) и точку,
координаты которой вводятся с
клавиатуры.
Предположим для начала, что нам нужно просто отобразить на экране оси координат (без разметки) и контуры заданной области. Это вполне можно сделать работая непосредственно с экранными координатами. Допустим, что разрешение экрана 640
х480 пикселов:
Для построения осей и контура области воспользуемся процедурами
Line и Rectangle:{ Оси }
Line(0,240,639,240);
Line(320,0,320,479);
{ контур }
Line(220,240,320,240);
Line(220,240,320,140);
Line(320,140,320,240);
Rectangle(320,240,420,340);
С построением области проблем не возникло. Пусть теперь необходимо отобразить на экране точку с координатами
x = 0.6, y = -0.8. Для того, чтобы “поставить” на экране точку нужно вызвать процедуру PutPixel. Но процедура PutPixel, также как и другие графические процедуры, работает только с экранными координатами. А какие экранные координаты соответствуют точке (0.6, -0.8)? Конечно, их можно рассчитать:X=320+int((420-320)*0.6) Y=240-int((340-240)*(-0.8)) |
Подумайте, как получены эти формулы? |
Но гораздо удобнее в этом случае с самого начала работать в мировой системе координат. Определим ее, например, следующим образом:
xmin = -2, xmax = 2, ymin = -2, ymax = 2
Построим контур области работая в мировой системе координат, при этом воспользуемся определенными ранее функциями
Xs(x) и Ys(y).{
Определяем параметры мировой СК }
xmin := -2;
xmax := 2;
ymin := -2;
ymax := 2;
{ Работаем в мировой СК }
Line( Xs(-1), Ys(0), Xs(0), Ys(1) );
Line( Xs(-1), Ys(0), Xs(0), Ys(0) );
Line( Xs(0), Ys(1), Xs(0), Ys(0) );
Rectangle( Xs(0), Ys(0), Xs(1), Ys(-1) );
Для того чтобы отобразить точку с координатами
x = 0.6, y = -0.8 теперь достаточно написать:PutPixel( Xs(0.6), Ys(-0.8), White );
Иногда при построении изображений возникает необходимость разделить экран на несколько прямоугольных областей (графических окон) с тем, чтобы в каждом из окон строить свое изображение. В модуле
GRAPH для этих целей имеется специальная процедура SetViewPort:SetViewPort( X1, Y1, X2, Y2 : Integer; ClipMode : Boolean );
где переменные X1, Y1, X2 и Y2 задают координаты диагонали окна, а параметр ClipMode определяет будет ли отображаться часть изображения попавшая за пределы окна или нет. Параметр ClipMode может принимать два значения. Значение ClipOn (true) указывает на то, что часть изображения попавшая за пределы окна не должна выводится на экран, а значение ClipOff (false) указывает на возможность вывода изображения попавшего за границы окна. После выполнения этой процедуры графический экран “сжимается” до размеров окна, текущий указатель перемещается левый верхний угол окна и туда же перемещается начало координат. Если параметр ClipMode равен ClipOn, то часть экрана вне окна становится недоступной.
Назначение графического окна можно использовать для перемещения начала системы координат устройства. Так, если задать окно вызовом
SetViewPort( GetMaxX div 2, GetMaxY div 2, GetMaxX, GetMaxY, ClipOff );
то получим систему координат с началом в центре экрана. При этом станет “видимой” адресация отрицательных координат.
Работу с графическими окнами можно организовать и без использования процедуры
SetViewPort. Для этого достаточно лишь немного модифицировать рассмотренные ранее функции преобразования координат.Выделим на экране прямоугольную область
с диагональю (XWmin,YWmin) – (XWmax,YWmax):Совместим с этой областью математическую систему координат, заданную параметрами
xmin, ymin, xmax и ymax. Формулы преобразования координат в этом случае будут иметь следующий вид:(2)
При работе с мировыми координатами и использовании данного способа организации графических окон, целесообразно включить в программу следующий блок описаний:
CONST
XWmin : integer = 0; { Переменные, определяющие окно
вывода }
YWmin : integer = 0; { по умолчанию – весь экран }
XWmax : integer = 639;
YWmax : integer = 479;
VAR
xmin, xmax, ymin, ymax : real; { Мировая система координат }
PROCEDURE SetWorldCoords( x1,
y1, x2, y2 : real );
{ Процедура назначения мировых координат }
BEGIN
xmin := x1; xmax := x2;
ymin := y1; ymax =: y2
END;
PROCEDURE SetWindow( x1, y1, x2,
y2 : integer );
{ Процедура установки параметров окна вывода }
BEGIN
XWmin := x1; XWmax := x2;
YWmin := y1; YWmax := y2
END;
{ Функции преобразования мировых координат к координатам устройства }
FUNCTION Xs( x : real ) :
integer;
BEGIN
Xs := XWmin + round( (XWmax-XWmin)* (x-xmin)/(xmax-xmin) )
END;
FUNCTION Ys( y : real ) :
integer;
BEGIN
ys := YWmax - round( (YWmax-YWmin)* (y-ymin)/(ymax-ymin) )
END;
Введение в программу процедур
SetWorldCoords и SetWindow не является обязательным, поскольку их действия можно реализовать и непосредственно в основном блоке программы. При организации нескольких окон вывода использование этих процедур становится целесообразным, поскольку сокращает текст программы и улучшает ее читабельность. Более того, согласно принципам структурного программирования, любую более или менее обособленную и законченную операцию желательно оформлять в виде отдельной подпрограммы.
График является самым наглядным способом представления экспериментальных данных, отображения зависимости, полученной в результате численного моделирования и т.п. Поэтому построение различных графиков является одним из важных этапов решения многих прикладных задач. Графики бывают разных видов: точечные, линейные (в виде ломаной линии, соединяющей отдельные точки), полигоны, графики в виде столбиковой диаграммы (гистограммы), в виде секторной диаграммы и т.д.
Рассмотрим в качестве примера задачу построения линейного графика произвольной функции
в заданном диапазоне изменения аргумента . Исходными данными в этой задаче являются: функция, график которой строится, диапазоны изменения аргумента a и b, и количество точек N, отображаемых на графике (или шаг изменения аргумента dx). При решения поставленной задачи желательно придерживаться следующей последовательности действий:Рассмотрим реализацию некоторых из перечисленных действий и оформим их в виде отдельных процедур и функций. В основной программе опишем следующие константы и переменные:
CONST
N = 50;
VAR
X, Y : array[1..N] of real;
Константа
N задает число точек отображаемых на графике. В массивы X и Y мы поместим результаты табулирования функции.Кроме этого, будем подразумевать, что в программу включено описание:
Табулирование функции:
PROCEDURE
TablFunc( a, b : real );
VAR
i : integer;
function F( x : real ) : real;
begin
F := выражение для функции, график которой
нужно построить;
end;
BEGIN
for i:=1 to N do begin
X[i] := a + (b-a)/(N-1)*(i-1);
Y[i] : F( X[i] );
end;
END;
Определение минимального и
максимального значений функции:
Определение минимального и максимального
значения функции в данном случае сводится к
поиску минимального и максимального элемента в
массиве Y, который получен на этапе табулирования
функции. Оформим эту операцию в виде отдельных
функций для минимального и максимального
значений, соответственно:
FUNCTION
MinY : real;
VAR i : integer;
m : real;
BEGIN
m := Y[1];
for i:=2 to N do if Y[i]<m then
m := Y[i];
MinY := m
END;
FUNCTION
MaxY : real;
VAR i : integer;
m : real;
BEGIN
m := Y[1];
for i:=2 to N do if Y[i]>m then
m := Y[i];
MaxY := m
END;
Оси координат:
Основой для построения любых графиков и диаграмм
является система координат. Ниже приведена
процедура построения на экране декартовой
системы координат с изображением осей и
разметкой по периметру. Процедура имеет два
параметра nx и ny, которые определяют количество
штрихов на оси OX и OY, соответственно.
PROCEDURE
BuildCoords( nx, ny : Byte );
VAR
i : Byte;
v : real;
s : string;
BEGIN
SetColor( White );
Rectangle( XWmin, YWmin, XWmax, YWmax ); { область графика }
Line( Xs(xmin), Ys(0), Xs(xmax), Ys(0) ); { ось ОХ }
Line( Xs(0), Ys(ymax), Xs(0), Ys(ymin) ); { ось OY }
SetTextStyle(SmallFont,0,5); { выбор шрифта Small }
SetTextJustify(CenterText,CenterText); { выравнивание текста }
{ Разметка оси Х }
for i:=0
to nx-1 do begin
v := xmin + (xmax-xmin)/(nx-1)*i; { координата i-го
штриха }
Str(v:5:2,s); { преобразование числа в строку
}
Line(Xs(v), YWmax, Xs(v), YWmax+5); { черчение i-го
штриха }
OutTextXY(Xs(v), YWmax+15, s); { вывод числа }
end;
{ Разметка оси Y }
for i:=0 to
ny-1 do begin
v := ymin + (ymax-ymin)/(ny-1)*i;
Str(v:5:2,s);
Line(XWmin, Ys(v), XWmin-5, Ys(v));
OutTextXY(XWmin-30, Ys(v), s);
end;
END;
Построение графика:
PROCEDURE Graphic(
Color : word );
VAR i : integer;
BEGIN
SetColor( Color );
MoveTo( Xs(X[1]), Ys(Y[1]) );
Circle( Xs(X[1]), Ys(Y[1]), 2);
FOR i:=2 TO N DO begin
LineTo( Xs(X[i]), Ys(Y[i]) );
Circle( Xs(X[i]), Ys(Y[i]), 2);
end;
END;
Процедура
Graphic строит точечно-линейный график по точкам, хранящимся в массивах X и Y. Параметр Color определяет цвет линии.Таким образом, с учетом рассмотренных выше подпрограмм, программа построения графиков функций будет иметь следующую структуру:
PROGRAM
Graphic_of_Function;
USES CRT, Graph;
CONST
N = 50;
VAR
X, Y : array[1..N] of real;
VAR
Описание переменных XWmin, XWmax, YWmin, YWmax;
Описание переменных xmin, xmax, ymin, ymax;
Описание процедуры SetWorldCoords;
Описание процедуры SetWindow;
Описание функции Xs;
Описание функции Ys;
Описание процедуры TablFunc;
Описание функции MinY;
Описание функции MaxY;
Описание процедуры BuildCoords;
Описание процедуры Graphic;
VAR
a, b : real;
GrDr, GrMd : integer;
BEGIN
ClrScr;
Write(‘Введите пределы изменения аргумента [a,
b]: ’);
ReadLn(a, b);
TablFunc( a, b );
GrDr := detect;
InitGraph( GrDr, GrMd, ‘C:\TP7\BGI’ );
SetWindow( 100, 50, 500, 400 );
SetWorldCoords( a, MinY, b, MaxY );
BuildCoords( 5, 5 );
Graphic( Yellow );
ReadKey;
CloseGraph;
END.
Эту программу легко изменить с тем, что бы она строила график по заданным точкам, полученным, например, в результате некоторого эксперимента. Собственно говоря, ничего менять для этого не нужно. Необходимо написать новую процедуру заполнения массивов
X и Y. Напомним, что раньше этим занималась процедура TablFunc. Процедура ввода данных может быть оформлена следующим образом:PROCEDURE
ReadDATA;
VAR
i : integer;
BEGIN
WriteLn(‘Введите попарно координаты точек X, Y:
’);
FOR i:=1 TO N DO begin
Write(i,‘-я точка: ’);
ReadLn( X[i], Y[i] )
end
END;
После чего в основном блоке программы вместо вызова процедуры
TablFunc нужно поставить вызов процедуры ReadDATA.На первый взгляд, рассмотренная выше программа построения линейных графиков может показаться несколько сложной (хотя это только кажущаяся сложность) и перегруженной различными процедурами и функциями. Основное достоинство рассмотренного подхода и программы в их относительной универсальности: какую бы Вы функцию не задали, какие бы Вы не ввели пределы изменения аргумента, программа обязательно построит желаемый график. Причем все изображение графика будет точно умещаться в рамках отведенного окна. Безусловно за универсальность приходится платить, и в данном случае, ценой введения разнообразных переменных и подпрограмм. Зато используя арсенал этих подпрограмм можно с одинаковой легкостью построить на экране как один график, так и целых десять, каждый из которых будет иметь свои оси координат.
При желании описание некоторых подпрограмм (таких, как SetWindow, SetWorldCoords, Xs, Ys), а также переменных XWmin, XWmax, YWmin, YWmax, xmin, xmax, ymin и ymax можно оформить в виде отдельного модуля. Можно также добавить процедуры построения графиков других видов. Все это позволит в дальнейшим значительно упростить написание программ, предусматривающих построение графиков.
При решении различных задач, например, связанных с компьютерным моделированием тех или иных процессов, часто возникает необходимость отобразить получаемые результаты графически. При этом возможно, что к оформлению предъявляются самые минимальные требования, особенно если результаты являются промежуточными. В этом случае, конечно же, нет необходимости писать универсальную программу для построения графиков, и поэтому многие вещи, рассмотренные ранее, можно существенно упростить.
Для примера рассмотрим простую задачу:
Камень брошен вверх под углом к горизонт с начальной скоростью . Требуется построить на экране траектории движения камня при разных значениях угла бросания .
Уравнения движения для координат камня
x и y в неподвижной инерциальной системе отсчета имеют вид:,
При заданных значения
и максимальная высота подъема камня над горизонтом и максимальная дальность полета определяются соответственно:,
.
Для построения траектории движения камня при заданных значения
и необходимо, изменяя значение t с некоторым шагом , вычислять координаты x и y и отображать их на экране. Для того чтобы на экране “уместились” все траектории с различными значениями угла бросания необходимо сначала определить максимальную дальность полета и высоту подъема при заданном значении . Очевидно, что максимальная дальность полета будет равна (при ), а максимальная высота подъема (при ).Определим декартову систему координат (мировые координаты) относительно которой будем строить траектории движения камня:
xmin = 0, xmax = , ymin = 0, ymax = .
С учетом формул (1) экранные координаты будут определятся следующими выражениями:
, ,
где введены обозначения
: и .Ниже приведена соответствующая программа.
PROGRAM
Stone;
USES CRT, Graph;
CONST
g = 9.8;
VAR
x, y, t, dt,
v0, alpha,
gx, gy : real;
xs, ys,
GrDr, GrMd : integer;
BEGIN
ClrScr;
Write(‘Введите начальную скорость камня Vo [м/с]:
’);
ReadLn(v0);
{ Инициализация графического режима }
GrDr := detect;
InitGraph(GrDr, GrMd, ‘C:\TP7\BGI’);
Line(0, GetMaxY, GetMaxX, GetMaxY ); { ось OX (поверхность
Земли) }
{ Масштабные множители }
gx := GetMaxX / ( sqr(v0)/g );
gy := GetMaxY / ( sqr(v0)/(2*g) );
{ Начальный угол бросания }
alpha := 10;
REPEAT
{ Шаг по времени = 1/1000 от времени полета
}
dt := (2*v0*sin(alpha*pi/180)/g)/1000;
t := 0;
REPEAT
x := v0*cos(alpha*pi/180)*t;
y := v0*sin(alpha*pi/180)*t-g*sqr(t)/2;
{ Вычисляем экранные координаты
и ставим желтую точку }
xs := round( gx*x );
ys := GetMaxY - round( gy*y );
PutPixel( xs, ys, Yellow );
t := t + dt;
UNTIL (y<0);
alpha := alpha + 5; { Увеличиваем угол бросания
на 5 градусов }
UNTIL (alpha>90);
ReadKey;
CloseGraph;
END.
Содержание | Назад | Вперед |