当前位置: 首页 > 工具软件 > SharpGL > 使用案例 >

C#,SharpGL开发的3D图表控件

邰伟彦
2023-12-01

另外一个版本是使用OpenTK开发,文字显示效果不太理想,因此换了SharpGL重新写了一个。

文字是缓存为图片,然后使用坐标映射的方式绘制到图表上。

资源忘了复制链接,欢迎来白嫖

  public partial class QChart : UserControl
    {
       

 
        public QChart()
        {
            InitializeComponent();

            BackColor = Color.White;
            LineColor = Color.Gray;
            DotColor = Color.Red;
            TitleColor = Color.Red;

            TxtColor = Color.Black;

            XTitle = "X";
            YTitle = "Y";
            ZTitle = "Z";
            XRange = 5;
            YRange = 3;
            ZRange = 3;
            _rotationy = 1;
            FontSize = 12;//文字大小
            minx = miny = minz = 0;
            maxx = maxy = maxz = 10;
            XStep = YStep = ZStep = 1;
          
        }

        private int _x;
        private float _rotationx, _rotationy;


        private float zIndex = 12;//俯视角度,好看

        private bool locked = false;//是否锁定

        private List<Point3D> points;

        public float FontSize { get; set; }


        /// <summary>
        /// 坐标线条颜色
        /// </summary>
        public Color LineColor { get; set; }
        /// <summary>
        /// 点颜色
        /// </summary>
        public Color DotColor { get; set; }
        /// <summary>
        /// 标题颜色
        /// </summary>
        public Color TitleColor { get; set; }
        /// <summary>
        /// 刻度颜色
        /// </summary>
        public Color TxtColor { get; set; }
        /// <summary>
        /// X轴标题
        /// </summary>
        public String XTitle { get; set; }
        /// <summary>
        /// Y轴标题
        /// </summary>
        public String YTitle { get; set; }
        /// <summary>
        /// Z轴标题
        /// </summary>
        public String ZTitle { get; set; }

        public int XRange { get; set; }
        public int YRange { get; set; }
        public int ZRange { get; set; }
        public double maxx { get; set; }
        public double minx { get; set; }
        public double maxy { get; set; }
        public double miny { get; set; }
        public double maxz { get; set; }
        public double minz { get; set; }
        /// <summary>
        /// X方向的刻度间隔,默认1
        /// </summary>
        public int XStep { get; set; }
        /// <summary>
        /// y方向的刻度间隔,默认1
        /// </summary>
        public int YStep { get; set; }

        /// <summary>
        /// Z方向的刻度间隔,默认1
        /// </summary>
        public int ZStep { get; set; }


        private Point mouse = new Point();//记录鼠标位置

        private void openGLControl1_OpenGLDraw(object sender, RenderEventArgs e)
        {
            //  Get the OpenGL object, just to clean up the code.
            OpenGL gl = this.openGLControl1.OpenGL;

            gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);


            gl.ClearColor((float)(BackColor.R / 255.0), (float)(BackColor.G / 255.0), (float)(BackColor.B / 255.0), 1f);//背景色
            gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT | OpenGL.GL_STENCIL_BUFFER_BIT);

            gl.MatrixMode(OpenGL.GL_MODELVIEW);

            gl.LoadIdentity();


            gl.Rotate(_rotationx, 1, 0, 0);//X轴旋转

            gl.Rotate(_rotationy, 0, 1, 0);//Y轴旋转


            //绘制坐标


            gl.Color(LineColor.R, LineColor.G, LineColor.B);//线颜色


            //gl.DrawText(0, 0, TitleColor, "宋体", FontSize, "ss");

            //水平线,X面
            for (int i = -XRange; i <= XRange; i++)
            {
                gl.Begin(OpenGL.GL_LINES);
                gl.Vertex(i, -YRange, -ZRange);
                gl.Vertex(i, -YRange, ZRange);
                gl.End();


            }
            for (int i = -ZRange; i <= ZRange; i++)
            {
                gl.Begin(OpenGL.GL_LINES);
                gl.Vertex(-XRange, -YRange, i);
                gl.Vertex(XRange, -YRange, i);
                gl.End();


            }


            //竖直线,Y面
            for (int i = -YRange; i <= YRange; i++)
            {
                gl.Begin(OpenGL.GL_LINES);
                gl.Vertex(-XRange, i, -ZRange);
                gl.Vertex(-XRange, i, ZRange);
                gl.End();
            }

            for (int i = -ZRange; i <= ZRange; i++)
            {
                gl.Begin(OpenGL.GL_LINES);
                gl.Vertex(-XRange, -YRange, i);
                gl.Vertex(-XRange, YRange, i);
                gl.End();
            }
            //Z面
            for (int i = -YRange; i <= YRange; i++)
            {
                gl.Begin(OpenGL.GL_LINES);
                gl.Vertex(-XRange, i, -ZRange);
                gl.Vertex(XRange, i, -ZRange);
                gl.End();
            }

            for (int i = -XRange; i <= XRange; i++)
            {
                gl.Begin(OpenGL.GL_LINES);
                gl.Vertex(i, -YRange, -ZRange);
                gl.Vertex(i, YRange, -ZRange);
                gl.End();
            }


            if (XStep < 1) XStep = 1;
            //刻度x
            for (int i = -XRange; i <= XRange; i+=XStep)
            {

                double v = (i + XRange) / (2.0 * XRange) * (maxx - minx);
                DrawText(String.Format("{0:f2}", v), i - 0.2f, -YRange - 0.5f, -ZRange - 0.2f, TxtColor);


            }
            //DrawTextCN(XTitle, XRange + 0.5f, -YRange - 0.25f, -ZRange, TitleColor);
            if (YStep < 1) YStep = 1;
            //刻度Y
            for (int i = -YRange; i < YRange; i+= YStep)
            {
                double v = (i + YRange) / (2.0 * YRange) * (maxy - miny);
                DrawText(String.Format("{0:f2}", v), -XRange - 0.2f, i, -ZRange - 0.2f, TxtColor);
            }
           // DrawTextCN(YTitle, -XRange, YRange, -ZRange, TitleColor);

            if (ZStep < 1) ZStep = 1;
            //刻度Z
            for (int i = -ZRange; i < ZRange; i+=ZStep)
            {
                double v = (i + ZRange) / (2.0 * YRange) * (maxz - minz);
                DrawText(String.Format("{0:f2}", v), -XRange - 0.5f, -YRange - 0.5f, i, TxtColor);
            }
           // DrawTextCN(ZTitle, -XRange, -YRange - 0.5f, ZRange + 0.5f, TitleColor);

            //输出三个坐标标题
            gl.Color(TitleColor.R, TitleColor.G, TitleColor.B);
            drawCNString("  ", 99, 99, 99, gl);
            drawCNString(ZTitle, -XRange, -YRange,ZRange+0.5f,  gl);
            drawCNString(XTitle+0.5f , XRange, -YRange, -ZRange, gl);
            drawCNString(YTitle , -XRange, YRange+0.5f, -ZRange, gl);

            //如果有点,才绘制点

            if (points != null && points.Count > 0)
            {
                gl.Color(DotColor.R, DotColor.G, DotColor.B);

                // gl.PointSize(4);

                foreach (Point3D p in points)
                {
                    double x = XRange * (2 * (p.X - minx) / (maxx - minx) - 1);
                    double y = YRange * (2 * (p.Y - miny) / (maxy - miny) - 1);
                    double z = ZRange * (2 * (p.Z - minz) / (maxz - minz) - 1);
                    if (p.OnMouse) gl.PointSize(6);
                    else gl.PointSize(4);
                    gl.Begin(OpenGL.GL_POINTS);
                    gl.Vertex(x, y, z);
                    gl.End();
                }


                // GL.DrawPixels


            }

            gl.Flush();
        }


        private void DrawText(String s, double x, double y, double z, Color color)
        {
            OpenGL gl = this.openGLControl1.OpenGL;
            var sv = SharpGL.SceneGraph.OpenGLSceneGraphExtensions.Project(gl, new SharpGL.SceneGraph.Vertex((float)x, (float)y, (float)z));
            gl.DrawText((int)sv.X, (int)sv.Y, (float)(color.R / 255.0), (float)(color.G / 255.0), (float)(color.B / 255.0), "宋体", FontSize, s);

        }

        private void drawCNString(string str, float x, float y,float z, OpenGL gl)
        {


          
            int i;

            //  Create the font based on the face name.
            var hFont = Win32.CreateFont((int)FontSize+4, 0, 0, 0, Win32.FW_DONTCARE, 0, 0, 0, Win32.DEFAULT_CHARSET,
                Win32.OUT_OUTLINE_PRECIS, Win32.CLIP_DEFAULT_PRECIS, Win32.CLEARTYPE_QUALITY, Win32.CLEARTYPE_NATURAL_QUALITY, "宋体");

            //  Select the font handle.
            var hOldObject = Win32.SelectObject(gl.RenderContextProvider.DeviceContextHandle, hFont);
            //  Create the list base.
            var list = gl.GenLists(1);

            gl.RasterPos(x, y,z);

            // 逐个输出字符
            for (i = 0; i < str.Length;i++)
            {
                bool result = Win32.wglUseFontBitmapsW(gl.RenderContextProvider.DeviceContextHandle, str[i], 1, list);
                gl.CallList(list);
            }
            // 回收所有临时资源
            //free(wstring);
            gl.DeleteLists(list, 1);
            //  Reselect the old font.
            Win32.SelectObject(gl.RenderContextProvider.DeviceContextHandle, hOldObject);
            //  Free the font.
            Win32.DeleteObject(hFont);
            //glDeleteLists(list, 1);
        }
       

        /// <summary>
        /// 传入坐标集合
        /// </summary>
        /// <param name="points"></param>
        public void ShowPoints(List<Point3D> points)
        {
            this.points = points;
            if (points == null || points.Count == 0) return;

            maxx = points[0].X;
            minx = points[0].X;
            maxy = points[0].Y;
            miny = points[0].Y;
            maxz = points[0].Z;
            minz = points[0].Z;

            foreach (Point3D p in points)
            {
                if (p.X > maxx) maxx = p.X;
                if (p.X < minx) minx = p.X;
                if (p.Y > maxy) maxy = p.Y;
                if (p.Y < miny) miny = p.Y;
                if (p.Z > maxz) maxz = p.Z;
                if (p.Z < minz) minz = p.Z;

            }
            //防止直接到边界
            maxx += 0.2;
            minx -= 0.2;
            maxy += 0.2;
            miny -= 0.2;
            maxz += 0.2;
            minz -= 0.2;

        }

        private void QChart_Load(object sender, EventArgs e)
        {

      
            openGLControl1.OpenGLInitialized += OpenGLControl1_OpenGLInitialized;
            this.Resize += QChart_Resize;
            openGLControl1.MouseDown += on_MouseDown;
            openGLControl1.MouseLeave += on_MouseLeave;
            openGLControl1.MouseMove += on_MouseMove;
            openGLControl1.MouseUp += on_MouseUp;
            openGLControl1.MouseWheel += on_MouseWheel;
            SetupViewport();
        }

        private void OpenGLControl1_OpenGLInitialized(object sender, EventArgs e)
        {
            SetupViewport();
        }

        private void QChart_Resize(object sender, EventArgs e)
        {
            SetupViewport();
        }

      

        //按键事件
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (e.KeyCode == Keys.Space)
            {
                this._x++;
                SetupViewport();
                this.Invalidate();
            }
        }

        private void SetupViewport()
        {
            var w = this.Width;
            var h = this.Height;
            OpenGL GL = openGLControl1.OpenGL;
            GL.MatrixMode(OpenGL.GL_PROJECTION);
            GL.LoadIdentity();


            GL.Viewport(0, 0, w, h); // Use all of the glControl painting area

            if (h == 0) h = 1;
            GL.Perspective(60.0f, w / h, 0.01, 100);
            GL.LookAt(0, 0, zIndex, 0, 0, 0, 0, 1, 0);
        }

        private bool msDown = false;//是否鼠标按下
        private int oldX = 0;//原始X坐标
        private int oldY = 0;
        private void on_MouseWheel(object sender, MouseEventArgs e)
        {

            // MessageBox.Show(e.Delta+"");
            if (e.Delta > 0)
            {
                zIndex += 1;
                if (zIndex > 20) zIndex = 20;
            }
            else if (e.Delta < 0)
            {

                zIndex -= 1;
                if (zIndex < 8) zIndex = 8;
            }

            SetupViewport();
            this.Invalidate();
        }
        private void on_MouseDown(object sender, MouseEventArgs e)
        {
            msDown = true;
            oldX = e.X;//记录
            oldY = e.Y;
            Cursor = Cursors.Hand;
            ;
        }

        private void on_MouseMove(object sender, MouseEventArgs e)
        {

            
            if (points != null && points.Count > 0)
            {
                OpenGL gl = openGLControl1.OpenGL;
                double[] viewmatrix=new double[16];
                double[] projection = new double[16];
                int[] viewport = new int[4];

                gl.GetDouble(OpenGL.GL_MODELVIEW_MATRIX, viewmatrix);
                gl.GetDouble(OpenGL.GL_PROJECTION,projection);
                gl.GetInteger(OpenGL.GL_VIEWPORT, viewport);


                for (int i = 0; i < points.Count; i++)
                {
                    points[i].OnMouse = false;
                }
                
                int x = e.X;
                int y = e.Y;
                for (int i = 0; i < points.Count; i++)
                {

                    var sv = SharpGL.SceneGraph.OpenGLSceneGraphExtensions.Project(gl, new SharpGL.SceneGraph.Vertex((float)points[i].X, (float)points[i].Y, (float)points[i].Z));
                    if (Math.Abs(sv.X - x) < 3 && Math.Abs(sv.Y - y) < 3)
                    {
                        points[i].OnMouse = true;
                        break;
                    }
                }


            }
            


            if (!msDown) return;
            int dx = e.X - oldX;
            int dy = e.Y - oldY;

            if (dy > 0) _rotationx += 0.5f;
            else if (dy < 0) _rotationx -= 0.5f;
            if (dx > 0) _rotationy += 0.5f;
            else if (dx < 0) _rotationy -= 0.5f;
            SetupViewport();
            this.Invalidate();


        }

        private void on_MouseUp(object sender, MouseEventArgs e)
        {
            msDown = false;
            Cursor = Cursors.Default;
        }

        private void on_MouseLeave(object sender, EventArgs e)
        {
            msDown = false;
        }
    }

核心代码如下

 类似资料: