代码是在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
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++;
}
}
}
}
}