unity 中多边形合并问题

发表于2016-05-31
评论1 4.7k浏览

     This is a very good question. I implemented the same algorithm on c# some time ago. The Algorithm constructs a common contour of two polygons (i.e. Constructs a union without holes). Here it is.


Step 1、Create graph that describes the polygons.

Input: first polygon (n points), second polygon (m points). Output: graph. Vertex - polygon point of intersection point.

We should find intersections. Iterate through all polygon sides in both polygons [O(n*m)] and find any intersections.

  • If an intersection is not found, simply add vertices and connect them to the edge.

  • If any intersections are found, sort them by length to their start point, add all vertexes (start, end and intersections) and connect them (already in sorted order) to the edge. 

Step 2、Check constructed graph

If we did not find any intersection points when graph was built, we have one of the following conditions:

1、Polygon1 contains polygon2 - return polygon1
2、Polygon2 contains polygon1 - return polygon2
3、Polygon1 and polygon2 do not intersect. Return polygon1 AND polygon2.

Step 3、 Find left-bottom vertex.

Find the minimum x and y coordinates (minx, miny). Then find the minimum distance between (minx, miny) and the polygon's points. This point will be the left-bottom point.

Step 4、 Construct common contour.

We start to traverse the graph from the left-bottom point and continue until we get back into it. At the beginning we mark all edges as unvisited. On every iteration you should select the next point and mark it as visited.

To choose the next point, choose an edge with a maximum internal angle in counter-clockwise direction.

I calculate two vectors: vector1 for current edge and vector2 for each next unvisited edge (as presented in the picture).

For vectors I calculate:

1、Scalar product (dot product). It returns a value related to an angle between vectors.
2、Vector product (cross product). It returns a new vector. If z-coordinate of this vector is positive, scalar product gives me right angle in counter-clockwise direction. Else (z-coordinate is negative), I calculate get angle between vectors as 360 - angle from scalar product.

As a result I get an edge (and a correspond next vertex) with the maximum angle.

I add to result list each passed vertex. Result list is the union polygon. 

Remarks

1、This algorithm allows us to merge multiple of polygons - to apply iteratively with polygon's pairs.
2、If you have a path that consists of many bezier curves and lines, you should flatten this path first.

以上转自

http://stackoverflow.com/questions/2667748/how-do-i-combine-complex-polygons/19475433#19475433

一下是算法代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
public class UnitPolygonsUtils
    {
         
 
        private List polygons = new List();
        private int curPgon;
        private Vector2 startPoint;
        private int curIndex;
 
        public UnitPolygonsUtils()
        {
             
        }
 
        private Vector2 dotV1 = Vector2.zero;
        private Vector2 dotV2 = Vector2.zero;
        private void findIntersectionVertex(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, ref List intersections, out float t1)
        {
            float t2 = 0;
            float dx12 = p2.x - p1.x;
            float dy12 = p2.y - p1.y;
            float dx34 = p4.x - p3.x;
            float dy34 = p4.y - p3.y;
 
            // Solve for t1 and t2
            float denominator = (dy12 * dx34 - dx12 * dy34);
            t1 = ((p1.x - p3.x) * dy34 (p3.y - p1.y) * dx34) / denominator;
            if (float.IsInfinity(t1))
            {
                return;
            }
 
            float dotNum = 0;
            float maxNum = 0;
            //直线重叠
            if (float.IsNaN(t1))
            {
                dotV1.Set(p2.x - p1.x, p2.y - p1.y);
                dotV2.Set(p2.x - p1.x, p2.y - p1.y);
                maxNum = Vector2.Dot(dotV1, dotV2);
 
                dotV1.Set(p3.x - p1.x, p3.y - p1.y);
                dotV2.Set(p2.x - p1.x, p2.y - p1.y);
                dotNum = Vector2.Dot(dotV1, dotV2);
                if (dotNum > 0 && dotNum < maxNum)
                    addIntersectionsPoint(ref intersections, p3, p1, p2);
                dotV1.Set(p4.x - p1.x, p4.y - p1.y);
                dotV2.Set(p2.x - p1.x, p2.y - p1.y);
                dotNum = Vector2.Dot(dotV1, dotV2);
                if (dotNum > 0 && dotNum < maxNum)
                    addIntersectionsPoint(ref intersections, p4, p1, p2);
            }
 
            // Find the point of intersection.
            t2 = ((p3.x - p1.x) * dy12 (p1.y - p3.y) * dx12) / -denominator;
            // The segments intersect if t1 and t2 are between 0 and 1.
            bool segments_intersect = ((t1 >= 0) && (t1 <= 1) && (t2 >= 0) && (t2 <= 1));
            if (t1 > 0.001 && segments_intersect)
            {
                Vector2 intersectP = new Vector2(p1.x dx12 * t1, p1.y dy12 * t1);
                addIntersectionsPoint(ref intersections, intersectP, p1, p2);
            }
             
        }
 
        private void addIntersectionsPoint(ref List intersections, Vector2 intersectP, Vector2 p1, Vector2 p2)
        {
            if (indexListItem(ref intersections, intersectP) == -1)
            {
                addWithOrde(ref intersections, p1, p2, intersectP);
            }
        }
 
        private int indexListItem(ref List list, Vector2 item)
        {
            int index = -1;
            for(int ix = 0; ix < list.Count; ix )
            {
                if (Vector2.Distance(list[ix],item) <= 0.01f)
                {
                    index = ix;
                    break;
                }
            }
            return index;
        }
 
        private void addWithOrde(ref List list, Vector2 p1, Vector2 p2, Vector2 intersecP)
        {
            if (intersecP.Equals(p1) || intersecP.Equals(p2)) return;
            bool insert = false;
            for (int ix = 0; ix < list.Count; ix )
            {
                if (Vector2.Distance(list[ix], p1) > Vector2.Distance(intersecP, p1))
                {
                    list.Insert(ix, intersecP);
                    insert = true;
                    break;
                }
            }
            if (!insert)
            {
                list.Add(intersecP);
            }
        }
 
        private float dotProduct(PointF p1, PointF p2, PointF p3, PointF p4)
        {
            return (p2.X - p1.X) * (p4.X - p3.X) (p2.Y - p1.Y) * (p4.Y - p3.Y);
        }
 
        private void findLeftBottomVertex(List polygons)
        {
            int cur_index = 0;
            curPgon = 0;
            startPoint = polygons[curPgon][cur_index];
            for (int pgon = 0; pgon < polygons.Count; pgon )
            {
                for (int index = 0; index < polygons[pgon].Count; index )
                {
                    Vector2 test_point = polygons[pgon][index];
                    if ((test_point.x < startPoint.x) ||
                        ((test_point.x == startPoint.x) &&
                         (test_point.y < startPoint.y)))
                    {
                        curPgon = pgon;
                        cur_index = index;
                        startPoint = polygons[curPgon][cur_index];
                    }
                }
            }
        }
 
        public List UnitPoligons(List polygons)
        {
            if (polygons.Count == 1)
            {
                return polygons[0].ToList();
            }
 
            List units = new List();
            int cur_index = 0, cur_pgon = 0, cur_index2 = 0;
            Vector2 p1, p2, p3, p4;
            List intersectPoints = new List();
            List<list<< code="">bool>> intersectVisitPoints = new List<list<< code="">bool>>();</list<<></list<<>
            List pointFs;
            List<bool> visits;
            List intersectPos = new List();
            float t1;
            int ix = 0;
            int pgon = 0;
            int pgon2 = 0;
            for (pgon = 0; pgon < polygons.Count; pgon )
            {
                pointFs = new List();
                intersectPoints.Add(pointFs);
                visits = new List<bool>();
                intersectVisitPoints.Add(visits);
                for (cur_index = 0; cur_index < polygons[pgon].Length; cur_index )
                {
                    p1 = polygons[pgon][cur_index];
                    p2 = cur_index != polygons[pgon].Length - 1 ? polygons[pgon][cur_index 1] : polygons[pgon][0];
                    intersectPos.Clear();
                    pointFs.Add(p1);
                    visits.Add(false);
                    for (pgon2 = 0; pgon2 < polygons.Count; pgon2 )
                    {
                        if (pgon != pgon2)
                        {
                            for (cur_index2 = 0; cur_index2 < polygons[pgon2].Length; cur_index2 )
                            {
                                p3 = polygons[pgon2][cur_index2];
                                p4 = cur_index2 != polygons[pgon2].Length - 1 ? polygons[pgon2][cur_index2 1] : polygons[pgon2][0];
                                findIntersectionVertex(p1, p2, p3, p4, ref intersectPos, out t1);
                            }
                        }
                    }
 
                    for (ix = 0; ix < intersectPos.Count; ix )
                    {
                        pointFs.Add(intersectPos[ix]);
                        visits.Add(false);
                    }
                }
            }
 
            findLeftBottomVertex(intersectPoints);
 
            Vector2 curPoint = new Vector2(startPoint.x, startPoint.y);
            Vector2 nextPoint = new Vector2(float.NaN, float.NaN);// = curIndex != intersectPoints[curPgon].Count - 1? intersectPoints[curPgon][curIndex 1]:intersectPoints[curPgon][0];
            float maxAngle = -1;
            float tempAngle = 0;
            int nextPgon = 0;
            Vector2 lastPoint = curPoint;
            int p1Index = 0;
            int p2Index = 0;
 
            bool firstPos = true;
            for (ix = 0; ix < 2000; ix )
            {
                maxAngle = -1;
                for (pgon = 0; pgon < intersectPoints.Count; pgon )
                {
                    for (cur_index = 0; cur_index < intersectPoints[pgon].Count; cur_index )
                    {
                        if (Vector2.Distance(curPoint,intersectPoints[pgon][cur_index]) <= 0.01f)
                        {
                            p1Index = cur_index == 0 ? intersectPoints[pgon].Count - 1 : cur_index - 1;
                            p2Index = cur_index < intersectPoints[pgon].Count - 1 ? cur_index 1 : 0;
                            p1 = intersectPoints[pgon][p1Index];
                            p2 = intersectPoints[pgon][p2Index];
                            if (curPgon != pgon && !intersectVisitPoints[pgon][p1Index] && !firstPos)
                            {
                                tempAngle = internalAngle(p1, lastPoint, curPoint);
                                if (tempAngle > maxAngle)
                                {
                                    maxAngle = tempAngle;
                                    nextPgon = pgon;
                                    nextPoint = p1;
                                }
                                intersectVisitPoints[pgon][p1Index] = true;
                            }
 
                            tempAngle = internalAngle(p2, lastPoint, curPoint);
                            if (tempAngle > maxAngle && !intersectVisitPoints[pgon][cur_index])
                            {
                                maxAngle = tempAngle;
                                nextPgon = pgon;
                                nextPoint = p2;
                                 
                            }
                            intersectVisitPoints[pgon][cur_index] = true;
                        }
                    }
                }
                curPgon = nextPgon;
                lastPoint.Set(curPoint.x, curPoint.y);
                firstPos = false;
                if (float.IsNaN(nextPoint.x))
                {
                    return null;
                }
                else
                {
                    units.Add(curPoint);
                }
                curPoint = nextPoint;
                if (curPoint.Equals(startPoint))
                    break;
            }
            return units;
        }
 
        private Vector2 angleP1 = Vector2.zero;
        private Vector2 angleP2 = Vector2.zero;
        private float internalAngle(Vector2 p1, Vector2 p2, Vector2 startP)
        {
            float angle = 0;
            angleP1.Set(p1.x - startP.x, p1.y - startP.y);
            angleP2.Set(startP.x - p2.x, startP.y - p2.y);
            if (angleP2.x == 0 && angleP2.y == 0)
            {
                angleP2.x = 10;
                return Vector2.Angle(angleP1, angleP2);
            }
            angle = Vector2.Angle(angleP1, angleP2);
            Vector3 crossVec = Vector3.Cross(angleP1, angleP2);
            if (angle != 0 && crossVec.z < 0)
            {
                angle = 360 - angle;
            }
            else
            {
                angle = 180 - angle;
            }
            return angle;
        }
 
        public bool pnpoly(int nvert, Vector2[] verts, Vector2 test)
        {
            int i, j;
            double minX = verts[0].x;
            double maxX = verts[0].x;
            double minY = verts[0].y;
            double maxY = verts[0].y;
            for (i = 1; i < verts.Length; i )
            {
                Vector2 q = verts[i];
                minX = Math.Min(q.x, minX);
                maxX = Math.Max(q.x, maxX);
                minY = Math.Min(q.y, minY);
                maxY = Math.Max(q.y, maxY);
            }
 
            if (test.x < minX || test.x > maxX || test.y < minY || test.y > maxY)
            {
                return false;
            }
 
            bool c = false;
            for (i = 0, j = nvert - 1; i < nvert; j = i )
            {
                if (((verts[i].y > test.y) != (verts[j].y > test.y)) &&
                 (test.x < (verts[j].x - verts[i].x) * (test.y - verts[i].y) / (verts[j].y - verts[i].y) verts[i].x))
                    c = !c;
            }
            return c;
        }
    }
 
 
//以下是使用
 
Vector2[] polygon1 = new Vector2[]{
            new Vector2(175,10),
            new Vector2(175,120),
            new Vector2(220,140),
            new Vector2(240,120),
            new Vector2(240,10)};
        Vector2[] polygon2 = new Vector2[]{
            new Vector2(115,10),
            new Vector2(115,40),
            new Vector2(200,40),
            new Vector2(200,10)};
        Vector2[] polygon3 = new Vector2[]{
            new Vector2(115,10),
            new Vector2(240,10),
            new Vector2(150,0)};
        Vector2[] polygon4 = new Vector2[]{
            new Vector2(115,10),
            new Vector2(200,100),
            new Vector2(240,0)};
        List polygons = new List();
        polygons.Add(polygon1);
        polygons.Add(polygon2);
        polygons.Add(polygon3);
        polygons.Add(polygon4);
        UnitPolygonsUtils unitUtil = new UnitPolygonsUtils();
        List units = unitUtil.UnitPoligons(polygons);bool>bool>bool>bool>

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引