GDI+ 绘制砂岩含量图版
图版是在工作中经常会使用到的工具,它能够大大提高我们日常工作效率。地质图版在地质工作中具有举足轻重的作用,不仅可以轻松判断岩性,也可以依据经验图版直接得到结果,十分方便。
本程序目的绘制出一个地质常用三角图版:砂岩含量图版。图版的每个角代表每种含量,目标图版如下:
程序源代码下载地址见文章末尾。
首先建立一个TrianglePlate类。确定TrianglePlate类的属性两个:三角形中心(Center)和三角形边长(SideLength)。
/// <summary> /// 三角形图版内心 /// </summary> public PointF Center { set; get; } /// <summary> /// 三角形图版边长 /// </summary> public float SideLength { get; set; }
定义一个PointF数组代表三角形图版三个点的具体坐标,采用PointF类型的原因是PointF比Point更加精确,能够保证图像的精度。为了对类进行封装,坐标数组采用private类型。
private PointF[] _points;
TrianglePlate类的构造函数如下:
public TrianglePlate(PointF center, float sidelength) { this.Center = center; this.SideLength = sidelength; this.Scale = sacle; //计算三个点 _points = new PointF[3]; _points[0] = new PointF(this.Center.X - this.SideLength/2, (float) (this.Center.Y - this.SideLength/(2*Math.Sqrt(3))) ); _points[1] = new PointF(this.Center.X + this.SideLength/2, (float) (this.Center.Y - this.SideLength/(2*Math.Sqrt(3))) ); _points[2] = new PointF(this.Center.X, (float) (this.Center.Y - 2*this.SideLength/(Math.Sqrt(3))) ); }
为了更好绘制图形,向TrianglePlate类加入DrawTrianglePlate方法,方便图形的绘制。
/// <summary> /// 绘制三角形图版 /// </summary> public void DrawTrianglePlate (Graphics g) { DrawSildes (g); } /// <summary> /// 绘制三角形图版三边 /// </summary> private void DrawSildes (Graphics g) { g.DrawLine (this.SidePen, _points[0], _points[1]); g.DrawLine (this.SidePen, _points[1], _points[2]); g.DrawLine (this.SidePen, _points[2], _points[0]); }
在From进行TrianglePlate初始化后,就可以调用Form的Paint函数进行绘制了。
public FrmMain() { InitializeComponent(); tp.SidePen = new Pen (Color.Black, 3); tp.SidePen.StartCap = LineCap.Round; tp.SaclePen = new Pen (Color.Black, 3); } TrianglePlate tp = new TrianglePlate (new PointF (200, 500), 350); private void tabPage1_Paint (object sender, PaintEventArgs e) { Graphics g = e.Graphics; tp.DrawTrianglePlate (g, pcoursor); myBuffer.Render (e.Graphics); g.Dispose(); myBuffer.Dispose(); //释放资源 }
一个简单的三角形就绘制完成了,如下:
接下来进行绘制三角形图版的倾斜刻度线,定义一个float数组Scale用来存储刻度值,方便使用。定义一个PointF数组_scalepoints用来存储刻度线与三角形边框相交的点。
public float[] Scale { get; set; } private PointF[] _scalepoints;
对Scale和_scalepoints的初始化操作放在构造函数DrawTrianglePlate中。修改后的构造函数如下:
public TrianglePlate (PointF center, float sidelength, float[] sacle) { this.Center = center; this.SideLength = sidelength; this.Scale = sacle; //计算三个点 _points = new PointF[3]; _points[0] = new PointF (this.Center.X - this.SideLength / 2, (float) (this.Center.Y - this.SideLength / (2 * Math.Sqrt (3) ) ) ); _points[1] = new PointF (this.Center.X + this.SideLength / 2, (float) (this.Center.Y - this.SideLength / (2 * Math.Sqrt (3) ) ) ); _points[2] = new PointF (this.Center.X, (float) (this.Center.Y - 2 * this.SideLength / (Math.Sqrt (3) ) ) ); //计算刻度点 _scalepoints = new PointF[12]; _scalepoints[0] = new PointF (_points[0].X + sidelength * this.Scale[0], _points[0].Y); _scalepoints[1] = new PointF (_points[0].X + sidelength * this.Scale[1], _points[0].Y); _scalepoints[2] = new PointF (_points[0].X + sidelength * (1 - this.Scale[1]), _points[0].Y); _scalepoints[3] = new PointF (_points[0].X + sidelength * (1 - this.Scale[0]), _points[0].Y); _scalepoints[4] = new PointF (_points[2].X - (float) (sidelength / 2) * this.Scale[0], _points[2].Y + (float) (sidelength * Math.Sqrt (3) / 2) * this.Scale[0]); _scalepoints[5] = new PointF (_points[2].X - (float) (sidelength / 2) * this.Scale[1], _points[2].Y + (float) (sidelength * Math.Sqrt (3) / 2) * this.Scale[1]); _scalepoints[6] = new PointF (_points[2].X + (float) (sidelength / 2) * this.Scale[0], _points[2].Y + (float) (sidelength * Math.Sqrt (3) / 2) * this.Scale[0]); _scalepoints[7] = new PointF (_points[2].X + (float) (sidelength / 2) * this.Scale[1], _points[2].Y + (float) (sidelength * Math.Sqrt (3) / 2) * this.Scale[1]); _scalepoints[8] = new PointF (_points[2].X, _points[1].Y - (0.5f - this.Scale[0]) * this.SideLength * (float) Math.Sqrt (3) ); _scalepoints[11] = new PointF (_points[2].X, _points[1].Y - (0.5f - this.Scale[1]) * this.SideLength * (float) Math.Sqrt (3) ); _scalepoints[9] = new PointF (_scalepoints[8].X - (_scalepoints[11].Y - _scalepoints[8].Y) / 2 / (float) Math.Sqrt (3), (_scalepoints[8].Y + _scalepoints[11].Y) / 2); _scalepoints[10] = new PointF (_scalepoints[8].X + (_scalepoints[11].Y - _scalepoints[8].Y) / 2 / (float) Math.Sqrt (3), (_scalepoints[8].Y + _scalepoints[11].Y) / 2); }
加入DrawScale函数绘制刻度线,并在DrawTrianglePlate绘制函数进行调用。
private void DrawScale (Graphics g) { g.DrawLine (this.SaclePen, _scalepoints[0], _scalepoints[6]); g.DrawLine (this.SaclePen, _scalepoints[1], _scalepoints[7]); g.DrawLine (this.SaclePen, _scalepoints[2], _scalepoints[5]); g.DrawLine (this.SaclePen, _scalepoints[3], _scalepoints[4]); }
接下来对图版的刻度和文字进行绘制。
private void DrawTag (Graphics g) { g.DrawString ("长石", new Font ("宋体", 11), Brushes.Black, new PointF (_points[0].X - 25f, _points[0].Y - 25f) ); g.DrawString ("岩屑", new Font ("宋体", 11), Brushes.Black, new PointF (_points[1].X + 5f, _points[1].Y - 25f) ); g.DrawString ("石英", new Font ("宋体", 11), Brushes.Black, new PointF (_points[2].X - 25f, _points[2].Y - 15f) ); g.DrawString ( (100 - this.Scale[0] * 100).ToString(), new Font ("宋体", 11), Brushes.Black, new PointF (_scalepoints[0].X, _scalepoints[0].Y + 3) ); g.DrawString ( (100 - this.Scale[1] * 100).ToString(), new Font ("宋体", 11), Brushes.Black, new PointF (_scalepoints[1].X, _scalepoints[1].Y + 3) ); g.DrawString ( (this.Scale[1] * 100).ToString(), new Font ("宋体", 11), Brushes.Black, new PointF (_scalepoints[2].X, _scalepoints[2].Y + 3) ); g.DrawString ( (this.Scale[0] * 100).ToString(), new Font ("宋体", 11), Brushes.Black, new PointF (_scalepoints[3].X, _scalepoints[3].Y + 3) ); g.DrawString ( (this.Scale[0] * 100).ToString(), new Font ("宋体", 11), Brushes.Black, new PointF (_scalepoints[4].X - 25f, _scalepoints[4].Y - 5) ); g.DrawString ( (this.Scale[1] * 100).ToString(), new Font ("宋体", 11), Brushes.Black, new PointF (_scalepoints[5].X - 25f, _scalepoints[5].Y - 5) ); g.DrawString ( (this.Scale[0] * 100).ToString(), new Font ("宋体", 11), Brushes.Black, new PointF (_scalepoints[6].X + 5f, _scalepoints[6].Y - 5) ); g.DrawString ( (this.Scale[1] * 100).ToString(), new Font ("宋体", 11), Brushes.Black, new PointF (_scalepoints[7].X + 5f, _scalepoints[7].Y - 5) ); }
DrawTag函数在DrawTrianglePlate绘制函数中进行调用,此时形成图形如下:
接下来是最后一步,绘制随鼠标移动的动态刻度线。目的实现结果如下:
为此,首先需要判断鼠标是否点在图版内,并获得鼠标的位置。向DrawTriangle类添加成员函数MouseIsIn。其中tpgp是一个GraphicsPath类型的变量,利用GraphicsPath提供的IsVisible函数判断鼠标是否在Path内。
public bool MouseIsIn (Graphics g, PointF pt) { if (tpgp.IsVisible (pt) ) { DrawDotDial (g, pt); return true; } else return false; }
然后根据鼠标点与已知的刻度点计算得出动态刻度点坐标。
private void DrawDotDial (Graphics g, PointF pt) { _dials = new PointF[6]; float py = _points[0].Y - pt.Y; float px = pt.X - _points[0].X; _dials[0] = new PointF (pt.X - py / (float) Math.Sqrt (3), _points[0].Y); _dials[1] = new PointF (pt.X + py / (float) Math.Sqrt (3), _points[0].Y); _dials[2] = new PointF ( (_dials[1].X + _points[0].X) / 2, _dials[0].Y - (_dials[1].X - _points[0].X) * (float) Math.Sqrt (3) / 2); _dials[4] = new PointF ( (_points[1].X + _dials[0].X) / 2, _points[1].Y - (_points[1].X - _dials[0].X) * (float) Math.Sqrt (3) / 2); _dials[3] = new PointF (_points[0].X + py / (float) Math.Sqrt (3), pt.Y); _dials[5] = new PointF (_points[1].X - (_dials[3].X - _points[0].X), pt.Y); Pen tmpen = new Pen (Color.Black, 1); tmpen.DashStyle = DashStyle.Dash; g.DrawLine (tmpen, _dials[0], _dials[4]); g.DrawLine (tmpen, _dials[1], _dials[2]); g.DrawLine (tmpen, _dials[3], _dials[5]); }
然后在DrawTrianglePlate函数内对DrawDotDial进行调用,就完成了。
完整代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using System.Drawing.Drawing2D; namespace Ch_地质图版 { class TrianglePlate { public TrianglePlate(PointF center, float sidelength, float[] sacle) { this.Center = center; this.SideLength = sidelength; this.Scale = sacle; //计算三个点 _points = new PointF[3]; _points[0] = new PointF(this.Center.X - this.SideLength/2, (float) (this.Center.Y - this.SideLength/(2*Math.Sqrt(3))) ); _points[1] = new PointF(this.Center.X + this.SideLength/2, (float) (this.Center.Y - this.SideLength/(2*Math.Sqrt(3))) ); _points[2] = new PointF(this.Center.X, (float) (this.Center.Y - 2*this.SideLength/(Math.Sqrt(3))) ); //计算刻度点 _scalepoints = new PointF[12]; _scalepoints[0] = new PointF(_points[0].X + sidelength * this.Scale[0], _points[0].Y); _scalepoints[1] = new PointF(_points[0].X + sidelength * this.Scale[1], _points[0].Y); _scalepoints[2] = new PointF(_points[0].X + sidelength * (1 - this.Scale[1]), _points[0].Y); _scalepoints[3] = new PointF(_points[0].X + sidelength * (1 - this.Scale[0]), _points[0].Y); _scalepoints[4] = new PointF(_points[2].X - (float)(sidelength / 2) * this.Scale[0], _points[2].Y + (float)(sidelength * Math.Sqrt(3) / 2) * this.Scale[0]); _scalepoints[5] = new PointF(_points[2].X - (float)(sidelength / 2) * this.Scale[1], _points[2].Y + (float)(sidelength * Math.Sqrt(3) / 2) * this.Scale[1]); _scalepoints[6] = new PointF(_points[2].X + (float)(sidelength / 2) * this.Scale[0], _points[2].Y + (float)(sidelength * Math.Sqrt(3) / 2) * this.Scale[0]); _scalepoints[7] = new PointF(_points[2].X + (float)(sidelength / 2) * this.Scale[1], _points[2].Y + (float)(sidelength * Math.Sqrt(3) / 2) * this.Scale[1]); _scalepoints[8] = new PointF(_points[2].X, _points[1].Y - (0.5f - this.Scale[0]) * this.SideLength * (float)Math.Sqrt(3)); _scalepoints[11] = new PointF(_points[2].X, _points[1].Y - (0.5f - this.Scale[1]) * this.SideLength * (float)Math.Sqrt(3)); _scalepoints[9] = new PointF(_scalepoints[8].X - (_scalepoints[11].Y - _scalepoints[8].Y) / 2 / (float)Math.Sqrt(3), (_scalepoints[8].Y + _scalepoints[11].Y) / 2); _scalepoints[10] = new PointF(_scalepoints[8].X + (_scalepoints[11].Y - _scalepoints[8].Y) / 2 / (float)Math.Sqrt(3), (_scalepoints[8].Y + _scalepoints[11].Y) / 2); tpgp = new GraphicsPath(); tpgp.AddLines(_points); //DrawDotDial() } /// <summary> /// 三角形图版内心 /// </summary> public PointF Center { set; get; } /// <summary> /// 三角形图版边长 /// </summary> public float SideLength { get; set; } /// <summary> /// 三角形图版画边笔 /// </summary> public Pen SidePen { get; set; } public Pen SaclePen { get; set; } public float[] Scale { get; set; } private PointF[] _points; private PointF[] _scalepoints; private GraphicsPath tpgp; private PointF[] _dials; public void DrawTrianglePlate(Graphics g,PointF tms) { DrawSildes(g); DrawScale(g); DrawTag(g); if(!tms.IsEmpty) DrawDotDial(g, tms); } private void DrawSildes(Graphics g) { g.DrawLine(this.SidePen, _points[0], _points[1]); g.DrawLine(this.SidePen, _points[1], _points[2]); g.DrawLine(this.SidePen, _points[2], _points[0]); } private void DrawScale(Graphics g) { g.DrawLine(this.SaclePen, _scalepoints[0], _scalepoints[6]); g.DrawLine(this.SaclePen, _scalepoints[1], _scalepoints[7]); g.DrawLine(this.SaclePen, _scalepoints[2], _scalepoints[5]); g.DrawLine(this.SaclePen, _scalepoints[3], _scalepoints[4]); } private void DrawTag(Graphics g) { //g.DrawString("①", new Font("楷体", 12), Brushes.Purple, _scalepoints[4].X, _scalepoints[4].Y); //g.DrawString("②", new Font("楷体", 12), Brushes.Purple, _scalepoints[5].X, _scalepoints[5].Y); //g.DrawString("③", new Font("楷体", 12), Brushes.Purple, _scalepoints[6].X, _scalepoints[6].Y); //g.DrawLine(Pens.Black, _scalepoints[8], _scalepoints[11]); //g.DrawLine(Pens.Black, _scalepoints[9], _scalepoints[10]); //GraphicsPath gp = new GraphicsPath(); //gp.AddLines(new PointF[] {_scalepoints[4], _scalepoints[8], _scalepoints[6], _points[2]}); //g.FillPath(Brushes.Red, gp); //gp = new GraphicsPath(); //gp.AddLines(new PointF[] { _scalepoints[4], _scalepoints[5], _scalepoints[9], _scalepoints[8] }); //g.FillPath(Brushes.Purple, gp); //gp = new GraphicsPath(); //gp.AddLines(new PointF[] { _scalepoints[6], _scalepoints[8], _scalepoints[10], _scalepoints[7] }); //g.FillPath(Brushes.Pink, gp); //gp = new GraphicsPath(); //gp.AddLines(new PointF[] { _scalepoints[8], _scalepoints[9], _scalepoints[11], _scalepoints[10] }); //g.FillPath(Brushes.RoyalBlue, gp); //gp = new GraphicsPath(); //gp.AddLines(new PointF[] { _scalepoints[5], _scalepoints[9], _scalepoints[0], _points[0] }); //g.FillPath(Brushes.SkyBlue, gp); //gp = new GraphicsPath(); //gp.AddLines(new PointF[] { _scalepoints[9], _scalepoints[11], _scalepoints[1], _scalepoints[0] }); //g.FillPath(Brushes.Green, gp); //gp = new GraphicsPath(); //gp.AddLines(new PointF[] { _scalepoints[11], _scalepoints[10], _scalepoints[3], _scalepoints[2] }); //g.FillPath(Brushes.Yellow, gp); //gp = new GraphicsPath(); //gp.AddLines(new PointF[] { _scalepoints[3], _scalepoints[10], _scalepoints[7], _points[1] }); //g.FillPath(Brushes.Tomato, gp); g.DrawString("长石", new Font("宋体", 11), Brushes.Black, new PointF(_points[0].X - 25f, _points[0].Y - 25f)); g.DrawString("岩屑", new Font("宋体", 11), Brushes.Black, new PointF(_points[1].X + 5f, _points[1].Y - 25f)); g.DrawString("石英", new Font("宋体", 11), Brushes.Black, new PointF(_points[2].X - 25f, _points[2].Y - 15f)); g.DrawString((100 - this.Scale[0]*100).ToString(), new Font("宋体", 11), Brushes.Black, new PointF(_scalepoints[0].X, _scalepoints[0].Y+3)); g.DrawString((100 - this.Scale[1] * 100).ToString(), new Font("宋体", 11), Brushes.Black, new PointF(_scalepoints[1].X, _scalepoints[1].Y+3)); g.DrawString((this.Scale[1] * 100).ToString(), new Font("宋体", 11), Brushes.Black, new PointF(_scalepoints[2].X, _scalepoints[2].Y+3)); g.DrawString((this.Scale[0] * 100).ToString(), new Font("宋体", 11), Brushes.Black, new PointF(_scalepoints[3].X, _scalepoints[3].Y+3)); g.DrawString((this.Scale[0] * 100).ToString(), new Font("宋体", 11), Brushes.Black, new PointF(_scalepoints[4].X-25f, _scalepoints[4].Y-5)); g.DrawString((this.Scale[1] * 100).ToString(), new Font("宋体", 11), Brushes.Black, new PointF(_scalepoints[5].X - 25f, _scalepoints[5].Y - 5)); g.DrawString((this.Scale[0] * 100).ToString(), new Font("宋体", 11), Brushes.Black, new PointF(_scalepoints[6].X+5f, _scalepoints[6].Y - 5)); g.DrawString((this.Scale[1] * 100).ToString(), new Font("宋体", 11), Brushes.Black, new PointF(_scalepoints[7].X + 5f, _scalepoints[7].Y - 5)); } public bool MouseIsIn(Graphics g, PointF pt) { if (tpgp.IsVisible(pt)) { DrawDotDial(g, pt); return true; } else return false; } private void DrawDotDial(Graphics g,PointF pt) { _dials = new PointF[6]; float py = _points[0].Y - pt.Y; float px = pt.X - _points[0].X; _dials[0] = new PointF(pt.X - py/(float) Math.Sqrt(3), _points[0].Y); _dials[1] = new PointF(pt.X + py/(float) Math.Sqrt(3), _points[0].Y); _dials[2] = new PointF((_dials[1].X + _points[0].X)/2, _dials[0].Y - (_dials[1].X - _points[0].X)*(float) Math.Sqrt(3)/2); _dials[4] = new PointF((_points[1].X + _dials[0].X)/2, _points[1].Y - (_points[1].X - _dials[0].X) * (float)Math.Sqrt(3) / 2); _dials[3] = new PointF(_points[0].X + py / (float)Math.Sqrt(3), pt.Y); _dials[5] = new PointF(_points[1].X-(_dials[3].X-_points[0].X), pt.Y); Pen tmpen = new Pen(Color.Black, 1); tmpen.DashStyle = DashStyle.Dash; g.DrawLine(tmpen, _dials[0], _dials[4]); g.DrawLine(tmpen, _dials[1], _dials[2]); g.DrawLine(tmpen, _dials[3], _dials[5]); //GetContentScale(pt); //g.DrawLine(tmpen,pt, new Point(0, 0)); } private float[] GetContentScale(PointF pt) { float[] content = new float[3]; content[0] = (_dials[1].X - _points[0].X)/this.SideLength; content[1] = (_points[1].X - _dials[0].X) / this.SideLength; //content[1] = content[1]/this.SideLength/this.SideLength; content[2] = Math.Abs(_points[2].X - _dials[0].X) * Math.Abs(_points[2].X - _dials[0].X) + Math.Abs(_points[2].Y - _dials[0].Y) * Math.Abs(_points[2].Y - _dials[0].Y); content[2] = content[2] / this.SideLength / this.SideLength; //content[1] = Math.Sqrt((double)content[1]); //Math.Sqrt((double)(Math.Abs(_points[2].X - _dials[3].X)*Math.Abs(_points[2].X - _dials[3].X)) + // (double)(Math.Abs(_points[2].Y - _dials[3].Y)*Math.Abs(_points[2].Y - _dials[3].Y) // ))/this.SideLength; return content; } public string GetScaleDes(PointF pt) { GraphicsPath gptmp = new GraphicsPath(); gptmp.AddLines(new PointF[] {_points[2], _scalepoints[4], _scalepoints[8], _scalepoints[6]}); if (gptmp.IsVisible(pt)) return "石英砂岩"; gptmp.AddLines(new PointF[] { _scalepoints[4], _scalepoints[5], _scalepoints[9], _scalepoints[8] }); if (gptmp.IsVisible(pt)) return "长石石英砂岩"; gptmp.AddLines(new PointF[] { _scalepoints[8], _scalepoints[9], _scalepoints[11], _scalepoints[10] }); if (gptmp.IsVisible(pt)) return "长石岩屑质石英砂岩"; gptmp.AddLines(new PointF[] { _scalepoints[6], _scalepoints[7], _scalepoints[9], _scalepoints[10] }); if (gptmp.IsVisible(pt)) return "岩屑石英砂岩"; gptmp.AddLines(new PointF[] { _scalepoints[5], _scalepoints[9], _scalepoints[0], _points[0] }); if (gptmp.IsVisible(pt)) return "长石砂岩"; gptmp.AddLines(new PointF[] { _scalepoints[9], _scalepoints[0], _scalepoints[1], _scalepoints[11] }); if (gptmp.IsVisible(pt)) return "碎屑质长石砂岩"; gptmp.AddLines(new PointF[] { _scalepoints[1], _scalepoints[2], _scalepoints[11] }); if (gptmp.IsVisible(pt)) return "混合砂岩"; gptmp.AddLines(new PointF[] { _scalepoints[10], _scalepoints[11], _scalepoints[2], _scalepoints[3] }); if (gptmp.IsVisible(pt)) return "长石质岩屑砂岩"; gptmp.AddLines(new PointF[] { _scalepoints[7], _scalepoints[11], _scalepoints[3], _points[1] }); if (gptmp.IsVisible(pt)) return "岩屑砂岩"; return ""; } } }
程序源代码地址:http://pan.baidu.com/s/1osgAU