rbpoly2tri 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|