計算機圖形常用演算法實現1 DDA,中點畫線法,bresenham演算法
阿新 • • 發佈:2018-11-27
打算手動實現圖形學中的絕大部分演算法。
執行環境winform+c# (程式碼是通用的,如果在其他地方畫圖,只需要替換掉畫點的函式即可)
我們的函式預設是按x座標順序遞增傳入的,因此在呼叫下面函式之前,需要保證p1.x<p2.x(可以減少討論數量)
Point pp = new Point(); if (p1.X > p2.X) { pp = p1; p1 = p2; p2 = pp; }
1.DDA演算法
根據-0.5,0,0.5分割斜率,可以把直線分成四個部分,這個演算法本質比較簡單,注意討論好這四個方向的直線也沒多大問題。
void DDADrawLine(Point p1,Point p2) { Graphics g = this.CreateGraphics(); Brush p = new SolidBrush(Color.Red); int dx = p2.X - p1.X; int dy = p2.Y - p1.Y; float x, y; float k; if (Math.Abs(dx) > Math.Abs(dy)) { k = (float)dy / dx; x = p1.X; y = p1.Y; for (int i = 0; i <= Math.Abs(dx); i++) { g.FillRectangle(p, new RectangleF(x, (int)(y + 0.5), 1, 1)); y += k; x++; } } else if (Math.Abs(dx) < Math.Abs(dy)) { k = (float)dx / dy; x = p1.X; y = p1.Y; for (int i = 0; i <= Math.Abs(dy); i++) { g.FillRectangle(p, new RectangleF((int)(x + 0.5), y, 1, 1)); if (p1.Y < p2.Y) { y++; x += k; } else { y--; x -= k; } } } }
2.中點畫線法 還是根據上述討論分割四個部分,然後四個部分的引數都有些許不同,需要重新推導,具體每個方向的增量可以參考程式碼部分,程式碼的長度實際上是可以優化的,但是這樣寫容易理解一點。
void MidPointDrawLine(Point p1, Point p2) { //分四種情況討論 Graphics g = this.CreateGraphics(); Brush p = new SolidBrush(Color.Red); int a = p1.Y - p2.Y; int b = p2.X - p1.X; int d; int d1; int d2; int x = p1.X; int y = p1.Y; if (Math.Abs(b) > Math.Abs(a)) { //case1 if (p1.Y < p2.Y) { d = 2 * a + b; d1 = 2 * a; d2 = 2 * a + 2 * b; for (; x < p2.X; x++) { g.FillRectangle(p, new RectangleF(x, y, 1, 1)); if (d < 0) { y++; d += d2; } else d += d1; } } //case2 else { a = -a; b = -b; d = 2 * a - b; d1 = 2 * a; d2 = 2 * a - 2 * b; for (; x < p2.X; x++) { g.FillRectangle(p, new RectangleF(x, y, 1, 1)); if (d < 0) { y--; d += d2; } else d += d1; } } } else { //case3 if(y < p2.Y) { a = -a; b = -b; d = 2 * b + a; d1 = 2 * b; d2 = 2 * a + 2 * b; for (; y < p2.Y; y++) { g.FillRectangle(p, new RectangleF(x, y, 1, 1)); if (d < 0) { d += d2; x++; } else { d += d1; } } } else { d = a - 2 * b; d1 = -2 * b; d2 = 2 * a - 2 * b; for (; y > p2.Y; y--) { g.FillRectangle(p, new RectangleF(x, y, 1, 1)); if (d < 0) { d += d2; x++; } else { d += d1; } } } } }
3.bresenham演算法,也是不同方向的引數不同,具體推導結果可以參照下面(建議每個方向都手動推導一遍,加深對演算法的理解)
void BresenhamDrawLine(Point p1, Point p2)
{
//分四種情況討論
Graphics g = this.CreateGraphics();
Brush p = new SolidBrush(Color.Red);
int dx = p2.X - p1.X;
int dy = p2.Y - p1.Y;
int e;
int x = p1.X;
int y = p1.Y;
//case 1:
if (Math.Abs(dx) > Math.Abs(dy) && p1.Y < p2.Y)
{
e = -dx;
for (int i = 0; i <= dx; i++)
{
g.FillRectangle(p, new RectangleF(x, y, 1, 1));
x++;
e += 2 * dy;
if (e > 0)
{
y++;
e -= 2 * dx;
}
}
}
//case2
if (Math.Abs(dx) < Math.Abs(dy) && p1.Y < p2.Y)
{
e = -dy;
for (int i = 0; i <= dy; i++)
{
g.FillRectangle(p, new RectangleF(x, y, 1, 1));
y++;
e += 2 * dx;
if (e > 0)
{
x++;
e -= 2 * dy;
}
}
}
//case 3:
if (Math.Abs(dx) > Math.Abs(dy) && p1.Y > p2.Y)
{
e = dx;
for (int i = 0; i <= dx; i++)
{
g.FillRectangle(p, new RectangleF(x, y, 1, 1));
x++;
e += 2 * dy;
if (e < 0)
{
y--;
e += 2 * dx;
}
}
}
//case4
if (Math.Abs(dx) < Math.Abs(dy) && p1.Y > p2.Y)
{
e = dy;
for (int i = 0; i <= Math.Abs(dy); i++)
{
g.FillRectangle(p, new RectangleF(x, y, 1, 1));
y--;
e += 2 * dx;
if (e > 0)
{
x++;
e += 2 * dy;
}
}
}
}
效果圖如下所示: