文章目录
- @[toc]
- 界面设计
- Form窗口
- MenuStrip
- 画笔
- 其他选项
- 界面美化
- 整体框架设计
- DataStructure
- CPoint
- Polyline
- Polygon
- Singleton
- GraphicFunction
- Form事件处理
- 成员变量
- 事件处理
- 总结
文章目录
- @[toc]
- 界面设计
- Form窗口
- MenuStrip
- 画笔
- 其他选项
- 界面美化
- 整体框架设计
- DataStructure
- CPoint
- Polyline
- Polygon
- Singleton
- GraphicFunction
- Form事件处理
- 成员变量
- 事件处理
- 总结
界面设计
Form窗口
首先添加MenuStrip控件,随后在Form窗口属性界面根据个人爱好修改其图标、Text、Font等属性,并修改窗口大小。
接下来重要的一点就是修改其FormBorderStyle为FixedSingle。具体原因后续解释。
除此之外,可以设计窗口每次弹出位置。
MenuStrip
画笔
由于不同图形有着不同的线宽、线性、颜色等,例如Windows自带的 画图 ,因此我们需考虑其画笔样式,故单独开辟一个菜单栏窗口用以设计画笔样式。
事实上,画笔不仅有颜色、线宽、类型,可根据需求自行设计。
在此选择了一些个人较喜欢的颜色,可根据需求增删查改。以下颜色参考自👉C# Color颜色RGB对照表
线宽仅选择了 1 ~ 4像素的宽度,亦可根据需求更改。
线条类型同理。
其他选项
接下来就是基于实验目的设计MenuStrip的其他选项。鉴于此次实验目的,设计如下。具体细节后续展开。
界面美化
刚设计的MenuStrip界面当类似如下:
我们选择需要设计的按键,右击进入属性表,在其Image属性中,点击下图所示图标,
选择项目资源文件,点击导入,即可导入所有准备的图像,
选择需显示的图像,点击确定即可,如下图所示:
后续只需对所需的按钮选择导入的图像中选择即可做到如上所示。
整体框架设计
本项目主要由以下3个部分构成:
- DataStructure:主要保存所绘制的图形的数据。记录了其图形的端点、绘制方式、形状、线条样式等。
- GraphicFunction:用以保存各种图形的绘制方法。根据用户传入的参数绘制出相应的图形。
- Form事件处理:根据用户行为响应不同事件。由于其图形及其变化根据用户选择,故需对用户不同选择做出相应的响应。
DataStructure
关于图形的数据保存,我们完全可以设计诸多类来保存。但这样的代码可读性维护性并不好。基于其一些共性(例如颜色、绘制、样式等)我们采用 继承+多态 的方式。
在基类的设计上,我们默认画笔工具,Draw(Graphics g)
方法。
**注:**以下代码为前期设计代码,后续给出完整代码。
namespace DataStructure
{
public abstract class Shape
{
public Pen pen;
public Shape() {}
public Shape(Pen _pen)
{
pen = new Pen(_pen.Color, _pen.Width);
pen.DashStyle = _pen.DashStyle;
}
//虚函数
public virtual void Draw(Graphics g) { }
}
}
CPoint
为便于代码统一以及可植性,我们采取自定义Point
结构。我们很容易想到其基本结构,如下:
public class CPoint : Shape //struct
{
double x;
double y;
public CPoint() {}
public CPoint(Pen _pen)
{
pen = new Pen(_pen.Color, _pen.Width);
pen.DashStyle = _pen.DashStyle;
}
public CPoint(double x, double y)
{
this.x = x;
this.y = y;
}
public CPoint(Point point)
{
this.x = (double)point.X;
this.y = (double)point.Y;
}
public CPoint(double x, double y, Pen _pen)
{
this.x = x;
this.y = y;
pen = new Pen(_pen.Color, _pen.Width);
pen.DashStyle = _pen.DashStyle;
dm = DrawingMethod.Point;
}
public double X
{
set { x = (double)value; }
get { return x; }
}
public double Y
{
set { y = value; }
get { return y; }
}
//draw的实现
public override void Draw(Graphics g)
{
g.FillRectangle(new SolidBrush(pen.Color), (float)x, (float)y, pen.Width, pen.Width);
}
}
Polyline
同理,Polyline
实现暂且如下:
public class Polyline : Shape
{
private List<CPoint> lineList = new List<CPoint>();
public Polyline() {}
public Polyline(Pen _pen)
{
pen = new Pen(_pen.Color, _pen.Width);
pen.DashStyle = _pen.DashStyle;
}
public Polyline(List<Point> list, Pen _pen)
{
pen = new Pen(_pen.Color, _pen.Width);
pen.DashStyle = _pen.DashStyle;
foreach (Point p in list) { lineList.Add(new CPoint(p)); }
}
//draw的实现
public override void Draw(Graphics g3)
{
Point[] ptsArray = new Point[lineList.Count];
for (int i = 0; i < lineList.Count; i++)
{
ptsArray[i].X = (int)lineList[i].X;
ptsArray[i].Y = (int)lineList[i].Y;
}
g3.DrawLines(pen, ptsArray);
}
}
Polygon
Polygon
前期基本思路如下:
public class Polygon : Shape
{
public List<CPoint> plgList = new List<CPoint>();
public Polygon(List<Point> list, Pen _pen)
{
for(int i = 0; i < list.Count; i++)
{
plgList.Add(new CPoint(list[i]));
}
pen = new Pen(_pen.Color, _pen.Width);
pen.DashStyle = _pen.DashStyle;
}
public Polygon() {}
public override void Draw(Graphics g3)
{
Point[] ptsArray = new Point[plgList.Count];
for (int i = 0; i < plgList.Count; i++)
{
ptsArray[i].X = (int)(plgList[i].X);
ptsArray[i].Y = (int)(plgList[i].Y);
}
g3.DrawPolygon(pen, ptsArray);
}
}
注:
关于代码中自定义的矩形类CRectangle
由于前期设计问题,后续时间不足未作出修改。可不必实现该类,具体解决思路后续给出。
Singleton
对于已绘制的图形,我们当存在一个对象将其保存。因本项目采取单线程模式,且仅有一个对象会保存已绘制的图形。故在此采取单线程下的单例模式。因其分为饿汉模式和懒汉模式,特点如下:
-
饿汉模式
-
优点:简单
-
缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定
-
-
懒汉模式
-
优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
-
缺点:复杂
-
在此,我们采取懒汉模式。与此同时我们可以看到,采取继承+多态方式,更利于保存。
public sealed class Singleton
{
public static Singleton instance = null;
private static List<Shape> shapes = new List<Shape>();
private Singleton() { }
public static Singleton GetInstance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
public void AddShape(Shape shape) { shapes.Add(shape); }
public ref List<Shape> GetShapes() { return ref shapes; }
public void Clear() { shapes.Clear(); }
}
GraphicFunction
此处保存着我们自定义得绘图方法,预先导入[DllImport("Gdi32.dll ")]
。事实上,我们自定义的绘图方法并不多。关于各算法原理,在此处也不再赘述,下列仅罗列其简单实现。
由于SetPixel
不支持Color
类型,故需要将Color
转化为int
类型,即Color.R + Color.G * 256 + Color.B * 256 * 256
。
关于自定义绘制直线的线宽问题:假设绘制的线段斜率为k,若 ∣ \mid ∣k ∣ \mid ∣$\geq 1 或 k 不存在,则在其水平方向上为其宽度个像素着色。若其线宽为 4 ,且 k 1或 k 不存在,则在其水平方向上为其宽度个像素着色。若其线宽为4,且k 1或k不存在,则在其水平方向上为其宽度个像素着色。若其线宽为4,且k\leq$-1,则绘制如下图所示。反之,则在其垂直方向上为其宽度个像素着色。
DDA
public static void DDALine(int x0, int y0, int x1, int y1, Graphics g, Pen pen) //DDA画线法。
{
IntPtr hdc = g.GetHdc(); //获取句柄
float dx, dy, k;
dx = x1 - x0;
dy = y1 - y0;
if (dx == 0)//斜率不存在情况
{
int y;
int left = x1 - (int)((pen.Width - 1) / 2) - (pen.Width % 2 == 0 ? 1 : 0);
left = left < 0 ? 0 : left;
int right = x1 + (int)((pen.Width - 1) / 2);
for (y = y0; y <= y1; y++)
{
//g.FillRectangle(new SolidBrush(pen.Color), x0, y, 1, 1);
for (int x = left; x <= right; x++)
{
SetPixel(hdc, x, y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
//RGB与int类型相互转换
}
}
for (y = y1; y <= y0; y++)
{
//g.FillRectangle(new SolidBrush(pen.Color), x0, y, 1, 1);
for (int x = left; x <= right; x++)
{
SetPixel(hdc, x, y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
//RGB与int类型相互转换
}
}
}
else
{
k = dy / dx;
if (Math.Abs(k) <= 1)//斜率小于1情况。
{
int x;
float y = y0;
for (x = x0; x <= x1; x++)//x0小于x1情况。
{
//g.FillRectangle(new SolidBrush(pen.Color), x, (int)Math.Floor(y + 0.5), 1, 1);
int bottom = (int)Math.Floor(y + 0.5) - (int)((pen.Width - 1) / 2) - (pen.Width % 2 == 0 ? 1 : 0);
bottom = bottom < 0 ? 0 : bottom;
int top = (int)Math.Floor(y + 0.5) + (int)((pen.Width - 1) / 2);
for (; bottom <= top; bottom++)
{
SetPixel(hdc, x, bottom, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
}
y += k;
}
y = y1;
for (x = x1; x <= x0; x++)//x1小于x0情况。
{
//g.FillRectangle(new SolidBrush(pen.Color), x, (int)Math.Floor(y + 0.5), 1, 1);
int bottom = (int)Math.Floor(y + 0.5) - (int)((pen.Width - 1) / 2) - (pen.Width % 2 == 0 ? 1 : 0);
bottom = bottom < 0 ? 0 : bottom;
int top = (int)Math.Floor(y + 0.5) + (int)((pen.Width - 1) / 2);
for (; bottom <= top; bottom++)
{
SetPixel(hdc, x, bottom, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
}
y += k;
}
}
if (Math.Abs(k) > 1)//斜率大于1情况。
{
float x = x0;
int y;
for (y = y0; y <= y1; y++)//y0小于y1情况。
{
int left = (int)Math.Floor(x) - (int)((pen.Width - 1) / 2) - (pen.Width % 2 == 0 ? 1 : 0);
left = left < 0 ? 0 : left;
int right = (int)Math.Floor(x) + (int)((pen.Width - 1) / 2);
for(; left <= right; left++)
{
SetPixel(hdc, left, y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
}
x += 1 / k;
}
x = x1;
for (y = y1; y <= y0; y++)//y1小于y0情况。
{
int left = (int)Math.Floor(x) - (int)((pen.Width - 1) / 2) - (pen.Width % 2 == 0 ? 1 : 0);
left = left < 0 ? 0 : left;
int right = (int)Math.Floor(x) + (int)((pen.Width - 1) / 2);
for (; left <= right; left++)
{
SetPixel(hdc, left, y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
}
x += 1 / k;
}
}
}
g.ReleaseHdc(); //释放句柄
}
中点直线生成
public static void MidpointLineGeneration(int x1, int y1, int x2, int y2, Graphics g, Pen pen)
{
IntPtr hdc = g.GetHdc();
if (x1 == x2)
{
if(y1 > y2)
{
y1 = y1 ^ y2;
y2 = y2 ^ y1;
y1 = y1 ^ y2;
}
//斜率不存在
for (int y0 = y1; y0 <= y2; y0++)
{
//g.FillRectangle(new SolidBrush(color), x1, y0, 1, 1);
int left = x1 - (int)((pen.Width - 1) / 2) - (pen.Width % 2 == 0 ? 1 : 0);
left = left < 0 ? 0 : left;
int right = x1 + (int)((pen.Width - 1) / 2);
for (int x = left; x <= right; x++)
{
SetPixel(hdc, x, (int)y0, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
}
}
g.ReleaseHdc();
return;
}
float dx, dy, y, k;
dx = x2 - x1;
dy = y2 - y1;
k = dy / dx;
if (Math.Abs(k) <= 1)
{
if (x1 > x2)
{
//交换
swap(ref y1, ref y2);
swap(ref x1, ref x2);
}
y = y1;
float a = y1 - y2, b = x2 - x1, c = x1 * y2 - x2 * y1;
for (int x = x1; x <= x2; x++)
{
//drawpixel(x, int(y + 0.5), color);
float d = (float)(a * x + b * (y + 0.5) + c);
int bottom = (int)Math.Floor(y + 0.5) - (int)((pen.Width - 1) / 2) - (pen.Width % 2 == 0 ? 1 : 0);
bottom = bottom < 0 ? 0 : bottom;
if (d >= 0)
{
int top = (int)Math.Floor(y + 0.5) + (int)((pen.Width - 1) / 2);
for (; bottom <= top; bottom++)
{
SetPixel(hdc, x, bottom, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
}
//g.FillRectangle(new SolidBrush(color), x, (int)(y + 0.5), 1, 1);
}
else
{
int top = (int)Math.Floor(y) + (int)((pen.Width - 1) / 2);
for (; bottom <= top; bottom++)
{
SetPixel(hdc, x, bottom, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
}
//g.FillRectangle(new SolidBrush(color), x, (int)(y), 1, 1);
}
y = y + k;
}
}
else
{
if (y1 > y2)
{
//交换
swap(ref y1, ref y2);
swap(ref x1, ref x2);
}
//swap(ref x1, ref y1);
//swap(ref x2, ref y2);
float x = x1;
float a = y1 - y2, b = x2 - x1, c = x1 * y2 - x2 * y1;
for (y = y1; y <= y2; y++)
{
//drawpixel(x, int(y + 0.5), color);
float d = (float)(a * x + b * (y + 0.5) + c);
int left = (int)Math.Floor(x + 0.5) - (int)((pen.Width - 1) / 2) - (pen.Width % 2 == 0 ? 1 : 0);
left = left < 0 ? 0 : left;
if (d >= 0)
{
int right = (int)Math.Floor(x) + (int)((pen.Width - 1) / 2);
for (; left <= right; left++)
{
SetPixel(hdc, left, (int)y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
}
//g.FillRectangle(new SolidBrush(color), (int)x, (int)y, 1, 1);
}
else
{
int right = (int)Math.Floor(x + 0.5) + (int)((pen.Width - 1) / 2);
for (; left <= right; left++)
{
SetPixel(hdc, left, (int)y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
}
//g.FillRectangle(new SolidBrush(color), (int)(x + 0.5), (int)y, 1, 1);
}
x += 1 / k;
}
}
g.ReleaseHdc();
}
中点画圆
public static void MidPointCircle(Point p1, Point p2, ref Graphics g, Pen pen)
{
float r = (float)Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
float x = p1.X;
float y = p1.Y + r;
float d = 1.25f - r;
while (x - p1.X <= y - p1.Y)
{
if (d < 0)
d += 2 * x + 3 - 2 * p1.X;
else
{
d += 2 * (x - y) + 5 + 2 * p1.Y - 2 * p1.X;
y--;
}
x++;
drawCirclePoints(p1, new Point((int)(x + 0.5), (int)(y + 0.5)), ref g, ref pen);
}
}
public static void drawCirclePoints(Point p1, Point p2, ref Graphics g, ref Pen pen)
{
IntPtr hdc = g.GetHdc();
SetPixel(hdc, p2.X, p2.Y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
SetPixel(hdc, 2 * p1.X - p2.X, p2.Y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
SetPixel(hdc, p2.X, 2 * p1.Y - p2.Y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
SetPixel(hdc, 2 * p1.X - p2.X, 2 * p1.Y - p2.Y, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
SetPixel(hdc, p1.X + p2.Y - p1.Y, p1.Y + p2.X - p1.X, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
SetPixel(hdc, p1.X - p2.Y + p1.Y, p1.Y + p2.X - p1.X, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
SetPixel(hdc, p1.X - p2.Y + p1.Y, p1.Y - p2.X + p1.X, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
SetPixel(hdc, p1.X + p2.Y - p1.Y, p1.Y - p2.X + p1.X, pen.Color.R + pen.Color.G * 256 + pen.Color.B * 256 * 256);
g.ReleaseHdc();
}
扫描线填充
public static void PlgScanFill(List<CPoint> ptList, ref Graphics g, Color color)
{
int minY, maxY;
ptList.Add(ptList[0]);//保证多边形封闭
//确定多边形所占有的最大扫描线数,得到多边形顶点的最小和最大y值(minY和maxY)
minY = int.MaxValue;
maxY = int.MinValue;
foreach (CPoint p in ptList)
{
if (p.Y > maxY)
maxY = (int)p.Y;
if (p.Y < minY)
minY = (int)p.Y;
}
//二维数组存储各条线段对应直线段方程的A、B和C值
int PointNum = ptList.Count - 1;
double[,] lineArray = new double[PointNum, 3];
Point p1 = new Point();
Point p2 = new Point();
for (int i = 0; i < PointNum; i++)
{
p1.X = (int)ptList[i].X;
p1.Y = (int)ptList[i].Y;
p2.X = (int)ptList[i + 1].X;
p2.Y = (int)ptList[i + 1].Y;
double A = p2.Y - p1.Y;
double B = p1.X - p2.X;
double C = p2.X * p1.Y - p1.X * p2.Y;
lineArray[i, 0] = A;
lineArray[i, 1] = B;
lineArray[i, 2] = C;
}
//计算y=i的扫描线与每条边的交点,如果有交点,加入到交点数组itsArray
List<double> itsXArray = new List<double>();
IntPtr hdc = g.GetHdc();
for (int y = minY; y <= maxY; y++)
{
for (int j = 0; j < PointNum; j++)
{
p1.X = (int)ptList[j].X;
p1.Y = (int)ptList[j].Y;
p2.X = (int)ptList[j + 1].X;
p2.Y = (int)ptList[j + 1].Y;
//如果y与第j条边有交点,加入到交点数组
if ((y < p1.Y && y < p2.Y) || (y > p1.Y && y > p2.Y))
{
//无交点
}
else if ((y < p1.Y && y > p2.Y) || (y > p1.Y && y < p2.Y))
{
//与边内部有交点,求出
double A = lineArray[j, 0];
double B = lineArray[j, 1];
double C = lineArray[j, 2];
double x = (-B * y - C) / A; //根据直线一般方程求解x
itsXArray.Add(x);
}
else if (p1.Y == y)//与边的端点有交,需判断交点的取舍(上一个和下一个顶点的y),会不会求了两次???
{
int intsNum = 0;
int preIndex = j - 1;
if (j == 0)//代表第一条线,也存在P2.y=y的情况
preIndex = PointNum - 1;
if (p2.Y > y && ptList[preIndex].Y > y)
intsNum = 2;
if (p2.Y < y && ptList[preIndex].Y > y)
intsNum = 1;
if (p2.Y > y && ptList[preIndex].Y < y)
intsNum = 1;
if (p2.Y < y && ptList[preIndex].Y < y)
intsNum = 0;
if (intsNum == 2)
{
itsXArray.Add(p1.X);
itsXArray.Add(p1.X);
}
if (intsNum == 1)
itsXArray.Add(p1.X);
}
}
itsXArray.Sort();
for (int i = 0; i < itsXArray.Count; i += 2)
{
for (int x = (int)itsXArray[i]; i + 1 < itsXArray.Count && x < (int)itsXArray[i + 1]; x++)
{
SetPixel(hdc, x + 1, y, color.R + color.G * 256 + color.B * 256 * 256);
}
}
itsXArray.Clear();
}
g.ReleaseHdc();
}
四联通区域填充
关于此算法有几点值得注意:
Color
之间判断颜色是否相同不可以使用==
,可以依据其RGB进行判别- 下列给出了两种方式,方式二参考网上转化为内存法进行递归,不过依旧无法解决递归带来的效率问题。当区域过大时程序会崩溃。
public static void SeedFill(Point pointSeed, Color backColor, Color fillColor, Bitmap bitmap)
{
//取出种子点(需要填充的图形边界内一点)的颜色
//IntPtr hdc = g.GetHdc();
if (pointSeed.X >= bitmap.Width || pointSeed.Y >= bitmap.Height || pointSeed.X < 0 || pointSeed.Y < 0) return;
Color color = bitmap.GetPixel(pointSeed.X, pointSeed.Y);
//四邻法,对种子点的颜色进行判断。
if (fillColor.R + fillColor.G * 256 + fillColor.B * 256 * 256 != color.R + color.G * 256 + color.B * 256 * 256
&& color.R + color.G * 256 + color.B * 256 * 256 == backColor.R + backColor.G * 256 + backColor.B * 256 * 256)
{
bitmap.SetPixel(pointSeed.X, pointSeed.Y, fillColor);
//效率问题
SeedFill(new Point(pointSeed.X, pointSeed.Y + 1), backColor, fillColor, bitmap);
SeedFill(new Point(pointSeed.X, pointSeed.Y - 1), backColor, fillColor, bitmap);
SeedFill(new Point(pointSeed.X + 1, pointSeed.Y), backColor, fillColor, bitmap);
SeedFill(new Point(pointSeed.X - 1, pointSeed.Y), backColor, fillColor, bitmap);
}
}
public static void SeedFill(byte[] rgbValues, int width, int height, int position, Color backColor, Color fillColor)
{
if (position < 0 || position > width * height * 3) return;
if (rgbValues[position] == backColor.R && rgbValues[position + 1] == backColor.G && rgbValues[position + 2] == backColor.B &&
rgbValues[position] != fillColor.R && rgbValues[position + 1] != fillColor.G && rgbValues[position + 2] != fillColor.B)
{
rgbValues[position] = fillColor.R;
rgbValues[position + 1] = fillColor.G;
rgbValues[position + 2] = fillColor.B;
SeedFill(rgbValues, width, height, position - 3 * width, backColor, fillColor); //上
SeedFill(rgbValues, width, height, position + 3 * width, backColor, fillColor); //下
SeedFill(rgbValues, width, height, position - 3, backColor, fillColor); //左
SeedFill(rgbValues, width, height, position + 3, backColor, fillColor); //右
}
/*Stack<int> st = new Stack<int>();
st.Push(position);
while (st.Count > 0)
{
int i = st.Pop();
int index = i;
/nt left = index - (index % (3 * width));
int right = index + (width - index % (3 * width));
while(index <= right && rgbValues[index] == backColor.R && rgbValues[index + 1] == backColor.G && rgbValues[index + 2] == backColor.B &&
rgbValues[index] != fillColor.R && rgbValues[index + 1] != fillColor.G && rgbValues[index + 2] != fillColor.B)
{
rgbValues[index] = fillColor.R;
rgbValues[index + 1] = fillColor.G;
rgbValues[index + 2] = fillColor.B;
index += 3;
}
right = index - 3 - 3 * width;
index = i;
while (index >= left && rgbValues[index] == backColor.R && rgbValues[index + 1] == backColor.G && rgbValues[index + 2] == backColor.B &&
rgbValues[index] != fillColor.R && rgbValues[index + 1] != fillColor.G && rgbValues[index + 2] != fillColor.B)
{
rgbValues[index] = fillColor.R;
rgbValues[index + 1] = fillColor.G;
rgbValues[index + 2] = fillColor.B;
index -= 3;
}
left = index + 3 - 3 * width;
//st.Pop();
if (index < 0 || index >= width * height * 3) return;
if (rgbValues[index] == backColor.R && rgbValues[index + 1] == backColor.G && rgbValues[index + 2] == backColor.B &&
rgbValues[index] != fillColor.R && rgbValues[index + 1] != fillColor.G && rgbValues[index + 2] != fillColor.B)
{
rgbValues[index] = fillColor.R;
rgbValues[index + 1] = fillColor.G;
rgbValues[index + 2] = fillColor.B;
st.Push(index - 3);
st.Push(index + 3);
st.Push(index - 3 * width);
st.Push(index + 3 * height);
}
}*/
}
Liang-Barsky 算法
public static void LBLineClip(int x1, int y1, int x2, int y2, int XL, int XR, int YB, int YT, Pen pen, ref Graphics g)
{
/// <summary>
/// LiangBarsky算法裁剪直线段
/// </summary>
/// <param name="x1">起点x坐标</param>
/// <param name="y1">起点y坐标</param>
/// <param name="x2">终点x坐标</param>
/// <param name="y2">终点y坐标</param>
/// <param name="XL">左边界X</param>
/// <param name="XR">右边界X</param>
/// <param name="YB">下边界Y</param>
/// <param name="YT">上边界Y</param>
int p1 = -(x2 - x1); int p2 = x2 - x1;
int q1 = x1 - XL; int q2 = XR - x1;
float p3 = -(y2 - y1), q3 = y1 - YB;
float p4 = y2 - y1, q4 = YT - y1;
float umax = 0.0f, umin = 0.0f;
float u1, u2, u3, u4;
if (p1 == 0) //边界值的测试
{
if (q1 < 0 || q2 < 0)
{
return;
}
else if (q1 >= 0 && q2 >= 0)
{
u3 = q3 / p3; u4 = q4 / p4;
if (p3 < 0)
{
umax = Math.Max(0, u3);
umin = Math.Min(1, u4);
}
else
{
umax = Math.Max(0, u4);
umin = Math.Min(1, u3);
}
}
}
else if (p3 == 0)
{
if (q3 < 0 || q4 < 0)
{
return;
}
else if (q3 >= 0 && q4 >= 0)
{
u1 = q1 / p1; u2 = q2 / p2;
if (p1 < 0) { umax = Math.Max(0, u1); umin = Math.Min(1, u2); }
else { umax = Math.Max(0, u2); umin = Math.Min(1, u1); }
}
}
else
{
u1 = (float)q1 / p1; u2 = (float)q2 / p2; u3 = (float)q3 / p3; u4 = (float)q4 / p4;//1.类型转换错误
if (p1 < 0 && p3 < 0)
{
umax = Math.Max(Math.Max(0, u1), Math.Max(u1, u3));
umin = Math.Min(Math.Min(1, u2), Math.Min(u2, u4));
}
else if (p1 < 0 && p4 < 0)//2.p值小于0时讨论情况少
{
umax = Math.Max(Math.Max(0, u1), Math.Max(u1, u4));
umin = Math.Min(Math.Min(1, u2), Math.Min(u2, u3));
}
else if (p2 < 0 && p3 < 0)
{
umax = Math.Max(Math.Max(0, u2), Math.Max(u2, u3));
umin = Math.Min(Math.Min(1, u1), Math.Min(u1, u4));
}
else if (p2 < 0 && p4 < 0)
{
umax = Math.Max(Math.Max(0, u2), Math.Max(u2, u4));
umin = Math.Min(Math.Min(1, u1), Math.Min(u1, u3));
}
}
if (umax <= umin)
{
//第一个被裁剪的点
float x11 = x1 + umax * (x2 - x1);
float y11 = y1 + umax * (y2 - y1);
//第二个被裁剪的点
float x21 = x1 + umin * (x2 - x1);
float y21 = y1 + umin * (y2 - y1);
float width = pen.Width;
pen.Width = 6;
g.DrawLine(pen, (int)x11, (int)y11, (int)x21, (int)y21);
pen.Width = width;
}
}
Form事件处理
Form中,需要处理的无非有以下几个事件:MouseDown、MouseMove、Load、FormClosed。。。重点在于如何联立其之间的联系。
成员变量
为知道用户当前的行为,我们添加枚举类型Args
用以记录。其中,None
必不可少。
enum Args
{
None,
DrawPoint, //画点
DrawPolyline, //画线
DrawRectangle, //画矩形
DrawPolygon, //画多边形
DDALine, //DDA
MidpointLineGen, //中点直线生成
MidPointCircle, //中点画圆法
ScanningLineFill, //扫描线填充
RegionFill, //区域填充
LBClip, //Liang-Barsky算法
Translation, //平移
Zoom, //缩放
Rotate, //旋转
HorizontalSymmetry, //水平对称
VerticalSummetry, //垂直对称
PointSummetry //水平垂直对称
}
此外,我们还需保存画笔样式,故画笔Pen
也是必不可少的。假设用户正在绘制Polyline
,则我们应当保存其所选点,故存在一结构用以保存用户所选点。为避免闪烁,采取Bitmap
进行绘图。
其中,g0展示最终绘制结果,g3展示临时(不完全)绘制结果。
private Graphics g0, g3; //窗口绘图面和临时绘图面
public Pen curPen; //所用的画笔
private Bitmap bp0 = null;
private Bitmap bp3 = null;
Args args;
List<Point> tempLineList;
事件处理
初始化
public Form()
{
InitializeComponent();
//激活双缓冲技术
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
//添加鼠标滚轮事件
this.MouseWheel += new System.Windows.Forms.MouseEventHandler(Form1_MouseWheel);
//初始化
tempLineList = new List<Point>();
//画笔默认情况
curPen = new Pen(Color.Black, 3);
curPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid;
//菜单栏背景色初始化
LineWidth3.BackColor = LineWidthBackColorSet();
黑色ToolStripMenuItem.BackColor = BrushColorSet();
实线ToolStripMenuItem.BackColor = LineTypeBackColorSet();
bp0 = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
g0 = Graphics.FromImage(bp0);
bp3 = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
g3 = Graphics.FromImage(bp3);
}
Load
首先,我们需要在当前路径下准备一张底图,并复制两份。其次,对已绘制的图形重绘,避免打开后图形消失。
private void Form1_Load(object sender, EventArgs e)
{
this.DoubleBuffered = true;
if (!File.Exists(@"D:\Users\Mr_xi\Desktop\bp0.bmp"))
{
FileInfo sourcefi = new FileInfo(@"source.bmp");
sourcefi.CopyTo(@"D:\Users\Mr_xi\Desktop\bp0.bmp");
sourcefi.CopyTo(@"D:\Users\Mr_xi\Desktop\bp3.bmp");
}
bp3 = FileToBitmap(@"D:\Users\Mr_xi\Desktop\bp3.bmp");
g3 = Graphics.FromImage(bp3);
Singleton instance = Singleton.GetInstance;
List<Shape> shapes = instance.GetShapes();
foreach(var shape in shapes)
{
shape.Draw(g3);
}
this.CreateGraphics().DrawImage(bp3, 0, 0);
}
背景色变化
接下来我们考虑,当用户选择某一颜色时,其背景色变化,其他颜色的按钮背景色均重置。例如:
我们以黑色为例,其他颜色同理。
private void BrushColorReduction()
{
Color backgroundcolor = Color.FromKnownColor(KnownColor.Control);
黑色ToolStripMenuItem.BackColor = backgroundcolor;
红色ToolStripMenuItem.BackColor = backgroundcolor;
闪蓝色ToolStripMenuItem.BackColor = backgroundcolor;
深天蓝色ToolStripMenuItem.BackColor = backgroundcolor;
草坪绿ToolStripMenuItem.BackColor = backgroundcolor;
明绿色ToolStripMenuItem.BackColor = backgroundcolor;
青色ToolStripMenuItem.BackColor = backgroundcolor;
黄色ToolStripMenuItem.BackColor = backgroundcolor;
灰石色ToolStripMenuItem.BackColor = backgroundcolor;
暗紫罗兰色ToolStripMenuItem.BackColor = backgroundcolor;
}
private Color BrushColorSet()
{
return Color.FromKnownColor(KnownColor.GradientActiveCaption);
}
private void 黑色ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (curPen.Color == Color.Black) return;
BitMapDataClone();
curPen.Color = Color.Black;
BrushColorReduction();
黑色ToolStripMenuItem.BackColor = BrushColorSet();
}
线宽、线条类型、其他事件同理。
MouseDown
倘若我们每次从已有的文件中new Bitmap
,事实上是做不到的,因此我们需要将已有的文件通过流转化成Bitmap
,即:
Bitmap FileToBitmap(string fileName)
{
// 打开文件
FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
// 读取文件的 byte[]
byte[] bytes = new byte[fileStream.Length];
fileStream.Read(bytes, 0, bytes.Length);
fileStream.Close();
// 把 byte[] 转换成 Stream
Stream stream = new MemoryStream(bytes);
stream.Read(bytes, 0, bytes.Length);
// 设置当前流的位置为流的开始
stream.Seek(0, SeekOrigin.Begin);
MemoryStream mstream = null;
try
{
mstream = new MemoryStream(bytes);
return new Bitmap((System.Drawing.Image)new Bitmap(stream));
}
catch (ArgumentNullException ex)
{
return null;
}
catch (ArgumentException ex)
{
return null;
}
finally
{
stream.Close();
}
}
当用户按下鼠标左键/右键时,当根据用户当前状态给予不同响应。
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (args == Args.DrawPoint)
{
//画点——只需要在g0上画即可
Singleton.GetInstance.AddShape(new CPoint((double)e.X, (double)e.Y, curPen));
g0 = Graphics.FromImage(bp0);
g0.FillRectangle(new SolidBrush(curPen.Color), e.X, e.Y, curPen.Width, curPen.Width);
this.CreateGraphics().DrawImage(bp0, 0, 0);
BitMapSave(0);
}
else if(args == Args.DrawPolyline)
{
//画线
tempLineList.Add(e.Location);
if (tempLineList.Count > 1)
{
g3 = Graphics.FromImage(bp3);
g3.SmoothingMode = SmoothingMode.AntiAlias;
Point[] newLine = new Point[2];
newLine[0] = tempLineList[tempLineList.Count() - 1];
newLine[1] = tempLineList[tempLineList.Count() - 2];
g3.DrawLines(curPen, newLine);
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
}
}
else if (args == Args.DrawRectangle)
{
//绘制矩形
tempLineList.Add(e.Location);
if (tempLineList.Count > 1)
{
Singleton.GetInstance.AddShape(new CRectangle(tempLineList, curPen));
g3 = Graphics.FromImage(bp3);
g3.SmoothingMode = SmoothingMode.AntiAlias;
Point[] rectanglePoints = tempLineList.ToArray();
if (rectanglePoints[0].X > rectanglePoints[1].X)
{
rectanglePoints[0].X = tempLineList[1].X;
rectanglePoints[1].X = tempLineList[0].X;
}
if (rectanglePoints[0].Y > rectanglePoints[1].Y)
{
rectanglePoints[0].Y = tempLineList[1].Y;
rectanglePoints[1].Y = tempLineList[0].Y;
}
g3.DrawRectangle(curPen, rectanglePoints[0].X, rectanglePoints[0].Y,
rectanglePoints[1].X - rectanglePoints[0].X, rectanglePoints[1].Y - rectanglePoints[0].Y);
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
}
else if (args == Args.DrawPolygon)
{
tempLineList.Add(e.Location);
}
else if (args == Args.DDALine)
{
tempLineList.Add(e.Location);
if (tempLineList.Count > 1)
{
g3 = Graphics.FromImage(bp3);
g3.SmoothingMode = SmoothingMode.AntiAlias;
GraphicDrawing.DDALine(tempLineList[tempLineList.Count - 1].X, tempLineList[tempLineList.Count - 1].Y,
tempLineList[tempLineList.Count - 2].X, tempLineList[tempLineList.Count - 2].Y,
curPen, g3);
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
}
}
else if (args == Args.MidpointLineGen)
{
tempLineList.Add(e.Location);
if (tempLineList.Count > 1)
{
g3 = Graphics.FromImage(bp3);
g3.SmoothingMode = SmoothingMode.AntiAlias;
GraphicDrawing.MidpointLineGeneration(tempLineList[tempLineList.Count - 1].X, tempLineList[tempLineList.Count - 1].Y,
tempLineList[tempLineList.Count - 2].X, tempLineList[tempLineList.Count - 2].Y,
g3, curPen);
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
}
}
else if (args == Args.MidPointCircle)
{
tempLineList.Add(e.Location);
if (tempLineList.Count > 1)
{
Singleton.GetInstance.AddShape(new Polygon(tempLineList, curPen, DrawingMethod.MidPointCircle));
g3 = Graphics.FromImage(bp3);
g3.SmoothingMode = SmoothingMode.AntiAlias;
GraphicDrawing.MidPointCircle(tempLineList[0], tempLineList[1], ref g3, curPen);
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
}
else if (args == Args.ScanningLineFill)
{
Cursor.Current = Cursors.WaitCursor;
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for(int i = 0; i < shapes.Count; i++)
{
if (shapes[i].dm == DrawingMethod.Polygon)
{
g3 = Graphics.FromImage(bp3);
g3.SmoothingMode = SmoothingMode.AntiAlias;
GraphicDrawing.PlgScanFill(((Polygon)shapes[i]).plgList, ref g3, curPen.Color);
}
}
Cursor.Current = Cursors.Default;
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
else if (args == Args.RegionFill)
{
bp0 = FileToBitmap(@"D:\Users\Mr_xi\Desktop\bp0.bmp");
g3 = Graphics.FromImage(bp0);
GraphicDrawing.SeedFill(e.Location, this.BackColor, curPen.Color, bp0);
this.CreateGraphics().DrawImage(bp0, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
else if (args == Args.LBClip)
{
tempLineList.Add(e.Location);
if(tempLineList.Count > 1 && tempLineList.Count % 2 == 0)
{
g3 = Graphics.FromImage(bp3);
g3.SmoothingMode = SmoothingMode.AntiAlias;
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for(int i = 0; i < shapes.Count; i++)
{
if (shapes[i].dm == DrawingMethod.CRectangle)
{
int XL = (int)Math.Min(((CRectangle)shapes[i]).rectanglePoints[0].X, ((CRectangle)shapes[i]).rectanglePoints[1].X);
int XR = (int)Math.Max(((CRectangle)shapes[i]).rectanglePoints[0].X, ((CRectangle)shapes[i]).rectanglePoints[1].X);
int YB = (int)Math.Min(((CRectangle)shapes[i]).rectanglePoints[0].Y, ((CRectangle)shapes[i]).rectanglePoints[1].Y);
int YT = (int)Math.Max(((CRectangle)shapes[i]).rectanglePoints[0].Y, ((CRectangle)shapes[i]).rectanglePoints[1].Y);
int count = tempLineList.Count;
GraphicDrawing.LBLineClip(tempLineList[count - 2].X, tempLineList[count - 2].Y,
tempLineList[count - 1].X, tempLineList[count - 1].Y,
XL, XR, YB, YT, curPen, ref g3);
}
}
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
}
else if (args == Args.Translation)
{
bp3 = FileToBitmap(@"source.bmp");
g3 = Graphics.FromImage(bp3);
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for (int i = 0; i < shapes.Count; i++)
{
Point[] ptArray = shapes[i].GetArray();
var m = new System.Drawing.Drawing2D.Matrix();
//计算中心点
float x = 0.0f;
float y = 0.0f;
for (int j = 0; j < ptArray.Length; j++)
{
x += (float)(ptArray[j].X);
y += (float)(ptArray[j].Y);
}
x /= ptArray.Length;
y /= ptArray.Length;
//平移
m.Translate(e.Location.X - (int)x, e.Location.Y - (int)y, System.Drawing.Drawing2D.MatrixOrder.Append);
m.TransformPoints(ptArray);
shapes[i].UpData(ptArray);
shapes[i].Draw(g3);
}
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
else if (args == Args.Zoom)
{
;
}
else if (args == Args.Rotate)
{
tempLineList.Add(e.Location);
if(tempLineList.Count > 1)
{
bp3 = FileToBitmap(@"source.bmp");
g3 = Graphics.FromImage(bp3);
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for (int i = 0; i < shapes.Count; i++)
{
Point[] ptArray = shapes[i].GetArray();
var m = new System.Drawing.Drawing2D.Matrix();
float angleOfLine = (float)(Math.Atan2((double)(tempLineList[1].Y - tempLineList[0].Y),
(double)(tempLineList[1].X - tempLineList[0].X)
) * 180 / Math.PI);
//平移
m.RotateAt(angleOfLine, tempLineList[0], MatrixOrder.Append);
m.TransformPoints(ptArray);
shapes[i].UpData(ptArray);
shapes[i].Draw(g3);
}
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
}
else if (args == Args.HorizontalSymmetry)
{
bp3 = FileToBitmap(@"source.bmp");
g3 = Graphics.FromImage(bp3);
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for (int i = 0; i < shapes.Count; i++)
{
Point[] ptArray = shapes[i].GetArray();
var m = new System.Drawing.Drawing2D.Matrix();
//计算中心点
float x = 0.0f;
float y = 0.0f;
for (int j = 0; j < ptArray.Length; j++)
{
x += (float)(ptArray[j].X);
y += (float)(ptArray[j].Y);
}
x /= ptArray.Length;
y /= ptArray.Length;
//
m.Translate(0, -e.Location.Y, System.Drawing.Drawing2D.MatrixOrder.Append);
m.Multiply(new Matrix(1, 0, 0, -1, 0, 0), MatrixOrder.Append);
m.Translate(0, e.Location.Y, System.Drawing.Drawing2D.MatrixOrder.Append);
m.TransformPoints(ptArray);
shapes[i].UpData(ptArray);
shapes[i].Draw(g3);
}
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
else if (args == Args.VerticalSummetry)
{
bp3 = FileToBitmap(@"source.bmp");
g3 = Graphics.FromImage(bp3);
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for (int i = 0; i < shapes.Count; i++)
{
Point[] ptArray = shapes[i].GetArray();
var m = new System.Drawing.Drawing2D.Matrix();
//计算中心点
float x = 0.0f;
float y = 0.0f;
for (int j = 0; j < ptArray.Length; j++)
{
x += (float)(ptArray[j].X);
y += (float)(ptArray[j].Y);
}
x /= ptArray.Length;
y /= ptArray.Length;
//平移
m.Translate(-e.Location.X, 0, System.Drawing.Drawing2D.MatrixOrder.Append);
m.Multiply(new Matrix(-1, 0, 0, 1, 0, 0), MatrixOrder.Append);
m.Translate(e.Location.X, 0, System.Drawing.Drawing2D.MatrixOrder.Append);
m.TransformPoints(ptArray);
shapes[i].UpData(ptArray);
shapes[i].Draw(g3);
}
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
else if (args == Args.PointSummetry)
{
bp3 = FileToBitmap(@"source.bmp");
g3 = Graphics.FromImage(bp3);
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for (int i = 0; i < shapes.Count; i++)
{
Point[] ptArray = shapes[i].GetArray();
var m = new System.Drawing.Drawing2D.Matrix();
//计算中心点
float x = 0.0f;
float y = 0.0f;
for (int j = 0; j < ptArray.Length; j++)
{
x += (float)(ptArray[j].X);
y += (float)(ptArray[j].Y);
}
x /= ptArray.Length;
y /= ptArray.Length;
//
m.Translate(0, -e.Location.Y, System.Drawing.Drawing2D.MatrixOrder.Append);
m.Multiply(new Matrix(1, 0, 0, -1, 0, 0), MatrixOrder.Append);
m.Translate(0, e.Location.Y, System.Drawing.Drawing2D.MatrixOrder.Append);
m.Translate(-e.Location.X, 0, System.Drawing.Drawing2D.MatrixOrder.Append);
m.Multiply(new Matrix(-1, 0, 0, 1, 0, 0), MatrixOrder.Append);
m.Translate(e.Location.X, 0, System.Drawing.Drawing2D.MatrixOrder.Append);
m.TransformPoints(ptArray);
shapes[i].UpData(ptArray);
shapes[i].Draw(g3);
}
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
else
{
}
}
if(e.Button == MouseButtons.Right)
{
//将g3的文件同步到g0,同时输出g0
if (args == Args.DrawPolyline)
{
if (tempLineList.Count < 2)
{
tempLineList.Clear();
this.CreateGraphics().DrawImage(bp3, 0, 0);
return;
}
Singleton.GetInstance.AddShape(new Polyline(tempLineList, curPen));
BitMapDataSynchronism();
}
else if (args == Args.DrawRectangle)
{
BitMapDataSynchronism();
}
else if(args == Args.DrawPolygon)
{
g3 = Graphics.FromImage(bp3);
g3.SmoothingMode = SmoothingMode.AntiAlias;
if (tempLineList.Count <= 2)
{
tempLineList.Clear();
this.CreateGraphics().DrawImage(bp3, 0, 0);
return;
}
Singleton.GetInstance.AddShape(new Polygon(tempLineList, curPen));
Point[] ptsArray = tempLineList.ToArray();
g3.DrawPolygon(curPen, ptsArray);
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
BitMapDataSynchronism();
}
else if(args == Args.DDALine)
{
if (tempLineList.Count < 2)
{
tempLineList.Clear();
this.CreateGraphics().DrawImage(bp3, 0, 0);
return;
}
Singleton.GetInstance.AddShape(new Polyline(tempLineList, curPen, DrawingMethod.DDA));
BitMapDataSynchronism();
}
else if (args == Args.MidpointLineGen)
{
if (tempLineList.Count < 2)
{
tempLineList.Clear();
this.CreateGraphics().DrawImage(bp3, 0, 0);
return;
}
Singleton.GetInstance.AddShape(new Polyline(tempLineList, curPen, DrawingMethod.MidpointLine));
BitMapDataSynchronism();
}
else if (args == Args.MidPointCircle)
{
if (tempLineList.Count < 2)
{
tempLineList.Clear();
this.CreateGraphics().DrawImage(bp3, 0, 0);
return;
}
Singleton.GetInstance.AddShape(new Polygon(tempLineList, curPen, DrawingMethod.MidPointCircle));
BitMapDataSynchronism();
}
else if (args == Args.LBClip)
{
if(tempLineList.Count % 2 == 1)
{
tempLineList.RemoveAt(tempLineList.Count - 1);
}
BitMapDataSynchronism();
}
else if (args == Args.Translation)
{
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for(int i = 0; i < shapes.Count; i++)
{
Point[] ptArray = shapes[i].GetArray();
var m = new System.Drawing.Drawing2D.Matrix();
//计算中心点
float x = 0.0f;
float y = 0.0f;
for(int j = 0; j < ptArray.Length; j++)
{
x += (float)(ptArray[j].X);
y += (float)(ptArray[j].Y);
}
x /= ptArray.Length;
y /= ptArray.Length;
//平移
m.Translate(e.Location.X - (int)x, e.Location.Y - (int)y, System.Drawing.Drawing2D.MatrixOrder.Append);
m.TransformPoints(ptArray);
}
}
else if (args == Args.Zoom)
{
}
else if (args == Args.Rotate)
{
}
else if (args == Args.HorizontalSymmetry)
{
}
else if (args == Args.VerticalSummetry)
{
}
else if (args == Args.PointSummetry)
{
}
Cursor.Current = Cursors.Arrow;
}
}
从上面的函数可以看到,前面所给的DataStructure
并不能满足我们的需求,因此我们添加变量和方法以满足需求。
public enum DrawingMethod
{
None,
Point,
Sys,
CRectangle,
DDA,
Polygon,
MidpointLine,
MidPointCircle
}
public virtual Point[] GetArray() { return new Point[0]; }
public virtual void UpData(Point[] points) {}
故后续子类需有所变化。具体可见完整代码。
MouseMove
当鼠标移动时,依据已有数据动态绘制出下一步可能的图形,达到橡皮筋效果。
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (tempLineList.Count >= 1 || args == Args.Translation || args == Args.HorizontalSymmetry || args == Args.VerticalSummetry
|| args == Args.PointSummetry)
{
Cursor.Current = Cursors.Hand;
bp3 = FileToBitmap(@"D:\Users\Mr_xi\Desktop\bp3.bmp");
Bitmap bmp = (Bitmap)bp3.Clone();
Graphics g1 = Graphics.FromImage(bmp);
g1.SmoothingMode = SmoothingMode.AntiAlias; //设置抗锯齿平滑模式
if (args == Args.DrawPolyline)
{
Point[] newLine = new Point[2];
newLine[0] = tempLineList[tempLineList.Count() - 1];
newLine[1] = e.Location;
g1.DrawLines(curPen, newLine);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.DrawRectangle)
{
Point[] rectanglePoints = new Point[2];
rectanglePoints[0] = e.Location;
rectanglePoints[1] = tempLineList[0];
if (rectanglePoints[0].X > rectanglePoints[1].X)
{
int temp = (int)rectanglePoints[0].X;
rectanglePoints[0].X = rectanglePoints[1].X;
rectanglePoints[1].X = temp;
}
if (rectanglePoints[0].Y > rectanglePoints[1].Y)
{
int temp = (int)rectanglePoints[0].Y;
rectanglePoints[0].Y = rectanglePoints[1].Y;
rectanglePoints[1].Y = temp;
}
g1.DrawRectangle(curPen, rectanglePoints[0].X, rectanglePoints[0].Y,
rectanglePoints[1].X - rectanglePoints[0].X, rectanglePoints[1].Y - rectanglePoints[0].Y);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.DrawPolygon)
{
if (tempLineList.Count == 1)
{
Point[] newLine = new Point[2];
newLine[0] = tempLineList[0];
newLine[1] = e.Location;
g1.DrawLines(curPen, newLine);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (tempLineList.Count > 1)
{
Point[] ptsArray = new Point[tempLineList.Count + 1];
for (int i = 0; i < tempLineList.Count; i++)
{
ptsArray[i].X = (int)tempLineList[i].X;
ptsArray[i].Y = (int)tempLineList[i].Y;
}
ptsArray[tempLineList.Count] = e.Location;
g1.DrawPolygon(curPen, ptsArray);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
}
else if (args == Args.DDALine)
{
GraphicDrawing.DDALine(tempLineList[tempLineList.Count - 1].X, tempLineList[tempLineList.Count - 1].Y,
e.Location.X, e.Location.Y,
g1, curPen);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.MidpointLineGen)
{
GraphicDrawing.MidpointLineGeneration(tempLineList[tempLineList.Count - 1].X, tempLineList[tempLineList.Count - 1].Y,
e.Location.X, e.Location.Y,
g1, curPen);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.MidPointCircle)
{
GraphicDrawing.MidPointCircle(tempLineList[0], e.Location, ref g1, curPen);
Point[] radius = new Point[2];
radius[0] = tempLineList[0];
radius[1] = new Point(e.Location.X, e.Location.Y);
DashStyle dashStyle = curPen.DashStyle;
curPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
g1.DrawLines(curPen, radius);
curPen.DashStyle = dashStyle;
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.LBClip)
{
Point[] newLine = new Point[2];
newLine[0] = tempLineList[tempLineList.Count() - 1];
newLine[1] = e.Location;
g1.DrawLines(curPen, newLine);
if(tempLineList.Count % 2 == 1)
{
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for (int i = 0; i < shapes.Count; i++)
{
if (shapes[i].dm == DrawingMethod.CRectangle)
{
int XL = (int)Math.Min(((CRectangle)shapes[i]).rectanglePoints[0].X, ((CRectangle)shapes[i]).rectanglePoints[1].X);
int XR = (int)Math.Max(((CRectangle)shapes[i]).rectanglePoints[0].X, ((CRectangle)shapes[i]).rectanglePoints[1].X);
int YB = (int)Math.Min(((CRectangle)shapes[i]).rectanglePoints[0].Y, ((CRectangle)shapes[i]).rectanglePoints[1].Y);
int YT = (int)Math.Max(((CRectangle)shapes[i]).rectanglePoints[0].Y, ((CRectangle)shapes[i]).rectanglePoints[1].Y);
int count = tempLineList.Count;
GraphicDrawing.LBLineClip(tempLineList[count - 1].X, tempLineList[count - 1].Y,
e.Location.X, e.Location.Y,
XL, XR, YB, YT, curPen, ref g1);
}
}
}
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.Translation)
{
bmp = (Bitmap)bp3.Clone();
g1 = Graphics.FromImage(bmp);
List<Shape> shapes = Singleton.GetInstance.GetShapes();
for (int i = 0; i < shapes.Count; i++)
{
Point[] ptArray = shapes[i].GetArray();
//计算中心点
float x = 0.0f;
float y = 0.0f;
for (int j = 0; j < ptArray.Length; j++)
{
x += (float)(ptArray[j].X);
y += (float)(ptArray[j].Y);
}
x /= ptArray.Length;
y /= ptArray.Length;
Pen pen = new Pen(Color.Black, 3);
pen.DashStyle = DashStyle.Dot;
Point[] ptArray2 = new Point[2];
ptArray2[0] = new Point((int)x, (int)y);
ptArray2[1] = new Point(e.Location.X, e.Location.Y);
g1.DrawLines(pen, ptArray2);
}
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.Rotate)
{
Pen pen = new Pen(Color.Black, 2);
pen.DashStyle = DashStyle.Dot;
Point[] ptArray2 = new Point[2];
ptArray2[0] = new Point(tempLineList[0].X, tempLineList[0].Y);
ptArray2[1] = new Point(e.Location.X, e.Location.Y);
g1.DrawLines(pen, ptArray2);
ptArray2[1] = new Point(tempLineList[0].X + 50, tempLineList[0].Y);
g1.DrawLines(pen, ptArray2);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.HorizontalSymmetry)
{
Pen pen = new Pen(Color.Black, 2);
pen.DashStyle = DashStyle.Dot;
Point[] ptArray2 = new Point[2];
ptArray2[0] = new Point(0, e.Location.Y);
ptArray2[1] = new Point(this.ClientSize.Width, e.Location.Y);
g1.DrawLines(pen, ptArray2);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.VerticalSummetry)
{
Pen pen = new Pen(Color.Black, 2);
pen.DashStyle = DashStyle.Dot;
Point[] ptArray2 = new Point[2];
ptArray2[0] = new Point(e.Location.X, 0);
ptArray2[1] = new Point(e.Location.X, this.ClientSize.Height);
g1.DrawLines(pen, ptArray2);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
else if (args == Args.PointSummetry)
{
Pen pen = new Pen(Color.Black, 2);
pen.DashStyle = DashStyle.Dot;
Point[] ptArray2 = new Point[2];
ptArray2[0] = new Point(e.Location.X, 0);
ptArray2[1] = new Point(e.Location.X, this.ClientSize.Height);
g1.DrawLines(pen, ptArray2);
ptArray2[0] = new Point(0, e.Location.Y);
ptArray2[1] = new Point(this.ClientSize.Width, e.Location.Y);
g1.DrawLines(pen, ptArray2);
this.CreateGraphics().DrawImage(bmp, 0, 0);
}
g1.Dispose();
}
}
数据同步
我们需要将bp3的结果同步到bp0中,则由下列函数完成
private void BitMapDataSynchronism()
{
//将bp3数据同步到bp0
tempLineList.Clear();
bp3 = FileToBitmap(@"D:\Users\Mr_xi\Desktop\bp3.bmp");
g3 = Graphics.FromImage(bp3);
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(0);
bp0 = FileToBitmap(@"D:\Users\Mr_xi\Desktop\bp0.bmp");
g0 = Graphics.FromImage(bp0);
this.CreateGraphics().DrawImage(bp0, 0, 0);
}
数据撤销
我们不需要将bp3的结果,则需要将bp0拷贝给bp3,则由下列函数完成
private void BitMapDataClone()
{
//将bp0数据克隆到bp3
tempLineList.Clear();
bp3 = FileToBitmap(@"D:\Users\Mr_xi\Desktop\bp0.bmp");
g3 = Graphics.FromImage(bp3);
this.CreateGraphics().DrawImage(bp3, 0, 0);
BitMapSave(3);
}
说明
至于二维图形变换中的功能为何没有放在GraphicFunction
中实现,其实可以放进去的,鉴于时间原因未修改。
总结
加强了自身对于图形学算法的理解,C#窗体的运用。但仍有一些不足之处,例如二维图形变换中,单个图形的选中。
在此由衷感谢老师的教导!