====== Построение двумерных примитивов ======
Существует несколько способов построения примитивов. Процесс построения векторных фигур в виде пикселей называется растеризацией. Можно выделить 2 наиболее значимых направления в растеризации. Трассировка лучей и сканирование линией.\\
В данной статье будет рассмотрены методы на основе сканирования линией. Когда фигура получается путем перебора линий сверху вниз и пикселеи линии с лева на права.\\
Всего на всего у нас несколько примитивов: прямая линия, кривая линия, окружность, треугольник, прямоугольник, много угольник. А также закрашенные текстурированные или с переливами цвета. Так вот процесс растеризиции сложной векторной картины. Можно разделить на стадии, как в конвейерной сборке. Применяя преобразования к координатам фигуры она растягивается, вращается. Затем фигура разбивается на примитивы и передаётся в блок растеризации. Где эти примитивы бьются на ещё более мелкие. Таким образом сводя сложную задачу к более простым.
Кривая линия разбивается на сегменты с малой кривизной такие как кривые Безье. Кривые безье разбиваются на отрезки прямых линий. Линии разбиваются на горизонтальные участки. А те на пиксели. Сплошные фигуры разбиваются на многоугольники. Те на треугольники или квадраты.
В первой части рассмотрим как строятся основные примитивы, а во второй части уже будут приведены профессиональные решения с учётом требования гладкости линий(Anti-aliasing).
===== Сглаживание линий =====
===== Сглаживание краёв =====
Для сглаживания краёв применяют различные методы. Это построенные особым образом примитивы.
Используя алгоритм Бу или [[wpru>Брезенхем|Брезенхема]]. Также вы можете просто нарисовать примитивы, а затем наложить фильтр [[Blur|Размывание]] или применив [[Anti-aliasing|устранение контурных неровностей]].
===== Прямая линия =====
==== Теория ====
==== Реализация ====
Procedure Line2D (x1, y1, x2, y2: Integer; Color: TColor);
Var e,i,x,y,dx,dy,sx,sy: Integer;
Begin
x := x1;
y := y1;
dx := Abs(x2 - x1);
dy := Abs(y2 - y1);
sx := Sign(x2 - x1);
sy := Sign(y2 - y1);
If (dx = 0) And (dy = 0) Then
Begin
SetPixel(x1, y1, Color);
Exit;
End;
If dy < dx Then
Begin
e := 2*dy - dx;
i := 1;
Repeat
SetPixel(x, y, Color);
While e >= 0 Do
Begin
inc(y, sy);
dec(e, 2*dx)
End;
inc(x, sx);
inc(e, 2*dy);
inc(i)
Until i > dx;
SetPixel (x, y, Color);
End
Else
Begin
e := 2*dx - dy;
i := 1;
Repeat
SetPixel(x, y, Color);
While e >= 0 Do
Begin
inc(x, sx);
dec(e, 2*dy)
End;
inc(y, sy);
inc(e, 2*dx);
inc(i)
Until i > dy;
SetPixel (x, y, Color);
End;
End;
===== Горизонтальная Линия ====
Вспомогательная процедура удобна для рисования закрашенных примитивов.
{рисуем горизонтальную линию}
procedure HLine(x1,x2,y:Integer; Color:Byte);
var x:Integer;
yy:Word;
begin
yy:=Word(y)*Width;
for x:=x1 to x2 do
Buffer^[yy+x]:=Color;
end;
===== Треугольник =====
===== Прямоугольник =====
==== Теория ====
==== Реализация ====
Тут все просто, рисуем в два цикла:
{Рисуем квадрат}
procedure Rectangle(x1,y1,x2,y2:Integer; Color:TColor);
var i:Integer;
yy:Word;
w,h:Integer;
begin
w:=x2-x1;
h:=y2-y1;
if w<0 then
begin
i:=x1; x1:=x2; x2:=i;
w:=-w;
end;
if h<0 then
begin
i:=y1; y1:=y2; y2:=i;
h:=-h;
end;
for i:=0 to w do
begin
SetPixel(x1+i,y1,Color);
SetPixel(x1+i,y2,Color);
end;
for i:=1 to h-1 do
begin
SetPixel(x1,y1+i,Color);
SetPixel(x2,y1+i,Color);
end;
end;
{Рисуем закрашенный квадрат}
procedure FillRectangle(x1,y1,x2,y2:Integer; Color:TColor);
var i:Integer;
w,h:Integer;
begin
w:=x2-x1;
h:=y2-y1;
if w<0 then
begin
i:=x1; x1:=x2; x2:=i;
w:=-w;
end;
//Отсечение не видимых частей которые выходят за облость видимости(полотна, экрана, битмапа)
if h<0 then
begin
i:=y1; y1:=y2; y2:=i;
h:=-h;
end;
if y1<0 then
begin
h:=h+y1;
y1:=0;
end;
if (y1=Height) then h:=Height-y1;
if x1<0 then
begin
w:=w+x1;
x1:=0;
end;
if (x1=Width) then w:=Width-x1;
if x1>=Width then w:=-1;
if y1>=Height then h:=-1;
// Вывод
for i:=0 to h do
HLine(x1,x1+w,i+y1,Color);
end;
===== Окружность =====
==== Теория ====
==== Реализация ====
Код на Delphi:
{Рисуем окружность}
procedure Circle(xc, yc,r:Integer; Color:TColor);
var y,x,d:Integer;
begin
x:=0;
y:=r;
d:=3-2*r;
while x <= y do
begin
SetPixel(xc+x, yc+y, Color);
SetPixel(xc+y, yc+x, Color);
SetPixel(xc+y, yc-x, Color);
SetPixel(xc+x, yc-y, Color);
SetPixel(xc-x, yc-y, Color);
SetPixel(xc-y, yc-x, Color);
SetPixel(xc-y, yc+x, Color);
SetPixel(xc-x, yc+y, Color);
if (d < 0) then
begin
d:= d + 4*x + 6
end
else begin
d:= d + 4*(x-y) + 10;
dec(y);
end;
inc(x);
end;
end;
Второй вариант вывода окружности через сплайны.
procedure CalcBezierEllipse(CX, CY, A, B: Integer; var BezPts: array of TPoint);
const
MP = 0.55228475;
var
i, CX2,CY2: Integer;
begin
Assert(Length(BezPts) = 13);
CX2 := 2 * CX; CY2 := 2 * CY;
BezPts[0] := Point(CX+1*A,CY+0*B);
BezPts[1] := Point(CX+1*A,CY+MP*B);
BezPts[2] := Point(CX+MP*A,CY+1*B);
BezPts[3] := Point(CX+0*A,CY+1*B);
BezPts[4] := Point(CX-MP*A,CY+1*B);
BezPts[5] := Point(CX-1*A,CY+MP*B);
for i := 0 to 5 do
BezPts[i + 6] := Point(CX2 - BezPts[i].X, CY2 - BezPts[i].Y);
BezPts[12] := BezPts[0];
end;
procedure Ellips(CX, CY, A, B: Integer);
var
BezPts: array [0..12] of TPoint;
i:Integer;
p0,p1,p2,p3:TPoint;
begin
CalcBezierEllipse(CX, CY, A, B,BezPts);
for i:=0 to 3 do
begin
p0:=BezPts[i*3+0];
p1:=BezPts[i*3+1];
p2:=BezPts[i*3+2];
p3:=BezPts[i*3+3]; // следующая кривая начинается с последний точки предыдущей.
Bezier(p0,p1,p2,p3);
end;
end;
====== Дополнительная литература ======
http://www.bsu.by/main.aspx?guid=168261 \\
http://www.cse.iitb.ac.in/~paragc/teaching/2010/cs475/papers/bresenham_line.pdf