GeoLeaning

GDI下用双缓冲实现橡皮筋技术C#

2017-11-18  本文已影响156人  放翁lcf

橡皮筋效果在图形系统中是很常见也很实用的功能。
在唐荣锡的《计算机图形学教程》中介绍为:“所谓橡皮筋技术就是在起点确定后,光标移出去定终点时,在屏幕上始终显示一条连结起点和光标中心的的直线,这条直线随着光标中心位置的变动而变动。

可以知道,一个实现过程如下:

  1. 按下鼠标左键:记录该点坐标。
  2. 移动鼠标:画出上一个端点到鼠标所在点的直线,并删除或者覆盖掉上一条直线。
  3. 再次点击左键时,又选择一个点,相当于回到第一步

用双缓冲技术实现的效果是比较好的,而用自动撤销线即:
ControlPaint.DrawReversibleLine(Point start,Point end, Color BackColor)
闪烁比较严重。


橡皮筋技术画多边形.jpg

下面给出C#中用双缓冲实现橡皮筋的代码,代码中注释还是相对详细的。该代码在VS2017下运行良好。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Threading.Tasks;
using System.Windows.Forms;
/*用双缓冲技术实现GDI+下的橡皮筋效果
 *控件:rubberInGDIplus--主窗体;intimePoiLbl--实时坐标展示
 * 左键选点,中键删除最后一个选择的点;右键完成多边形选择;Del键可删除所有点;
 * */
namespace rubberInGDIplus {
    public partial class rubberEffectForm : Form {
        Pen rubPen = new Pen(Color.SpringGreen, 2);//橡皮筋效果用笔;rubber pen
        Point readPoi; //intime point
        bool useRubber = true;
        Graphics gp,gh;
        private Bitmap bitmap = null;//虽然可以不用怎么多的Bitmap和 Graphics
        public List<Point> poilst = new List<Point>(); //多边形端点

        public rubberEffectForm() {
            InitializeComponent();
            this.Paint += new PaintEventHandler(this.rubberEffectForm_Paint); //初始化载入的像素方格
            this.MouseClick += new MouseEventHandler(this.rubberEffectForm_Click); //监听点击事件
            this.MouseMove += new MouseEventHandler(this.rubberEffectForm_MouseMove); //监听鼠标移动事件 
            this.KeyUp += new KeyEventHandler(this.rubberEffectForm_KeyUp);//键盘按键事件
            //激活双缓冲技术
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);

        }

        private void rubberEffectForm_Load(object sender, EventArgs e) {

        }
        private void rubberEffectForm_Paint(object sender, PaintEventArgs e) {
            gh = e.Graphics;
            bitmap = new Bitmap(ClientSize.Width, ClientSize.Height);

            gp = Graphics.FromImage(bitmap);
            gp.Clear(this.BackColor);
            gp.SmoothingMode = SmoothingMode.AntiAlias;//设置抗锯齿平滑模式
            if (useRubber && readPoi != null) {//橡皮筋在使用中
                int plct = poilst.Count;
                if (plct == 0) {//还没有点
                } else if (plct == 1) {//只存了一个点
                    gp.DrawLine(rubPen, poilst[0], readPoi);
                } else {//两点及以上
                    for (int i = 0; i < plct; i++) {
                        if (i == plct - 1) {
                            gp.DrawLine(rubPen, poilst[0], readPoi);
                            gp.DrawLine(rubPen, poilst[i], readPoi);
                        } else {
                            gp.DrawLine(rubPen, poilst[i], poilst[i + 1]);
                        }
                    }
                }
            } else if (useRubber == false && readPoi != null) {//按下中键后
                int plct = poilst.Count;
                if (plct == 0 | plct == 1) {
                } else {//两点及以上
                    for (int i = 0; i < plct; i++) {
                        if (i == plct - 1) {
                            gp.DrawLine(rubPen, poilst[0], poilst[i]);
                        } else {
                            gp.DrawLine(rubPen, poilst[i], poilst[i + 1]);
                        }
                    }
                }
            }
            

            gh.DrawImage(bitmap, 0, 0);//display

        }

        private void rubberEffectForm_Click(object sender, MouseEventArgs e) {

            if (e.Button == MouseButtons.Left) {//鼠标左击
                useRubber = true;
                Point readPoint = this.PointToClient(Control.MousePosition);//基于工作区的坐标
                readPoi = readPoint;
                intimePoiLbl.Text = readPoint.ToString();
                //drawVertex(gp, readPoint); //画端点(顶点) 由于橡皮筋的覆盖,端点看不出来
                poilst.Add(readPoint);//加点到list<point>里

            } else if (e.Button == MouseButtons.Right) {  //右键
                useRubber = false;
                this.Refresh();

                //drawRim();//画边框
            } else if (e.Button == MouseButtons.Middle) {  //中键
                int plast = poilst.Count - 1;
                poilst.RemoveAt(plast);
                useRubber = true;
                this.Refresh();
            }

        }

        private void rubberEffectForm_MouseMove(object sender, MouseEventArgs e) {
            readPoi = this.PointToClient(Control.MousePosition);//基于工作区的坐标
            Graphics gw = this.CreateGraphics();
            if (useRubber) { //在橡皮筋模式内
                gw.Clear(BackColor);
                int plct = poilst.Count;
                if (plct == 0) {// ==0: pass
                } else if (plct == 1) {
                    gw.DrawLine(rubPen, poilst[0], readPoi);
                } else {//两点及以上
                    for (int i = 0; i < plct; i++) {
                        if (i == plct - 1) {//画到最后一点了
                            gw.DrawLine(rubPen, poilst[i], readPoi);
                            gw.DrawLine(rubPen, poilst[0], readPoi);
                        } else {
                            gw.DrawLine(rubPen, poilst[i], poilst[i + 1]);
                        }
                    }
                }
                
            } else {
            }
            intimePoiLbl.Text = readPoi.ToString();
        }

        private void rubberEffectForm_KeyUp(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.Delete) {
                poilst.Clear(); //画的点也要清除
                this.Refresh();
            } else if (e.KeyCode == Keys.Back | e.KeyCode == Keys.Escape) {
                int plast = poilst.Count - 1;
                poilst.RemoveAt(plast);
                useRubber = true;
                this.Refresh();
            }

        }

        #region 可用可不用的函数
        //画端点(顶点)
        private void drawVertex(Graphics g, Point poi) {
            Size sz = new Size(4, 4);
            g.FillEllipse(Brushes.Red, new Rectangle(poi, sz));
        }
        private void drawRim() {//画边框
            if (poilst.Count == 0)
                return;
            Point[] poi = new Point[poilst.Count];
            for (int i = 0; i < poilst.Count; i++) {
                poi[i] = poilst[i];
            }
            gp.DrawPolygon(new Pen(Color.Blue, 2), poi);
        }
        
        #endregion

    }
}


还可以设置鼠标指针的形状,在窗体属性的Cursor中,由Default变为Cross,这样就像ArcGIS或者CorelDRAW的效果了。

设置Cursor.jpg

完整工程以及更新文件可以参见我的GitHub-rubberInGDIplus
直接点击或者复制链接:
https://github.com/QLWeilcf/pixelCGframewk/tree/master/rubberInGDIplus
我觉得它具有的功能是:能够很好地作为图形学以及矢量多边形小软件的框架

具体的应用项目可以看我的扫描线填充多边形的代码。

完成效果-五角星.jpg
上一篇 下一篇

猜你喜欢

热点阅读