Министерство общего и профессионального образования Российской Федерации Уфимский государственный авиационный технический университет Кафедра технической кибернетики
МЕТОДИЧЕСКИЕ УКАЗАНИЯ к лабораторным работам по курсу "Интерактивная машинная графика" для подготовки инженеров по специальности 220200 "Автоматизированные системы обработки информации и управления" Часть 2 "Линейчатые графические образы"
Уфа 1997
2
Составитель: О.В. Трушин УДК 681.3.06
Методические указания к лабораторным работам по курсу "Интерактивная машинная графика" для подготовки инженеров по специальности 220200 "Автоматизированные системы обработки информации и управления" Часть 2. "Линейчатые графические образы" / Уфимск. гос. авиац. техн. унив-т; Сост. О. В. Трушин. - Уфа, 1997. - 35 с.
Приведены задания для лабораторных работ по второй части курса "Интерактивная машинная графика", где рассматриваются приемы и методы синтеза линейчатых графических образов с использованием как простых, так и достаточно сложных алгоритмов. Рассматриваемые алгоритмы реализованы на языке Turbo-Pascal_7 в форме, позволяющей при необходимости легко адаптировать их для других систем программирования. Ил. 6 . Библиогр.: 10 наимен.
Рецензенты:
В. Н. Мукасеева М. Е. Сидоров
3
Содержание Стр. Введение ........................................................................................ 4 Лабораторная работа № 1. Алгоритмы синтеза фигур из отрезков прямых ............................ 6 Лабораторная работа № 2. Приемы синтеза линейчатых графических узоров ....................... 12 Лабораторная работа № 3. Применение рекурсии при синтезе графических образов ............. 19 Лабораторная работа № 4. Растровые алгоритмы ................................................................... 26 Список литературы ....................................................................... 34
4 Введение Большинство компьютерных графических образов при ограниченности вычислительных ресурсов чаще всего создается как набор отрезков прямых линий, отображающий обычно каркас моделируемого объекта. Подобные модели объектов легко и быстро можно отображать и при необходимости трансформировать как для статических, так и для динамических сцен, в особенности трехмерных. Объекты при этом кодируются как совокупность набора координат вершин и последовательности обхода контура по этим вершинам. При работе с линиями можно определять их режимы вывода и стили. Для отрезков прямых линий режим построения задается оператором SetWriteMode( N ) N=0 ( режим CopyPut ) - замещение линией изображения на экране, этот режим используется по умолчанию, N=1 ( режим XorPut ). - изображение комбинируется. По логической операции Xor ("исключающее ИЛИ") исходное значение цвета пиксела на экране побитово складывается с цветом пиксела выводимой линии. Результирующий бит равен единице при различных значениях соответствующих бит, иначе - равен нулю. Например, при выводе красной линии на желтом фоне в режиме XorPut получим 0100 ( красный ) Xor 1110 ( желтый ) 1010 ( светло-зеленый) Функция Xor, примененная к одной переменной дважды, восстанавливает ее исходное значение ( J xor I ) xor I = J . При повторном проведении линии на том же месте в режиме XorPut изображение этой линии уничтожается, а цвет пикселов экрана становится исходным Xor
1010 ( светло-зеленый) 0100 ( красный ) 1110 ( желтый )
Этот прием удобно использовать при создании движущихся линейчатых изображений без порчи фонового изображения. Толщину и форму ( стиль ) линий отрезков прямых, прямоугольников, окружностей, эллипсов можно задать оператором SetLineStyle( F, P, T ) , где F - форма, Р - стиль, T - толщина линии.
5 Форма линии задается как: F=0 - сплошная, F=1 - пунктирная, F=2 - штрих-пунктирная, F=3 - штриховая, F=4 - форма линии определится параметром Р, причем при F<4 значение Р игнорируется. Стиль линии Р - это число, двоичное представление которого отображает заполнение повторяющегося фрагмента линии длиной 16 пикселов. Если разряд числа равен 1, то пиксел высвечивается, 0 - не высвечивается. Удобно использовать шестнадцатеричную форму записи числа. Например: P = $35F1 = 0 0 1 1 0 1 0 1 1 1 1 1 0 0 0 1 ( двоичное ) отобразит стиль: Можно использовать десятичную форму записи: 0<= P <= 65535= 2 16 -1. Толщина линии может принимать два значения: T=1 - тонкая ( 1 пиксел ), T=3 - толстая ( 3 пиксела ). Применяя к исходным стилям линий пользователя логические операции, можно получать новые стили. Например: P1 and P2 P1 or P2 P1 xor P2 not P2 -
- линии из совпадающих элементов, - добавление элементов стилей, - обнуление совпадающих элементов, инверсный для P2 стиль.
6 Лабораторная работа № 1 АЛГОРИТМЫ СИНТЕЗА ФИГУР ИЗ ОТРЕЗКОВ ПРЯМЫХ 1 Цель работы Практическое освоение простых приемов создания линейчатых графических образов и синтез с использованием этих приемов сложных графических объектов. 2 Построение многоугольников. Наиболее универсальным в графике BGI при построении линейчатых образов является использование процедур Line, LineTo и LineRel. Причем процедуры LineTo( x, y) и LineRel( dX, dY) в отличие от процедуры Line( x1,y1, x2,y2) при своем исполнении перемещают текущий указатель. Это удобно при последовательном выводе контуров из отрезков прямых. Иногда целесообразно применение процедур DrawPoly и FillPoly, обрабатывающих типизированные наборы координат вершин объекта. Пример программы, включающей процедуры построения правильных многоугольников и звездообразных фигур uses Graph, Crt; var Gd,Gm, i: Integer; {------------------------------------------------------------Процедура рисования звезды с центром Xc,Yc, радиусами описанной ( r1 ) и вписанной ( r2 ) окружностей, числом лучей N и цветом color. При r1=r2 получим правильный многоугольник. ----------------------------------------------------------------------------------------} PROCEDURE Star1( Xc,Yc, r1,r2, N, color: integer); var ugol: real; r,i: integer; { угол и радиус для текущей вершины } x,y: array[1..1000] of integer; { массивы координат вершин фигуры } begin if n>500 then Exit; { ограничение по числу вершин звезды } setColor(color); { цвет фигуры } { перебор вершин звезды } for i:=1 to 2*N do begin ugol:= i*pi/N; { угол для текущей вершины } if i mod 2 =0 then R:=r1 else R:=r2; { чередование радиуса для текущей вершины } { массивы координат } x[i]:= Xc + round( R*cos(ugol));
7 { для вершин фигуры } y[i]:= Yc + round( R*sin(ugol)) end; MoveTo( x[2*N], y[2*N]); { переход к последней вершине } { последовательный обход вершин } for i:=1 to 2*N do LineTo( x[i], y[i]) end; {------------------------------------------------------------------Процедура рисования звезды с центром Xc,Yc, радиусом R описанной окружности, числом вершин N и цветом color с самопересечениями сторон. --------------------------------------------------------------------} PROCEDURE Star2( Xc,Yc, R, N, color: integer); var i,j: integer; ugol: real; { угол для текущей вершины } x,y: array[1..500] of integer; { массивы координат вершин фигуры } begin if n>500 then exit; { ограничение по числу вершин звезды } setColor(color); { цвет фигуры } for i:=1 to N do begin { перебор вершин звезды } { угол для текущей вершины } ugol:= i*2*pi/N; { массивы координат } x[i]:= Xc + round( R*cos(ugol)); { вершин фигуры } y[i]:= Yc + round( R*sin(ugol)) end; MoveTo( x[N], y[N]); { переход к последней вершине } for i:=1 to N do begin { обход вершин с перескоками } if N mod 2 =0 then j:=(3*i-1)mod N +1 { если N четно, то шаг через две вершины } else j:=(2*i-1)mod N +1; { если нечетно, то шаг через одну вершину } if( n<5)or( n=6) then j:=i; LineTo( x[j], y[j]) end; end; begin Gd:=0; InitGraph(Gd, Gm, { демонстрация работы программ
{ вырожденные случаи } { рисование при обходе вершин } 'c:\tp7\bgi'); построения звезд }
for i:=1 to 7 do Star1(i*80, i*60, 50,20, i+3, i+8); ReadKey; Cleardevice; for i:=1 to 7 do Star2(i*80, i*60, 50, i+4, i+8); ReadKey; CloseGraph 3 Практическое задание
end.
8 3.1 Составить процедуры рисования многоугольников и звезд с дополнительными отрезками. 3.2 Составить процедуры рисования многоугольников и звезд с растяжкой по горизонтали или вертикали. 3.3 Составить программу заполнения прямоугольных областей орнаментом с использованием процедур рисования правильных многоугольников и звезд. а б в г
4 Построение сложных линейчатых фигур Рассмотрим принцип составления алгоритма построения линейчатых узоров с большим числом самопересечений составляющих узор элементов, а также пример алгоритма демонстрации разновидностей получаемых узоров. uses Graph,Crt; var Gd,Gm, Gx,Gy, i,j, k,n, xc,yc, r, rx,ry, nx,ny,nn: integer; {---------------------------------------------------Процедура рисования звезды с центром Xc,Yc, радиусом R описанной окружности, числом вершин N и цветом color с самопересечениями сторон. Отрезками соединяются наиболее удаленные пары вершин, но не диаметрально противоположные. ----------------------------------------------------------------------------------------} PROCEDURE Star3( Xc,Yc, R, N, color: integer); var i,j,k, step: integer;
9 ugol: real; { угол для текущей вершины } x,y: array[1..500] of integer; { массивы координат } { вершин фигуры } begin if n>500 then exit; setColor(color); { цвет фигуры } for i:= 1 to N do begin { перебор вершин звезды } ugol:= i*2*pi/N; { угол для текущей вершины } x[i]:= Xc + round( R*cos(ugol)); y[i]:= Yc + round( R*sin(ugol)); Circle( x[i],y[i], 2) end; MoveTo( x[N], y[N]); step:=(N-1) div 2; k:=1; for i:=1 to N do begin j:=(step*i-2+k)mod N +1; LineTo( x[j], y[j]);
{ переход к последней вершине } { число пропускаемых вершин } { счетчик числа обходов } { обход вершин с перескоками } { номер очередной вершины } { рисование при обходе вершин }
{ если контур обхода замыкается раньше перебора всех точек } if ( j=N)and( i
Gd:=0; InitGraph(Gd, Gm, 'c:\tp7\bgi'); { демонстрация работы программы построения звезд }
Gx:=GetMaxX; Gy:=GetMaxY; nn:=20; { общее количество рисунков } nx:=5; { количество рисунков по горизонтали } ny:=(nn-1) div nx +1; { и по вертикали } rx:=Gx div nx; ry:=Gy div ny; { размеры области для одного рисунка } if rx>ry then r:=ry div 2 -5 else r:=rx div 2 -5; { размер рисунка } for k:=1 to nn do begin i:=(k-1) mod nx +1; j:=(k-1) div nx +1;
{ позиция рисунка по горизонтали } { позиция рисунка по вертикали }
xc:= rx div 2 +(i-1)*rx; { координаты центров рисунков yc:= ry div 2 +(j-1)*ry; N:=k+2; { число вершин фигуры } Star3(xc,yc, r, N, 8) end;
}
10 ReadKey; CloseGraph end. 5 Практическое задание 5.1 Составить процедуру рисования N-угольной звезды, где N - четное число. Звезда должна состоять из двух пересекающихся правильных N/2-угольников ( например, при N=8 - пересечение двух квадратов ). 5.2 Составить процедуру рисования N-угольной звезды, где N кратно трем. Звезда должна состоять из трех пересекающихся правильных N/3-угольников ( при N=9 - пересечение трех треугольников ). 6 Стили и режимы вывода линейчатых фигур Приведем пример операторов для рисования движущейся линейчатой фигуры (прямоугольника), демонстрирующий в режиме XorPut взаимодействие цветов пикселов выводимых линий и фона. SetWriteMode(1); { режим вывода XorPut } a:= 200; b:= 100; { стороны прямоугольника } x:= GetMaxX div 2; y:= GetMaxY div 2; { координаты левого верхнего угла } SetFillStyle(1,3); Bar(x-50,y-50,x+100,y+100); { фон для картинки } Randomize; Repeat x:=x-9+random(19); y:=y-9+random(19); { случайные смещения координат } Rectangle(x, y, x+a, y+b); { рисование фигуры } delay(300); Rectangle(x, y, x+a, y+b); { стирание фигуры } Until KeyPressed; Вызывая при перерисовке процедуры вывода линейчатых фигур, можно организовать образ движущейся фигуры. 7 Практическое задание 7.1 Составить программу, моделирующую прямолинейное перемещение звезды в пределах экрана. При достижении края экрана звезда должна менять направление и скорость перемещения (отражаться от преграды) и менять свой цвет.
11 7.2 Перемещать три звезды разного цвета в пределах экрана. 7.3 Организовать перемещение звезды с вращением. Для этого в процедуре рисования звезды ввести дополнительно в качестве параметра начальный угол, от которого будет проводиться расчет координат вершин звезды. 7.4 Модернизировать пункты задания 7.1 - 7.3, предварительно (с целью демонстрации особенностей режима XorPut) выведя на экран ряд вертикальных толстых линий разного цвета с одним из двоичных представлений (см. введение ): 0101010101010101, 0111011101110111,
1100011000110001, 1110001110001110,
1111110011111100, 1111000000001111.
7.5 Вывести на экран набор линий со стилями, заданными параметром Р N = 16N 2 +K, где N= 1 .. 24, K=0 .. 4. Линии располагать горизонтально. 7.6 Вывести на экран набор линий со стилями, заданными параметром Р N = 2 N , +K, где N= 1 .. 15, K=0 .. 4. Линии располагать под углом 45 градусов. 7.7 Создать эффект "бегущих огней" перерисовкой в режиме XorPut инверсными стилями набора из трех касающихся толстых пунктирных линий. Модернизировать этот метод, периодически перерисовывая линии с параметром, изменяемым регистровыми сдвигами (использовать процедуры shl или shr ).
12 Лабораторная работа № 2 ПРИЕМЫ СИНТЕЗА ЛИНЕЙЧАТЫХ ГРАФИЧЕСКИХ УЗОРОВ 1 Цель работы Практическое освоение классических алгоритмов, методов и приемов создания линейчатых графических узоров. 2 Многократное рисование фигуры с поворотом относительно центра узора Наиболее простой, но при этом достаточно эффективно работающий метод синтеза узоров состоит в том, что центр многократно рисуемой фигуры (xf, yf) перемещают после очередного рисования относительно центра узора (xc,yc) по какому-либо закону ( рисунок 1). За "центр фигуры" может быть принята любая точка, жестко связанная с фигурой. 0 xc
yc
xf c
X
Fx xf = xc + Fx("параметры"), yf = yc + Fy("параметры").
Fy yf
f
Y Рисунок 1 Опишем в общем виде принцип составления программы рисования при вычислении Fx и Fy через радиус-вектор из центра узора. for i:=1 to n do begin { перебор фигур, составляющих узор } ugol:=F_u(i); { угол поворота очередной фигуры } r:=F_r(i); { расстояние от центра узора до центра фигуры } rf:=F_f(i); { размер фигуры } color:=F_c(i); { цвет фигуры } x:=xc+round(r*cos(ugol));{ координаты центра очередной фигуры } y:=yc+round(r*sin(ugol)); { вывод фигуры вызовом процедуры рисования } F_picture( x,y, rf, color) end; Подбором функций F_u( i), F_r( i), F_f( i), F_c( i), числа и вида фигур можно добиться самых разнообразных декоративных эффектов. В общем случае фигура может перемещаться с вращением и деформировать-
13 ся. Примеры функций для построения узоров из окружностей и из квадратов ( рисунок 2): xc:=100; yc:=100; { центр узора } n:=12; { число фигур, составляющих узор } for i:=1 to n do begin ugol:=2*pi*i/n; r:=10*(i mod 2 +2); rf:=20*(i mod 2 +1); color:=(i-1) mod 3 +10; x:=xc+round( r*cos(ugol)); y:=yc+round( r*sin(ugol)); setcolor(color); circle(x,y,rf) end; yc:=300; for i:=1 to n do begin ugol:=2*pi*i/n; r:=40; rf:=30; x:=xc+round(r*cos(ugol)); y:=yc+round(r*sin(ugol)); rectangle(x-rf, y-rf, x+rf, y+rf) end; Рисунок 2 3 Практическое задание 3.1 Нарисовать узор из 24 эллипсов с центром узора в середине экрана. Радиусы очередного эллипса (Rx, Ry) и расстояние до центра узора циклически изменять. 3.2 Нарисовать узор из 20 прямоугольников. Длины сторон прямоугольников и расстояние от центра узора до левого верхнего угла прямоугольника изменять по закону, который обеспечит приемлемый декоративный эффект. 3.3 Нарисовать узор отрезком прямой , который вращается вокруг своего центра, а этот центр в свою очередь делает один оборот вокруг центра узора. 4 Создание узоров построением зеркальных отображений. Большинство людей впервые знакомятся с некоторыми принципами построения узоров, изучая детский зеркальный калейдоскоп, где система из трех зеркал создает эффект нескольких шестикратных отражений набора цветных кристаллов. Математически такой принцип построения узора можно описать следующим образом. Имеется N выходящих из одной точки лучей - осей симметрии. Угол между лучами равен 2 * π /N. Строится исходная фигура в секторе между первым и вторым лучами. Затем строится вторая фигура как зеркальное отображение первой фигуры относительно второго луча, третья фигура, как зеркальное отображение второй фигуры относительно третьего луча и
14 так далее. Если N - четно, то исходная фигура будет также зеркальным отражением последней фигуры и получившийся узор будет симметричным . Если нет необходимости моделировать реальные отражения, то исходную фигуру можно строить с пересечением осей симметрии и по любому количеству секторов. Ниже приведен пример программы создания узора построением отражений m точек относительно N осей отражения (зеркал). Исходная фигура - замкнутая ломаная, состоящая из разноцветных отрезков прямых ( рисунок 4). Для каждой узловой точки линии определяется расстояние до центра узора (радиус r). Угол (ugol), под которым расположена узловая точка относительно горизонтальной линии, вычисляется как арктангенс отношения ординаты X к абсциссе Y (в системе координат центра узора). Угол, под которым расположена отраженная точка ( рисунок 3), увеличивается на величину, равную удвоенной разности между углом наклона оси отражения и углом расположения отражаемой точки. uses Graph, Crt; type mas= array[1..20] of longint; var Gd,Gm, N,M, i,j, k,l, xc,yc: integer;{ центр узора} r: real; { расстояние от центра узора до узла фигуры } xm,ym, { координаты узлов исходной фигуры } xn,yn, { координаты узлов отражения фигуры } xr,yr: mas; { координаты точек осей симметрии ("зеркал")} alf: array[1..20] of real; { угол между "зеркалом" и осью X } x,y: array[1..30,1..20] of integer;{ координаты отражений узлов} {---------------------------------------------------------} PROCEDURE UZOR(xm,ym: mas); { процедура построения узора в виде ломаной линии из M отрезков} begin moveto( xm[M],ym[M]); for l:=1 to M do begin { в зависимости от значения k фигура либо рисуется, либо стирается ранее нарисованная фигура } if k=1 then setcolor(l mod 7+9) else setcolor(0); lineto( xm[l],ym[l]) end end; {---------------------------------------------------------} PROCEDURE MIRROR(k,xn,yn: integer); { процедура расчета координат отражений узла узора относительно центра узора } var delt, { разность углов отражаемого узла и "зеркала" } ugol: { угол для узла-отражения }
15 array[1..20] of real;
begin xm[1]:=xc+xn; Y ym[1]:=yc-yn; { абсолютные координаты } ugol[1]:=arctan((yc-ym[1])/(xm[1]-xc)); r:=sqrt( (xm[1]-xc)*(xm[1]-xc) +(ym[1]-yc)*(ym[1]-yc) ); for i:=1 to N-1 do begin delt[i]:=alf[i]-ugol[i];
ось симметрии
X Рисунок 3
ugol[i+1]:=ugol[i]+2*delt[i];{ угол очередного узла-отражения } xm[i+1]:=xc +round( r*cos(ugol[i+1])); { и его координаты } end; ym[i+1]:=yc -round( r*sin(ugol[i+1])) end {---------------------------------------------------------} begin Gd:=0; InitGraph(Gd,Gm,''); xc:=GetmaxX div 2; yc:=GetmaxY div 2; N:=12; { число "зеркал" и их цвет } setcolor(8); for i:=1 to N do begin { вывод "зеркал"} alf[i]:=2*PI*i/N; xr[i]:= xc +round( 150*cos(alf[i])); yr[i]:= yc -round( 150*sin(alf[i])); line( xr[i],yr[i],xc,yc) end; M:=10; { число отрезков узора } REPEAT Randomize; { расчет массивов координат M * N всех отражений } for j:=1 to M do begin MIRROR( N,random(200)+1,random(100)); for i:=1 to N do begin x[j,i]:=xm[i]; Рисунок 4 y[j,i]:=ym[i] end end; { построение N фигур по M узлов в каждой } for k:=1 to 2 do begin if k=2 then delay(3000); { задержка для просмотра } for i:=1 to N do begin for j:=1 to M do begin xn[j]:=x[j,i]; yn[j]:=y[j,i] end; UZOR(xn,yn) end end { вызов процедуры построения узора } until KeyPressed; CloseGraph end.
16 5 Практическое задание 5.1 Смоделировать детский калейдоскоп для набора из трех разноцветных "кристаллов" (многоугольников с разным числом углов ), размещенных в одном секторе. 5.2 Смоделировать калейдоскоп с 8 осями симметрии. Смещая при каждом изменении фигуры на пять пикселов диапазон задания координат Х точек исходной ломаной ( 10 раз увеличивая, затем 10 раз уменьшая), получить пульсирующее движение фигур. 5.3 Смоделировать вращение узора, полученного из отражений ломаной линии. 5.4 Смоделировать случайное перемещение узора небольших размеров, полученного из отражений отрезка прямой линии. Контролировать выход узора за границу экрана. 6 Масштабирование вписыванием фигур Рассмотрим один из классических методов создания узоров, состоящий в последовательном делении сторон линейчатой фигуры с построением на точках раздела ( как на вершинах) новой фигуры. PROCEDURE RUNIN(xc,yc,N,R,k: integer); { Многократное вписывание многоугольника делением его сторон в отношении 1/ k } var ugol: real; i,x1,y1: integer; x,y: array[1..100] of integer; begin for i:=1 to N do begin { строим исходный многоугольник } ugol:=i*2*pi/N; x[i]:=xc +round( R*cos(ugol)); y[i]:=yc +round( R*sin(ugol)) end; MoveTo( x[N],y[N]); for i:=1 to N do LineTo( x[i],y[i]); REPEAT { находим координаты точек на сторонах многоугольника, отступая от вершины на определенную часть длины стороны, и переопределим этими точками вершины многоугольника } x1:=x[1]; y1:=y[1]; { запоминаем координаты первой вершины } for i:=1 to N-1 do begin { пересчет координат вершин } x[i]:=x[i] +( x[i+1]-x[i]) div k;
17 y[i]:=y[i] +( y[i+1]-y[i]) div k end; { для последней вершины нужны старые координаты первой и последней вершин } x[N]:=x[N] +(x1-x[N]) div k; y[N]:=y[N] +(y1-y[N]) div k; { строим многоугольник по новым точкам } MoveTo( x[N],y[N]); for i:=1 to N do LineTo( x[i],y[i]) { процесс останавливается, когда меньшая часть стороны становится менее 1 пиксела } UNTIL ( abs(x[1]-x[2])
18 { координаты точки на второй стороне } xx2:=x2+round((x3-x2)*k); yy2:=y2+round((y3-y2)*k); Line( xx1,yy1, xx2,yy2) end
end;
Построим фигуру из M заштрихованных углов r:=100; { размер узора } M:=6; ug:=pi/M; { число углов и их величина } for i:=1 to M do begin alf2:= 2*pi*i/M; {ось симметрии угла} alf1:=alf2+ug; { углы для первой } alf3:=alf2-ug; { и третьей вершины } x1:=x2+round( r*cos( alf1)); y1:=y2+round( r*sin( alf1)); x3:=x2+round( r*cos( alf3)); y3:=y2+round( r*sin( alf3)); UGOL( x1,y1, x2,y2, x3,y3, 6)
end;
Здесь x2, y2 - координаты вершины угла, x1, y1, x3, y3 - координаты крайних точек сторон. Коэффициент k определяет удаленность точки от вершины угла. Огибающая пересекающихся отрезков образует параболическую границу. 9 Практическое задание С использованием небольших модификаций процедуры штриховки угла построить следующие фигуры: а б в г
19 Лабораторная работа № 3 ПРИМЕНЕНИЕ РЕКУРСИИ ПРИ СИНТЕЗЕ ГРАФИЧЕСКИХ ОБРАЗОВ 1 Цель работы Практическое изучение возможностей, особенностей и конкретных приемов использования рекурсивных процедур при синтезе сложных графических образов. 2 Наборы последовательно выводимых отрезков с изменением их ориентации В программировании иногда используют рекурсивные операторы, например, процедуры, которые содержат обращение к самим себе. Это обращение может быть прямое - вызовом процедуры внутри самой процедуры, или косвенное - вызовом других процедур, внутри которых есть вызов исходной процедуры. Рекурсия позволяет компактно реализовать некоторые алгоритмы. При этом нужно корректно определять условие прекращения работы рекурсивной процедуры во избежание зацикливания программы. Приведем пример рекурсивного построения узора последовательным построением отрезков прямых: PROCEDURE OTREZOK(x,y, L,dL, ugol,du: integer); var x1,y1: Integer; { Последовательное построение отрезков с постепенным изменением их длины L и угла ориентации ugol позволяет строить различные спиралевидные узоры, варьируя приращения длины dL и угла du } begin inc(ugol,du); { изменение угла } inc(L,dL); { изменение длины отрезка } { координаты конца отрезка} x1:=x+round( L*cos( ugol*pi/180)); y1:=y-round( L*sin( ugol*pi/180)); line(x,y, x1,y1); { условие продолжения рекурсии } if( x1>0) and( x1
0) and( y1
20 Узор получается перемещением отрезка с вращением вокруг точки (x, y). Поскольку внутри процедуры OTREZOK происходит вызов этой же процедуры с фактическими параметрами (x1, y1), то новое построение отрезка начинается с конца предыдущего. Варьируя alfa и delta, можно строить различные спиралевидные узоры. Следующий пример показывает использование рекурсии при построении узоров, напоминающих кружева. Задается размер "стежка" - короткой линии длиной dl, и функция варьирования угла перемещения ugol в зависимости от номера шага n. uses Graph,Crt; var gD,gM, n,s, x,y, x1,y1: integer; dl, ugol,base_ug, a,b: real; c: char; s1,s2: string; stop: boolean; begin
PROCEDURE ANGLE_1(x,y: integer);
inc(n); { счетчик стежков узора } base_ug:=2*pi*n/s; { базовое направление рисования - окружность наложение пульсаций на базовое направление } ugol:=base_ug + a*sin( b*base_ug); { координаты конца очередного стежка} x1:=round( x +dl*cos(ugol)); y1:=round( y +dl*sin(ugol)); Line(x,y, x1,y1); { строим стежок } { условие прекращения рекурсии } if n<=s then ANGLE_1( x1,y1) end; PROCEDURE ANGLE_2(x,y: integer; base,a,b: real); begin inc(n); { счетчик стежков узора } { Базовое направление рисования - прямая под углом base (рад.), производится наложение пульсаций на базовое направление с амплитудой "a" и с частотой "b" } ugol:=base + a*sin(b*n); { координаты конца очередного стежка} x1:=round( x+dl*cos(ugol)); y1:=round( y+dl*sin(ugol)); Line( x,y, x1,y1);{ строим стежок } { условие прекращения рекурсии } if n<=s then ANGLE_2( x1,y1, base, a,b)
21 end;
begin gD:=0; InitGraph(gD,gM,''); x:=200; y:=170; { координаты начала рисования } dl:=6; { размер пошагового перемещения ("стежка") } s:=500; { число "стежков" узора } { вызов процедуры рисования кружевной полосы } ANGLE_2(x,y, pi/8, 2.2,0.2); { исходные параметры вывода кругового узора } a:=6.0; b:=-6; stop:=false; REPEAT { выбор узора подбором коэффициентов функциональной зависимости } c:=readkey; if c=#0 then c:=readkey; ClearDevice; Case c of { анализируем код нажатой клавиши } #72: a:=a+0.1; { вверх } #80: a:=a-0.1; { вниз } #73: b:=b+1.0; { PgUp } #81: b:=b-1.0; { PgDn } #82: dl:=dl*1.2; { Ins - увеличение размера стежка } #83: dl:=dl/1.2; { Del - уменьшение размера стежка } #27: stop:=true; { при нажатии Esc прекращаем } end; n:=0; ANGLE_1(x,y); { вызов процедуры рисования } Str(a:6:1, s1); outtextxy(500,400,'a='+s1);{ контроль значений коэффициентов } Str(b:6:1, s2); outtextxy(500,420,'b='+s2) until stop; CloseGraph end. 3 Практическое задание 3.1 С использованием модифицированной процедуры OTREZOK построить прямоугольный и ромбический спиралевидные лабиринты из отрезков разного цвета. 3.2 Построить узор из квадратов, диагональ которых определяется согласно процедуре OTREZOK.
22 3.3 Создать программу построения звезды с нечетным числом лучей, где отрезками соединяются наиболее удаленные друг от друга пары вершин. 3.4 Создать процедуру построения кружевных прямоугольных рамок. 3.5 Создать программу построения узоров с подбором коэффициентов при использовании наложения на базовое направление узора двух пульсаций с разными частотой и амплитудой: ugol:=base_ug +a1 * sin( b1 * base_ug) +a2 * sin( b2 * base_ug). Построить показанные справа фигуры. 4 Разветвляющиеся наборы отрезков Некоторые задачи построения графических образов успешно решаются только при использовании рекурсии при построении алгоритма. Пример - построение непрерывно разветвляющихся наборов ломаных линий при постепенном изменении размера составляющих ломаные отрезков. PROCEDURE BETKA(_dl: integer); { Рекурсивное рисование "веток" - наборов последовательно расположенных отрезков, постепенно меняющих размер и ориентацию. } begin if (_dl< 9 ) or (_dl>100) then exit; { Ограничение диапазона размеров "ветки" является условием прекращения рекурсии } x:=x +round(_dl*cos(ugol)); y:=y +round(_dl*sin(ugol)); { x,y - текущие координаты конца "ветки", начало "ветки" - текущее положение графического указателя. Установка стилей для элементов рисунка } SetLineStyle(0,0,3); if(_dl>40) then Setcolor( 6) else Setcolor(10); if(_dl<30) then SetLineStyle(0,0,1); Lineto(x,y); { рисование "ветки" в виде отрезка } ugol:=ugol-ug_b/2; { смещение направления очередного "отростка"} { рекурсивный вызов строит первую ломаную линию } BETKA( round(_dl*m));
23 ugol:=ugol +ug_b; { смена направления для очередной ломаной} { рекурсивный вызов строит вторую ломаную линию } BETKA( round(_dl*m)); { возврат к предшествующей координате перед началом новой ломаной} ugol:=ugol-ug_b/2; x:=x-round(_dl*cos(ugol)); y:=y-round(_dl*sin(ugol)); Moveto(x,y); end; begin
gD:=Detect; InitGraph(gD, gM,'c:\tp7\bgi'); SetBkColor(8); x:=300; y:=450; { координаты начала рисования } ugol:=-pi/2; { начальный угол рисования } ug_b:=pi/4; { угол между " ветками" } m:=0.8; { коэффициент пошагового изменения размера "ветки" } MoveTo(x,y); { переход к точке начала рисования } BETKA(90); { в процедуру передается длина "базовой ветки"} readkey; CloseGraph end. При необходимости увеличения количества разветвлений в узлах узора необходимо увеличивать количество рекурсивных вызовов внутри процедуры рисования узора. Пример построения "дерева с четырьмя отростками от каждой ветки": PROCEDURE BETKA_4(_dl: integer);
begin if (_dl< 24)or(_dl>180) then exit; x:=x+round(_dl*cos(ugol)); y:=y+round(_dl*sin(ugol)); { текущие координаты конца "ветки" } Lineto(x,y); { рисование "ветки" в виде отрезка } { смещение направления очередного "отростка" и построение ломаной линии } ugol:=ugol-u[1]; BETKA_4(round(_dl*m[1])); ugol:=ugol+u[1]-u[2]; BETKA_4(round(_dl*m[2])); ugol:=ugol+u[2]-u[3]; BETKA_4(round(_dl*m[3])); ugol:=ugol+u[3]-u[4]; BETKA_4(round(_dl*m[4])); ugol:=ugol+u[4]; x:=x-round(_dl*cos(ugol)); y:=y-round(_dl*sin(ugol)); Moveto(x,y) end; begin
gD:=Detect; InitGraph(gD,gM,'c:\tp7\bgi'); x:=300; y:=450; { координаты начала рисования } ugol:=-pi/2; { начальный угол рисования }
{ коэффициенты пошагового изменения размера "веток" } m[1]:= 0.7; m[2]:=0.7; m[3]:= 0.7; m[4]:=0.7; { углы между ветками и начальным углом рисования }
24 u[1]:=-0.7;
u[2]:=0.3;
MoveTo(x,y); BETKA_4(140); 5 Практическое задание
u[3]:=-0.3; readkey;
u[4]:=0.7; CloseGraph
end.
Создать процедуру построения деревьев с тремя разветвлениями в узлах, в параметры этой процедуры включить размер дерева и густоту кроны. Многократным вызовом процедуры создать образ лесного массива. 6 Лабиринты Гильберта При неизменной длине отрезков рисования по описанным выше принципам можно спрограммировать построение лабиринтов по алгоритму Гильберта. В приведенной ниже программе размер лабиринта определяется глубиной вложенности рекурсивного вызова процедур рисования стенок лабиринта. uses Graph,Crt; var gD,gM, x,y: integer;
ugol: real;
PROCEDURE STEP( ugol,_dl: real; var x,y: integer); { рисование отрезка длиной _dl под углом ugol от текущего указателя ( x,y ) } begin x:=x+round(_dl*cos(ugol)); y:=y+round(_dl*sin(ugol)); Lineto(x,y) end; PROCEDURE LAB(_dl,Rr,i: integer); { Рекурсивное рисование лабиринтов : _dl - длина отрезков стенок лабиринта, определяет ширину проходов; Rr - глубина вложенности рекурсии, определяющая размер лабиринта; i - коэффициент смены угла поворота проходов ( под прямым углом ) } begin if Rr=0 then exit;{ ограничение .рекурсивного вызова процедуры } рекурсия {смена направления |_________________| |________________| |_________________|} LAB(_dl,Rr-1,-i); ugol:=ugol+i*pi/2; STEP(ugol,_dl,x,y); LAB(_dl,Rr-1, i); ugol:=ugol-i*pi/2; STEP(ugol,_dl,x,y);
вывод линии
.
25 ugol:=ugol-i*pi/2; STEP(ugol,_dl,x,y);
LAB(_dl,Rr-1, i); LAB(_dl,Rr-1,-i);
ugol:=ugol+i*pi/2; end; { инициализация процедуры построения лабиринтов Гильберта } begin gD:=Detect; InitGraph(gD,gM, 'c:\tp7\bgi'); x:=10; y:=10; { координаты начала рисования } ugol:=0; { начальный угол рисования } MoveTo(x,y); { переход к точке начала рисования } LAB(14,5,1); { вызов процедуры рисования } readkey; CloseGraph end. 5 Практическое задание 5.1 Построить лабиринт Гильберта с различным цветом вертикальных и горизонтальных стенок. 5.2 Создать процедуру построения лабиринтов, в которых направление меняется под углом 45 или 60 градусов.
26 Лабораторная работа № 4 РАСТРОВЫЕ АЛГОРИТМЫ 1 Цель работы Практическое освоение стандартных алгоритмов построения линий как набора пикселов на растровой сетке, а также разработка подобных алгоритмов для специфических задач, не охваченных в классических методиках. 2 Алгоритмы Брезенхейма Большинство графических устройств ввода и вывода информации оперируют с изображением как с набором пикселов в виде прямоугольной матрицы - растровой сеткой. Перевод геометрических объектов в их растровые образы в большинстве графических библиотек реализован лишь для простых наиболее часто используемых на практике случаев ( построение отрезков прямых, прямоугольников, окружностей ). Часто возникает необходимость геометрических построений, не охваченных стандартным набором графических функций. В алгоритмах геометрических построений как наборов пикселов используется понятие связности - при каких условиях этот набор считается непрерывным. Для 4-связности необходимо, чтобы а координаты соседних пикселов по оси X либо по оси Y отличались не более чем на единицу, для другой оси координаты б должны быть одинаковы ( рисунок 1,а). Рисунок 1 Для 8-связности необходимо, чтобы координаты соседних пикселов по обеим осям отличались не более чем на единицу ( рисунок 1,б). Примеры типовых алгоритмов построения точечных образов: PROCEDURE Line1(x1,y1,x2,y2,color: integer); var a,b: real; { Типовой алгоритм построения отрезка прямой. Необходимо x1<>x2, y1<>y2, желательно dx>dy } begin { коэффициенты для представления прямой в декартовой системе координат } a:=(y2-y1)/(x2-x1); b:=y1-a*x1;
27 repeat
putpixel(x1, round( a*x1+b),color); inc(x1) { пошаговый перебор по оси абсцисс } until x1=x2 end; { Алгоритм построения отрезка прямой с модернизациями для непрерывности } PROCEDURE Line2( x1,y1,x2,y2, color: integer); var a,b, x,y, d: real; begin { если угол между отрезком и горизонталью меньше 45 град. то строим отрезок, перебирая по точкам X } if ( abs( x2-x1)>abs( y2-y1) ) and( x1<>x2) then begin { коэффициенты прямой } a:=(y2-y1)/(x2-x1); b:=y1-a*x1; { направление перебора координат } if(x1<x2) then d:= 1 else d:=-1; x:=x1; repeat putpixel( round(x),round( a*x+b),color); x:=x+d until round(x)=x2 end { если угол между отрезком и горизонталью больше о 45 , то строим отрезок, перебирая по точкам Y } else if y1<>y2 then begin a:=(x2-x1)/(y2-y1); b:=x1-a*y1; if(y10 then dx:=dx div ix; if dy<>0 then dy:=dy div iy;
28 { Максимальный из диапазонов координат } if ix>iy then max:=ix else max:=iy; p_X:=x1; { начальная точка построения } p_Y:=y1; putpixel(p_X, p_Y, color); { приращения контрольных величин равны диапазонам координат отрезка } x:=ix; y:=iy;. { Перебираем по точкам максимальный диапазон координат. При этом наращиваются контрольные величины x и y. Когда какая-нибудь из них превышает max, то производится приращение соответствующей координаты для пикселов, вывод пиксела и сброс контрольной величины на значение max. Таким образом пикселы "стягиваются" к положению идеальной прямой. } for i:=1 to max do begin inc( x,ix); inc( y,iy); plot:=false; if x>=max then begin plot:=true; dec( x,max); if y>=max then begin plot:=true; dec( y,max);
inc( p_X,dx)
end;
inc( p_Y,dy)
end;
if plot then putpixel( p_X,p_Y,color); {***} end
end;
При необходимости построения прерывистой линии несложно доработать алгоритм Брезенхейма путем задания маски чередования вывода пикселов. Маску удобно задавать двоичной формой числа, единичные биты которого обозначают вывод, нулевые - пропуск очередного пиксела. Например, добавим после {***} в приведенную выше подпрограмму операторы j:=i mod 16; { выделение очередного бита в маске ( с конца двухбайтового числа) } if (mask shr j) and 1 =0 then plot:=false; При этом удобно определять маску одним из параметров подпрограммы. На рисунке 2 приведено семейство выходящих из одной точки отрезков прямых, полученных при задании маски двухбайтовым числом mask=$0AA8.
Рисунок 2
29 Алгоритм Брезенхейма построения окружности также основан на наращивании контрольных величин и корректировке координат последовательно выводимых пикселов в зависимости от поведения контрольных величин. При этом строится восьмая часть окружности в секторе от 0 до 45 градусов, которая затем восьмикратно отражается для получения полной окружности. PROCEDURE SIM8(xc,yc, p_X,p_Y, color: integer); begin { восьмикратное отображение точки вокруг центра xc,yc } putpixel( xc+p_X, yc+p_Y, color); putpixel( xc+p_X, yc-p_Y, color); { симметрия относительно } putpixel( xc-p_X, yc+p_Y, color); { осей X , Y и центра} putpixel( xc-p_X, yc-p_Y, color); putpixel( xc+p_Y, yc+p_X, color); putpixel( xc+p_Y, yc-p_X, color); { симметрия относительно } о putpixel( xc-p_Y, yc+p_X, color); { осей, повернутых на 45 } putpixel( xc-p_Y, yc-p_X, color) end; {---------------------------------------------------------} PROCEDURE Circle_B(xc,yc,r,color: integer); { Построение окружности по алгоритму Брезенхейма } var p_X, p_Y, d: integer; begin p_Y:=r; p_X:=0; { начальная точка рисования } d:=3-2*r; { начальное значение параметра, по которому корректируем Y } while p_X
30 сетке. Ниже приведен пример подобной программы для контроля алгоритма Брезенхейма при построении окружности. Uses crt,Graph; var ks, i,j, r, Gd,Gm, Gx,Gy: integer; {---------------------------------------------------------} PROCEDURE SIM8_BAR(xc,yc, p_X,p_Y, color: integer); begin { восьмикратное отображение точки вокруг центра xc,yc } bar(ks*(xc+p_X), ks*(yc+p_Y), ks*(xc+p_X+1), ks*(yc+p_Y+1)); bar(ks*(xc+p_X), ks*(yc-p_Y), ks*(xc+p_X+1), ks*(yc-p_Y+1)); bar(ks*(xc-p_X), ks*(yc+p_Y), ks*(xc-p_X+1), ks*(yc+p_Y+1)); bar(ks*(xc-p_X), ks*(yc-p_Y), ks*(xc-p_X+1), ks*(yc-p_Y+1)); bar(ks*(xc+p_Y), ks*(yc+p_X), ks*(xc+p_Y+1), ks*(yc+p_X+1)); bar(ks*(xc+p_Y), ks*(yc-p_X), ks*(xc+p_Y+1), ks*(yc-p_X+1)); bar(ks*(xc-p_Y), ks*(yc+p_X), ks*(xc-p_Y+1), ks*(yc+p_X+1)); bar(ks*(xc-p_Y), ks*(yc-p_X), ks*(xc-p_Y+1), ks*(yc-p_X+1)); end; {---------------------------------------------------------} PROCEDURE Circle_B( xc,yc, r, color: integer); var p_X, p_Y, d: integer; begin p_Y:=r; p_X:=0; d:=3-2*r; while p_X
InitGraph(Gd,Gm,''); Gx:=GetMaxX; Gy:=GetMaxY; r:=22; { радиус окружности } ks:=Gy div (2*r); { отображаемый размер пиксела } setFillStyle(1,1); Circle_b(r,r,r,14);
31 readkey; 3 Практическое задание
CloseGraph
End.
3.1 Модернизировать процедуру построения окружности, задав разные цвета по ее секторам. 3.2 Составить процедуру рисования отрезков с чередованием цвета составляющих их пикселов. 3.3 Модернизировать алгоритм Брезенхейма, организовав построение 4-связных отрезков прямых. Проконтролировать работу этого алгоритма на крупной сетке. 4 Построение кривых второго порядка При построении сложных кривых наиболее простой метод - вычисление точных координат этой кривой, округление их до целых величин и вывод точек с получаемыми координатами. При этом необходимо, чтобы последовательно получаемые точки были связными. Приведем пример процедур рисования кривых второго порядка с использованием их представления в полярных координатах P R= , где p - параметр; e - эксцентриситет; alf - угол 1 + e ⋅ cos( alf ) для радиус-вектора R, конец которого определяет множество точек кривой. Полюс находится в фокусе кривой, а полярная ось направлена от фокуса к ближайшей вершине. При 0<=e<1 получаем эллипс, при e=1 - параболу, при e>1 - гиперболу. Для построения непрерывной кривой из множества точек используется процедура _TRACE, в которой для получения очередной точки кривой первоначально полярный угол от предшествующей точки увеличивается на 1/R - для получения перпендикулярного радиусу смещения единичной длины. Если условие связности очередной и предшествующей точек не выполняется, то приращение угла уменьшается. Кривые ограничиваются либо диапазоном изменения полярного угла, либо величиной полярного радиуса. {---------------------------------------------------------} PROCEDURE _TRACE( xf,yf, s, alf1,alf2, co,d: integer; p,e,ug: real); { построение кривой второго порядка: xf,yf - координаты фокуса F; s - размер (ограничение величины R полярного радиуса); ось кривой alf1...alf2 - диапазон изменения alf полярного угла; F
32 co - цвет; d - направление приращения полярного угла ( 1 - по часовой стрелке, -1 - против); p - фокальный параметр; e - эксцентриситет; ug - угол наклона оси симметрии кривой } var r,alf,du,rot: real; x,y, x_old,y_old: integer; begin { полярный угол как параметр - начальное значение } alf:= d*alf1*pi/180+ug; { радиус-вектор из фокуса } r:= p/(1+e*cos(alf-ug)); { координаты первой точки - высчитываем и запоминаем } x:=xf +round( r*cos(alf)); y:=yf +round( r*sin(alf)); x_old:=x; y_old:=y; rot:=0; { счетчик угла поворота радиус-вектора } REPEAT { определение очередной точки приращением полярного угла начальное приближение обратно пропорционально радиусу } du:=d/r; Repeat { радиус-вектор из фокуса } r:=p/(1+e*cos(alf-ug+du)); x:=xf+round( r*cos(alf+du)); { координаты новой точки } y:=yf+round( r*sin(alf+du)); If(abs( x-x_old)>1){ если очередная точка несвязна с предыдущей } or(abs( y-y_old)>1) then du:=du/2 { то уменьшаем приращение угла } Until(abs(x-x_old)<=1)and(abs(y-y_old)<=1); alf:=alf+du; { очередной угол обхода } rot:=rot+abs(du); { расчет поворота радиус-вектора } PutPixel( x,y,co); { очередная точка контура } x_old:=x; y_old:=y { запоминаем координаты выведенной точки } { заканчиваем построение при полном проходе интервала по углу обхода или достижении нужного размера ( расстояния от точки кривой до фокуса ) } UNTIL( rot>(alf2-alf1)*pi/180)or( r>s) end; {---------------------------------------------------------} PROCEDURE _ELLIPSE(xc,yc,ugol,r1,r2,alf1,alf2,co: integer); { xc,yc - центр эллипса, ugol - наклон первой оси по горизонтали (град.), r1,r2 - полуоси, alf1...alf2 - диапазон изменения полярного угла, со - цвет }
33 var r0: integer; f,p,e: real; begin if( r1<=0)or( r2<=0) then exit; { если первая полуось меньше второй, то меняем оси и их ориентацию на 90 градусов } if( r1
34 PROCEDURE _PARAB(xc,yc, ugol, a,s,alf1,alf2, co: integer); { xc,yc - вершина параболы, ugol - наклон оси, a - расстояние от вершины до фокуса, s - размер, alf1..alf2 - диапазон изменения полярного угла, со - цвет } var xf,yf: integer; p,ug: real; begin if( a<=0)or( s<=0) then exit; R p ug:=pi-ugol*pi/180; p:=2*a; { фокальный параметр } { координаты фокуса } С F xf:=round( xc+a*cos(ug-pi)); yf:=round( yc+a*sin(ug-pi)); _TRACE(xf,yf,s,alf1,alf2,co, 1,p,1,ug); { первая ветка кривой } _TRACE(xf,yf,s,alf1,alf2,co,-1,p,1,ug) { вторая ветка кривой } end; 3 Практическое задание 3.1 Составить узор не менее чем из 12 элементов с использованием процедур построения одной из кривых второго порядка (центр узора совпадает с фокусом кривых, составляющих узор). 3.2 Составить программу, моделирующую поступательное движение с вращением эллипса, размер которого постепенно уменьшается. 3.3 Составить программу, демонстрирующую изменение формы кривой второго порядка при изменении ее эксцентриситета.
35 Список литературы 1. Епанешников А.М., Епанешников В.А. Программирование в среде Turbo Pascal 7.0. - М.: Диалог - МИФИ, 1995. 2. Иванов В. М., Батраков А. С. Трехмерная компьютерная графика. - М.: Радио и связь, 1995. 3. Лапшин Е. Графика для IBM PC., - М.: СОЛОН, 1995. 4. Котов Ю. В. Как рисует машина. - М.: Наука, 1988. 5. Котов В.Ю., Павлова А.А. Основы машинной графики. - М.: Просвещение, 1993. 6. Дж. Корриган. Компьютерная графика: Секреты и решения. - М.: Диалог - МИФИ, 1995. 7 Д. Хирн, М. Бейкер. Микрокомпьютерная графика. Пер. с англ. М.: Мир, 1987. 8. Фаронов В.В. Турбо Паскаль ( в 3-х книгах ). Практика программирования. - М.: УНЦ ”МВТУ ФЕСТО ДИДАКТИК”, 1993. 9. Шикин Е.В., Боресков А.В. Компьютерная графика. Динамика, реалистические изображения. - М.: Диалог- МИФИ, 1995. 10. Шикин Е.В., Боресков А.В., Зайцев А.А. Начала компьютерной графики. - М.: Диалог- МИФИ, 1993.
36
Составитель: ТРУШИН Олег Владимирович
МЕТОДИЧЕСКИЕ УКАЗАНИЯ к лабораторным работам по курсу "Интерактивная машинная графика" для подготовки инженеров по специальности 220200 "Автоматизированные системы обработки информации и управления" Часть 2 "Линейчатые графические образы"
Редактор
Г.Р. Орлова
ЛБ № 01 92 от 16.10.96
Подписано к печати
Формат 60 Х 84 1/16.
Бумага оберточная. Печать плоская. Усл. печ. л. 2,1 Усл. кр. – отт. 2,0 Заказ №
Уч.-изд. л. 2,0. .
Тираж 100 экз. Бесплатно.
Уфимский государственный авиационный технический университет Уфимская типография № 2 Министерства печати и массовой информации Республики Башкортостан 450000, Уфа – центр, ул. К. Маркса, 12