C#窗体绘制等值线

绘制等值线的基本步骤基本上都是差不多的(当然每个人会有些差别因个人而异)(PS:下面的是矩阵点的计算不是离散点的计算)

数据处理->获取网格数据->通过三角剖分法计算等值线(当然还有其他的各种剖分方式)->绘制等值线(在绘制之前肯定会记录等值线的一些数据方便之后进行追踪)->填充等值线

关于等值线的一些基础概念和三角剖分的基本思想可以参考这篇->
[http://www.cnblogs.com/think8848/archive/2011/04/29/2032751.html](http://www.cnblogs.com/think8848/archive/2011/04/29/2032751.html)

我就简单的表示一下三角剖分法:
- 图片链接和图片上传这里写图片描述

- 这个可以很清楚的看出来,当有三条等值线(5,15,25)要绘制的时候,这个最小三角形就肯定能画出这样子的2条,当我们把整个区域划分为很多的三角形的时候,然后每个三角形都这样子判断过去循环所有的等值线,能绘制的就绘制不能绘制的就跳过,当绘制完成的时候就能等到等值线了,当划分的等值线越多精度越高,网格点越多精度越高。

    /// <summary>
    /// 数据结构
    /// </summary>

    public class DataPoint
    {

    /// <summary>
    /// 坐标X
    /// </summary>
    public float X;
    /// <summary>
    /// 坐标Y
    /// </summary>
    public float Y;
    /// <summary>
    /// 温度T
    /// </summary>
    public float T;
    /// <summary>
    /// 矩阵坐标i
    /// </summary>

    public float PosI;
    /// <summary>
    /// 矩阵坐标j
    /// </summary>
    public float PosJ;
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="x">坐标X</param>
    /// <param name="y">坐标Y</param>
    /// <param name="t">温度T</param>

    public DataPoint(float x, float y, float t)
    {
        X = x;
        Y = y;
        T = t;
    }

    /// <summary>
    /// 默认构造函数
    /// </summary>
    public DataPoint()
    {

    }
    /// <summary>
    /// 判断2个对象的XY是否相等
    /// </summary>
    /// <param name="point">点坐标</param>
    /// <returns></returns>
    public bool EqualXY(Point point)
    {
        return (this.X == point.X && this.Y == point.Y) ? true : false;
    }
    /// <summary>
    /// 判断2个对象的共有值是否都相等
    /// </summary>
    /// <param name="left">左边对象</param>
    /// <param name="right">右边对象</param>
    /// <returns>是否相等</returns>
    public static bool operator ==(DataPoint left, DataPoint right)
    {
        if (left.X == right.X && left.Y == right.Y && left.T == right.T)
            return true;
        else
            return false;
    }
    /// <summary>
    /// 判断2个对象的共有值是否不等
    /// </summary>
    /// <param name="left"></param>
    /// <param name="right"></param>
    /// <returns></returns>
    public static bool operator !=(DataPoint left, DataPoint right)
    {
        if (left.X != right.X || left.Y != right.Y || left.T != right.T)
            return true;
        else
            return false;
    }
    /// <summary>
    /// 哈希值
    /// </summary>
    /// <returns></returns>
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
    /// <summary>
    /// 是否指向同一个对象地址
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    }

-这是我的代码中作为传入数据点的类的结构,X,Y作为在外面赋值好的原始坐标,后面要转换到PosI和PosJ中 当然如果已经计算好的可以直接赋值PosI和PosJ(后面简称I和J),I和J的含义是指这个数据点是矩阵点的第几行和第几列的点。

/// <summary>
        /// 等值线分析及绘图
        /// </summary>
        /// <param name="size"></param>
        private void CurLines(Size size)
        {
            DataPoint maxPoint = new DataPoint();  ///三角网格中最大温度值的点
            DataPoint midPoint = new DataPoint();  ///三角网格中中间温度值的点
            DataPoint minPoint = new DataPoint();  ///三角网格中最低温度值的点
            int pos1, pos2, pos3;
            float temp = 0;

            for (int j = 0; j < Data.GetLength(1) - 1; j++)
            {
                for (int i = 0; i < Data.GetLength(0) - 1; i++)
                {
                    //网格四个角坐标,变量值
                    CurPoints[0] = Data[i, j];
                    CurPoints[1] = Data[i + 1, j];
                    CurPoints[2] = Data[i + 1, j + 1];
                    CurPoints[3] = Data[i, j + 1];

                    CurPoints[4] = new DataPoint();
                    CurPoints[4].T = 0.25F * (CurPoints[0].T + CurPoints[1].T + CurPoints[2].T + CurPoints[3].T);
                    CurPoints[4].PosI = 0.5F * (CurPoints[0].PosI + CurPoints[1].PosI);
                    CurPoints[4].PosJ = 0.5F * (CurPoints[1].PosJ + CurPoints[2].PosJ);
                    pos3 = 4;

                    //巡检单元格范围内所有的点信息
                    for (pos1 = 0; pos1 < 4; pos1++)
                    {
                        pos2 = pos1 + 1;
                        if (pos1 == 3) pos2 = 0;
                        PointF p1, p2;  ///绘制用的点

                        OrderDataPoint(CurPoints[pos1], CurPoints[pos2], CurPoints[pos3], ref maxPoint, ref midPoint, ref minPoint);
                        //巡检点范围内所有的点信息
                        for (int lines = 0; lines < ContourValues.Length; lines++)
                        {
                            temp = ContourValues[lines];

                            if (minPoint.T < temp && temp < maxPoint.T)
                            {
                                p1 = interp(minPoint, maxPoint, temp, this.Size);
                                if (temp > midPoint.T)
                                {

                                    p2 = interp(midPoint, maxPoint, temp, this.Size);
                                }
                                else
                                {
                                    p2 = interp(minPoint, midPoint, temp, this.Size);
                                }
                                Drawings.Add(new DrawingPoints(p1, p2, GetTempColor(temp), temp));
                            }
                        }
                    }
                }
            }
        }

这段代码就是利用上面图上的三角剖分法去计算等值线的函数,最重要的在计算的时候一定要吧相邻的点保存下来Drawings.Add(new DrawingPoints(p1, p2, GetTempColor(temp), temp));。。。。这里就是吧等值线最小单位线段保存下来,数据有线段的2个端点,该线段的等值线的值,以及绘制的颜色。下面就是这个保存线段的类的结构

/// <summary>
    /// 画等值线时候的点的集合
    /// </summary>
    public class DrawingPoints
    {
        /// <summary>
        /// 起始点
        /// </summary>
        public PointF StartPoint;
        /// <summary>
        /// 结束点
        /// </summary>
        public PointF EndPoint;
        /// <summary>
        /// 等值线的值
        /// </summary>
        public float Value;
        /// <summary>
        /// 2个点之间的连线的颜色
        /// </summary>
        public Color LineColor;
        /// <summary>
        /// 计算的时候使用表示是否被使用过
        /// </summary>
        public bool IsUser;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="p1">起始点</param>
        /// <param name="p2">结束点</param>
        public DrawingPoints(PointF p1, PointF p2, Color color, float Value = 10, bool IsUser = false)
        {
            StartPoint = p1;
            EndPoint = p2;
            LineColor = color;
            this.Value = Value;
            this.IsUser = IsUser;
        }
        /// <summary>
        /// 设置是否使用过
        /// </summary>
        /// <param name="isUser"></param>
        public void SetUser(bool isUser)
        {
            this.IsUser = isUser;
        }
    }

由于上面我们保存下来所有等值线的最小单位:线段以及线段对应的等值线的值。这样下面就可以开始检索。根据等值线的值把所有的和该等值线的值相同的线段找出来,由于线段都是2个端点的所有我们只要根据2根线段首位相同就可以吧线段的点组合起来这样就能变成一条完整的等值线。
这里写图片描述

-根据上图可以吧同样值的等值线线段 连接起来就是等值线。

/// <summary>
        /// 检索
        /// </summary>
        /// <param name="listPoints"></param>
        /// <param name="firstPoint"></param>
        /// <param name="temp"></param>
        /// <param name="isUpSideNotSide">当不是边界的时候也需要倒置一次进行寻找</param>
        protected bool FindNode(List<PointF> listPoints, ref PointF firstPoint, float temp, bool isUpSideNotSide = false)
        {
            for (int i = 0; i < Drawings.Count; i++)
            {
                DrawingPoints point = Drawings[i];
                if (point.Value == temp && !point.IsUser)
                {
                    if (firstPoint.X == -1)
                    {
                        listPoints.Add(point.StartPoint);
                        listPoints.Add(point.EndPoint);
                        firstPoint = point.EndPoint;
                        point.IsUser = true;
                        return true;
                    }
                    else if (GetEqualPointF(firstPoint, point.StartPoint))
                    {
                        listPoints.Add(point.EndPoint);
                        firstPoint = point.EndPoint;
                        point.IsUser = true;
                        ///检测边界
                        return IsUpSide(ref firstPoint, ref listPoints);
                    }
                    else if (GetEqualPointF(firstPoint, point.EndPoint))
                    {
                        listPoints.Add(point.StartPoint);
                        firstPoint = point.StartPoint;
                        point.IsUser = true;
                        ///检测边界                   
                        return IsUpSide(ref firstPoint, ref listPoints);
                    }
                }
            }
            /*  加快运行速度
            ///当不是边界的时候也需要倒置一次进行寻找
            if (!isUpSideNotSide && listPoints.Count > 1)
            {
                firstPoint = listPoints[0];
                Tools.UpsideArray(ref listPoints);
                FindNode(listPoints, ref firstPoint, temp, true);
            }
            */
            return false;
        }

这个是检索函数,这个只是部分 (原先写的递归的方式检索的感觉不是很理想后来改成循环检索)检索完后 吧相对应的点都组合成了对应的等值线,当然同一个值可能有多条等值线,所以检索的时候当我们随着一个线段的一端检索成一个环的时候就可以退出 或者到达边界,到达边界的情况必须是2端都是边界,所以当一端到达边界的时候需要倒置然后再次检索。
这样一个值的等值线最后出来的结构是PointF [] [] pointfs 这样的二维数组。
这里写图片描述

最后得出的等值线的情况就是上图所示的情况,不管是那种情况我们都可以让它们和自己或者和边界形成一个多边形,不管是凸多边形还是凹多边形。这时候我们可以计算多变的面积
这里写图片描述
这个就是任意多边形的面积公式,推导过程我就不再说明了网上自行查阅,我也是看来的。
最后吧我们得到的图形面积计算,然后排序将面积从大到小排列,最后依次填充就完成所有工作了。

如果有问题,欢迎指出我会及时修改。谢谢!

PS:转载请注明出处。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值