점을 찍었다면 이제 선을 그리는 단계.
점을 여러번 찍어서 선을 만들어 낸다.
여기에 선이 있다.
모니터 화면에서 이 선은 어떤 의미를 지니는 걸까?
우선 픽셀과 해상도에 대한 개념이있어야 한다.
여기서는 선긋기 알고리즘에 대한 글이라는 기본적인 개념은 언급하지 않는다.
위 빨간선을 확대 해 보자.
선이 무척 우둘투둘 하다.
모니터의 픽셀을 기준으로 생각해 보자.
V1에서 V2로 선을 그어보고 싶다.
사각형하나하나가 픽셀이며 v1에서 v2로 선을 긋는다면 과연 어느 픽셀들이 선택되어 질까?
위 이미지는 하나의 예시일 뿐이며 어느 픽셀들을 선택할지는 알고리즘에 따라 다르다.
1. 직선의 방정식
y = mx + n
//직선의 방정식. y = mx + n
void Dib::DrawLine(NVertex v1, NVertex v2)
{
float dx, dy, m, x, y, n;
dx = v2.x - v1.x;
dy = v2.y - v1.y;
//x, y 증가량이 0인 경우 = 그리지 않는다.
if(dx == 0 && dy == 0)
return;
//기준 정하기. 음수, 양수 방향성 - x기준
if((dx < 0 ? dx*(-1) : dx) > (dy < 0 ? dy*(-1) : dy))
{
m = dy/dx; //x의 증가량이 기준이 된다
// 만약 v1이 오른쪽에 있다면 v1과 v2 swap
if(v1.x > v2.x)
{
swap(v1.x, v2.x);
swap(v1.y, v2.y);
}
// 왼쪽부터 오른쪽으로 그린다.
y = v1.y;
n = y - (m * v1.x);
for(int x = v1.x; x<= v2.x; x++)
{
y = m*x + n;
DrawPixel(x, (int)floor(y+0.5));
}
}
else
{
m = dx/dy; //y증가량이 더 높다. 기울기가 1 이상.
if(v1.y > v2.y)
{
swap(v1.x,v2.x);
swap(v1.y,v2.y);
}
x = v1.x;
y = v1.y;
n = x - (m*v1.y);
for(int y = v1.y; y <= v2.y; y++)
{
x = m*y + n;
DrawPixel((int)floor(x+0.5), y);
}
}
}
2. DDA 알고리즘
직선의 기울기로 다음 점을 알아낸다.
void Dib::DrawLineDDA(NVertex v1, NVertex v2)
{
float dx, dy, m, x, y;
dx = v2.x - v1.x; //1. 길이 계산
dy = v2.y - v1.y;
//기준 정하기. 음수, 양수 방향성 - x기준
if((dy < 0 ? dy*(-1) : dy) <= (dx < 0 ? dx*(-1) : dx))
{
m = dy/dx; //x의 증가량이 기준이 된다.
if(v1.x > v2.x) //뒤집어 버린다.
{
swap(v1.x, v2.x);
swap(v1.y, v2.y);
}
y = v1.y;
for(int x = v1.x; x<= v2.x; x++)
{
DrawPixel(x, (int)floor(y+0.5));
y += m;
}
}
else
{
m = dx/dy; //y증가량이 더 높다. 기울기가 1 이상.
if(v1.y > v2.y)
{
swap(v1.x, v2.x);
swap(v1.y, v2.y);
}
x = v1.x;
for(int y = v1.y; y <= v2.y; y++)
{
DrawPixel((int)floor(x+0.5), y);
x += m;
}
}
}
3. Mid 알고리즘
반올림 없이 중단점과 더하기 연산으로 다음점을 찾음
void Dib::DrawLineMid(NVertex v1, NVertex v2)
{
int dx, dy, d, inc_x, inc_y; //더하기 연산만을 필요로 하므로.
POINT sign, p;
//1. 길이 계산
dx = v2.x - v1.x;
dy = v2.y - v1.y;
// 2.부호를 체크한다.
if(dx<0)
sign.x = -1;
else
sign.x = 1;
if(dy<0)
sign.y = -1;
else
sign.y = 1;
//3. 좌표계 이동
if(dx * sign.x > dy * sign.y)
{
v2.x = dy;
v2.y = dx;
v2.x *= sign.y;
v2.y *= sign.x;
}
else
{
v2.x = dx;
v2.y = dy;
v2.x *= sign.x;
v2.y *= sign.y;
}
//라인 스캐닝.
d = v2.x * 2 - v2.y;
inc_y = 2 * v2.x;
inc_x = 2 * (v2.x - v2.y);
p.x = 0;
p.y = 0;
DrawPixel(v1.x, v1.y);
while(p.y < v2.y)
{
if(d>0)
{
d += inc_x;
p.x++;
p.y++;
}
else
{
d += inc_y;
p.y++;
}
if(dx * sign.x > dy*sign.y)
DrawPixel(v1.x + p.y * sign.x, v1.y + p.x * sign.y);
else
DrawPixel(v1.x + p.x * sign.x, v1.y + p.y * sign.y);
}
}
TEST
직선의 방정식, DDA, Mid 를 같은 v1(30,30), v2(430,120)로 테스트
직선의 방정식을 먼저 그리고 DDA, Mid 순으로 덧그렸는데 머 거의 흡사하다.