-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathMesh.cs
More file actions
1768 lines (1567 loc) · 69.7 KB
/
Mesh.cs
File metadata and controls
1768 lines (1567 loc) · 69.7 KB
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
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// -----------------------------------------------------------------------
// <copyright file="Mesh.cs">
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
// </copyright>
// -----------------------------------------------------------------------
namespace TriangleNet
{
using System;
using System.Collections.Generic;
using TriangleNet.Geometry;
using TriangleNet.Logging;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Data;
using TriangleNet.Meshing.Iterators;
using TriangleNet.Tools;
using TriangleNet.Topology;
/// <summary>
/// Mesh data structure.
/// </summary>
public class Mesh : IMesh
{
#region Variables
IPredicates predicates;
ILog<LogItem> logger;
QualityMesher qualityMesher;
// Stack that maintains a list of recently flipped triangles.
Stack<Otri> flipstack;
// TODO: Check if custom hashmap implementation could be faster.
// Using hashsets for memory management should quite fast.
internal TrianglePool triangles;
internal Dictionary<int, SubSegment> subsegs;
internal Dictionary<int, Vertex> vertices;
// Hash seeds (should belong to mesh instance)
internal int hash_vtx = 0;
internal int hash_seg = 0;
internal int hash_tri = 0;
internal List<Point> holes;
internal List<RegionPointer> regions;
// TODO: remove mesh_dim, invertices and insegments
// Other variables.
internal Rectangle bounds; // x and y bounds.
internal int invertices; // Number of input vertices.
internal int insegments; // Number of input segments.
internal int undeads; // Number of input vertices that don't appear in the mesh.
internal int mesh_dim; // Dimension (ought to be 2).
internal int nextras; // Number of attributes per vertex.
//internal int eextras; // Number of attributes per triangle.
internal int hullsize; // Number of edges in convex hull.
internal int steinerleft; // Number of Steiner points not yet used.
internal bool checksegments; // Are there segments in the triangulation yet?
internal bool checkquality; // Has quality triangulation begun yet?
// Triangular bounding box vertices.
internal Vertex infvertex1, infvertex2, infvertex3;
internal TriangleLocator locator;
// Controls the behavior of the mesh instance.
internal Behavior behavior;
// The current node numbering
internal NodeNumbering numbering;
#endregion
#region Public properties
/// <summary>
/// Gets the mesh bounding box.
/// </summary>
public Rectangle Bounds
{
get { return this.bounds; }
}
/// <summary>
/// Gets the mesh vertices.
/// </summary>
public ICollection<Vertex> Vertices
{
get { return this.vertices.Values; }
}
/// <summary>
/// Gets the mesh holes.
/// </summary>
public IList<Point> Holes
{
get { return this.holes; }
}
/// <summary>
/// Gets the mesh triangles.
/// </summary>
public ICollection<Triangle> Triangles
{
get { return this.triangles; }
}
/// <summary>
/// Gets the mesh segments.
/// </summary>
public ICollection<SubSegment> Segments
{
get { return this.subsegs.Values; }
}
/// <summary>
/// Gets the mesh edges.
/// </summary>
public IEnumerable<Edge> Edges
{
get
{
var e = new EdgeIterator(this);
while (e.MoveNext())
{
yield return e.Current;
}
}
}
/// <summary>
/// Gets the number of input vertices.
/// </summary>
public int NumberOfInputPoints
{
get { return invertices; }
}
/// <summary>
/// Gets the number of mesh edges.
/// </summary>
public int NumberOfEdges
{
get { return (3 * triangles.Count + hullsize) / 2; }
}
/// <summary>
/// Indicates whether the input is a PSLG or a point set.
/// </summary>
public bool IsPolygon
{
get { return this.insegments > 0; }
}
/// <summary>
/// Gets the current node numbering.
/// </summary>
public NodeNumbering CurrentNumbering
{
get { return numbering; }
}
#endregion
#region "Outer space" variables
internal const int DUMMY = -1;
// The triangle that fills "outer space," called 'dummytri', is pointed to
// by every triangle and subsegment on a boundary (be it outer or inner) of
// the triangulation. Also, 'dummytri' points to one of the triangles on
// the convex hull (until the holes and concavities are carved), making it
// possible to find a starting triangle for point location.
// 'dummytri' and 'dummysub' are generally required to fulfill only a few
// invariants: their vertices must remain NULL and 'dummytri' must always
// be bonded (at offset zero) to some triangle on the convex hull of the
// mesh, via a boundary edge. Otherwise, the connections of 'dummytri' and
// 'dummysub' may change willy-nilly. This makes it possible to avoid
// writing a good deal of special-case code (in the edge flip, for example)
// for dealing with the boundary of the mesh, places where no subsegment is
// present, and so forth. Other entities are frequently bonded to
// 'dummytri' and 'dummysub' as if they were real mesh entities, with no
// harm done.
internal Triangle dummytri;
// Set up 'dummysub', the omnipresent subsegment pointed to by any
// triangle side or subsegment end that isn't attached to a real
// subsegment.
internal SubSegment dummysub;
private void Initialize()
{
dummysub = new SubSegment();
dummysub.hash = DUMMY;
// Initialize the two adjoining subsegments to be the omnipresent
// subsegment. These will eventually be changed by various bonding
// operations, but their values don't really matter, as long as they
// can legally be dereferenced.
dummysub.subsegs[0].seg = dummysub;
dummysub.subsegs[1].seg = dummysub;
// Set up 'dummytri', the 'triangle' that occupies "outer space."
dummytri = new Triangle();
dummytri.hash = dummytri.id = DUMMY;
// Initialize the three adjoining triangles to be "outer space." These
// will eventually be changed by various bonding operations, but their
// values don't really matter, as long as they can legally be
// dereferenced.
dummytri.neighbors[0].tri = dummytri;
dummytri.neighbors[1].tri = dummytri;
dummytri.neighbors[2].tri = dummytri;
// Initialize the three adjoining subsegments of 'dummytri' to be
// the omnipresent subsegment.
dummytri.subsegs[0].seg = dummysub;
dummytri.subsegs[1].seg = dummysub;
dummytri.subsegs[2].seg = dummysub;
}
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="Mesh" /> class.
/// </summary>
public Mesh(Configuration config)
{
Initialize();
logger = Log.Instance;
behavior = new Behavior();
vertices = new Dictionary<int, Vertex>();
subsegs = new Dictionary<int, SubSegment>();
triangles = config.TrianglePool();
flipstack = new Stack<Otri>();
holes = new List<Point>();
regions = new List<RegionPointer>();
steinerleft = -1;
this.predicates = config.Predicates();
this.locator = new TriangleLocator(this, predicates);
}
public void Refine(QualityOptions quality, bool delaunay = false)
{
invertices = vertices.Count;
if (behavior.Poly)
{
insegments = behavior.useSegments ? subsegs.Count : hullsize;
}
Reset();
if (qualityMesher == null)
{
qualityMesher = new QualityMesher(this, new Configuration());
}
// Enforce angle and area constraints.
qualityMesher.Apply(quality, delaunay);
}
/// <summary>
/// Renumber vertex and triangle id's.
/// </summary>
public void Renumber()
{
this.Renumber(NodeNumbering.Linear);
}
/// <summary>
/// Renumber vertex and triangle id's.
/// </summary>
public void Renumber(NodeNumbering num)
{
// Don't need to do anything if the nodes are already numbered.
if (num == this.numbering)
{
return;
}
int id;
if (num == NodeNumbering.Linear)
{
id = 0;
foreach (var node in this.vertices.Values)
{
node.id = id++;
}
}
else if (num == NodeNumbering.CuthillMcKee)
{
var rcm = new CuthillMcKee();
var iperm = rcm.Renumber(this);
// Permute the node indices.
foreach (var node in this.vertices.Values)
{
node.id = iperm[node.id];
}
}
// Remember the current numbering.
numbering = num;
// Triangles will always be numbered from 0 to n-1
id = 0;
foreach (var item in this.triangles)
{
item.id = id++;
}
}
#region Misc
/// <summary>
/// Set QualityMesher for mesh refinement.
/// </summary>
/// <param name="qmesher"></param>
internal void SetQualityMesher(QualityMesher qmesher)
{
qualityMesher = qmesher;
}
internal void CopyTo(Mesh target)
{
target.vertices = this.vertices;
target.triangles = this.triangles;
target.subsegs = this.subsegs;
target.holes = this.holes;
target.regions = this.regions;
target.hash_vtx = this.hash_vtx;
target.hash_seg = this.hash_seg;
target.hash_tri = this.hash_tri;
target.numbering = this.numbering;
target.hullsize = this.hullsize;
}
/// <summary>
/// Reset all the mesh data. This method will also wipe
/// out all mesh data.
/// </summary>
private void ResetData()
{
vertices.Clear();
triangles.Restart();
subsegs.Clear();
holes.Clear();
regions.Clear();
this.hash_vtx = 0;
this.hash_seg = 0;
this.hash_tri = 0;
flipstack.Clear();
hullsize = 0;
Reset();
locator.Reset();
}
/// <summary>
/// Reset the mesh triangulation state.
/// </summary>
private void Reset()
{
numbering = NodeNumbering.None;
undeads = 0; // No eliminated input vertices yet.
checksegments = false; // There are no segments in the triangulation yet.
checkquality = false; // The quality triangulation stage has not begun.
Statistic.InCircleCount = 0;
Statistic.CounterClockwiseCount = 0;
Statistic.InCircleAdaptCount = 0;
Statistic.CounterClockwiseAdaptCount = 0;
Statistic.Orient3dCount = 0;
Statistic.HyperbolaCount = 0;
Statistic.CircleTopCount = 0;
Statistic.CircumcenterCount = 0;
}
/// <summary>
/// Read the vertices from memory.
/// </summary>
/// <param name="data">The input data.</param>
internal void TransferNodes(IList<Vertex> points)
{
this.invertices = points.Count;
this.mesh_dim = 2;
this.bounds = new Rectangle();
if (this.invertices < 3)
{
logger.Error("Input must have at least three input vertices.", "Mesh.TransferNodes()");
throw new Exception("Input must have at least three input vertices.");
}
var v = points[0];
#if USE_ATTRIBS
// Check attributes.
this.nextras = v.attributes == null ? 0 : v.attributes.Length;
#endif
// Simple heuristic to check if ids are already set. We assume that if the
// first two vertex ids are distinct, then all input vertices have pairwise
// distinct ids.
bool userId = (v.id != points[1].id);
foreach (var p in points)
{
if (userId)
{
p.hash = p.id;
// Make sure the hash counter gets updated.
hash_vtx = Math.Max(p.hash + 1, hash_vtx);
}
else
{
p.hash = p.id = hash_vtx++;
}
this.vertices.Add(p.hash, p);
this.bounds.Expand(p);
}
}
/// <summary>
/// Construct a mapping from vertices to triangles to improve the speed of
/// point location for segment insertion.
/// </summary>
/// <remarks>
/// Traverses all the triangles, and provides each corner of each triangle
/// with a pointer to that triangle. Of course, pointers will be overwritten
/// by other pointers because (almost) each vertex is a corner of several
/// triangles, but in the end every vertex will point to some triangle
/// that contains it.
/// </remarks>
internal void MakeVertexMap()
{
Otri tri = default(Otri);
Vertex triorg;
foreach (var t in this.triangles)
{
tri.tri = t;
// Check all three vertices of the triangle.
for (tri.orient = 0; tri.orient < 3; tri.orient++)
{
triorg = tri.Org();
triorg.tri = tri;
}
}
}
#endregion
#region Factory
/// <summary>
/// Create a new triangle with orientation zero.
/// </summary>
/// <param name="newotri">Reference to the new triangle.</param>
internal void MakeTriangle(ref Otri newotri)
{
Triangle tri = triangles.Get();
//tri.id = tri.hash;
tri.subsegs[0].seg = dummysub;
tri.subsegs[1].seg = dummysub;
tri.subsegs[2].seg = dummysub;
tri.neighbors[0].tri = dummytri;
tri.neighbors[1].tri = dummytri;
tri.neighbors[2].tri = dummytri;
newotri.tri = tri;
newotri.orient = 0;
}
/// <summary>
/// Create a new subsegment with orientation zero.
/// </summary>
/// <param name="newsubseg">Reference to the new subseg.</param>
internal void MakeSegment(ref Osub newsubseg)
{
var seg = new SubSegment();
seg.hash = this.hash_seg++;
seg.subsegs[0].seg = dummysub;
seg.subsegs[1].seg = dummysub;
seg.triangles[0].tri = dummytri;
seg.triangles[1].tri = dummytri;
newsubseg.seg = seg;
newsubseg.orient = 0;
subsegs.Add(seg.hash, seg);
}
#endregion
#region Manipulation
/// <summary>
/// Insert a vertex into a Delaunay triangulation, performing flips as necessary
/// to maintain the Delaunay property.
/// </summary>
/// <param name="newvertex">The point to be inserted.</param>
/// <param name="searchtri">The triangle to start the search.</param>
/// <param name="splitseg">Segment to split.</param>
/// <param name="segmentflaws">Check for creation of encroached subsegments.</param>
/// <param name="triflaws">Check for creation of bad quality triangles.</param>
/// <returns>If a duplicate vertex or violated segment does not prevent the
/// vertex from being inserted, the return value will be ENCROACHINGVERTEX if
/// the vertex encroaches upon a subsegment (and checking is enabled), or
/// SUCCESSFULVERTEX otherwise. In either case, 'searchtri' is set to a handle
/// whose origin is the newly inserted vertex.</returns>
/// <remarks>
/// The point 'newvertex' is located. If 'searchtri.triangle' is not NULL,
/// the search for the containing triangle begins from 'searchtri'. If
/// 'searchtri.triangle' is NULL, a full point location procedure is called.
/// If 'insertvertex' is found inside a triangle, the triangle is split into
/// three; if 'insertvertex' lies on an edge, the edge is split in two,
/// thereby splitting the two adjacent triangles into four. Edge flips are
/// used to restore the Delaunay property. If 'insertvertex' lies on an
/// existing vertex, no action is taken, and the value DUPLICATEVERTEX is
/// returned. On return, 'searchtri' is set to a handle whose origin is the
/// existing vertex.
///
/// InsertVertex() does not use flip() for reasons of speed; some
/// information can be reused from edge flip to edge flip, like the
/// locations of subsegments.
///
/// Param 'splitseg': Normally, the parameter 'splitseg' is set to NULL,
/// implying that no subsegment should be split. In this case, if 'insertvertex'
/// is found to lie on a segment, no action is taken, and the value VIOLATINGVERTEX
/// is returned. On return, 'searchtri' is set to a handle whose primary edge is the
/// violated subsegment.
/// If the calling routine wishes to split a subsegment by inserting a vertex in it,
/// the parameter 'splitseg' should be that subsegment. In this case, 'searchtri'
/// MUST be the triangle handle reached by pivoting from that subsegment; no point
/// location is done.
///
/// Param 'segmentflaws': Flags that indicate whether or not there should
/// be checks for the creation of encroached subsegments. If a newly inserted
/// vertex encroaches upon subsegments, these subsegments are added to the list
/// of subsegments to be split if 'segmentflaws' is set.
///
/// Param 'triflaws': Flags that indicate whether or not there should be
/// checks for the creation of bad quality triangles. If bad triangles are
/// created, these are added to the queue if 'triflaws' is set.
/// </remarks>
internal InsertVertexResult InsertVertex(Vertex newvertex, ref Otri searchtri,
ref Osub splitseg, bool segmentflaws, bool triflaws)
{
Otri horiz = default(Otri);
Otri top = default(Otri);
Otri botleft = default(Otri), botright = default(Otri);
Otri topleft = default(Otri), topright = default(Otri);
Otri newbotleft = default(Otri), newbotright = default(Otri);
Otri newtopright = default(Otri);
Otri botlcasing = default(Otri), botrcasing = default(Otri);
Otri toplcasing = default(Otri), toprcasing = default(Otri);
Otri testtri = default(Otri);
Osub botlsubseg = default(Osub), botrsubseg = default(Osub);
Osub toplsubseg = default(Osub), toprsubseg = default(Osub);
Osub brokensubseg = default(Osub);
Osub checksubseg = default(Osub);
Osub rightsubseg = default(Osub);
Osub newsubseg = default(Osub);
BadSubseg encroached;
//FlipStacker newflip;
Vertex first;
Vertex leftvertex, rightvertex, botvertex, topvertex, farvertex;
Vertex segmentorg, segmentdest;
int region;
double area;
InsertVertexResult success;
LocateResult intersect;
bool doflip;
bool mirrorflag;
bool enq;
if (splitseg.seg == null)
{
// Find the location of the vertex to be inserted. Check if a good
// starting triangle has already been provided by the caller.
if (searchtri.tri.id == DUMMY)
{
// Find a boundary triangle.
horiz.tri = dummytri;
horiz.orient = 0;
horiz.Sym();
// Search for a triangle containing 'newvertex'.
intersect = locator.Locate(newvertex, ref horiz);
}
else
{
// Start searching from the triangle provided by the caller.
searchtri.Copy(ref horiz);
intersect = locator.PreciseLocate(newvertex, ref horiz, true);
}
}
else
{
// The calling routine provides the subsegment in which
// the vertex is inserted.
searchtri.Copy(ref horiz);
intersect = LocateResult.OnEdge;
}
if (intersect == LocateResult.OnVertex)
{
// There's already a vertex there. Return in 'searchtri' a triangle
// whose origin is the existing vertex.
horiz.Copy(ref searchtri);
locator.Update(ref horiz);
return InsertVertexResult.Duplicate;
}
if ((intersect == LocateResult.OnEdge) || (intersect == LocateResult.Outside))
{
// The vertex falls on an edge or boundary.
if (checksegments && (splitseg.seg == null))
{
// Check whether the vertex falls on a subsegment.
horiz.Pivot(ref brokensubseg);
if (brokensubseg.seg.hash != DUMMY)
{
// The vertex falls on a subsegment, and hence will not be inserted.
if (segmentflaws)
{
enq = behavior.NoBisect != 2;
if (enq && (behavior.NoBisect == 1))
{
// This subsegment may be split only if it is an
// internal boundary.
horiz.Sym(ref testtri);
enq = testtri.tri.id != DUMMY;
}
if (enq)
{
// Add the subsegment to the list of encroached subsegments.
encroached = new BadSubseg();
encroached.subseg = brokensubseg;
encroached.org = brokensubseg.Org();
encroached.dest = brokensubseg.Dest();
qualityMesher.AddBadSubseg(encroached);
}
}
// Return a handle whose primary edge contains the vertex,
// which has not been inserted.
horiz.Copy(ref searchtri);
locator.Update(ref horiz);
return InsertVertexResult.Violating;
}
}
// Insert the vertex on an edge, dividing one triangle into two (if
// the edge lies on a boundary) or two triangles into four.
horiz.Lprev(ref botright);
botright.Sym(ref botrcasing);
horiz.Sym(ref topright);
// Is there a second triangle? (Or does this edge lie on a boundary?)
mirrorflag = topright.tri.id != DUMMY;
if (mirrorflag)
{
topright.Lnext();
topright.Sym(ref toprcasing);
MakeTriangle(ref newtopright);
}
else
{
// Splitting a boundary edge increases the number of boundary edges.
hullsize++;
}
MakeTriangle(ref newbotright);
// Set the vertices of changed and new triangles.
rightvertex = horiz.Org();
leftvertex = horiz.Dest();
botvertex = horiz.Apex();
newbotright.SetOrg(botvertex);
newbotright.SetDest(rightvertex);
newbotright.SetApex(newvertex);
horiz.SetOrg(newvertex);
// Set the region of a new triangle.
newbotright.tri.label = botright.tri.label;
if (behavior.VarArea)
{
// Set the area constraint of a new triangle.
newbotright.tri.area = botright.tri.area;
}
if (mirrorflag)
{
topvertex = topright.Dest();
newtopright.SetOrg(rightvertex);
newtopright.SetDest(topvertex);
newtopright.SetApex(newvertex);
topright.SetOrg(newvertex);
// Set the region of another new triangle.
newtopright.tri.label = topright.tri.label;
if (behavior.VarArea)
{
// Set the area constraint of another new triangle.
newtopright.tri.area = topright.tri.area;
}
}
// There may be subsegments that need to be bonded
// to the new triangle(s).
if (checksegments)
{
botright.Pivot(ref botrsubseg);
if (botrsubseg.seg.hash != DUMMY)
{
botright.SegDissolve(dummysub);
newbotright.SegBond(ref botrsubseg);
}
if (mirrorflag)
{
topright.Pivot(ref toprsubseg);
if (toprsubseg.seg.hash != DUMMY)
{
topright.SegDissolve(dummysub);
newtopright.SegBond(ref toprsubseg);
}
}
}
// Bond the new triangle(s) to the surrounding triangles.
newbotright.Bond(ref botrcasing);
newbotright.Lprev();
newbotright.Bond(ref botright);
newbotright.Lprev();
if (mirrorflag)
{
newtopright.Bond(ref toprcasing);
newtopright.Lnext();
newtopright.Bond(ref topright);
newtopright.Lnext();
newtopright.Bond(ref newbotright);
}
if (splitseg.seg != null)
{
// Split the subsegment into two.
splitseg.SetDest(newvertex);
segmentorg = splitseg.SegOrg();
segmentdest = splitseg.SegDest();
splitseg.Sym();
splitseg.Pivot(ref rightsubseg);
InsertSubseg(ref newbotright, splitseg.seg.boundary);
newbotright.Pivot(ref newsubseg);
newsubseg.SetSegOrg(segmentorg);
newsubseg.SetSegDest(segmentdest);
splitseg.Bond(ref newsubseg);
newsubseg.Sym();
newsubseg.Bond(ref rightsubseg);
splitseg.Sym();
// Transfer the subsegment's boundary marker to the vertex if required.
if (newvertex.label == 0)
{
newvertex.label = splitseg.seg.boundary;
}
}
if (checkquality)
{
flipstack.Clear();
flipstack.Push(default(Otri)); // Dummy flip (see UndoVertex)
flipstack.Push(horiz);
}
// Position 'horiz' on the first edge to check for
// the Delaunay property.
horiz.Lnext();
}
else
{
// Insert the vertex in a triangle, splitting it into three.
horiz.Lnext(ref botleft);
horiz.Lprev(ref botright);
botleft.Sym(ref botlcasing);
botright.Sym(ref botrcasing);
MakeTriangle(ref newbotleft);
MakeTriangle(ref newbotright);
// Set the vertices of changed and new triangles.
rightvertex = horiz.Org();
leftvertex = horiz.Dest();
botvertex = horiz.Apex();
newbotleft.SetOrg(leftvertex);
newbotleft.SetDest(botvertex);
newbotleft.SetApex(newvertex);
newbotright.SetOrg(botvertex);
newbotright.SetDest(rightvertex);
newbotright.SetApex(newvertex);
horiz.SetApex(newvertex);
// Set the region of the new triangles.
newbotleft.tri.label = horiz.tri.label;
newbotright.tri.label = horiz.tri.label;
if (behavior.VarArea)
{
// Set the area constraint of the new triangles.
area = horiz.tri.area;
newbotleft.tri.area = area;
newbotright.tri.area = area;
}
// There may be subsegments that need to be bonded
// to the new triangles.
if (checksegments)
{
botleft.Pivot(ref botlsubseg);
if (botlsubseg.seg.hash != DUMMY)
{
botleft.SegDissolve(dummysub);
newbotleft.SegBond(ref botlsubseg);
}
botright.Pivot(ref botrsubseg);
if (botrsubseg.seg.hash != DUMMY)
{
botright.SegDissolve(dummysub);
newbotright.SegBond(ref botrsubseg);
}
}
// Bond the new triangles to the surrounding triangles.
newbotleft.Bond(ref botlcasing);
newbotright.Bond(ref botrcasing);
newbotleft.Lnext();
newbotright.Lprev();
newbotleft.Bond(ref newbotright);
newbotleft.Lnext();
botleft.Bond(ref newbotleft);
newbotright.Lprev();
botright.Bond(ref newbotright);
if (checkquality)
{
flipstack.Clear();
flipstack.Push(horiz);
}
}
// The insertion is successful by default, unless an encroached
// subsegment is found.
success = InsertVertexResult.Successful;
if (newvertex.tri.tri != null)
{
// Store the coordinates of the triangle that contains newvertex.
newvertex.tri.SetOrg(rightvertex);
newvertex.tri.SetDest(leftvertex);
newvertex.tri.SetApex(botvertex);
}
// Circle around the newly inserted vertex, checking each edge opposite it
// for the Delaunay property. Non-Delaunay edges are flipped. 'horiz' is
// always the edge being checked. 'first' marks where to stop circling.
first = horiz.Org();
rightvertex = first;
leftvertex = horiz.Dest();
// Circle until finished.
while (true)
{
// By default, the edge will be flipped.
doflip = true;
if (checksegments)
{
// Check for a subsegment, which cannot be flipped.
horiz.Pivot(ref checksubseg);
if (checksubseg.seg.hash != DUMMY)
{
// The edge is a subsegment and cannot be flipped.
doflip = false;
if (segmentflaws)
{
// Does the new vertex encroach upon this subsegment?
if (qualityMesher.CheckSeg4Encroach(ref checksubseg) > 0)
{
success = InsertVertexResult.Encroaching;
}
}
}
}
if (doflip)
{
// Check if the edge is a boundary edge.
horiz.Sym(ref top);
if (top.tri.id == DUMMY)
{
// The edge is a boundary edge and cannot be flipped.
doflip = false;
}
else
{
// Find the vertex on the other side of the edge.
farvertex = top.Apex();
// In the incremental Delaunay triangulation algorithm, any of
// 'leftvertex', 'rightvertex', and 'farvertex' could be vertices
// of the triangular bounding box. These vertices must be
// treated as if they are infinitely distant, even though their
// "coordinates" are not.
if ((leftvertex == infvertex1) || (leftvertex == infvertex2) ||
(leftvertex == infvertex3))
{
// 'leftvertex' is infinitely distant. Check the convexity of
// the boundary of the triangulation. 'farvertex' might be
// infinite as well, but trust me, this same condition should
// be applied.
doflip = predicates.CounterClockwise(newvertex, rightvertex, farvertex) > 0.0;
}
else if ((rightvertex == infvertex1) ||
(rightvertex == infvertex2) ||
(rightvertex == infvertex3))
{
// 'rightvertex' is infinitely distant. Check the convexity of
// the boundary of the triangulation. 'farvertex' might be
// infinite as well, but trust me, this same condition should
// be applied.
doflip = predicates.CounterClockwise(farvertex, leftvertex, newvertex) > 0.0;
}
else if ((farvertex == infvertex1) ||
(farvertex == infvertex2) ||
(farvertex == infvertex3))
{
// 'farvertex' is infinitely distant and cannot be inside
// the circumcircle of the triangle 'horiz'.
doflip = false;
}
else
{
// Test whether the edge is locally Delaunay.
doflip = predicates.InCircle(leftvertex, newvertex, rightvertex, farvertex) > 0.0;
}
if (doflip)
{
// We made it! Flip the edge 'horiz' by rotating its containing
// quadrilateral (the two triangles adjacent to 'horiz').
// Identify the casing of the quadrilateral.
top.Lprev(ref topleft);
topleft.Sym(ref toplcasing);
top.Lnext(ref topright);
topright.Sym(ref toprcasing);
horiz.Lnext(ref botleft);
botleft.Sym(ref botlcasing);
horiz.Lprev(ref botright);
botright.Sym(ref botrcasing);
// Rotate the quadrilateral one-quarter turn counterclockwise.
topleft.Bond(ref botlcasing);
botleft.Bond(ref botrcasing);
botright.Bond(ref toprcasing);