MyGDI+

news2025/1/11 10:48:52

文章目录

    • @[toc]
  • 界面设计
    • Form窗口
    • MenuStrip
      • 画笔
      • 其他选项
    • 界面美化
  • 整体框架设计
  • DataStructure
    • CPoint
    • Polyline
    • Polygon
  • Singleton
  • GraphicFunction
  • Form事件处理
    • 成员变量
    • 事件处理
  • 总结

界面设计

Form窗口

  首先添加MenuStrip控件,随后在Form窗口属性界面根据个人爱好修改其图标、Text、Font等属性,并修改窗口大小。

image-20221227112532462

  接下来重要的一点就是修改其FormBorderStyleFixedSingle。具体原因后续解释。

image-20221227112924050

  除此之外,可以设计窗口每次弹出位置。

image-20221227113217132

MenuStrip

画笔

  由于不同图形有着不同的线宽、线性、颜色等,例如Windows自带的 画图 ,因此我们需考虑其画笔样式,故单独开辟一个菜单栏窗口用以设计画笔样式。

image-20221227105842221

  事实上,画笔不仅有颜色、线宽、类型,可根据需求自行设计。
  在此选择了一些个人较喜欢的颜色,可根据需求增删查改。以下颜色参考自👉C# Color颜色RGB对照表

image-20221227110225818

  线宽仅选择了 1 ~ 4像素的宽度,亦可根据需求更改。

image-20221227111153458

  线条类型同理。

image-20221227111304711

其他选项

  接下来就是基于实验目的设计MenuStrip的其他选项。鉴于此次实验目的,设计如下。具体细节后续展开。

image-20221227111714194

界面美化

  刚设计的MenuStrip界面当类似如下:

image-20221227113731052

我们选择需要设计的按键,右击进入属性表,在其Image属性中,点击下图所示图标,

image-20221227113951366

选择项目资源文件,点击导入,即可导入所有准备的图像,

image-20221227113851003

选择需显示的图像,点击确定即可,如下图所示:

image-20221227114309604

后续只需对所需的按钮选择导入的图像中选择即可做到如上所示。

整体框架设计

  本项目主要由以下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 1k不存在,则在其水平方向上为其宽度个像素着色。若其线宽为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中,需要处理的无非有以下几个事件:MouseDownMouseMoveLoadFormClosed。。。重点在于如何联立其之间的联系。

成员变量

  为知道用户当前的行为,我们添加枚举类型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);
}

背景色变化

  接下来我们考虑,当用户选择某一颜色时,其背景色变化,其他颜色的按钮背景色均重置。例如:

image-20221227143911401

我们以黑色为例,其他颜色同理。

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#窗体的运用。但仍有一些不足之处,例如二维图形变换中,单个图形的选中。

在此由衷感谢老师的教导!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/118289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

请收下这份数字IC面试超强攻略!(内附大厂面试题目)

2022年马上就要结束了&#xff0c;想必今年有很多同学也已经感受到IC行业的门槛在不断提升&#xff0c;这一点尤其在面试的过程中感受明显。 前两年的时候&#xff0c;面试官有可能问一些比较简单的问题就能通过&#xff0c;今年可就没那么简单了&#xff0c;必须提前做好相关…

SQL的模型类

在Qt的数据库中&#xff0c;除了QSqlQuery访问数据库&#xff0c;还可以使用QSqlQueryModel&#xff0c;QSqlTableModel和QSqlRelationalTableModel&#xff0c;这三个类是从QAbstractTableModel派生下来的&#xff0c;可以很直观的查看数据库的数据 QSqlQueryModel 提供一个…

Educational Codeforces Round 98 (Rated for Div. 2) D. Radio Towers

翻译&#xff1a; 坐标线上有&#x1d45b;2个城镇&#xff0c;编号从0到&#x1d45b;1。&#x1d456;-th镇位于&#x1d456;点。 你在城镇1、2、…、&#x1d45b;以12的概率建造一个无线电塔(这些事件是独立的)。之后&#xff0c;您希望将每个塔上的信号功率设置为从1到…

C/C++开发工具CLion v2022.3全新发布——支持C++ 20

CLion是一款专为开发C及C所设计的跨平台IDE。它是以IntelliJ为基础设计的&#xff0c;包含了许多智能功能来提高开发人员的生产力。这种强大的IDE帮助开发人员在Linux、OS X和Windows上来开发C/C&#xff0c;同时它还使用智能编辑器来提高代码质量、自动代码重构并且深度整合CM…

C++ 当基类为抽象类时如何析构派生类

前言&#xff1a;本教程不涉及基础&#xff0c;稍微了解一下Cvirtual多态的知识就可以了&#xff0c;不了解的话可以先去看一下菜鸟教程&#xff0c;也可以看我往期的文章《virtual》、《虚函数表》 多态分为静态多态和动态多态 静态多态&#xff1a;也成为编译时的多态&#…

使用Word模板导出Word后,表格后面产生空白页

目录 背景 解决 参考 背景 项目中有导出Word功能,其实现逻辑是先整理一个Word文档,里面使用占位符;代码读取Word文档,然后替换占位符。 但出现这样的问题:填充某个表格后,表格后面出现了空白页。 解决 调查发现是段落标记导致的,如何显示段落标记?File -> Op…

生成对抗:DCGAN

DCGAN简介 Generative Adversarial Networks(GANs),GANs有两个模型组成,一个是生成器,用于训练生成假的数据,另一个是判别器,用于预测生成器的输出结果。其中生成器提供训练数据给判别器&#xff0c;提高判别器的准确率。判别器提供生成样本的预测结果&#xff0c;给生成器提供…

【Java系列】小小练习——带你回顾Java基本运算符

返回主篇章         &#x1f447; 【Java】才疏学浅小石Java问道之路 Java小练习1. 练习一1.1 题目1.2 题解(附解析)2. 练习二2.1 题目2.2 题解(附解析)3. 练习三3.1 题目3.2 题解(附解析)小结1. 练习一 1.1 题目 一个三位数&#xff0c;将其拆分为个位、十位、百位后…

mac安装cocoapods完整步骤

一、概念理解 首先不要急着搜索终端命令&#xff0c;你需要明白安装 cocoapods 都需要什么环境&#xff0c;这对于安装途中如果遇到问题该如何解决很重要&#xff0c;很重要&#xff0c;很重要&#xff01; 1、安装pods需要依赖 ruby 环境&#xff0c;而安装 ruby 你需要借助工…

[网鼎杯 2020 白虎组]PicDown(任意文件读取)

打开界面发现有一个get传参然后&#xff0c;尝试任意文件读取漏洞&#xff0c;/etc/passwd看一下,提示下载了一个jpg图片然后 打不开只能用 010查看一下信息 看来是猜对了&#xff0c;然后 如果日记没删掉可以查看历史记录 .bash_history呃呃呃差不到&#xff0c;那就看一下现…

Python 现代控制理论 —— 梯度下降法实现的线性回归系统

线性回归是有监督学习中的经典问题&#xff0c;其核心在于找到样本的多个特征与标签值之间的线性关系。样本集中的第j个样本可被表示为&#xff1a; 特征向量&#xff1a;标签值&#xff1a; 而线性回归系统给出权重向量&#xff1a; 使得该样本的预测值为&#xff1a; 当所有…

Python采集某网站m3u8内容,美女我来了~

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 环境使用: Python 3.8 Pycharm 模块使用: import requests >>> pip install requests import re 正则表达式 解析数据 import json 安装python第三方模块: win R 输入 cmd 点击确…

不止一面的百变 ACE

这个时代&#xff0c;可谓是云原生的黄金时代。 站在这个云原生的风口&#xff0c;年轻一代的开发者如何看待自己所处的环境&#xff1f;他们眼中的云原生未来是什么样&#xff1f; 今天我们就将走近一位年轻的“云原生原住民”&#xff0c;听听他作为开发者的成长经历。 War…

【python3】9.python高阶内容(上)_基础

9.python高阶内容&#xff08;上&#xff09;_基础 2022.12.27 python高阶内容&#xff08;上&#xff09;_基础9.1 字符串的高阶玩法 9.1.1 %百分号模式 %d:整数%i:整数%s:字符%f:小数 【方式1】&#xff1a;前面用格式占位&#xff0c;后面用具体的内容 name "莫烦…

Android设计模式详解之访问者模式

前言 访问者模式是一种将数据操作与数据结构分离的设计模式&#xff1b; 定义&#xff1a;封装一些作用于某种数据结构中的各元素的操作&#xff0c;它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作&#xff1b; 使用场景&#xff1a; 对象结构比较稳定&a…

大厂与小厂招人的区别,看完多少有点不敢相信

前两天在头条发了一条招人的感慨&#xff0c;关于大厂招人和小公司招人的区别。 大厂&#xff1a;有影响力&#xff0c;有钱&#xff0c;能够吸引了大量的应聘者。因此&#xff0c;也就有了筛选的资格&#xff0c;比如必须985名校毕业&#xff0c;必须35岁以下&#xff0c;不能…

基于DoIP使用CANoe对ECU进行诊断测试

伴随以太网引入到车载网络中,本文分享通过常用工具CANoe怎么样对ECU进行通信以及测试。 相比在车载CAN总线,以太网又有什么与众不同之处? 1、硬件接口卡(收发器) 以往车载CAN网络较常使用的是VN 16XX 系列,在连接ECU进行通信时,除了配置波特率也要进行通道分配: 而…

7个学习UI、UX设计一定要经历的步骤

我们不是一些有才华的设计师。我们天生就有艺术天赋。后天我们学会了设计技巧。设计的根本目的是解决问题。设计是不断发现和解决问题。 有许多设计领域&#xff1a;UI、UX.产品设计师.平面设计师.交互设计师.信息架构师等&#xff0c;所以要找出你最感兴趣的设计专业。 现在让…

美颜sdk动态贴纸技术、代码分析

目前&#xff0c;美颜sdk动态贴纸已经成了各大直播平台主播的必备“直播伴侣”&#xff0c;在其他的视频拍摄场景动态贴纸的热度同样很高&#xff0c;本篇文章小编将为大家深度盘点一下美颜sdk动态贴纸的技术实现以及代码。 一、多终端适配 对于如今的直播平台终端来说&#x…

CAPL学习之路-测试功能集函数(测试结构化)

用户可以使用如下函数在测试报告中对每一条测试用例设置结构化的输出内容 TestCaseDescription 添加测试用例的描述文本 此函数用于测试用例中,描述文本会添加在固定区域(测试用例title的下方)。多次调用该函数,描述文本会合并显示在固定区域。如果想让描述文本换行,可以…