幾何算法:點集合構造簡單多邊形


問題:給定平面中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);
        }
    }
GeometryPoint 定義

簡單封閉圖形定義,並定義初始化簡單封閉圖形的方法,該方法隨機產生多邊形的頂點:

 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);
        }
    }
SimplePolygon定義

控制台程序調用方法,按照連線順序打印頂點:

 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();
        }
    }
SimplePolygon 控制台調用方法

如果用界面繪圖,應用WPF幾何繪圖可實現如下效果,紅線為計算正切值的示例連線,綠色線為生成的簡單多邊形:

關於坐標系與繪圖的方法,請參照另一篇文章“輪廓算法”。

完畢。

作者:Andy Zeng

歡迎任何形式的轉載,但請務必注明出處。

http://www.cnblogs.com/andyzeng/p/3754005.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM