• 几何算法:点集合构造简单多边形


    问题:给定平面中n个点所组成的集合,将它们连接起来形成一条简单的封闭路径。所谓简单路径,是指边与边无交叉。

    如下图所示10个点组成的简单轮廓:

    思路:取x坐标最大的点A(如果最大x坐标的点不止一个,则取Y坐标最小的点),依次计算A点与其余各点的连线与水平线之间夹角的正切值,然后按照正切值排序,依次连接排序后的各点即组成一个简单图形。

    原理:其它所有点都在A点的左侧,所有夹角的范围为-Pi/2~Pi/2,单调递增函数。

    举一个例子如下:

     

    各点坐标与A点的角度斜率如下(已经排序好):

    x:426.192518536091,y:30.5668629242884,slope:-2.21036105157629
    x:132.904271903869,y:111.805767306036,slope:0.0233827696146631
    x:209.153583263584,y:158.396180071121,slope:0.216615047225945
    x:51.2625493860163,y:271.425922467106,slope:0.409713066051227
    x:172.80558813494,y:320.363658168522,slope:0.754116336162768
    x:174.841647802313,y:361.474091434606,slope:0.903935084923323
    x:262.993097888768,y:306.679940091763,slope:1.03059799172764
    x:405.520514378101,y:212.478244240618,slope:2.00680658499766
    x:410.405247491042,y:324.597360433357,slope:4.49064367657446
    x:459.491329337233,y:104.169257382941,slope:1.79769313486232E+308

    其中A点为:x:459.491329337233,y:104.169257382941,slope:1.79769313486232E+308

    下面给出具体算法(C#实现):

    几何点定义,实现IComparable<T>接口,按照正切值排序要用到:

    复制代码

    public struct GeometryPoint : IComparable<GeometryPoint>
        {        
            public GeometryPoint(double x, double y, double slope = double.NaN)
            {
                this.x = x;
                this.y = y;
                this.slope = slope;
            }
            private double x;
            public double X
            {
                get { return x; }
                set { x = value; }
            }
            private double y;
            public double Y
            {
                get { return y; }
                set { y = value; }
            }
            private double slope;
            public double SLOPE
            {
                get { return slope; }
                set { slope = value; }
            }
          
            public int CompareTo(GeometryPoint p)
            {
                if (this.slope < p.slope)
                {
                    return -1;
                }
                else if (this.slope > p.slope)
                {
                    return 1;
                }
                else
                {
                    if (this.x == p.x && this.SLOPE == p.SLOPE && this.SLOPE == double.MaxValue)
                    {
                        if (this.y == p.y)
                        {
                            return 0;
                        }
                        else if (this.y < p.y)
                        {
                            return 1;
                        }
                        else//(this.y > p.y)
                        {
                            return -1;
                        }
                    }
                    return 0;
                }
            }
            public override string ToString()
            {
                return string.Format("x:{0},y:{1},slope:{2}", x, y, slope);
            }
        }
    

    复制代码

    简单封闭图形定义,并定义初始化简单封闭图形的方法,该方法随机产生多边形的顶点:

    复制代码

    public class SimplePolygon
        {
            private GeometryPoint[] geometrypoints;
    
            public GeometryPoint[] GeometryPoints
            {
                get { return geometrypoints; }
                set { geometrypoints = value; }
            }
    
    
            public SimplePolygon()
            {
            }
            public void Initialize(int size, double minX, double maxX, double minY, double maxY)
            {
                if (size <= 0) throw new ArgumentOutOfRangeException();
                geometrypoints = new GeometryPoint[size];
                Random rnd = new Random(DateTime.Now.Millisecond);
                double xRange = maxX - minX;
                double yRange = maxY - minY;
                int MaxXPointIndex = 0;//选取x坐标最大的点
                for (int i = 0; i < size; i++)
                {
                    GeometryPoint gp = new GeometryPoint(minX + xRange * rnd.NextDouble(), minY + yRange * rnd.NextDouble());
                    geometrypoints[i] = gp;
                    if (geometrypoints[MaxXPointIndex].X < gp.X)////选取x坐标最大的点
                    {
                        MaxXPointIndex = i;
                    }
                    else if (geometrypoints[MaxXPointIndex].X < gp.X && geometrypoints[MaxXPointIndex].Y > gp.Y)//选取x坐标最大的点,如果最大x坐标点有多个,去y最小者
                    {
                        MaxXPointIndex = i;
                    }
                }
                //计算斜率
                for (int i = 0; i < size; i++)
                {
                    if (i == MaxXPointIndex)
                    {
                        geometrypoints[MaxXPointIndex].SLOPE = double.MaxValue;
                    }
                    else
                    {
                        if (geometrypoints[i].X == geometrypoints[MaxXPointIndex].X)//与最大x坐标的x相同的点,因为x坐标之差为零,所以取SLOPE最大值
                        {
                            geometrypoints[i].SLOPE = double.MaxValue;
                        }
                        else//计算斜率,注意正切函数在-0.5Pi和0.5Pi之间是单调递增的
                        {
                            geometrypoints[i].SLOPE = (geometrypoints[i].Y - geometrypoints[MaxXPointIndex].Y) / (geometrypoints[MaxXPointIndex].X - geometrypoints[i].X);
                        }
                    }
                }
                //按照斜率slope排序,取稳定排序方法的堆排序。
                HeapSort<GeometryPoint> heapsort = new HeapSort<GeometryPoint>();
                heapsort.Sort(this.geometrypoints,0,size-1);
            }
        }

    复制代码

    控制台程序调用方法,按照连线顺序打印顶点:

    复制代码

    class Program
        {
            static void Main(string[] args)
            {
                SimplePolygon sp = new SimplePolygon();
                sp.Initialize(10, -50, 50, -50, 50);
                for (int i = 0; i < sp.GeometryPoints.Length; i++)
                {
                    Console.WriteLine(sp.GeometryPoints[i]);
                }
                Console.ReadKey();
            }
        }

    复制代码

    如果用界面绘图,应用WPF几何绘图可实现如下效果,红线为计算正切值的示例连线,绿色线为生成的简单多边形:

    关于坐标系与绘图的方法

      完毕。

  • 相关阅读:
    [hihocoder1509][异或排序]
    [hdu6148][Valley Numer]
    [hdu2089][不要62]
    [luoguU42591][小T的绝对值]
    [luogu2073][送花]
    [bzoj4709][柠檬]
    [luogu2114][起床困难综合症]
    [codevs3342][绿色通道]
    [luoguU42591][小T的面试题]
    [noip][2014]
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13309214.html
Copyright © 2020-2023  润新知