GDI+绘图轻松入门[6]-路径变形和表盘的绘制

news2024/11/25 3:25:32

有了前面的坐标相关知识的了解,我们这次来实战一把,绘制一个表盘。当然,绘制表盘我们要学会GDI+绘图中的路径绘图与两个必要的Transform变形函数,RotateTransform(旋转变形)、TranslateTransform(移动变形)。我们先来看看效果:
在这里插入图片描述

一、路径

1、GraphicsPath介绍

什么是图形路径呢?用官方的话来说就是“表示一系列相互连接的直线和曲线”,为什么会有这么一个类呢?很显然,以前我们学习的图形类,要么是圆,要么是直线,这个类显然就是要把他们都结合在一起。这个类的使命决定它的用法是比较丰富的,我们可以具体参看官网这里我们来介绍几个比较常用的方法,满足我们后面的绘图需要即可。

2、初识GraphicsPath与基本元素

绘制一个GraphicPath最基本的条件是要拥有绘制的元素,所以GDI+为GraphicPath提供了一大堆的添加元素的方法,都是以Addxxx命名的函数(如:AddArc,AddBezier,AddCuve,AddEllipse,AddLine,AddRectangle,AddString等及他们对应的复数形式方法和AddPie,AddPath,AddPloygon)。他们的用法都很简单,这里举例看看效果(我们准备了一个干净的窗体):

private void drawPath(Graphics gp)
{
    
    Point[] myArray =
             {
         new Point(30,30),
         new Point(60,60),
         new Point(0,60),
         new Point(30,30)
     };
    GraphicsPath myPath = new GraphicsPath();
    myPath.AddLines(myArray);

    Point[] myArray2 =
             {
         new Point(30,30),
         new Point(0,0),
         new Point(60,0),
         new Point(30,30)
     };
    GraphicsPath myPath2 = new GraphicsPath();
    myPath2.AddLines(myArray2);
    myPath.AddPath(myPath2, true);
    gp.DrawPath(new Pen(Color.Black, 4), myPath);

}

在这里插入图片描述

2、StartFigure

StartFigure的作用是不闭合当前图形即开始一个新图形。 后面添加到该路径的所有点都被添加到此新图形中。
一般我们使用StartFigure绘制图形路径基本就三个步骤:
1、实例化一个GraphicPath
2、利用实例的StartFigure方法开启一个形状
3、利用各种Addxxx函数增加图形元素
4、利用实例的StartFigure方法结束一个形状
5、在至少拥有一个Figure后,调用draw函数绘制出来即可。
来一个实例:

 private void drawPath(Graphics gp)
 {
     // Create a GraphicsPath object.
     GraphicsPath myPath = new GraphicsPath();
     // First set of figures.
     myPath.StartFigure();
     myPath.AddArc(10, 10, 50, 50, 0, 270);
     myPath.AddLine(new Point(50, 0), new Point(100, 50));
     myPath.AddArc(50, 100, 75, 75, 0, 270);
     myPath.CloseFigure();
     myPath.StartFigure();
     myPath.AddArc(100, 10, 50, 50, 0, 270);

     // Second set of figures.
     myPath.StartFigure();
     myPath.AddArc(10, 200, 50, 50, 0, 270);
     myPath.CloseFigure();
     myPath.StartFigure();
     myPath.AddLine(new Point(60, 200), new Point(110, 250));
     myPath.AddArc(50, 300, 75, 75, 0, 270);
     myPath.CloseFigure();
     myPath.StartFigure();
     myPath.AddArc(100, 200, 50, 50, 0, 270);


     gp.DrawPath(new Pen(Color.Black, 4), myPath);

 }

效果如下:
在这里插入图片描述

二、变形函数

1、TranslateTransform与RotateTransform

这两个方法存在与Pen、Graphics、TextureBrush、PathGradientBrush、LinearGradientBrush中,当然最常见的还是Graphics中的变形,所以这里我们也将以它为例来演示这个函数的用法。
在这里插入图片描述

 gp.ResetTransform();
 Image newImage = Image.FromFile("csharp.jpg");
 Point ulCorner = new Point(0, 0);
 gp.TranslateTransform(240, 60);
 gp.ScaleTransform(0.3f, 0.3f);
 gp.RotateTransform(30.0f);
 gp.DrawImage(newImage, ulCorner);

代码一开始,我们重置了图形的变形ResetTransform,以防前面的变形对我们此次变形产生影响。
在这里插入图片描述

2、ScaleTransform

我们常用的除了上述两个函数,上面我们还是用了一个ScaleTransform函数,很简单一看就会它专用与缩放,参数接受的是一个浮点型。

三、表盘绘制

1、圆盘绘制

  graphics.FillEllipse(new SolidBrush(Color.Black), getCircleRect(centerPoint, 120));
  graphics.DrawArc(p, getCircleRect(centerPoint,outlineRadius), mapStartRadias, 360);//绘制一个由clock边框定义的椭圆

在这里插入图片描述

2、指针绘制

Point sNeedlePoint = new Point((int)(centerPoint.X + longNeedleLength * Math.Sin(angel_s)), (int)(centerPoint.Y - longNeedleLength * Math.Cos(angel_s)));    
graphics.DrawLine(new Pen(Color.Red, 2), centerPoint, sNeedlePoint);
//绘制指针圆柄
graphics.FillEllipse(new SolidBrush(Color.Yellow), getCircleRect(centerPoint, 6));

在这里插入图片描述

3、着色知识

1、渐变

关于渐变,如果我们有耐心,可以直接参考官网的说明,为了我们完成目标,我们选择最常用的两种渐变来熟悉一下他们的用法。

(1)、PathGradientBrush

1、初始化PathGradientBrush
2、设定CenterColor
3、设定SurroundColors
4、利用draw绘制渐变
在这里插入图片描述

GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(centerPoint.X,centPoint.Y,100,50);
Color[] surroundColor = new Color[] { Color.White};
PathGradientBrush pb = new PathGradientBrush(gp);
pb.CenterColor = Color.Blue;
pb.SurroundColors = surroundColor;
graphics.FillPath(pb, gp);

(2)、LinearGradientBrush

我们首先来看看它的构造函数:

public LinearGradientBrush(Point point1, Point point2, Color color1, Color color2);
public LinearGradientBrush(PointF point1, PointF point2, Color color1, Color color2);
public LinearGradientBrush(Rectangle rect, Color color1, Color color2, LinearGradientMode linearGradientMode);
public LinearGradientBrush(Rectangle rect, Color color1, Color color2, float angle);
public LinearGradientBrush(RectangleF rect, Color color1, Color color2, LinearGradientMode linearGradientMode);
public LinearGradientBrush(RectangleF rect, Color color1, Color color2, float angle);
public LinearGradientBrush(Rectangle rect, Color color1, Color color2, float angle, bool isAngleScaleable);
public LinearGradientBrush(RectangleF rect, Color color1, Color color2, float angle, bool isAngleScaleable);

很显然,这个类的构造函数决定了我们一般有四种方式来使用它;
1、初始化LinearGradientBrush时设定好渐变的起点和终点及对应的两种颜色
2、初始化LinearGradientBrush时设定好的矩形、对应的两种颜色及渐变角度
3、初始化LinearGradientBrush时设定好的矩形、对应的两种颜色及渐变方向(本质同第二种,只是把典型的角度做成了枚举LinearGradientMode )
4、初始化LinearGradientBrush时设定好的矩形、对应的两种颜色、渐变角度及缩放

所以,我们列举了典型了应用,代码如下:

 private void drawPath(Graphics gp)
 {
	LinearGradientBrush linGrBrush = new LinearGradientBrush(
	new Point(0, 10),
    new Point(220, 10),
	Color.FromArgb(255, 255, 0, 0),   // Opaque red
	Color.FromArgb(255, 0, 0, 255));  // Opaque blue
	
	Pen pen = new Pen(linGrBrush);
	
	gp.DrawLine(pen, 200, 10, 500, 10);
	gp.FillEllipse(linGrBrush, 240, 20, 200, 100);
	gp.FillRectangle(linGrBrush, 0, 155, 500, 30);
	
	
	Point pt=new Point(0, 0);
	Brush b = new LinearGradientBrush(button1.ClientRectangle, Color.Green, Color.Honeydew, 75);//最后一个参数渐变颜色方向或角度,button1窗体中有一个隐藏的按钮
	gp.FillRectangle(b, button1.ClientRectangle);

 }

在这里插入图片描述

这里尤其要注意,渐变的起点和终点,比如上图的椭圆,起点x=240,长200,颜色的起点一点要能够覆盖这段长度,那么颜色的起点x=230,终点x=450,可算是万无一失。我们看下面的矩形渐变就对渐变起点终点一目了然了。

4、 指针着色

Point sNeedlePoint = new Point((int)(centerPoint.X + longNeedleLength * Math.Sin(angel_s)), (int)(centerPoint.Y - longNeedleLength * Math.Cos(angel_s)));  
GraphicsPath spath1 = new GraphicsPath();
spath.AddArc(getCircleRect(centerPoint, 6), 0, 360);
Point[] points = { centerPoint, new Point((int)(centerPoint.X + 12 * Math.Sin(angel_s - Math.PI / 6)), (int)(centerPoint.Y - 12 * Math.Cos(angel_s - Math.PI / 6))), sNeedlePoint, new Point((int)(centerPoint.X + 12 * Math.Sin(angel_s + Math.PI / 6)), (int)(centerPoint.Y - 12 * Math.Cos(angel_s + Math.PI / 6))) };
GraphicsPath spath2 = new GraphicsPath();
spath2.AddLines(points);
graphics.FillPath(new SolidBrush(Color.Yellow), spath2);
graphics.FillPath(new SolidBrush(Color.Yellow) , spath1);

在这里插入图片描述

5、表盘着色

表盘着色,我们采用的就是fill系列的函数,如FillPath结合渐变,FillEllipse等。所以我们前面的基础要掌握。

g.FillEllipse(new SolidBrush(Color.DarkSlateGray), getCircleRect(centerPoint, 152));
g.FillEllipse(new SolidBrush(Color.Black), getCircleRect(centerPoint, 120));
 g.FillEllipse(new SolidBrush(Color.FromArgb(50, 100, 100, 255)), getCircleRect(centerPoint, 96));
GraphicsPath gp = new GraphicsPath();

gp.AddArc(getCircleRect(centerPoint, 76), 0, 360);
Color[] surroundColor = new Color[] { Color.FromArgb(100, 20, 20, 200)};
PathGradientBrush pb = new PathGradientBrush(gp);
pb.CenterColor = Color.FromArgb(60, 10, 10, 20);
pb.SurroundColors = surroundColor;
g.FillPath(pb, gp);
g.FillEllipse(new SolidBrush(Color.FromArgb(10, 10, 40)), getCircleRect(centerPoint, 50));
g.DrawArc(p, getCircleRect(centerPoint,outlineRadius), mapStartRadias, 360);//绘制边框圆

在这里插入图片描述

6、增加刻度和文字

g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Pen p = new Pen(Color.Black, 3);
Pen k = new Pen(Color.White, 2);
GraphicsPath spath = new GraphicsPath();
 spath.StartFigure();
for (int i = 0; i <= fullGauge / bigUnit; i++)//时刻标度0-12, 30度一格
{
    double angel = i * (Math.PI) / 6;

    Point xy1 = new Point((int)(outlineRadiusOffset * Math.Cos(angelStart + angel) + centerPoint.X), (int)(outlineRadiusOffset * Math.Sin(angelStart + angel) + centerPoint.Y));//圆心定义为(centerPoint.X,centerPoint.Y)
    Point xy2 = new Point((int)(inlineRadiusOffset * Math.Cos(angelStart + angel) + centerPoint.X), (int)(inlineRadiusOffset * Math.Sin(angelStart + angel) + centerPoint.Y));
    Point xystr = new Point((int)(strRadius * Math.Cos(angelStart + angel) + centerPoint.X), (int)(strRadius * Math.Sin(angelStart + angel) + centerPoint.Y));

    g.DrawLine(p, xy1, xy2);
    //刻度文字
    String str = ((i) * 10).ToString();
    SizeF sizeg = g.MeasureString(str, new Font("宋体", 9));
    if ((angelStart + angel) >= Math.PI * 3 / 2)
    {
        if ((angelStart + angel) == Math.PI * 3 / 2)
            g.DrawString(str, new Font("宋体", 9), new SolidBrush(k.Color), new Point(xystr.X - 10, xystr.Y - (int)((sizeg.Height / 3) * Math.Cos(angelStart + angel))));
        else
            g.DrawString(str, new Font("宋体", 9), new SolidBrush(k.Color), new Point(xystr.X - (int)((sizeg.Width) * Math.Cos(angelStart + angel)), xystr.Y - (int)((sizeg.Height / 3) * Math.Cos(angelStart + angel))));
    }
    else
        g.DrawString(str, new Font("宋体", 9), new SolidBrush(k.Color), new Point(xystr.X, xystr.Y + (int)((sizeg.Height / 3) * Math.Cos(angelStart + angel)))); //-(int)((sizeg.Width) * Math.Cos(angelStart + angel)),- (int)((sizeg.Height / 2) * Math.Cos(angelStart + angel))

}
for (int i = 0; i <= fullGauge / smallUnit; i++)//小刻标度0-60
{
    double angel = i * (Math.PI) / 30;

    Point xy1 = new Point((int)(outlineRadiusOffset * Math.Cos(angelStart + angel) + centerPoint.X), (int)(outlineRadiusOffset * Math.Sin(angelStart + angel) + centerPoint.Y));
    Point xy2 = new Point((int)(middleRadiusOffset * Math.Cos(angelStart + angel) + centerPoint.X), (int)(middleRadiusOffset * Math.Sin(angelStart + angel) + centerPoint.Y));

    if (i % 5 != 0) 
           spath.AddLine( xy1, xy2);
   
}
 spath.CloseFigure();

这里要注意,如果我们要实现警告刻度区,那就要分别来绘制刻度线。用一个if语句即可完成。
在这里插入图片描述
刻度喜欢亮一些可以提亮点,如下图:
在这里插入图片描述
可以选择加上一个仪表盘的标签

String strt = "仪表盘";
SizeF sizeF = g.MeasureString(strt, new Font("宋体", 9));
int strFrameEdgeOffset = 4;
g.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(centerPoint.X - ((int)sizeF.Width / 2) - strFrameEdgeOffset, centerPoint.Y + 80- strFrameEdgeOffset, (int)sizeF.Width + strFrameEdgeOffset, (int)sizeF.Height + strFrameEdgeOffset));
g.DrawString(strt, new Font("宋体", 9),new SolidBrush(Color.White),new Point(centerPoint.X-((int)sizeF.Width/2),centerPoint.Y+80));
            

在这里插入图片描述
到这里,仪表盘的绘制基本完成,这个时候我们就可以和事件的联系在一起实现转动了。我这里的指针都是使用的可变化的角度,所以可以根据事件的值来变化,就如我们开头看到的那样可以转动。
当然,你完全可以自己新建一个自定义控件,把这些代码拷贝进去,那你就有了一个表盘自定义控件了。

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

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

相关文章

《爆肝整理》保姆级系列教程python接口自动化(十四)--session关联接口(详解)

简介 上一篇cookie绕过验证码模拟登录博客园&#xff0c;但这只是第一步&#xff0c;一般登录后&#xff0c;还会有其它的操作&#xff0c;如发帖&#xff0c;评论等等&#xff0c;这时候如何保持会话呢&#xff1f;这里我以jenkins平台为例&#xff0c;给小伙伴们在沙场演练一…

10.Jenkins用tags的方式自动发布java应用

Jenkins用tags的方式自动发布java应用1.配置jenkins&#xff0c;告诉jenkins&#xff0c;jdk的安装目录&#xff0c;maven的安装目录2.构建一个maven项目指定构建参数&#xff0c;选择Git Paramete在源码管理中&#xff0c;填写我们git项目的地址&#xff0c;调用变量构建前执行…

秒杀项目之分布式锁运用

目录一、创建Redisson模块二、模拟高并发场景秒杀下单2.1 场景模拟2.2 案例演示三、JVM级锁与redis级分布式锁3.1 JVM级锁3.2 redis级分布式锁3.2.1 什么是setnx3.2.2 场景分析四、redisson分布式锁源码解读4.1 什么是Redisson4.2 Redisson工作原理4.3 入门案例五、秒杀项目整合…

StarRocks 企业行|走进 58 同城,探索极速统一 3.0 时代的企业实践

新的一年&#xff0c;新的征程。随着 StarRocks 项目的演进&#xff0c;StarRocks 也迈入了极速统一 3.0 的时代。极速 OLAP 极速数据湖分析将带给企业什么价值&#xff1f;StarRocks 的用户又是如何在企业内打造专属的大数据平台&#xff0c;让数据驱动业务增长和优化&#x…

尚医通(十三)后台医院管理模块

目录一、医院管理模块需求分析1、医院列表2、医院详情二、医院列表功能(接口)1、添加service分页接口与实现2、添加controller方法3、service_cmn模块提供接口3.1 添加service接口与实现3.2 添加controller4、封装Feign服务调用4.1 搭建service_client父模块4.2 在service_clie…

论文投稿指南——中文核心期刊推荐(园艺)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

Spring3之注解(Annotation)

简介 前面介绍的都是通过 xml 文件配置的方式&#xff0c;Spring 也可以通过注解的方式添加配置&#xff0c;在实际开发过程中&#xff0c;最佳实践是&#xff1a;属于 Spring 的系统配置配置在 xml 文件中&#xff0c;而像注入 bean 之类的配置则通过注解的方式&#xff1b;这…

IDEA根据wsdl生成java代码(Generate Java Code from WSDL)以及乱码问题的解决

目录 一、根据wsdl生成java代码 1、创建待存放java代码的目录&#xff0c;点击“帮助”>“查找操作”&#xff0c;打开查找窗口&#xff1b; 2、输入wsdl并查找&#xff0c;点击“从WSDL生成Java代码”&#xff0c;打开新的窗口&#xff1b; 3、选择wsdl文件&#xff0c…

LeetCode 2.两数相加

原题链接 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表…

工作记录------lombok中@Data包含哪些功能?

工作记录------lombok中Data包含哪些功能&#xff1f; 在实体类中加上Data后&#xff0c;实体类有哪些增强&#xff1f; Data public class BaseProcedure {TableId(value "id", type IdType.ASSIGN_UUID)private String id;private String procedureCode;写上Da…

字节青训营——秒杀系统设计学习笔记(二)

一、两次MD5加密设计 加密&#xff1a;出于安全考虑 第一次 &#xff08;在前端加密&#xff0c;客户端&#xff09;&#xff1a;密码加密是&#xff08;明文密码固定盐值&#xff09;生成md5用于传输&#xff0c;目的&#xff0c;由于http是明文传输&#xff0c;当输入密码若…

Linux进程线程管理

目录 存储管理 linux内存管理基本框架 系统空间管理和用户空间管理 进程与进程调度 进程四要素 用户堆栈的扩展 进程三部曲&#xff1a;创建&#xff0c;执行&#xff0c;消亡 系统调用exit(),wait() 内核中的互斥操作 存储管理 linux内存管理基本框架 系统空间管理…

sql手工注入练习拿flag

sql手工注入练习拿flag 记录一下自己重新开始学习web安全之路⑤。 1、找注入点 ①url ②搜索框 ③登录框 2、找交互点 用单引号判断是否存在交互点&#xff0c;发现回显不正常&#xff0c;说明url存在有交互点 3、判断类型&#xff08;char类型&#xff09; 利用and 11 和…

Linux Shell脚本讲解

目录 Shell脚本基础 Shell脚本组成 Shell脚本工作方式 编写简单的Shell脚本 Shell脚本参数 Shell脚本接收参数 Shell脚本判断用户参数 文件测试与逻辑测试语句 整数测试比较语句 字符串比较语句 Shell流程控制 if条件判断语句 单分支 双分支 多分支 for循环语句…

第五章.与学习相关技巧—参数更新的最优化方法(SGD,Momentum,AdaGrad,Adam)

第五章.与学习相关技巧 5.1 参数更新的最优化方法 神经网络学习的目的是找到使损失函数的值尽可能小的参数&#xff0c;这是寻找最优参数的问题&#xff0c;解决这个问题的过程称为最优化。很多深度学习框架都实现了各种最优化方法&#xff0c;比如Lasagne深度学习框架&#xf…

Vue中使用天地图

Vue项目引入天地图 在vue的静态资源目录下的index.html中引入天地图的底图&#xff0c;开发天地图源码路径&#xff1a;天地图API 方法一&#xff1a;加载天地图&#xff0c;引用&#xff1a;public/index.html <script type"text/javascript" src"http:/…

来来来,手摸手写一个hook

hello&#xff0c;这里是潇晨&#xff0c;今天就带着大家一起来手写一个迷你版的hooks&#xff0c;方便大家理解hook在源码中的运行机制&#xff0c;配有图解&#xff0c;保姆级的教程&#xff0c;只求同学一个小小的&#x1f44d;&#xff0c;&#x1f436;。 第一步&#xf…

【软件测试】团队测试技术体现,遇到不可复现bug处理方式......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 测试技术带来的是PP…

ThingsBoard-规则链-check alarm status

1、概述 从今天开始,专门讲解ThingsBoard的规则链,每一个节点都会详细讲解,并且配套案例,让大家都能理解,喜欢请点个关注。今天讲的是筛选器的第第一个节点【check alarm status】,意思是检测告警状态。 2、节点理解 2.1、概述 【check alarm status】节点如图所示:…

解立方根-蓝桥杯

题目 分析 主要是运用二分法使复杂度减低。 代码 #include<iostream> #include<iomanip> #include<cmath> using namespace std; #define double long double const double eps 1e-12; int main() {int T 1;cin >> T;while (T--){double n;cin &g…