rbpoly2tri 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/poly2tri/common/shapes.cc +334 -0
- data/ext/poly2tri/common/shapes.h +320 -0
- data/ext/poly2tri/common/utils.h +102 -0
- data/ext/poly2tri/poly2tri.h +39 -0
- data/ext/poly2tri/sweep/advancing_front.cc +108 -0
- data/ext/poly2tri/sweep/advancing_front.h +119 -0
- data/ext/poly2tri/sweep/cdt.cc +72 -0
- data/ext/poly2tri/sweep/cdt.h +69 -0
- data/ext/poly2tri/sweep/sweep.cc +820 -0
- data/ext/poly2tri/sweep/sweep.h +126 -0
- data/ext/poly2tri/sweep/sweep_context.cc +201 -0
- data/ext/poly2tri/sweep/sweep_context.h +185 -0
- data/ext/rbpoly2tri/extconf.rb +16 -0
- data/ext/rbpoly2tri/rbpoly2tri.cc +198 -0
- data/ext/throw_assert/assert.h +19 -0
- data/lib/rbpoly2tri.rb +1 -0
- metadata +62 -0
@@ -0,0 +1,820 @@
|
|
1
|
+
/*
|
2
|
+
* Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
|
3
|
+
* http://code.google.com/p/poly2tri/
|
4
|
+
*
|
5
|
+
* All rights reserved.
|
6
|
+
*
|
7
|
+
* Redistribution and use in source and binary forms, with or without modification,
|
8
|
+
* are permitted provided that the following conditions are met:
|
9
|
+
*
|
10
|
+
* * Redistributions of source code must retain the above copyright notice,
|
11
|
+
* this list of conditions and the following disclaimer.
|
12
|
+
* * Redistributions in binary form must reproduce the above copyright notice,
|
13
|
+
* this list of conditions and the following disclaimer in the documentation
|
14
|
+
* and/or other materials provided with the distribution.
|
15
|
+
* * Neither the name of Poly2Tri nor the names of its contributors may be
|
16
|
+
* used to endorse or promote products derived from this software without specific
|
17
|
+
* prior written permission.
|
18
|
+
*
|
19
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
20
|
+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
21
|
+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
22
|
+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
23
|
+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
24
|
+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
25
|
+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
26
|
+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
27
|
+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
*/
|
31
|
+
#include "sweep.h"
|
32
|
+
#include "sweep_context.h"
|
33
|
+
#include "advancing_front.h"
|
34
|
+
#include "../common/utils.h"
|
35
|
+
|
36
|
+
namespace p2t {
|
37
|
+
|
38
|
+
// Triangulate simple polygon with holes
|
39
|
+
void Sweep::Triangulate(SweepContext& tcx)
|
40
|
+
{
|
41
|
+
tcx.InitTriangulation();
|
42
|
+
tcx.CreateAdvancingFront(nodes_);
|
43
|
+
// Sweep points; build mesh
|
44
|
+
SweepPoints(tcx);
|
45
|
+
// Clean up
|
46
|
+
FinalizationPolygon(tcx);
|
47
|
+
}
|
48
|
+
|
49
|
+
void Sweep::SweepPoints(SweepContext& tcx)
|
50
|
+
{
|
51
|
+
for (int i = 1; i < tcx.point_count(); i++) {
|
52
|
+
Point& point = *tcx.GetPoint(i);
|
53
|
+
Node* node = &PointEvent(tcx, point);
|
54
|
+
for (int i = 0; i < point.edge_list.size(); i++) {
|
55
|
+
EdgeEvent(tcx, point.edge_list[i], node);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
void Sweep::FinalizationPolygon(SweepContext& tcx)
|
61
|
+
{
|
62
|
+
// Get an Internal triangle to start with
|
63
|
+
Triangle* t = tcx.front()->head()->next->triangle;
|
64
|
+
Point* p = tcx.front()->head()->next->point;
|
65
|
+
while (!t->GetConstrainedEdgeCW(*p)) {
|
66
|
+
t = t->NeighborCCW(*p);
|
67
|
+
}
|
68
|
+
|
69
|
+
// Collect interior triangles constrained by edges
|
70
|
+
tcx.MeshClean(*t);
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Find closes node to the left of the new point and
|
75
|
+
* create a new triangle. If needed new holes and basins
|
76
|
+
* will be filled to.
|
77
|
+
*
|
78
|
+
* @param tcx
|
79
|
+
* @param point
|
80
|
+
* @return
|
81
|
+
*/
|
82
|
+
Node& Sweep::PointEvent(SweepContext& tcx, Point& point)
|
83
|
+
{
|
84
|
+
Node& node = tcx.LocateNode(point);
|
85
|
+
Node& new_node = NewFrontTriangle(tcx, point, node);
|
86
|
+
|
87
|
+
// Only need to check +epsilon since point never have smaller
|
88
|
+
// x value than node due to how we fetch nodes from the front
|
89
|
+
if (point.x <= node.point->x + EPSILON) {
|
90
|
+
Fill(tcx, node);
|
91
|
+
}
|
92
|
+
|
93
|
+
//tcx.AddNode(new_node);
|
94
|
+
|
95
|
+
FillAdvancingFront(tcx, new_node);
|
96
|
+
return new_node;
|
97
|
+
}
|
98
|
+
|
99
|
+
void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
100
|
+
{
|
101
|
+
tcx.edge_event.constrained_edge = edge;
|
102
|
+
tcx.edge_event.right = (edge->p->x > edge->q->x);
|
103
|
+
|
104
|
+
if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) {
|
105
|
+
return;
|
106
|
+
}
|
107
|
+
|
108
|
+
// For now we will do all needed filling
|
109
|
+
// TODO: integrate with flip process might give some better performance
|
110
|
+
// but for now this avoid the issue with cases that needs both flips and fills
|
111
|
+
FillEdgeEvent(tcx, edge, node);
|
112
|
+
EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q);
|
113
|
+
}
|
114
|
+
|
115
|
+
void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point)
|
116
|
+
{
|
117
|
+
if (IsEdgeSideOfTriangle(*triangle, ep, eq)) {
|
118
|
+
return;
|
119
|
+
}
|
120
|
+
|
121
|
+
Point* p1 = triangle->PointCCW(point);
|
122
|
+
Orientation o1 = Orient2d(eq, *p1, ep);
|
123
|
+
if (o1 == COLLINEAR) {
|
124
|
+
//throw new RuntimeException( "EdgeEvent - Collinear not supported" );
|
125
|
+
assert(false);
|
126
|
+
}
|
127
|
+
|
128
|
+
Point* p2 = triangle->PointCW(point);
|
129
|
+
Orientation o2 = Orient2d(eq, *p2, ep);
|
130
|
+
if (o2 == COLLINEAR) {
|
131
|
+
//throw new RuntimeException( "EdgeEvent - Collinear not supported" );
|
132
|
+
assert(false);
|
133
|
+
}
|
134
|
+
|
135
|
+
if (o1 == o2) {
|
136
|
+
// Need to decide if we are rotating CW or CCW to get to a triangle
|
137
|
+
// that will cross edge
|
138
|
+
if (o1 == CW) {
|
139
|
+
triangle = triangle->NeighborCCW(point);
|
140
|
+
} else{
|
141
|
+
triangle = triangle->NeighborCW(point);
|
142
|
+
}
|
143
|
+
EdgeEvent(tcx, ep, eq, triangle, point);
|
144
|
+
} else {
|
145
|
+
// This triangle crosses constraint so lets flippin start!
|
146
|
+
FlipEdgeEvent(tcx, ep, eq, triangle, point);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq)
|
151
|
+
{
|
152
|
+
int index = triangle.EdgeIndex(&ep, &eq);
|
153
|
+
|
154
|
+
if (index != -1) {
|
155
|
+
triangle.MarkConstrainedEdge(index);
|
156
|
+
Triangle* t = triangle.GetNeighbor(index);
|
157
|
+
if (t) {
|
158
|
+
t->MarkConstrainedEdge(&ep, &eq);
|
159
|
+
}
|
160
|
+
return true;
|
161
|
+
}
|
162
|
+
return false;
|
163
|
+
}
|
164
|
+
|
165
|
+
Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node)
|
166
|
+
{
|
167
|
+
Triangle* triangle = new Triangle(point, *node.point, *node.next->point);
|
168
|
+
|
169
|
+
triangle->MarkNeighbor(*node.triangle);
|
170
|
+
tcx.AddToMap(triangle);
|
171
|
+
|
172
|
+
Node* new_node = new Node(point);
|
173
|
+
nodes_.push_back(new_node);
|
174
|
+
|
175
|
+
new_node->next = node.next;
|
176
|
+
new_node->prev = &node;
|
177
|
+
node.next->prev = new_node;
|
178
|
+
node.next = new_node;
|
179
|
+
|
180
|
+
if (!Legalize(tcx, *triangle)) {
|
181
|
+
tcx.MapTriangleToNodes(*triangle);
|
182
|
+
}
|
183
|
+
|
184
|
+
return *new_node;
|
185
|
+
}
|
186
|
+
|
187
|
+
/**
|
188
|
+
* Adds a triangle to the advancing front to fill a hole.
|
189
|
+
* @param tcx
|
190
|
+
* @param node - middle node, that is the bottom of the hole
|
191
|
+
*/
|
192
|
+
void Sweep::Fill(SweepContext& tcx, Node& node)
|
193
|
+
{
|
194
|
+
Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point);
|
195
|
+
|
196
|
+
// TODO: should copy the constrained_edge value from neighbor triangles
|
197
|
+
// for now constrained_edge values are copied during the legalize
|
198
|
+
triangle->MarkNeighbor(*node.prev->triangle);
|
199
|
+
triangle->MarkNeighbor(*node.triangle);
|
200
|
+
|
201
|
+
tcx.AddToMap(triangle);
|
202
|
+
|
203
|
+
// Update the advancing front
|
204
|
+
node.prev->next = node.next;
|
205
|
+
node.next->prev = node.prev;
|
206
|
+
|
207
|
+
// If it was legalized the triangle has already been mapped
|
208
|
+
if (!Legalize(tcx, *triangle)) {
|
209
|
+
tcx.MapTriangleToNodes(*triangle);
|
210
|
+
}
|
211
|
+
|
212
|
+
}
|
213
|
+
|
214
|
+
/**
|
215
|
+
* Fills holes in the Advancing Front
|
216
|
+
*
|
217
|
+
*
|
218
|
+
* @param tcx
|
219
|
+
* @param n
|
220
|
+
*/
|
221
|
+
void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n)
|
222
|
+
{
|
223
|
+
|
224
|
+
// Fill right holes
|
225
|
+
Node* node = n.next;
|
226
|
+
|
227
|
+
while (node->next) {
|
228
|
+
double angle = HoleAngle(*node);
|
229
|
+
if (angle > M_PI_2 || angle < -M_PI_2) break;
|
230
|
+
Fill(tcx, *node);
|
231
|
+
node = node->next;
|
232
|
+
}
|
233
|
+
|
234
|
+
// Fill left holes
|
235
|
+
node = n.prev;
|
236
|
+
|
237
|
+
while (node->prev) {
|
238
|
+
double angle = HoleAngle(*node);
|
239
|
+
if (angle > M_PI_2 || angle < -M_PI_2) break;
|
240
|
+
Fill(tcx, *node);
|
241
|
+
node = node->prev;
|
242
|
+
}
|
243
|
+
|
244
|
+
// Fill right basins
|
245
|
+
if (n.next && n.next->next) {
|
246
|
+
double angle = BasinAngle(n);
|
247
|
+
if (angle < PI_3div4) {
|
248
|
+
FillBasin(tcx, n);
|
249
|
+
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
|
253
|
+
double Sweep::BasinAngle(Node& node)
|
254
|
+
{
|
255
|
+
double ax = node.point->x - node.next->next->point->x;
|
256
|
+
double ay = node.point->y - node.next->next->point->y;
|
257
|
+
return atan2(ay, ax);
|
258
|
+
}
|
259
|
+
|
260
|
+
/**
|
261
|
+
*
|
262
|
+
* @param node - middle node
|
263
|
+
* @return the angle between 3 front nodes
|
264
|
+
*/
|
265
|
+
double Sweep::HoleAngle(Node& node)
|
266
|
+
{
|
267
|
+
/* Complex plane
|
268
|
+
* ab = cosA +i*sinA
|
269
|
+
* ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
|
270
|
+
* atan2(y,x) computes the principal value of the argument function
|
271
|
+
* applied to the complex number x+iy
|
272
|
+
* Where x = ax*bx + ay*by
|
273
|
+
* y = ax*by - ay*bx
|
274
|
+
*/
|
275
|
+
double ax = node.next->point->x - node.point->x;
|
276
|
+
double ay = node.next->point->y - node.point->y;
|
277
|
+
double bx = node.prev->point->x - node.point->x;
|
278
|
+
double by = node.prev->point->y - node.point->y;
|
279
|
+
return atan2(ax * by - ay * bx, ax * bx + ay * by);
|
280
|
+
}
|
281
|
+
|
282
|
+
/**
|
283
|
+
* Returns true if triangle was legalized
|
284
|
+
*/
|
285
|
+
bool Sweep::Legalize(SweepContext& tcx, Triangle& t)
|
286
|
+
{
|
287
|
+
// To legalize a triangle we start by finding if any of the three edges
|
288
|
+
// violate the Delaunay condition
|
289
|
+
for (int i = 0; i < 3; i++) {
|
290
|
+
if (t.delaunay_edge[i])
|
291
|
+
continue;
|
292
|
+
|
293
|
+
Triangle* ot = t.GetNeighbor(i);
|
294
|
+
|
295
|
+
if (ot) {
|
296
|
+
Point* p = t.GetPoint(i);
|
297
|
+
Point* op = ot->OppositePoint(t, *p);
|
298
|
+
int oi = ot->Index(op);
|
299
|
+
|
300
|
+
// If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
|
301
|
+
// then we should not try to legalize
|
302
|
+
if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) {
|
303
|
+
t.constrained_edge[i] = ot->constrained_edge[oi];
|
304
|
+
continue;
|
305
|
+
}
|
306
|
+
|
307
|
+
bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op);
|
308
|
+
|
309
|
+
if (inside) {
|
310
|
+
// Lets mark this shared edge as Delaunay
|
311
|
+
t.delaunay_edge[i] = true;
|
312
|
+
ot->delaunay_edge[oi] = true;
|
313
|
+
|
314
|
+
// Lets rotate shared edge one vertex CW to legalize it
|
315
|
+
RotateTrianglePair(t, *p, *ot, *op);
|
316
|
+
|
317
|
+
// We now got one valid Delaunay Edge shared by two triangles
|
318
|
+
// This gives us 4 new edges to check for Delaunay
|
319
|
+
|
320
|
+
// Make sure that triangle to node mapping is done only one time for a specific triangle
|
321
|
+
bool not_legalized = !Legalize(tcx, t);
|
322
|
+
if (not_legalized) {
|
323
|
+
tcx.MapTriangleToNodes(t);
|
324
|
+
}
|
325
|
+
|
326
|
+
not_legalized = !Legalize(tcx, *ot);
|
327
|
+
if (not_legalized)
|
328
|
+
tcx.MapTriangleToNodes(*ot);
|
329
|
+
|
330
|
+
// Reset the Delaunay edges, since they only are valid Delaunay edges
|
331
|
+
// until we add a new triangle or point.
|
332
|
+
// XXX: need to think about this. Can these edges be tried after we
|
333
|
+
// return to previous recursive level?
|
334
|
+
t.delaunay_edge[i] = false;
|
335
|
+
ot->delaunay_edge[oi] = false;
|
336
|
+
|
337
|
+
// If triangle have been legalized no need to check the other edges since
|
338
|
+
// the recursive legalization will handles those so we can end here.
|
339
|
+
return true;
|
340
|
+
}
|
341
|
+
}
|
342
|
+
}
|
343
|
+
return false;
|
344
|
+
}
|
345
|
+
|
346
|
+
/**
|
347
|
+
* <b>Requirement</b>:<br>
|
348
|
+
* 1. a,b and c form a triangle.<br>
|
349
|
+
* 2. a and d is know to be on opposite side of bc<br>
|
350
|
+
* <pre>
|
351
|
+
* a
|
352
|
+
* +
|
353
|
+
* / \
|
354
|
+
* / \
|
355
|
+
* b/ \c
|
356
|
+
* +-------+
|
357
|
+
* / d \
|
358
|
+
* / \
|
359
|
+
* </pre>
|
360
|
+
* <b>Fact</b>: d has to be in area B to have a chance to be inside the circle formed by
|
361
|
+
* a,b and c<br>
|
362
|
+
* d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
|
363
|
+
* This preknowledge gives us a way to optimize the incircle test
|
364
|
+
* @param a - triangle point, opposite d
|
365
|
+
* @param b - triangle point
|
366
|
+
* @param c - triangle point
|
367
|
+
* @param d - point opposite a
|
368
|
+
* @return true if d is inside circle, false if on circle edge
|
369
|
+
*/
|
370
|
+
bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd)
|
371
|
+
{
|
372
|
+
double adx = pa.x - pd.x;
|
373
|
+
double ady = pa.y - pd.y;
|
374
|
+
double bdx = pb.x - pd.x;
|
375
|
+
double bdy = pb.y - pd.y;
|
376
|
+
|
377
|
+
double adxbdy = adx * bdy;
|
378
|
+
double bdxady = bdx * ady;
|
379
|
+
double oabd = adxbdy - bdxady;
|
380
|
+
|
381
|
+
if (oabd <= 0)
|
382
|
+
return false;
|
383
|
+
|
384
|
+
double cdx = pc.x - pd.x;
|
385
|
+
double cdy = pc.y - pd.y;
|
386
|
+
|
387
|
+
double cdxady = cdx * ady;
|
388
|
+
double adxcdy = adx * cdy;
|
389
|
+
double ocad = cdxady - adxcdy;
|
390
|
+
|
391
|
+
if (ocad <= 0)
|
392
|
+
return false;
|
393
|
+
|
394
|
+
double bdxcdy = bdx * cdy;
|
395
|
+
double cdxbdy = cdx * bdy;
|
396
|
+
|
397
|
+
double alift = adx * adx + ady * ady;
|
398
|
+
double blift = bdx * bdx + bdy * bdy;
|
399
|
+
double clift = cdx * cdx + cdy * cdy;
|
400
|
+
|
401
|
+
double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd;
|
402
|
+
|
403
|
+
return det > 0;
|
404
|
+
}
|
405
|
+
|
406
|
+
/**
|
407
|
+
* Rotates a triangle pair one vertex CW
|
408
|
+
*<pre>
|
409
|
+
* n2 n2
|
410
|
+
* P +-----+ P +-----+
|
411
|
+
* | t /| |\ t |
|
412
|
+
* | / | | \ |
|
413
|
+
* n1| / |n3 n1| \ |n3
|
414
|
+
* | / | after CW | \ |
|
415
|
+
* |/ oT | | oT \|
|
416
|
+
* +-----+ oP +-----+
|
417
|
+
* n4 n4
|
418
|
+
* </pre>
|
419
|
+
*/
|
420
|
+
void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op)
|
421
|
+
{
|
422
|
+
Triangle* n1, *n2, *n3, *n4;
|
423
|
+
n1 = t.NeighborCCW(p);
|
424
|
+
n2 = t.NeighborCW(p);
|
425
|
+
n3 = ot.NeighborCCW(op);
|
426
|
+
n4 = ot.NeighborCW(op);
|
427
|
+
|
428
|
+
bool ce1, ce2, ce3, ce4;
|
429
|
+
ce1 = t.GetConstrainedEdgeCCW(p);
|
430
|
+
ce2 = t.GetConstrainedEdgeCW(p);
|
431
|
+
ce3 = ot.GetConstrainedEdgeCCW(op);
|
432
|
+
ce4 = ot.GetConstrainedEdgeCW(op);
|
433
|
+
|
434
|
+
bool de1, de2, de3, de4;
|
435
|
+
de1 = t.GetDelunayEdgeCCW(p);
|
436
|
+
de2 = t.GetDelunayEdgeCW(p);
|
437
|
+
de3 = ot.GetDelunayEdgeCCW(op);
|
438
|
+
de4 = ot.GetDelunayEdgeCW(op);
|
439
|
+
|
440
|
+
t.Legalize(p, op);
|
441
|
+
ot.Legalize(op, p);
|
442
|
+
|
443
|
+
// Remap delaunay_edge
|
444
|
+
ot.SetDelunayEdgeCCW(p, de1);
|
445
|
+
t.SetDelunayEdgeCW(p, de2);
|
446
|
+
t.SetDelunayEdgeCCW(op, de3);
|
447
|
+
ot.SetDelunayEdgeCW(op, de4);
|
448
|
+
|
449
|
+
// Remap constrained_edge
|
450
|
+
ot.SetConstrainedEdgeCCW(p, ce1);
|
451
|
+
t.SetConstrainedEdgeCW(p, ce2);
|
452
|
+
t.SetConstrainedEdgeCCW(op, ce3);
|
453
|
+
ot.SetConstrainedEdgeCW(op, ce4);
|
454
|
+
|
455
|
+
// Remap neighbors
|
456
|
+
// XXX: might optimize the markNeighbor by keeping track of
|
457
|
+
// what side should be assigned to what neighbor after the
|
458
|
+
// rotation. Now mark neighbor does lots of testing to find
|
459
|
+
// the right side.
|
460
|
+
t.ClearNeighbors();
|
461
|
+
ot.ClearNeighbors();
|
462
|
+
if (n1) ot.MarkNeighbor(*n1);
|
463
|
+
if (n2) t.MarkNeighbor(*n2);
|
464
|
+
if (n3) t.MarkNeighbor(*n3);
|
465
|
+
if (n4) ot.MarkNeighbor(*n4);
|
466
|
+
t.MarkNeighbor(ot);
|
467
|
+
}
|
468
|
+
|
469
|
+
/**
|
470
|
+
* Fills a basin that has formed on the Advancing Front to the right
|
471
|
+
* of given node.<br>
|
472
|
+
* First we decide a left,bottom and right node that forms the
|
473
|
+
* boundaries of the basin. Then we do a reqursive fill.
|
474
|
+
*
|
475
|
+
* @param tcx
|
476
|
+
* @param node - starting node, this or next node will be left node
|
477
|
+
*/
|
478
|
+
void Sweep::FillBasin(SweepContext& tcx, Node& node)
|
479
|
+
{
|
480
|
+
if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
|
481
|
+
tcx.basin.left_node = node.next->next;
|
482
|
+
} else {
|
483
|
+
tcx.basin.left_node = node.next;
|
484
|
+
}
|
485
|
+
|
486
|
+
// Find the bottom and right node
|
487
|
+
tcx.basin.bottom_node = tcx.basin.left_node;
|
488
|
+
while (tcx.basin.bottom_node->next
|
489
|
+
&& tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) {
|
490
|
+
tcx.basin.bottom_node = tcx.basin.bottom_node->next;
|
491
|
+
}
|
492
|
+
if (tcx.basin.bottom_node == tcx.basin.left_node) {
|
493
|
+
// No valid basin
|
494
|
+
return;
|
495
|
+
}
|
496
|
+
|
497
|
+
tcx.basin.right_node = tcx.basin.bottom_node;
|
498
|
+
while (tcx.basin.right_node->next
|
499
|
+
&& tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) {
|
500
|
+
tcx.basin.right_node = tcx.basin.right_node->next;
|
501
|
+
}
|
502
|
+
if (tcx.basin.right_node == tcx.basin.bottom_node) {
|
503
|
+
// No valid basins
|
504
|
+
return;
|
505
|
+
}
|
506
|
+
|
507
|
+
tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x;
|
508
|
+
tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y;
|
509
|
+
|
510
|
+
FillBasinReq(tcx, tcx.basin.bottom_node);
|
511
|
+
}
|
512
|
+
|
513
|
+
/**
|
514
|
+
* Recursive algorithm to fill a Basin with triangles
|
515
|
+
*
|
516
|
+
* @param tcx
|
517
|
+
* @param node - bottom_node
|
518
|
+
* @param cnt - counter used to alternate on even and odd numbers
|
519
|
+
*/
|
520
|
+
void Sweep::FillBasinReq(SweepContext& tcx, Node* node)
|
521
|
+
{
|
522
|
+
// if shallow stop filling
|
523
|
+
if (IsShallow(tcx, *node)) {
|
524
|
+
return;
|
525
|
+
}
|
526
|
+
|
527
|
+
Fill(tcx, *node);
|
528
|
+
|
529
|
+
if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) {
|
530
|
+
return;
|
531
|
+
} else if (node->prev == tcx.basin.left_node) {
|
532
|
+
Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point);
|
533
|
+
if (o == CW) {
|
534
|
+
return;
|
535
|
+
}
|
536
|
+
node = node->next;
|
537
|
+
} else if (node->next == tcx.basin.right_node) {
|
538
|
+
Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point);
|
539
|
+
if (o == CCW) {
|
540
|
+
return;
|
541
|
+
}
|
542
|
+
node = node->prev;
|
543
|
+
} else {
|
544
|
+
// Continue with the neighbor node with lowest Y value
|
545
|
+
if (node->prev->point->y < node->next->point->y) {
|
546
|
+
node = node->prev;
|
547
|
+
} else {
|
548
|
+
node = node->next;
|
549
|
+
}
|
550
|
+
}
|
551
|
+
|
552
|
+
FillBasinReq(tcx, node);
|
553
|
+
}
|
554
|
+
|
555
|
+
bool Sweep::IsShallow(SweepContext& tcx, Node& node)
|
556
|
+
{
|
557
|
+
double height;
|
558
|
+
|
559
|
+
if (tcx.basin.left_highest) {
|
560
|
+
height = tcx.basin.left_node->point->y - node.point->y;
|
561
|
+
} else {
|
562
|
+
height = tcx.basin.right_node->point->y - node.point->y;
|
563
|
+
}
|
564
|
+
|
565
|
+
// if shallow stop filling
|
566
|
+
if (tcx.basin.width > height) {
|
567
|
+
return true;
|
568
|
+
}
|
569
|
+
return false;
|
570
|
+
}
|
571
|
+
|
572
|
+
void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
573
|
+
{
|
574
|
+
if (tcx.edge_event.right) {
|
575
|
+
FillRightAboveEdgeEvent(tcx, edge, node);
|
576
|
+
} else {
|
577
|
+
FillLeftAboveEdgeEvent(tcx, edge, node);
|
578
|
+
}
|
579
|
+
}
|
580
|
+
|
581
|
+
void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
582
|
+
{
|
583
|
+
while (node->next->point->x < edge->p->x) {
|
584
|
+
// Check if next node is below the edge
|
585
|
+
if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) {
|
586
|
+
FillRightBelowEdgeEvent(tcx, edge, *node);
|
587
|
+
} else {
|
588
|
+
node = node->next;
|
589
|
+
}
|
590
|
+
}
|
591
|
+
}
|
592
|
+
|
593
|
+
void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
594
|
+
{
|
595
|
+
if (node.point->x < edge->p->x) {
|
596
|
+
if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
|
597
|
+
// Concave
|
598
|
+
FillRightConcaveEdgeEvent(tcx, edge, node);
|
599
|
+
} else{
|
600
|
+
// Convex
|
601
|
+
FillRightConvexEdgeEvent(tcx, edge, node);
|
602
|
+
// Retry this one
|
603
|
+
FillRightBelowEdgeEvent(tcx, edge, node);
|
604
|
+
}
|
605
|
+
}
|
606
|
+
}
|
607
|
+
|
608
|
+
void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
609
|
+
{
|
610
|
+
Fill(tcx, *node.next);
|
611
|
+
if (node.next->point != edge->p) {
|
612
|
+
// Next above or below edge?
|
613
|
+
if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) {
|
614
|
+
// Below
|
615
|
+
if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) {
|
616
|
+
// Next is concave
|
617
|
+
FillRightConcaveEdgeEvent(tcx, edge, node);
|
618
|
+
} else {
|
619
|
+
// Next is convex
|
620
|
+
}
|
621
|
+
}
|
622
|
+
}
|
623
|
+
|
624
|
+
}
|
625
|
+
|
626
|
+
void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
627
|
+
{
|
628
|
+
// Next concave or convex?
|
629
|
+
if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) {
|
630
|
+
// Concave
|
631
|
+
FillRightConcaveEdgeEvent(tcx, edge, *node.next);
|
632
|
+
} else{
|
633
|
+
// Convex
|
634
|
+
// Next above or below edge?
|
635
|
+
if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) {
|
636
|
+
// Below
|
637
|
+
FillRightConvexEdgeEvent(tcx, edge, *node.next);
|
638
|
+
} else{
|
639
|
+
// Above
|
640
|
+
}
|
641
|
+
}
|
642
|
+
}
|
643
|
+
|
644
|
+
void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node)
|
645
|
+
{
|
646
|
+
while (node->prev->point->x > edge->p->x) {
|
647
|
+
// Check if next node is below the edge
|
648
|
+
if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) {
|
649
|
+
FillLeftBelowEdgeEvent(tcx, edge, *node);
|
650
|
+
} else {
|
651
|
+
node = node->prev;
|
652
|
+
}
|
653
|
+
}
|
654
|
+
}
|
655
|
+
|
656
|
+
void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
657
|
+
{
|
658
|
+
if (node.point->x > edge->p->x) {
|
659
|
+
if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
|
660
|
+
// Concave
|
661
|
+
FillLeftConcaveEdgeEvent(tcx, edge, node);
|
662
|
+
} else {
|
663
|
+
// Convex
|
664
|
+
FillLeftConvexEdgeEvent(tcx, edge, node);
|
665
|
+
// Retry this one
|
666
|
+
FillLeftBelowEdgeEvent(tcx, edge, node);
|
667
|
+
}
|
668
|
+
}
|
669
|
+
}
|
670
|
+
|
671
|
+
void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
672
|
+
{
|
673
|
+
// Next concave or convex?
|
674
|
+
if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) {
|
675
|
+
// Concave
|
676
|
+
FillLeftConcaveEdgeEvent(tcx, edge, *node.prev);
|
677
|
+
} else{
|
678
|
+
// Convex
|
679
|
+
// Next above or below edge?
|
680
|
+
if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) {
|
681
|
+
// Below
|
682
|
+
FillLeftConvexEdgeEvent(tcx, edge, *node.prev);
|
683
|
+
} else{
|
684
|
+
// Above
|
685
|
+
}
|
686
|
+
}
|
687
|
+
}
|
688
|
+
|
689
|
+
void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node)
|
690
|
+
{
|
691
|
+
Fill(tcx, *node.prev);
|
692
|
+
if (node.prev->point != edge->p) {
|
693
|
+
// Next above or below edge?
|
694
|
+
if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) {
|
695
|
+
// Below
|
696
|
+
if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) {
|
697
|
+
// Next is concave
|
698
|
+
FillLeftConcaveEdgeEvent(tcx, edge, node);
|
699
|
+
} else{
|
700
|
+
// Next is convex
|
701
|
+
}
|
702
|
+
}
|
703
|
+
}
|
704
|
+
|
705
|
+
}
|
706
|
+
|
707
|
+
void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p)
|
708
|
+
{
|
709
|
+
Triangle& ot = t->NeighborAcross(p);
|
710
|
+
Point& op = *ot.OppositePoint(*t, p);
|
711
|
+
|
712
|
+
if (&ot == NULL) {
|
713
|
+
// If we want to integrate the fillEdgeEvent do it here
|
714
|
+
// With current implementation we should never get here
|
715
|
+
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
|
716
|
+
assert(0);
|
717
|
+
}
|
718
|
+
|
719
|
+
if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) {
|
720
|
+
// Lets rotate shared edge one vertex CW
|
721
|
+
RotateTrianglePair(*t, p, ot, op);
|
722
|
+
tcx.MapTriangleToNodes(*t);
|
723
|
+
tcx.MapTriangleToNodes(ot);
|
724
|
+
|
725
|
+
if (p == eq && op == ep) {
|
726
|
+
if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) {
|
727
|
+
t->MarkConstrainedEdge(&ep, &eq);
|
728
|
+
ot.MarkConstrainedEdge(&ep, &eq);
|
729
|
+
Legalize(tcx, *t);
|
730
|
+
Legalize(tcx, ot);
|
731
|
+
} else {
|
732
|
+
// XXX: I think one of the triangles should be legalized here?
|
733
|
+
}
|
734
|
+
} else {
|
735
|
+
Orientation o = Orient2d(eq, op, ep);
|
736
|
+
t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op);
|
737
|
+
FlipEdgeEvent(tcx, ep, eq, t, p);
|
738
|
+
}
|
739
|
+
} else {
|
740
|
+
Point& newP = NextFlipPoint(ep, eq, ot, op);
|
741
|
+
FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP);
|
742
|
+
EdgeEvent(tcx, ep, eq, t, p);
|
743
|
+
}
|
744
|
+
}
|
745
|
+
|
746
|
+
Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op)
|
747
|
+
{
|
748
|
+
if (o == CCW) {
|
749
|
+
// ot is not crossing edge after flip
|
750
|
+
int edge_index = ot.EdgeIndex(&p, &op);
|
751
|
+
ot.delaunay_edge[edge_index] = true;
|
752
|
+
Legalize(tcx, ot);
|
753
|
+
ot.ClearDelunayEdges();
|
754
|
+
return t;
|
755
|
+
}
|
756
|
+
|
757
|
+
// t is not crossing edge after flip
|
758
|
+
int edge_index = t.EdgeIndex(&p, &op);
|
759
|
+
|
760
|
+
t.delaunay_edge[edge_index] = true;
|
761
|
+
Legalize(tcx, t);
|
762
|
+
t.ClearDelunayEdges();
|
763
|
+
return ot;
|
764
|
+
}
|
765
|
+
|
766
|
+
Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op)
|
767
|
+
{
|
768
|
+
Orientation o2d = Orient2d(eq, op, ep);
|
769
|
+
if (o2d == CW) {
|
770
|
+
// Right
|
771
|
+
return *ot.PointCCW(op);
|
772
|
+
} else if (o2d == CCW) {
|
773
|
+
// Left
|
774
|
+
return *ot.PointCW(op);
|
775
|
+
} else{
|
776
|
+
//throw new RuntimeException("[Unsupported] Opposing point on constrained edge");
|
777
|
+
assert(0);
|
778
|
+
}
|
779
|
+
}
|
780
|
+
|
781
|
+
void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle,
|
782
|
+
Triangle& t, Point& p)
|
783
|
+
{
|
784
|
+
Triangle& ot = t.NeighborAcross(p);
|
785
|
+
Point& op = *ot.OppositePoint(t, p);
|
786
|
+
|
787
|
+
if (&t.NeighborAcross(p) == NULL) {
|
788
|
+
// If we want to integrate the fillEdgeEvent do it here
|
789
|
+
// With current implementation we should never get here
|
790
|
+
//throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
|
791
|
+
assert(0);
|
792
|
+
}
|
793
|
+
|
794
|
+
if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) {
|
795
|
+
// flip with new edge op->eq
|
796
|
+
FlipEdgeEvent(tcx, eq, op, &ot, op);
|
797
|
+
// TODO: Actually I just figured out that it should be possible to
|
798
|
+
// improve this by getting the next ot and op before the the above
|
799
|
+
// flip and continue the flipScanEdgeEvent here
|
800
|
+
// set new ot and op here and loop back to inScanArea test
|
801
|
+
// also need to set a new flip_triangle first
|
802
|
+
// Turns out at first glance that this is somewhat complicated
|
803
|
+
// so it will have to wait.
|
804
|
+
} else{
|
805
|
+
Point& newP = NextFlipPoint(ep, eq, ot, op);
|
806
|
+
FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP);
|
807
|
+
}
|
808
|
+
}
|
809
|
+
|
810
|
+
Sweep::~Sweep() {
|
811
|
+
|
812
|
+
// Clean up memory
|
813
|
+
for(int i = 0; i < nodes_.size(); i++) {
|
814
|
+
delete nodes_[i];
|
815
|
+
}
|
816
|
+
|
817
|
+
}
|
818
|
+
|
819
|
+
}
|
820
|
+
|