計算機圖形學常用演算法實現4 多邊形掃描轉換演算法-邊界標誌演算法
阿新 • • 發佈:2019-01-01
程式碼是在winform中執行的。
看書上這個演算法寫起來輕描淡寫的,實際上實現起來還是有很多難點的,難點如下:
1.無法判斷經過某個點的時候是不是應該變號。
2.掃描演算法畫直線的時候,可能同一行有多個點相鄰的情況,如果遇到這樣的點就變號結果會出現錯誤。
3.兩條相鄰邊的路徑可能經過同一個點,尤其是dy>>dx的時候,不加以判斷還是會出錯。
解決方法如下:
1.借用掃描線演算法的思路,如果經過某個點的兩條邊的另外一個定點分別位於該點兩邊,則保留該定點,否則捨去該定點。
2.掃描線演算法進行掃描的時候,如果存在某處x++,y不變時,填充顏色,但是不標記該點,只有y變化才能標記,這樣也就是讓同一條直線每一行只能有一個點被標記。
3.不用bool而是改用int作為標記陣列,每次標記進行++,如果某點的值為2,表示兩條直線都經過了該點,遇到這裡的時候變兩次符號(也就是不變號)。
經過以上改良之後,結果雖然是實現了,但是執行速度很明顯感覺得到沒有掃描線演算法好~
程式碼如下:
1.變數宣告
int xmin, ymin, xmax, ymax; private Point[] polygon = new Point[10000]; private int indexOfPolygon=0; private int[,] map = new int[1024, 768];//作為標記用 void initMap() { for (int i = 0; i < 1024; i++) for (int j = 0; j < 768; j++) map[i, j] = 0; }
2.修改後的DDA畫線
void DDADrawLine(Point p1, Point p2) { Point pp = new Point(); if (p1.X > p2.X) { pp = p1; p1 = p2; p2 = pp; } 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; bool key; if (Math.Abs(dx) >= Math.Abs(dy)) { k = (float)dy / dx; x = p1.X; y = p1.Y; key = true; for (int i = 0; i <= Math.Abs(dx); i++) { if (key)//key的作用是確保一條直線同一行只標記一個 { map[(int)x, (int)(y + 0.5)] ++; key = !key; } g.FillRectangle(p, new RectangleF(x, (int)(y + 0.5), 1, 1)); if ((int)(y + 0.5) != (int)(y + k + 0.5)&& (int)(y + k + 0.5)!=p2.Y) key = true; 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++) {//dx<dy的時候不需要key標記,因為不會出現上述情況 if((int)y!=p2.Y) map[(int)(x + 0.5), (int)y] ++; g.FillRectangle(p, new RectangleF((int)(x + 0.5), y, 1, 1)); if (p1.Y < p2.Y) { y++; x += k; } else { y--; x -= k; } } } }
3.掃描轉換
void edgeMarkFill()
{
for (int i = 0; i < indexOfPolygon; i++)
{
if (xmin > polygon[i].X)
xmin = polygon[i].X;
if (ymin > polygon[i].Y)
ymin = polygon[i].Y;
if (xmax < polygon[i].X)
xmax = polygon[i].X;
if (ymax < polygon[i].Y)
ymax = polygon[i].Y;
}
for (int i = 0; i < indexOfPolygon; i++)//解決方案1的實現
{
if (polygon[(i - 1 + indexOfPolygon) % indexOfPolygon].Y <= polygon[i].Y && polygon[(i + 1) % indexOfPolygon].Y <= polygon[i].Y || polygon[(i - 1 + indexOfPolygon) % indexOfPolygon].Y >= polygon[i].Y && polygon[(i + 1) % indexOfPolygon].Y >= polygon[i].Y)
map[polygon[i].X, polygon[i].Y] = 0;
else
map[polygon[i].X, polygon[i].Y] = 1;
if(polygon[i].Y==polygon[i+1].Y)//特殊處理直線段,直線只保留一個點
map[polygon[i].X, polygon[i].Y] = 1;
}
bool inSide;
Graphics g = this.CreateGraphics();
Brush p = new SolidBrush(Color.Red);
for (int i = ymin; i <=ymax; i++)
{
inSide = false;
for (int j=xmin; j <= xmax; j++)
{
if (map[j, i]%2==1)//標記偶數次也就是沒標記
inSide = !inSide;
if(inSide)
g.FillRectangle(p, new RectangleF(j, i, 1, 1));
}
}
}
效果截圖:
完整程式碼:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace cgdemo2
{
public partial class Form1 : Form
{
private Point[] polygon = new Point[10000];
private int indexOfPolygon=0;
private int[,] map = new int[1024, 768];
bool isClick=false;
int xmin, ymin, xmax, ymax;
public Form1()
{
InitializeComponent();
}
void initMap()
{
for (int i = 0; i < 1024; i++)
for (int j = 0; j < 768; j++)
map[i, j] = 0;
}
void DDADrawLine(Point p1, Point p2)
{
Point pp = new Point();
if (p1.X > p2.X)
{
pp = p1;
p1 = p2;
p2 = pp;
}
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;
bool key;
if (Math.Abs(dx) >= Math.Abs(dy))
{
k = (float)dy / dx;
x = p1.X;
y = p1.Y;
key = true;
for (int i = 0; i <= Math.Abs(dx); i++)
{
if (key)
{
map[(int)x, (int)(y + 0.5)] ++;
key = !key;
}
g.FillRectangle(p, new RectangleF(x, (int)(y + 0.5), 1, 1));
if ((int)(y + 0.5) != (int)(y + k + 0.5)&& (int)(y + k + 0.5)!=p2.Y)
key = true;
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++)
{
if((int)y!=p2.Y)
map[(int)(x + 0.5), (int)y] ++;
g.FillRectangle(p, new RectangleF((int)(x + 0.5), y, 1, 1));
if (p1.Y < p2.Y)
{
y++;
x += k;
}
else
{
y--;
x -= k;
}
}
}
}
void edgeMarkFill()
{
for (int i = 0; i < indexOfPolygon; i++)
{
if (polygon[(i - 1 + indexOfPolygon) % indexOfPolygon].Y <= polygon[i].Y && polygon[(i + 1) % indexOfPolygon].Y <= polygon[i].Y || polygon[(i - 1 + indexOfPolygon) % indexOfPolygon].Y >= polygon[i].Y && polygon[(i + 1) % indexOfPolygon].Y >= polygon[i].Y)
map[polygon[i].X, polygon[i].Y] = 0;
else
map[polygon[i].X, polygon[i].Y] = 1;
if(polygon[i].Y==polygon[i+1].Y)
map[polygon[i].X, polygon[i].Y] = 1;
}
bool inSide;
Graphics g = this.CreateGraphics();
Brush p = new SolidBrush(Color.Red);
for (int i = ymin; i <=ymax; i++)
{
inSide = false;
for (int j=xmin; j <= xmax; j++)
{
if (map[j, i]%2==1)
inSide = !inSide;
if(inSide)
g.FillRectangle(p, new RectangleF(j, i, 1, 1));
}
}
}
void debug()
{
Point p1 = new Point(100, 200);
Point p2 = new Point(200, 200);
Point p3 = new Point(200, 300);
Point p4 = new Point(300, 200);
Point p5 = new Point(200, 100);
indexOfPolygon = 5;
polygon[0] = p1;
polygon[1] = p2;
polygon[2] = p3;
polygon[3] = p4;
polygon[4] = p5;
initMap();
//BresenhamDrawLine(p2, p3);
//DDADrawLine(p2, p3);
xmin = 0x3f3f3f;
ymin = 0x3f3f3f;
xmax = -1;
ymax = -1;
for (int i = 0; i < indexOfPolygon; i++)
DDADrawLine(polygon[i], polygon[(i + 1) % indexOfPolygon]);
for (int i = 0; i < indexOfPolygon; i++)
{
if (xmin > polygon[i].X)
xmin = polygon[i].X;
if (ymin > polygon[i].Y)
ymin = polygon[i].Y;
if (xmax < polygon[i].X)
xmax = polygon[i].X;
if (ymax < polygon[i].Y)
ymax = polygon[i].Y;
}
edgeMarkFill();
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
//debug();
Graphics g = this.CreateGraphics();
Pen p = new Pen(Brushes.Black);
RectangleF rec = new RectangleF(e.Location.X - 1, e.Location.Y - 1, 3, 3);
g.DrawEllipse(p, rec);
if (isClick == false)
{
xmin = 0x3f3f3f;
ymin = 0x3f3f3f;
xmax = -1;
ymax = -1;
indexOfPolygon = 0;
polygon[indexOfPolygon] = e.Location;
if (xmin > polygon[indexOfPolygon].X)
xmin = polygon[indexOfPolygon].X;
if (ymin > polygon[indexOfPolygon].Y)
ymin = polygon[indexOfPolygon].Y;
if (xmax < polygon[indexOfPolygon].X)
xmax = polygon[indexOfPolygon].X;
if (ymax < polygon[indexOfPolygon].Y)
ymax = polygon[indexOfPolygon].Y;
indexOfPolygon++;
isClick = true;
}
else
{
if (Math.Sqrt((e.Location.X - polygon[0].X) * (e.Location.X - polygon[0].X) + (e.Location.Y - polygon[0].Y) * (e.Location.Y - polygon[0].Y)) < 8)
{
initMap();
for (int i = 0; i < indexOfPolygon; i++)
DDADrawLine(polygon[i], polygon[(i + 1) % indexOfPolygon]);
edgeMarkFill();
isClick = false;
}
else
{
polygon[indexOfPolygon] = e.Location;
if (xmin > polygon[indexOfPolygon].X)
xmin = polygon[indexOfPolygon].X;
if (ymin > polygon[indexOfPolygon].Y)
ymin = polygon[indexOfPolygon].Y;
if (xmax < polygon[indexOfPolygon].X)
xmax = polygon[indexOfPolygon].X;
if (ymax < polygon[indexOfPolygon].Y)
ymax = polygon[indexOfPolygon].Y;
indexOfPolygon++;
}
}
}
}
}