Сайт Льва Волкова
  
· Первый Роллс-Ройс стоил 600 тыс. дол. (1906 г). Сегодня самый дешевый из Роллс-Ройсов стоит 200 тыс. дол.
 
      На главную  
 Личное
  Статьи
  Задачи 
 Ссылки
 АТ-531
www.levvol.ru    
 

Кривые и повороты на вашем мониторе

Чтобы дать читателю минимальные сведения о рисовании кривых на экране, рассмотрим, в качестве примера, как на экране получить обыкновенную окружность. Вспомним три способа задания окружности. Первый и наиболее привычный способ знаком со школы:
  x2+y2=r2  

Это уравнение множества точек, лежащих на окружности. Любая точка с декартовыми (прямоугольными) координатами лежит на окружности тогда и только тогда, когда сумма квадратов x и y равна квадрату радиуса окружности r.

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

На Турбо Паскале это выглядит так:


Program Test_1;
Uses CRT, Graph;
Var d,m,r,y:integer;
x:real;
Begin
{установка графического режима}
d:=detect; m:=detect;
InitGraph(d,m,'');
{----------------------------}
r:=100; {радиус окружности}
x:=-r;
while x<=r do begin
y:=round(sqrt(r*r-x*x));
{окружность с центром в точке 320, 240}
PutPixel(320+round(x),240+y,white);
PutPixel(320+round(x),240-y,white);
x:=x+0.01;
end;
{ждать нажатия клавиши}
repeat
until KeyPressed;
CloseGraph {закрыть графический режим}
End.


Сохранить эту программу

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

В декартовой системе координаты точки x, y указывают удаление точки по горизонтали и вертикали от некой центральной точки, называемой началом отсчета (в нашей программе это точка с координатами 320, 240).

Полярные координаты R, θ  определяют точку совсем по - другому: R-расстояние от точки до начала отсчета, θ  — угол между линией, соединяющей данную точку с точкой начала отсчета, и горизонтальной прямой. Уравнение окружности в полярных координатах имеет очень простой вид R=r.

Независимо от величины угла  θ  расстояние R всегда равно радиусу окружности r. К сожалению, программа для изображения окружности в полярных координатах весьма сложна и мы её не приводим.

Третье представление основано на так называемых параметрических уравнениях. Для окружности они имеют вид:
   

Эти уравнения очевидны из рисунка.
   
  OM=r; OA=x=r cos(t) ; AM=y=r sin(t)  

Если параметр t заставить принимать значения от 0 до 2π, то точка M будет описывать окружность. Часть программы будет выглядеть так:



t:=0; r:=100;
while t<=2*pi do
begin
x:=round(320+r*cos(t));
y:=round(240+r*sin(t));
{окружность с центром в
точке 320, 240}

PutPixel(x,y,white);
t:=t+0.01
end;




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

 
 


Если взять  r1>r2,  то эллипс будет вытянут вдоль оси x и наоборот —  при r1<r2 он вытянется вдоль оси y.

Уравнения кардиоиды - кривой напоминающей человеческое сердце:

   

Параметрические уравнения нефроиды - кривой представлющей почки:
   

В Сети существует множество сайтов, посвященных параметрическим кривым [1-3]. Названия этихк кривых звучат весьма загадочно: ведьмин локон, астроида, циклоида, лемнисиката Бернулли, декартов лист и т.п. Получено уравнение кривой под названием каннабола - напоминающей лист конопли [4,5]:
  R = (1 - Sin(t))·(1 - .9·|(Sin[4t)|)·(.9 - .05·Cos(200t))
x = R·Cos(t)
y = R·Sin(t)
 

Вот еще одна кривая, которая носит название «безумие» (madness)[6]
  x = Sin(0.0099t)-0.7Cos(0.0301t)
y = 0.1Sin(0.1503t)+Cos(0.0101t)
 

Программа для рисования этой кривой:



Program Madness;
Uses CRT, Graph;
Var d,m,x,y :integer;
t:real;
Begin
d:=detect;
m:=detect;
InitGraph(d,m,'');
t:=0;
repeat
x:=320+round(180*(sin(0.0099*t)-0.7*cos(0.0301*t)));
y:=240-round(200*(0.1*sin(0.1503*t)+cos(0.0101*t)));
PutPixel(x,y,white);
t:=t+0.01;
until KeyPressed;
CloseGraph;
End.

Сохранить эту программу

Естественно, при рисовании, например кардиоиды, надо параметр t менять от 0 до 2π, используя цикл while, а её центр поместить в центр экрана (320, 240).
   
  Кардиоида.  

Кстати, кардиоиду можно изобразить не прибегая к параметрическим кривым. Возьмем окружность, разобьем её на 144 равные дуги. При обходе окружности пронумеруем точки дважды: сначала от 1 до 144, затем от 145 до 288. Точку 1 соединим линией (хордой) с точкой 2, точку 2 соединим с точкой 4, точку 3 с точкой 6 и т.д., точку 144 соединим с точкой 288. Пересечения хорд образуют кардиоиду.

Программа для рисования кардиоиды:


Program Cardioid;
Uses CRT, Graph;
Const n=144;
r=200;
t=2*pi/n;
Var x,y: array[1..2*n] of integer;
i,d,m:integer;
Begin
d:=detect; m:=detect;
InitGraph(d,m,'');
SetColor(LightRed);
Circle(340,240,r);
for i:=1 to n do begin
x[i]:=340+round(r*cos(i*t));
y[i]:=240+round(r*sin(i*t));
x[i+n]:=x[i];
y[i+n]:=y[i]
end;
SetColor(green);
for i:=1 to n do line(x[i],y[i],x[i*2],y[i*2]);
repeat
until KeyPressed;
CloseGraph
End.

Сохранить эту программу
   

Продолжим рассмотрение задач компьютерной графики [7].

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

Допустим исходный квадрат имеет вершины с координатами (x[i], y[i]), где i=1,2,3,4, a i-ая сторона квадрата является линией, соединяющей вершину с координатами (x[i], y[i]) с вершиной (x[i+1], y[i+1]), при этом, дополнительно предположим, что если i=4, то i+1 будет равным 1, т.е. 4+1=1 (это называется сложением по модулю 4). Каждая сторона квадрата рассекается точкой (x'[i],y'[i]) в отношении  μ/(1-μ),  где 0≤ μ ≤1. Параметр  μ  равен отношению расстояний от (x[i], y[i]) до (x'[i],y'[i]) и от (x'[i],y'[i]) до (x[i+1], y[i+1]).

Следовательно (x'[i],y'[i]) = (x[i](1- μ)+x[i+1]μ, y[i](1- μ)+y[i+1]μ)


Program Squares;
Uses CRT, Graph;
Procedure Square;
Type crd=record
x,y:integer
end;
{Массив координат вершин}
a_t=array [1..4] of crd;
Procedure Sq(p:a_t;col:integer);
{Рисование квадратов}
Var i,k:integer;
Begin
SetColor(col);
for i:=1 to 4 do begin
if i=4 then k:=1
else k:=i+1;
line(p[i].x,p[i].y,p[k].x,p[k].y);
end;
End;
Var p:a_t;
d,m,i,j,L,k:integer;
mu:real;
xx,yy:integer;
Begin
d:=detect; m:=detect; InitGraph(d,m,'');
mu:=0.075; L:=400;
{Вершины исходного квадрата}
p[1].x:=320-L div 2; p[2].x:=p[1].x;
p[1].y:=240+L div 2; p[4].y:=p[1].y;
p[3].x:=320+L div 2; p[4].x:=p[3].x;
p[2].y:=240-L div 2; p[3].y:=p[2].y;
for i:=1 to 50 do begin
{Строится 50 квадратов}
xx:=p[1].x; yy:=p[1].y;
sq(p,white);
{Движение квадрата - квадрат белого цвета,
задержка, квадрат зеленого цвета}

delay(5000); {Задержка зависит от параметров компьютера}
sq(p,lightgreen);
{Пересчет координат вершин квадрата}
for j:=1 to 4 do
if j<4 then begin
p[j].x:=round((1-mu)*p[j].x+mu*p[j+1].x);
p[j].y:=round((1-mu)*p[j].y+mu*p[j+1].y)
end
else begin
{4-я вершина соединяется с первой}
p[j].x:=round((1-mu)*p[j].x+mu*xx);
p[j].y:=round((1-mu)*p[j].y+mu*yy)
end;
end;
repeat
until KeyPressed;
CloseGraph
End;
Begin
Square
End.

Сохранить эту программу

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

Мы выяснили, как можно изобразить эллипс и как повернуть квадраты. Рассмотрим, как можно получить эллипс, повернутый на заданный угол  α . Для этого к каждой точке эллипса с координатами (x,y) надо применить преобразование поворота.

Для того чтобы получить эти уравнения нужно взять точку в прямоугольной системе координат (x',y') , а затем повернуть систему координат на угол  α, в новой системе координат точка будет иметь координаты (x,y).
Необходимо получить зависимость координаты x' от x и y' от y.
Угол  α  закрашен красным цветом.
Тогда
OA=OCcosα=xcosα 
PC=MCsinα=ysinα 
OB=x'=xcosα-ysinα 
MP=MCcosα=ycosα 
PB=CA=OCsinα=xsinα 
BM=y'=xsinα+ycosα 

Приведенная программа рисует 20 поворачивающихся эллипсов.


Program Ellipses;
Uses CRT, Graph;
Var d,m,x,y:integer;
xx,yy,t,a:real;
Const
{Центр экрана}
xo=320; yo=240;
Begin
d:=detect; m:=detect;
InitGraph(d,m,'');
a:=0;
{Изменение угла поворота}
while a<=2*pi do begin
t:=0;
while t<=2*pi do begin
{Координаты эллипса}
xx:=200*cos(t);
yy:=80*sin(t);
{Поворот}
x:=round(xo+(xx*cos(a)-yy*sin(a)));
y:=round(yo-(xx*sin(a)+yy*cos(a)));
PutPixel(x,y,white);
t:=t+0.001;
end;
a:=a+pi/20;
end;
repeat
until KeyPressed;
CloseGraph
End.


Сохранить эту программу

Наконец программа, рисующая вращающийся кубик.


Program Cube;
Uses CRT, Graph;
Type a_t=array [1..8] of integer;
Procedure Cubik(x,y:a_t;c:integer);
{Процедура рисования кубика,
координаты вершин в массивах х,y}

Begin
SetColor(c);
line(x[1],y[1],x[2],y[2]);
line(x[2],y[2],x[3],y[3]);
line(x[3],y[3],x[4],y[4]);
line(x[4],y[4],x[1],y[1]);
line(x[5],y[5],x[6],y[6]);
line(x[6],y[6],x[7],y[7]);
line(x[7],y[7],x[8],y[8]);
line(x[8],y[8],x[5],y[5]);
line(x[1],y[1],x[5],y[5]);
line(x[2],y[2],x[6],y[6]);
line(x[3],y[3],x[7],y[7]);
line(x[4],y[4],x[8],y[8]);
End;

Var x1,x:a_t;
y1,y:a_t;
d,m:integer;
x0,y0:integer;
t:real;
i:integer;
Begin
d:=detect; m:=detect; InitGraph(d,m,'');
{Начальные координаты вершин кубика}
x[1]:=300; y[1]:=220; x[5]:=320; y[5]:=200;
x[2]:=300; y[2]:=260; x[6]:=320; y[6]:=240;
x[3]:=340; y[3]:=260; x[7]:=360; y[7]:=240;
x[4]:=340; y[4]:=220; x[8]:=360; y[8]:=200;
{Координаты центра вращения}
x0:=310; y0:=200;
t:=0;
repeat
for i:=1 to 8 do begin
{Пересчет координат вершин для поворота}
x1[i]:=x0+round((x[i]-x0)*cos(t)-(y[i]-y0)*sin(t));
y1[i]:=y0-round((x[i]-x0)*sin(t)+(y[i]-y0)*cos(t));
{Минус - вращение против часовой стрелки}
end;
{Рисование кубика белым цветом}
Cubik(x1,y1,white);
delay(1000); {Задержка}
{Рисование кубика черным цветом - цветом фона}
Cubik(x1,y1,black);
t:=t+0.05; {Новый угол поворота}
until KeyPressed; {Ждать нажатия клавиши}
CloseGraph;
End.


Сохранить эту программу


Источники:

  1. Википедия. Кривые
  2. Параметрические кривые
  3. Графики основных функций
  4. Каннабола. Российскими учеными выведена формула конопли
  5. Каннабола. Арбузный форум
  6. Madness curve
  7. Эйджел Й. Практическое введение в машинную графику. М.: Радио и связь, 1984. - 136 с.

Назад