C#曲线绘制1-自定义封装曲线

news2024/11/16 17:27:58

目录

0、简要说明

1、GraphEdit.cs类封装

2、效果1

 (1)创建窗体程序

(2)检测鼠标是否在数据点上

3、效果2

 (1)创建窗体程序

4、工程下载连接


0、简要说明

C#绘制曲线自定义类封装

1、封装绘制曲线类。

2、类包括:面板绘制、数据输入、曲线显示。

3、面板大小、字体、曲线颜色和粗细提供属性更改。

4、静态数据曲线和动态数据曲线生成。

1、GraphEdit.cs类封装

 public class GraphEdit
    {
            /// <summary>  
            /// 画板宽度  
            /// </summary>  
            public int BoardWidth { get; set; }
            /// <summary>  
            /// 画板高度  
            /// </summary>  
            public int BoardHeight { get; set; }
            /// <summary>  
            /// 画板背景颜色  
            /// </summary>  
            public Color BoardColor { get; set; }
            /// <summary>  
            /// 画图区域颜色  
            /// </summary>  
            public Color AreasColor { get; set; }
            /// <summary>  
            /// 曲线图颜色  
            /// </summary>  
            public Color GraphColor { get; set; }
            /// <summary>  
            /// 坐标轴颜色  
            /// </summary>  
            public Color AxisColor { get; set; }
            /// <summary>  
            /// 刻度线颜色  
            /// </summary>  
            public Color ScaleColor { get; set; }

            /// <summary>  
            /// 当前绘制的图  
            /// </summary>  
            public Bitmap CurrentImage { get; set; }

            /// <summary>  
            /// 垂直(纵向)边距(画图区域距离左右两边长度)  
            /// </summary>  
            public int VerticalMargin { get; set; }
            /// <summary>  
            /// 平行(横向)边距(画图区域距离左右两边长度)  
            /// </summary>  
            public int HorizontalMargin { get; set; }
            /// <summary>  
            /// X轴刻度线数量  
            /// </summary>  
            public int XScaleCount { get; set; }
            /// <summary>  
            /// Y轴刻度线数量  
            /// </summary>  
            public int YScaleCount { get; set; }

            public GraphEdit(int width, int height, Color boradColor)
            {
                this.BoardWidth = width;
                this.BoardHeight = height;
                this.BoardColor = boradColor;

                //默认值  
                this.XScaleCount = 12;
                this.YScaleCount = 5;
            }

            /// <summary>  
            /// 获得当前数据画出的曲线面积图  
            /// </summary>  
            /// <param name="data">需要绘制的数据</param>  
            /// <param name="xRange">X轴范围(data数据里面的实际范围)</param>  
            /// <param name="yRange">Y轴范围(data数据里面的实际范围)</param>  
            /// <returns>当前的曲线面积图</returns>  
            public Image GetCurrentGraph(List<Point> data, float xRange, float yRange, Point[] pointData)
            {
                CurrentImage = new Bitmap(BoardWidth, BoardHeight);
                Graphics g = Graphics.FromImage(CurrentImage);
                g.SmoothingMode = SmoothingMode.AntiAlias;   //反锯齿  
                g.Clear(BoardColor);

                //1.确定曲线图区域  
                int iAreaWidth = BoardWidth - 2 * HorizontalMargin;              //画图区域宽度  
                int iAreaHeight = BoardHeight - 2 * VerticalMargin;              //画图区域高度  
                Point pAreaStart = new Point(HorizontalMargin, VerticalMargin);  //画图区域起点  
                Point pAreaEnd = new Point(BoardWidth - HorizontalMargin, BoardHeight - VerticalMargin);   //画图区域终点  
                Point pOrigin = new Point(HorizontalMargin, BoardHeight - VerticalMargin);  //原点  
                Rectangle rectArea = new Rectangle(pAreaStart, new Size(iAreaWidth, iAreaHeight));
                SolidBrush sbAreaBG = new SolidBrush(AreasColor);
                g.FillRectangle(sbAreaBG, rectArea);

                sbAreaBG.Dispose();

                //2.确定坐标轴  
                Pen penAxis = new Pen(AxisColor, 3);
                penAxis.EndCap = LineCap.ArrowAnchor;
                g.DrawLine(penAxis, pOrigin, new Point(pAreaStart.X, pAreaStart.Y - VerticalMargin / 2));
                g.DrawLine(penAxis, pOrigin, new Point(pAreaEnd.X + HorizontalMargin / 2, pAreaEnd.Y));

                penAxis.Dispose();

                //3.确定刻度线和标签  
                Pen penScale = new Pen(ScaleColor, 1);
                int fontSize = 8;
                for (int i = 0; i <= XScaleCount; i++)
                {
                    int x = i * (iAreaWidth / XScaleCount) + pAreaStart.X;
                    g.DrawLine(penScale, x, pAreaStart.Y, x, pAreaEnd.Y);
                    string lbl = (i * (xRange / XScaleCount)).ToString();
                    if (xRange == 1440)   //如果按照一天分钟时间显示  
                        lbl = (i * (xRange / XScaleCount) / 60).ToString();
                    if (i != 0)
                    { g.DrawString(lbl, new Font("微软雅黑", fontSize, FontStyle.Regular), new SolidBrush(AxisColor), new Point(x - fontSize, pAreaEnd.Y + VerticalMargin / 9)); }
                }
                for (int i = 0; i <= YScaleCount; i++)
                {
                    int y = pAreaEnd.Y - (i * (iAreaHeight / YScaleCount));
                    g.DrawLine(penScale, pAreaStart.X, y, pAreaEnd.X, y);
                    string lbl = (i * (yRange / YScaleCount)).ToString();
                    g.DrawString(lbl, new Font("微软雅黑", fontSize, FontStyle.Regular), new SolidBrush(AxisColor), new Point(pAreaStart.X - (fontSize * lbl.Length) - HorizontalMargin / 9, y - fontSize / 2));
                }

                //4.画曲线面积  
                //4.1得到数据  
                //4.2数据排序 :为了能顺序画出图,需要对X轴上的数据进行排序  冒泡排序 
                List<Point> listPointData = SortingData(data);

                //4.3.数据转换:将实际的数据转换到图上的点  
                List<Point> listPointGraphics = new List<Point>();//图上的点  
                foreach (Point point in listPointData)
                {
                    Point p = new Point();
                    p.X = pAreaStart.X + Convert.ToInt32((iAreaWidth / xRange) * point.X);     //120为实际值的取值范围0-120  
                    p.Y = pAreaStart.Y + (iAreaHeight - Convert.ToInt32((iAreaHeight / yRange) * point.Y)); //1000为实际值取值范围0-1000  
                    listPointGraphics.Add(p);
                }
                Point[] pointCircle = listPointGraphics.ToArray();          //点集合转数组
                for (int i = 0; i < listPointGraphics.Count;i++ )
                {
                    //点用圆圈标出
                    g.FillEllipse(new SolidBrush(GraphColor), pointCircle[i].X - 4, pointCircle[i].Y - 4, 8, 8);

                    pointData[i].X = pointCircle[i].X;
                    pointData[i].Y = pointCircle[i].Y;
                }
                g.DrawCurve(new Pen(GraphColor, 2), listPointGraphics.ToArray());

                return CurrentImage;
            }

            /// <summary>  
            /// 数据排序  
            /// </summary>  
            /// <param name="lp"></param>  
            /// <returns></returns>  
            private List<Point> SortingData(List<Point> lp)
            {
                for (int i = 0; i < lp.Count - 1; i++)
                {
                    for (int j = 0; j < lp.Count - 1 - i; j++)// j开始等于0,    
                    {
                        if (lp[j].X > lp[j + 1].X)
                        {
                            Point temp = lp[j];
                            lp[j] = lp[j + 1];
                            lp[j + 1] = temp;
                        }
                    }
                }
                return lp;
            }

2、效果1

检测鼠标是否在数据点上

 (1)创建窗体程序

加载曲线面板、并创建线程更新面板数据

        private void Form1_Load(object sender, EventArgs e)
        {
            LoadingUI(); 
        }
        private void LoadingUI()
        {
            graphEdit = new GraphEdit(this.Width, this.Height, boardColor);     //显示面板
            graphEdit.HorizontalMargin = 50;                                   //横水平边距  
            graphEdit.VerticalMargin = 80;                                     //竖垂直边距  
            graphEdit.AreasColor = Color.FromArgb(100, 0, 0, 0);               //画图区域颜色  
            graphEdit.GraphColor = Color.FromArgb(255, 110, 176);              //曲线面积颜色  
            graphEdit.AxisColor = Color.FromArgb(255, 255, 255);               //坐标轴颜色  
            graphEdit.ScaleColor = Color.FromArgb(20, 255, 255, 255);          //刻度线颜色  

            graphEdit.XScaleCount = 24;          //X轴刻度线数量  
            graphEdit.YScaleCount = 10;          //Y轴刻度线数量  


            toUpdate = new Thread(new ThreadStart(Run));
            toUpdate.Start();
            Thread.Sleep(100);
            timer1.Start();
        }

线程函数运行

        private void Run()
        {
            while (true)
            {
                graphEdit.BoardWidth = this.Width;
                graphEdit.BoardHeight = this.Height;

                if(splineDataflag==0)
                {
                    splineDataflag = 1;
                    listSplineData = this.GetBaseData();
                    pointData = new Point[listSplineData.Count];
                }

                //如果是面积曲线图将最后一个参数设为true  
                //Image image = graphEdit.GetCurrentGraph(this.GetBaseData(), XRange, YRange);
                Image image = graphEdit.GetCurrentGraph(listSplineData, XRange, YRange, pointData);
                Graphics g = this.CreateGraphics();  //指定使用那个控件来接受曲线图  
                g.DrawImage(image, 0, 0);
                g.Dispose();
                Thread.Sleep(100);                 //每2秒钟刷新一次    
            }
        }

添加按钮->曲线数据生成,控制变量splineDataflag

        int splineDataflag = 0;
        private void btnSplineData_Click(object sender, EventArgs e)
        {
            splineDataflag = 0;
            
        }

生成随机的数据

        /// <summary>  
        /// 得到(数据库)数据  
        /// </summary>  
        /// <returns></returns>  
        private List<Point> GetBaseData()
        {
            Random r = new Random();
            List<Point> result = new List<Point>();  //数据  
            for (int i = 0; i < XRange - 200; i += 30)
            {
                Point p;
                if (i < 100)
                    p = new Point(i, r.Next(20, 200));
                else
                    p = new Point(i, r.Next(20, 220));
                result.Add(p);
            }
            return result;
        }

(2)检测鼠标是否在数据点上

封装函数

        bool isMouseInEllipse = false;
        private void PointCheck()
        {
            //检测鼠标是否在圆点上
            GraphicsPath vGraphicsPath = new GraphicsPath();          
            foreach(Point p in pointData)
            {
                vGraphicsPath.AddEllipse(p.X - 4, p.Y - 4, 8, 8);     // 添加需要检测识别的点
                Region vRegion = new Region(vGraphicsPath);
                isMouseInEllipse = vRegion.IsVisible(mouseP.X, mouseP.Y); // 判断点是否在圆中
                if (isMouseInEllipse)     //检测在点上,鼠标箭头变为手型
                {
                    Cursor.Current = Cursors.SizeNS;//设置鼠标为手指形                       
                }
            }
            int row = 0;
            int colum = 0;
            if (isLeftButtondowm)
            {
                
            }            
            //Invalidate();   //刷新界面
        }

在定时器函数中调用

        private void timer1_Tick(object sender, EventArgs e)
        {
            PointCheck();
        }

编译运行生成静态数据曲线效果、并检测鼠标在数据原点上是否变化。

3、效果2

 (1)创建窗体程序

加载曲线面板、并创建线程更新面板数据

        private void Form1_Load(object sender, EventArgs e)
        {
            LoadingUI(); 
        }
        private void LoadingUI()
        {
            graphEdit = new GraphEdit(this.Width, this.Height, boardColor);     //显示面板
            graphEdit.HorizontalMargin = 50;                                   //横水平边距  
            graphEdit.VerticalMargin = 80;                                     //竖垂直边距  
            graphEdit.AreasColor = Color.FromArgb(100, 0, 0, 0);               //画图区域颜色  
            graphEdit.GraphColor = Color.FromArgb(255, 110, 176);              //曲线面积颜色  
            graphEdit.AxisColor = Color.FromArgb(255, 255, 255);               //坐标轴颜色  
            graphEdit.ScaleColor = Color.FromArgb(20, 255, 255, 255);          //刻度线颜色  

            graphEdit.XScaleCount = 24;          //X轴刻度线数量  
            graphEdit.YScaleCount = 10;          //Y轴刻度线数量  
            toUpdate = new Thread(new ThreadStart(Run));
            toUpdate.Start();
        }

线程函数运行

        private void Run()
        {
            while (true)
            {
                Image image = graphEdit.GetCurrentGraph(this.GetBaseData(), XRange, YRange, false);  //如果是面积曲线图将最后一个参数设为true  
                Graphics g = this.CreateGraphics();  //指定使用那个控件来接受曲线图  

                g.DrawImage(image, 0, 0);
                g.Dispose();
                Thread.Sleep(500);                 //每2秒钟刷新一次    
            }
        }

创建随机生成获得的数据列表

        /// <summary>  
        /// 得到(数据库)数据  
        /// </summary>  
        /// <returns></returns>  
        private List<Point> GetBaseData()
        {
            Random r = new Random();
            List<Point> result = new List<Point>();  //数据  
            for (int i = 0; i < XRange - 200; i += 30)
            {
                Point p;
                if (i < 100)
                    p = new Point(i, r.Next(180, 200));
                else
                    p = new Point(i, r.Next(200, 220));
                result.Add(p);
            }
            return result;
        }

编译运行生成动态数据曲线效果。
 

4、工程下载连接

https://download.csdn.net/download/panjinliang066333/87897486

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

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

相关文章

Android架构灵魂组件Lifecycle的生命周期机制详解

前言&#xff1a;写作就跟生活一样&#xff0c;是一趟发现之旅。 前言 在早期 Andorid 架构中&#xff0c;生命周期的事件监听和状态查询&#xff0c;我们需要自定义一套提供侦测功能的 Activity/Fragment 基类及回调接口&#xff0c;在 Activity/Fragment 的生命周期方法中实…

618必买清单,几款实用的数码好物分享

只剩一周的618&#xff0c;各大电商平台都在为我们献上一场豪华盛宴&#xff01;小伙伴们&#xff0c;你们的购物车已经快满了吗&#xff1f;楼主翻了翻自己的账单&#xff0c;发现很多商品的活动力度都非常给力&#xff0c;简直就是给我们的钱包来了一次“减负”操作。今天&am…

记录基于Vue.js的移动端Tree树形组件

目录 一、Liquor Tree 入门 &#xff1a; Development Component Options 组件选项 Structure 结构 二、vue-treeselect Introduction 介绍 Getting Started 入门 Vue 树形选择器&#xff08; Vue tree select &#xff09;组件在搭建 Vue 的 app 中特别常用&#xff0…

算法提高-图论- 负环

负环 负环AcWing 904. 虫洞AcWing 361. 观光奶牛AcWing 1165. 单词环 负环 本博客主要介绍spfa求负环 一般用第二种方法 第一种方法如果每个点入队n次&#xff0c;每次入队也要遍历n次&#xff0c;那么时间复杂度就是n2 第二种方法时间复杂度是n&#xff0c;只要发现最短路边数…

城市道路路面病害检测识别分析,以RDD赛事捷克-印度-日本集成融合数据集为例,基于yolov5m模型开发构建城市道路病害检测识别系统

城市道路病害检测是最近比较热门的一个任务领域&#xff0c;核心就是迁移深度学习目前已有的研究成果来实现实时城市道路路面病害的检测识别分析&#xff0c;在我之前的很多博文中都有做过类似桥梁、大坝、基建、隧道等水泥设施裂缝裂痕等目标检测相关的项目&#xff0c;除此之…

SQL Server 2008 定时自动备份和自动删除方法

SQL Server 2008 数据定时自动备份和自动删除方法&#xff0c;同一个计划兼备数据备份数数据删除的操作方法 工具/原料 SQL Server 2008 方法/步骤 1、 点击实例名下的【管理】-【维护计划】-点击鼠标右键&#xff0c;点击【维护计划向导】&#xff0c;填写计划名称&…

崛起的中国卫浴:市场与创新双驱动

5月28日&#xff0c;国产大飞机C919完美完成了商业航班首飞。从中国制造到中国创造&#xff0c;C919的成功是无数中国企业、中国品牌的缩影。 改革开放至今的短短四十年间&#xff0c;中国经历了“以市场换技术-模仿式创新-源创新”三个阶段&#xff0c;上世纪90年代&#xff…

【IMX6ULL驱动开发学习】07.注册驱动设备_分配固定的次设备号_cdev

一、register_chrdev 在之前的hello驱动中&#xff0c;注册驱动设备的方式如下 /*初始化设备方法1&#xff1a;自动分配设备号&#xff0c;占用所有次设备号*/ major register_chrdev(0,"hello_drv",&hello_fops);使用 register_chrdev 分配设备号的方式比较…

【JAVA开发环境配置】 我也可以让JDK版本来去自由的切换了! 哈哈哈哈 舒服!

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

单页面控制中心 vue-router

一、 路由的基本配置 1. 在router->index.js中&#xff0c;配置一个懒路由&#xff0c;定义页面加载哪个组件 import Vue from vue import VueRouter from vue-routerVue.use(VueRouter)const routes []// 配置一个懒路由,不然会加载页面下所有组件 const router new Vu…

基于微信小程序的失物招领系统设计与实现

博主介绍&#xff1a;✌擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案…

BI工具+方案:火速构建电商数据分析架构

电商数据分析该怎么做&#xff1f;谁都知道电商数据分析讲究效率和精细化&#xff0c;光是围绕电商销售分析&#xff0c;就需要制作包括管理驾驶舱、销售预算分析、店铺销售增长趋势、店铺排名分析、商品退货分析等近20种电商数据分析报表。怎么才能又快又好地完成智能数据分析…

java设计模式之:装饰器模式

前言 在软件设计中&#xff0c;我们也有一种类似新房装修的技术可以对已有对象&#xff08;新房&#xff09;的功能进行扩展&#xff08;装修&#xff09;&#xff0c;以获得更加符合用户需求的对象&#xff0c;使得对象具有更加强大的功能。这种技术对应于一种被称之为装饰模…

Bug序列——容器内给/root目录777权限后无法使用ssh免密登录

Linux——创建容器并将本地调试完全的前后端分离项目打包上传docker运行_北岭山脚鼠鼠的博客-CSDN博客 接着上一篇文章结尾出现403错误时通过赋予/root目录以777权限解决403错误。 chmod 777 /root 现在又出现新的问题&#xff0c;远程ssh无法免密登录了&#xff0c;即使通过…

准备了2个月,怒刷面试题,4面字节跳动,顺利拿到 offer

说到字节跳动的经历还是比较搞笑的。一开始我特别想去那个游戏测试部门&#xff0c;当然 data测试部门也是特别想去的&#xff0c;但是提前批只能投一个&#xff0c;于是投了游戏&#xff0c;结果第二天就给我挂了。。。中间北京的捞我&#xff0c;但是不想去北京所以拒绝了&am…

说透缓存击穿、穿透、雪崩及常用解决方案

文章目录 缓存击穿、穿透、雪崩及解决方案击穿、穿透、雪崩的意思缓存击穿缓存穿透缓存雪崩总结 系列文章目录 本文是系列文章&#xff0c;为了增强您的阅读体验&#xff0c;已将系列文章目录放入文章末尾。&#x1f44d;&#x1f44d;&#x1f44d; 缓存击穿、穿透、雪崩及解决…

我的内网渗透-metasploit基础

目录 MSF postgresql msf模块 永恒之蓝 木马下放 后渗透一些简单命令 MSF Msfconsole是Metasploit框架的主要控制台界面。 开源的渗透软件 postgresql 使用的是postgresql数据库&#xff08;metasploit所依载的数据库&#xff0c;没有他也可以运行metasploit框架&…

Vue中如何进行二维码生成与扫描?

Vue中如何进行二维码生成与扫描&#xff1f; 二维码是一种广泛应用于各种场合的编码方式&#xff0c;它可以将信息编码成一张二维图案&#xff0c;方便快捷地传递信息。在Vue.js中&#xff0c;我们可以使用一些库和组件来实现二维码的生成和扫描。本文将介绍如何在Vue中实现二…

高频RFID工业读写器在自动化产线上如何应用?

工业读写器在自动化生产上具有十分重要的作用&#xff0c;它可以对工业生产中的贴上RFID标签的各种零部件和产品&#xff0c;进行跟踪与识别。利用RFID技术进行非接触的物体识别和追踪&#xff0c;更好的掌握产线上的物料信息。 高频RFID工业读写器在自动化产线上如何应用&…

Android studio C++调试问题汇总

问题1&#xff1a;如下图所示&#xff0c;cpp目录不显示或cpp目录不显示C源文件。 此问题由由于abiFilter指定为armeabi&#xff0c;但armeabi架构已经不再支持的原因导致&#xff0c;将armeabi修改为armeabi-v7a或arm64等其他支持的架构即可&#xff0c;修改后如下图所示&…