今天公司要求实现一个算法,在给出一堆离散点中,画出最外层的边界。实际就是从离散点中找出最外面的点(或称边界点),这些点用线连接起来即可。
一、实现的原理:
1.任意点(point1)找出最远距离的一个点(point2),该点point2即为其中一个边界点。
2.point1与point2组成的向量v1(point1 - point2)中找出与其最大夹角的一个向量v2。
3.重复:v2中找与其最大夹角的向量v3.....
4.直到最后找到的向量方向为点point2的向量时结束。
二、这里实现的代码为C#,只要搞懂原理,可以转为各种语言的实现
1.首先是平面向量类 PlaneVector

1 /// <summary>
2 /// 平面向量类
3 /// </summary>
4 public struct PlaneVector
5 {
6 public static readonly PlaneVector Empty;
7
8 /// <summary>
9 /// 向量的X值
10 /// </summary>
11 private double _X;
12
13 /// <summary>
14 /// 向量的Y值
15 /// </summary>
16 private double _Y;
17
18 public double X
19 {
20 get
21 {
22 return _X;
23 }
24 set
25 {
26 _X = value;
27 }
28 }
29
30 public double Y
31 {
32 get
33 {
34 return _Y;
35 }
36 set
37 {
38 _Y = value;
39 }
40 }
41
42 public PlaneVector(double x, double y)
43 {
44 _X = x;
45 _Y = y;
46 }
47
48 public PlaneVector(Point destPoint, Point sourcePoint)
49 {
50 _X = destPoint.X - sourcePoint.X;
51 _Y = destPoint.Y - sourcePoint.Y;
52 }
53
54 public PlaneVector(PointF destPoint, PointF sourcePoint)
55 {
56 _X = destPoint.X - sourcePoint.X;
57 _Y = destPoint.Y - sourcePoint.Y;
58 }
59
60 public PlaneVector(PlaneVector destPlaneVector, PlaneVector sourcePlaneVector)
61 {
62 _X = destPlaneVector._X - sourcePlaneVector._X;
63 _Y = destPlaneVector._Y - sourcePlaneVector._Y;
64 }
65
66 public void Add(PlaneVector pv)
67 {
68 _X += pv._X;
69 _Y += pv._Y;
70 }
71
72 public void Subtract(PlaneVector pv)
73 {
74 _X -= pv._X;
75 _Y -= pv._Y;
76 }
77
78 public override bool Equals(object obj)
79 {
80 if (!(obj is PlaneVector))
81 {
82 return false;
83 }
84 PlaneVector tf = (PlaneVector) obj;
85 return (((tf.X == this.X) && (tf.Y == this.Y)) && tf.GetType().Equals(base.GetType()));
86 }
87
88 public override int GetHashCode()
89 {
90 return base.GetHashCode();
91 }
92
93 public override string ToString()
94 {
95 return string.Format("[{0},{1}]", this._X, this._Y);
96 }
97
98 static PlaneVector()
99 {
100 Empty = new PlaneVector(0f, 0f);
101 }
102
103 public static PlaneVector operator +(PlaneVector left, PlaneVector right)
104 {
105 return Add(left, right);
106 }
107
108 public static PlaneVector operator -(PlaneVector left, PlaneVector right)
109 {
110 return Subtract(left, right);
111 }
112
113 public static bool operator ==(PlaneVector left, PlaneVector right)
114 {
115 return ((left.X == right.X) && (left.Y == right.Y));
116 }
117
118 public static bool operator !=(PlaneVector left, PlaneVector right)
119 {
120 return !(left == right);
121 }
122
123 public static PlaneVector Add(PlaneVector left, PlaneVector right)
124 {
125 return new PlaneVector(left.X + right.X, left.Y + right.Y);
126 }
127
128 public static PlaneVector Subtract(PlaneVector left, PlaneVector right)
129 {
130 return new PlaneVector(left.X - right.X, left.Y - right.Y);
131 }
132
133 }
2 /// 平面向量类
3 /// </summary>
4 public struct PlaneVector
5 {
6 public static readonly PlaneVector Empty;
7
8 /// <summary>
9 /// 向量的X值
10 /// </summary>
11 private double _X;
12
13 /// <summary>
14 /// 向量的Y值
15 /// </summary>
16 private double _Y;
17
18 public double X
19 {
20 get
21 {
22 return _X;
23 }
24 set
25 {
26 _X = value;
27 }
28 }
29
30 public double Y
31 {
32 get
33 {
34 return _Y;
35 }
36 set
37 {
38 _Y = value;
39 }
40 }
41
42 public PlaneVector(double x, double y)
43 {
44 _X = x;
45 _Y = y;
46 }
47
48 public PlaneVector(Point destPoint, Point sourcePoint)
49 {
50 _X = destPoint.X - sourcePoint.X;
51 _Y = destPoint.Y - sourcePoint.Y;
52 }
53
54 public PlaneVector(PointF destPoint, PointF sourcePoint)
55 {
56 _X = destPoint.X - sourcePoint.X;
57 _Y = destPoint.Y - sourcePoint.Y;
58 }
59
60 public PlaneVector(PlaneVector destPlaneVector, PlaneVector sourcePlaneVector)
61 {
62 _X = destPlaneVector._X - sourcePlaneVector._X;
63 _Y = destPlaneVector._Y - sourcePlaneVector._Y;
64 }
65
66 public void Add(PlaneVector pv)
67 {
68 _X += pv._X;
69 _Y += pv._Y;
70 }
71
72 public void Subtract(PlaneVector pv)
73 {
74 _X -= pv._X;
75 _Y -= pv._Y;
76 }
77
78 public override bool Equals(object obj)
79 {
80 if (!(obj is PlaneVector))
81 {
82 return false;
83 }
84 PlaneVector tf = (PlaneVector) obj;
85 return (((tf.X == this.X) && (tf.Y == this.Y)) && tf.GetType().Equals(base.GetType()));
86 }
87
88 public override int GetHashCode()
89 {
90 return base.GetHashCode();
91 }
92
93 public override string ToString()
94 {
95 return string.Format("[{0},{1}]", this._X, this._Y);
96 }
97
98 static PlaneVector()
99 {
100 Empty = new PlaneVector(0f, 0f);
101 }
102
103 public static PlaneVector operator +(PlaneVector left, PlaneVector right)
104 {
105 return Add(left, right);
106 }
107
108 public static PlaneVector operator -(PlaneVector left, PlaneVector right)
109 {
110 return Subtract(left, right);
111 }
112
113 public static bool operator ==(PlaneVector left, PlaneVector right)
114 {
115 return ((left.X == right.X) && (left.Y == right.Y));
116 }
117
118 public static bool operator !=(PlaneVector left, PlaneVector right)
119 {
120 return !(left == right);
121 }
122
123 public static PlaneVector Add(PlaneVector left, PlaneVector right)
124 {
125 return new PlaneVector(left.X + right.X, left.Y + right.Y);
126 }
127
128 public static PlaneVector Subtract(PlaneVector left, PlaneVector right)
129 {
130 return new PlaneVector(left.X - right.X, left.Y - right.Y);
131 }
132
133 }
2.算法的实现

1 public static class Common
2 {
3 /// <summary>
4 /// 获取两个向量的夹角
5 /// </summary>
6 /// <param name="sourcePlaneVector">源向量</param>
7 /// <param name="destPlaneVector">目标向量</param>
8 /// <returns></returns>
9 public static double GetAngle(PlaneVector sourcePlaneVector, PlaneVector destPlaneVector)
10 {
11 double temp = 0;
12 double norm1 = 0;
13 double norm2 = 0;
14
15 norm1 = System.Math.Sqrt(sourcePlaneVector.X * sourcePlaneVector.X + sourcePlaneVector.Y * sourcePlaneVector.Y);
16 norm2 = System.Math.Sqrt(destPlaneVector.X * destPlaneVector.X + destPlaneVector.Y * destPlaneVector.Y);
17
18 temp = sourcePlaneVector.X * destPlaneVector.X + sourcePlaneVector.Y * destPlaneVector.Y;
19
20 if (norm1 == 0 || norm2 == 0)
21 {
22 return 0;
23 }
24 else
25 {
26 temp = temp / (norm1 * norm2);
27
28 if (temp > 1.0)
29 {
30 temp = 1.0;
31 }
32 else if (temp < -1.0)
33 {
34 temp = -1.0;
35 }
36
37 return System.Math.Acos(temp);
38 }
39 }
40
41 /// <summary>
42 /// 获取一个点在一个点集合中的最远点
43 /// </summary>
44 /// <param name="aPointF">点</param>
45 /// <param name="aPoints">点集合</param>
46 /// <returns>最远点</returns>
47 public static System.Drawing.PointF GetFarthestPointF(System.Drawing.PointF aPointF, List<System.Drawing.PointF> aPoints)
48 {
49 if (aPointF == null)
50 {
51 throw new ArgumentNullException("aPointF");
52 }
53
54 if (aPoints == null)
55 {
56 throw new ArgumentNullException("aPoints");
57 }
58
59 System.Drawing.PointF farthestPointF = System.Drawing.PointF.Empty;
60
61 double maxLength = 0;
62 double tempLength = 0;
63
64 double x = 0;
65 double y = 0;
66
67 foreach (System.Drawing.PointF aDestPoint in aPoints)
68 {
69 x = (double)(aDestPoint.X - aPointF.X);
70 y = (double)(aDestPoint.Y - aPointF.Y);
71
72 tempLength = Math.Sqrt(x * x + y * y);
73
74 if (maxLength < tempLength)
75 {
76 maxLength = tempLength;
77 farthestPointF = aDestPoint;
78 }
79 }
80
81 return farthestPointF;
82 }
83
84 /// <summary>
85 /// 获取离散点集的边界点
86 /// </summary>
87 /// <param name="aPoints">一组离散点</param>
88 /// <returns>离散点的边界点</returns>
89 public static List<System.Drawing.PointF> GetBorderPointFs(List<System.Drawing.PointF> aPoints)
90 {
91 List<System.Drawing.PointF> aBorderPoints = new List<System.Drawing.PointF>();
92
93 if (aPoints == null)
94 {
95 throw new ArgumentNullException("aPoints");
96 }
97
98 if (aPoints.Count < 3)
99 {
100 aBorderPoints.AddRange(aPoints);
101
102 return aBorderPoints;
103 }
104
105 //1.获取任意点的最远点
106
107 System.Drawing.PointF farthestPointF = GetFarthestPointF(aPoints.First(), aPoints);
108
109 //2.获取边界点 --------------------------------------->
110
111 System.Drawing.PointF sourcePointF = farthestPointF;
112 System.Drawing.PointF destPointF = aPoints.First();
113
114 System.Drawing.PointF maxAnglePoint = System.Drawing.PointF.Empty;
115
116 while (!farthestPointF.Equals(maxAnglePoint))
117 {
118 PlaneVector sourcePlaneVector = new PlaneVector(destPointF, sourcePointF);
119
120 double maxAngle = 0;
121
122 foreach (System.Drawing.PointF aDestPoint in aPoints)
123 {
124 PlaneVector destPlaneVector = new PlaneVector(aDestPoint, sourcePointF);
125
126 double tempAngle = GetAngle(sourcePlaneVector, destPlaneVector);
127
128 if (maxAngle <= tempAngle)
129 {
130 maxAngle =tempAngle;
131 maxAnglePoint = aDestPoint;
132 }
133 }
134
135 aBorderPoints.Add(maxAnglePoint);
136
137 destPointF = sourcePointF;
138 sourcePointF = maxAnglePoint;
139
140
141 } //---------------------------------------< 2.获取边界点
142
143 return aBorderPoints;
144 }
145 }
2 {
3 /// <summary>
4 /// 获取两个向量的夹角
5 /// </summary>
6 /// <param name="sourcePlaneVector">源向量</param>
7 /// <param name="destPlaneVector">目标向量</param>
8 /// <returns></returns>
9 public static double GetAngle(PlaneVector sourcePlaneVector, PlaneVector destPlaneVector)
10 {
11 double temp = 0;
12 double norm1 = 0;
13 double norm2 = 0;
14
15 norm1 = System.Math.Sqrt(sourcePlaneVector.X * sourcePlaneVector.X + sourcePlaneVector.Y * sourcePlaneVector.Y);
16 norm2 = System.Math.Sqrt(destPlaneVector.X * destPlaneVector.X + destPlaneVector.Y * destPlaneVector.Y);
17
18 temp = sourcePlaneVector.X * destPlaneVector.X + sourcePlaneVector.Y * destPlaneVector.Y;
19
20 if (norm1 == 0 || norm2 == 0)
21 {
22 return 0;
23 }
24 else
25 {
26 temp = temp / (norm1 * norm2);
27
28 if (temp > 1.0)
29 {
30 temp = 1.0;
31 }
32 else if (temp < -1.0)
33 {
34 temp = -1.0;
35 }
36
37 return System.Math.Acos(temp);
38 }
39 }
40
41 /// <summary>
42 /// 获取一个点在一个点集合中的最远点
43 /// </summary>
44 /// <param name="aPointF">点</param>
45 /// <param name="aPoints">点集合</param>
46 /// <returns>最远点</returns>
47 public static System.Drawing.PointF GetFarthestPointF(System.Drawing.PointF aPointF, List<System.Drawing.PointF> aPoints)
48 {
49 if (aPointF == null)
50 {
51 throw new ArgumentNullException("aPointF");
52 }
53
54 if (aPoints == null)
55 {
56 throw new ArgumentNullException("aPoints");
57 }
58
59 System.Drawing.PointF farthestPointF = System.Drawing.PointF.Empty;
60
61 double maxLength = 0;
62 double tempLength = 0;
63
64 double x = 0;
65 double y = 0;
66
67 foreach (System.Drawing.PointF aDestPoint in aPoints)
68 {
69 x = (double)(aDestPoint.X - aPointF.X);
70 y = (double)(aDestPoint.Y - aPointF.Y);
71
72 tempLength = Math.Sqrt(x * x + y * y);
73
74 if (maxLength < tempLength)
75 {
76 maxLength = tempLength;
77 farthestPointF = aDestPoint;
78 }
79 }
80
81 return farthestPointF;
82 }
83
84 /// <summary>
85 /// 获取离散点集的边界点
86 /// </summary>
87 /// <param name="aPoints">一组离散点</param>
88 /// <returns>离散点的边界点</returns>
89 public static List<System.Drawing.PointF> GetBorderPointFs(List<System.Drawing.PointF> aPoints)
90 {
91 List<System.Drawing.PointF> aBorderPoints = new List<System.Drawing.PointF>();
92
93 if (aPoints == null)
94 {
95 throw new ArgumentNullException("aPoints");
96 }
97
98 if (aPoints.Count < 3)
99 {
100 aBorderPoints.AddRange(aPoints);
101
102 return aBorderPoints;
103 }
104
105 //1.获取任意点的最远点
106
107 System.Drawing.PointF farthestPointF = GetFarthestPointF(aPoints.First(), aPoints);
108
109 //2.获取边界点 --------------------------------------->
110
111 System.Drawing.PointF sourcePointF = farthestPointF;
112 System.Drawing.PointF destPointF = aPoints.First();
113
114 System.Drawing.PointF maxAnglePoint = System.Drawing.PointF.Empty;
115
116 while (!farthestPointF.Equals(maxAnglePoint))
117 {
118 PlaneVector sourcePlaneVector = new PlaneVector(destPointF, sourcePointF);
119
120 double maxAngle = 0;
121
122 foreach (System.Drawing.PointF aDestPoint in aPoints)
123 {
124 PlaneVector destPlaneVector = new PlaneVector(aDestPoint, sourcePointF);
125
126 double tempAngle = GetAngle(sourcePlaneVector, destPlaneVector);
127
128 if (maxAngle <= tempAngle)
129 {
130 maxAngle =tempAngle;
131 maxAnglePoint = aDestPoint;
132 }
133 }
134
135 aBorderPoints.Add(maxAnglePoint);
136
137 destPointF = sourcePointF;
138 sourcePointF = maxAnglePoint;
139
140
141 } //---------------------------------------< 2.获取边界点
142
143 return aBorderPoints;
144 }
145 }
GetBorderPointFs 返回的经过计算最后得出的边界点集合。这里主要是使用GetAngle方法来获取连个向量的角度大小,注意的是,角度的单位不是一般的度,而是pi。
这里是实现的代码,里面做有一个测试的程序,在窗体上随便点上几个点,按生成图形即可看到结果。
附件: 实现的代码.rar