Работа с графикой. Системы координат. Построение графиков функций

Содержание |  Назад  |  Вперед

Система координат устройства и мировая система координат
Окно вывода
Построение графиков
Когда нет времени...

1. Система координат устройства и мировая система координат

При работе с компьютерной графикой приходится иметь дело с двумя системами координат. Первая система – это система координат устройства (или экранная система координат). Координатами точки в этой системе являются номер пиксела в строке X и номер строки пикселов Y:

dev_cs.gif (3373 bytes)

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;

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

В качестве примера использования функций преобразования координат рассмотрим следующую задачу. В центре экрана необходимо отобразить некоторую область (см. рис.) и точку, координаты которой вводятся с
клавиатуры.

wpe13.jpg (4414 bytes)

Предположим для начала, что нам нужно просто отобразить на экране оси координат (без разметки) и контуры заданной области. Это вполне можно сделать работая непосредственно с экранными координатами. Допустим, что разрешение экрана 640х480 пикселов:

wpe14.jpg (6324 bytes)

 

Для построения осей и контура области воспользуемся процедурами 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 );

 

2. Окно вывода

Иногда при построении изображений возникает необходимость разделить экран на несколько прямоугольных областей (графических окон) с тем, чтобы в каждом из окон строить свое изображение. В модуле 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):

ww_cs.gif (5054 bytes)

Совместим с этой областью математическую систему координат, заданную параметрами 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 не является обязательным, поскольку их действия можно реализовать и непосредственно в основном блоке программы. При организации нескольких окон вывода использование этих процедур становится целесообразным, поскольку сокращает текст программы и улучшает ее читабельность. Более того, согласно принципам структурного программирования, любую более или менее обособленную и законченную операцию желательно оформлять в виде отдельной подпрограммы.

 

3. Построение графиков

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

Рассмотрим в качестве примера задачу построения линейного графика произвольной функции в заданном диапазоне изменения аргумента . Исходными данными в этой задаче являются: функция, график которой строится, диапазоны изменения аргумента a и b, и количество точек N, отображаемых на графике (или шаг изменения аргумента dx). При решения поставленной задачи желательно придерживаться следующей последовательности действий:

  1. ввод исходных данных;
  2. табулирование функции (вычисление значений функции при изменении аргумента от a до b с шагом dx);
  3. определение минимального и максимального значений функции на заданном интервале;
  4. определение мировой системы координат;
  5. построение осей координат;
  6. построение графика функции.

Рассмотрим реализацию некоторых из перечисленных действий и оформим их в виде отдельных процедур и функций. В основной программе опишем следующие константы и переменные:

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 можно оформить в виде отдельного модуля. Можно также добавить процедуры построения графиков других видов. Все это позволит в дальнейшим значительно упростить написание программ, предусматривающих построение графиков.

 

4. Когда нет времени…

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

Для примера рассмотрим простую задачу:

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

Уравнения движения для координат камня 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.

 

Содержание |  Назад  |  Вперед

Права на материал принадлежат их авторам
Вернуться на главную страницу