Triangulation Output
In this section, we discuss the output given from triangulate
. Let's take a curve-bounded domain and inspect its output in detail; for information about this domain in particular, see the curve-bounded domain tutorial. First, here is the triangulation.
using DelaunayTriangulation
using StableRNGs
using DelaunayTriangulation: EllipticalArc
curve = [
[
[1, 2, 3], [EllipticalArc((2.0, 0.0), (-2.0, 0.0), (0.0, 0.0), 2, 1 / 2, 0.0)]
],
[
[BSpline([(0.0, 0.4), (1.0, 0.2), (0.0, 0.1), (-1.0, 0.2), (0.0, 0.4)])]
],
[
[4, 5, 6, 7, 4]
],
[
[BezierCurve([(0.0, -2.0), (0.0, -2.5), (-1.0, -2.5), (-1.0, -3.0)])], [CatmullRomSpline([(-1.0, -3.0), (0.0, -4.0), (1.0, -3.0), (0.0, -2.0)])]
],
[
[12, 11, 10, 12]
],
[
[CircularArc((1.1, -3.0), (1.1, -3.0), (0.0, -3.0), positive=false)]
]
]
points = [(-2.0, 0.0), (0.0, 0.0), (2.0, 0.0), (-2.0, -5.0), (2.0, -5.0), (2.0, -1 / 10), (-2.0, -1 / 10), (-1.0, -3.0), (0.0, -4.0), (0.0, -2.3), (-0.5, -3.5), (0.9, -3.0)]
rng = StableRNG(123)
tri = triangulate(points; boundary_nodes=curve, rng)
refine!(tri; max_area=1e-2, rng)
tri
Delaunay Triangulation.
Number of vertices: 1901
Number of triangles: 3401
Number of edges: 5302
Has boundary nodes: true
Has ghost triangles: true
Curve-bounded: true
Weighted: false
Constrained: true
Now let's inspect tri
. If we look at the fields in tri
, we see that there is a lot of information stored in tri
:
propertynames(tri)
(:points, :triangles, :boundary_nodes, :interior_segments, :all_segments, :weights, :adjacent, :adjacent2vertex, :graph, :boundary_curves, :boundary_edge_map, :ghost_vertex_map, :ghost_vertex_ranges, :convex_hull, :representative_point_list, :polygon_hierarchy, :boundary_enricher, :cache)
Note that each field X
has an associated accessor DelaunayTriangulation.get_X
. Let's go through each field.
Geometry Fields
First, we list the fields relating to the actual geometry.
get_points(tri)
This field stores the points that were triangulated. This may be a mutated version (not a copy, though, tri.points === points
above) of the points provided into tri
, note.
get_points(tri)
2040-element Vector{Tuple{Float64, Float64}}:
(-2.0, 0.0)
(0.0, 0.0)
(2.0, 0.0)
(-2.0, -5.0)
(2.0, -5.0)
(2.0, -0.1)
(-2.0, -0.1)
(-1.0, -3.0)
(0.0, -4.0)
(0.0, -2.3)
⋮
(-0.6691499520988587, -0.36911179341389855)
(1.0467999699355768, -1.1715446627840307)
(1.9552856332240731, -3.0859374999999996)
(-1.6982565243086727, -1.1015931034194555)
(1.081585161425475, -4.197474851713336)
(-0.3000883207300964, -0.7559157191180302)
(0.8408588439570197, -0.41996566069514074)
(-1.5397696504092864, -2.0379365189133525)
(-1.8033813809067116, -0.5255248910528072)
In some cases, the points in this vector will not all appear in tri
, which is why it is recommend you work with the vertices instead, via each_vertex(tri)
or, for the solid or ghost vertices use each_solid_vertex(tri)
or each_ghost_vertex(tri)
, respectively.
get_triangles(tri)
This field stores all the triangles in the triangulation, including both solid and ghost triangles.
get_triangles(tri)
Set{Tuple{Int64, Int64, Int64}} with 3802 elements:
(73, 36, -6)
(1839, 1695, 758)
(2007, 490, 1815)
(412, 254, 411)
(969, 1642, -8)
(1966, 1438, 1439)
(572, 480, 569)
(1774, 1125, 1126)
(1999, 925, 1559)
(1958, 190, 1666)
(1585, 1584, 944)
(1982, 1204, 1288)
(394, 1925, -7)
(683, 1538, -4)
(1800, 788, 1396)
(826, 749, 625)
(1148, 1147, 584)
(252, 135, 175)
(1999, 956, 1156)
⋮
This is also the same as each_triangle(tri)
. If you only wanted the solid triangles, you could use each_solid_triangle(tri)
, and similarly for the ghost triangles with each_ghost_triangle(tri)
. All the triangles in these sets are defined to be counter-clockwise:
using DelaunayTriangulation: is_positively_oriented, triangle_orientation
all(T -> is_positively_oriented(triangle_orientation(get_point(tri, triangle_vertices(T)...)...)), each_solid_triangle(tri))
true
The above check is not true for all the ghost triangles since the domain is non-convex.
get_boundary_nodes(tri)
This field stores the boundary of the triangulation.
get_boundary_nodes(tri)
6-element Vector{Vector{Vector{Int64}}}:
[[1, 487, 59, 1633, 58, 570, 378, 525, 57, 380 … 342, 54, 530, 291, 1447, 55, 1631, 56, 483, 3], [3, 17, 484, 48, 2004, 465, 1449, 60, 1455, 210 … 196, 1533, 50, 539, 478, 2006, 49, 488, 18, 1]]
[[13, 1777, 305, 154, 98, 97, 400, 96, 95, 20 … 21, 86, 87, 409, 88, 89, 102, 109, 1776, 13]]
[[4, 1568, 1030, 1563, 706, 1687, 1026, 1517, 482, 1507 … 1003, 69, 1009, 735, 1374, 707, 1371, 989, 1569, 4]]
[[14, 23, 150, 457, 493, 497, 22, 1596, 469, 452, 450, 24, 8], [8, 40, 41, 42, 43, 44, 79, 80, 78, 77 … 511, 517, 91, 514, 504, 542, 38, 546, 357, 14]]
[[12, 1925, 394, 174, 1677, 354, 65, 576, 302, 460 … 458, 10, 506, 199, 540, 66, 565, 67, 513, 12]]
[[15, 1378, 1057, 921, 1656, 923, 1079, 31, 1048, 918 … 1214, 1615, 34, 1764, 1186, 681, 1699, 1080, 1358, 15]]
Notice that these boundary nodes are not the same as those provided into triangulate
above. Instead of having curve
, triangulate
constructs a new boundary_nodes
vector and populates that with the piecewise linear approximation. You could work with these nodes using get_boundary_nodes
again. For examples, the nodes associated with the first curve are given by
get_boundary_nodes(tri, 1)
2-element Vector{Vector{Int64}}:
[1, 487, 59, 1633, 58, 570, 378, 525, 57, 380 … 342, 54, 530, 291, 1447, 55, 1631, 56, 483, 3]
[3, 17, 484, 48, 2004, 465, 1449, 60, 1455, 210 … 196, 1533, 50, 539, 478, 2006, 49, 488, 18, 1]
The second section of this curve is given by
get_boundary_nodes(tri, 1, 2)
39-element Vector{Int64}:
3
17
484
48
2004
465
1449
60
1455
210
⋮
1533
50
539
478
2006
49
488
18
1
get_interior_segments(tri)
This field stores the interior segments of the triangulation. These are the segments that are not part of the boundary.
get_interior_segments(tri)
Set{Tuple{Int64, Int64}}()
In our case, there are no such segments, but there would be if we had used segments
when calling triangulate
. We could add a segment now, for example
r = DelaunayTriangulation.num_points(tri)
add_point!(tri, -3/2, -4.0, concavity_protection = true; rng)
add_point!(tri, -3/2, -1.0, concavity_protection = true; rng)
add_segment!(tri, r + 1, r + 2; rng)
Delaunay Triangulation.
Number of vertices: 1903
Number of triangles: 3405
Number of edges: 5308
Has boundary nodes: true
Has ghost triangles: true
Curve-bounded: true
Weighted: false
Constrained: true
Now we see that we have some segments:
get_interior_segments(tri)
Set{Tuple{Int64, Int64}} with 1 element:
(2041, 2042)
Note that, for any segment (i, j)
, only one of (i, j)
or (j, i)
will appear in the list of interior segments.
get_all_segments(tri)
In contrast to the interior_segments
field, which only stores the interior segments, this field stores both the interior and boundary segments.
get_all_segments(tri)
Set{Tuple{Int64, Int64}} with 402 elements:
(913, 950)
(1711, 595)
(697, 967)
(117, 118)
(92, 93)
(7, 1437)
(867, 68)
(174, 1677)
(487, 59)
(354, 65)
(36, 73)
(33, 1685)
(103, 2)
(55, 1631)
(115, 114)
(1385, 695)
(82, 444)
(111, 108)
(382, 196)
⋮
Just as for the interior segments, only one of (i, j)
or (j, i)
will appear in the list of all segments.
get_weights(tri)
This field stores the weights associated with each point in the triangulation. By default, you will see a ZeroWeight()
:
get_weights(tri)
DelaunayTriangulation.ZeroWeight{Float64}()
If you do provide weights, this will return those.
Topology Fields
Now we list the fields relating to the topology of the triangulation itself.
get_adjacent(tri)
This field stores the adjacent map, mapping each edge (u, v)
in the triangulation to the vertex w
such that (u, v, w)
is a positively oriented triangle in the triangulation. In cases where there is no such triangle, the vertex 0
is returned. For this triangulation, we have:
get_adjacent(tri)
Adjacent{Int64, Tuple{Int64, Int64}}, with map:
Dict{Tuple{Int64, Int64}, Int64} with 11418 entries:
(697, 967) => 968
(1848, 1068) => 1291
(1215, 671) => 1430
(765, 1803) => 608
(843, 1529) => 1959
(353, 304) => 1958
(717, 1334) => 1915
(1782, 549) => 66
(1653, 777) => 1651
(1386, 723) => 1385
(1410, 1414) => 1574
(1857, 1825) => 736
(267, 123) => 266
(1162, 764) => 1161
(1186, 681) => 1717
(1230, 1389) => 1255
(1987, 675) => 1879
(1649, 1178) => 1863
(1150, 1534) => 481
⋮ => ⋮
For example, the mapping (697, 967) => 968
implies that the triangle (697, 967, 968)
is in the triangulation and is positively oriented, which we can verify:
u, v, w = 697, 967, 968
DelaunayTriangulation.contains_triangle(tri, u, v, w)
((968, 697, 967), true)
It is important to note that, for any triangle (u, v, w)
, the mappings (u, v) => w
, (v, w) => u
, and (w, u) => v
will all appear in the adjacent map. For example:
get_adjacent(tri, u, v), get_adjacent(tri, v, w), get_adjacent(tri, w, u)
(968, 697, 967)
You can use get_adjacent(tri, u, v)
to find this vertex w
, as we demonstrated above. For cases where the edge does not exist, you will get 0
:
get_adjacent(tri, 1, 2)
0
One last thing to notice is that some of the vertices w
will be ghost vertices, and similarly some of the edges (u, v)
will be ghost edges. For example,
get_adjacent(tri, 73, 36)
-6
This vertex -6
means that (73, 36)
is an edge of the boundary associated with the ghost vertex -6
.
get_adjacent2vertex(tri)
The adjacent2vertex
field is similar to adjacent
. The difference is that, instead of mapping edges to vertices, adjacent2vertex
maps vertices w
to the set of all edges (u, v)
such that (u, v, w)
is a positively oriented triangle in the triangulation. For this triangulation, we have:
get_adjacent2vertex(tri)
Adjacent2Vertex{Int64, Set{Tuple{Int64, Int64}}} with map:
Dict{Int64, Set{Tuple{Int64, Int64}}} with 1911 entries:
1144 => Set([(1955, 603), (1143, 658), (744, 1955), (658, 744), (603, 1145), …
1175 => Set([(1775, 1323), (1316, 1315), (1315, 1312), (1312, 1775), (1323, 1…
1953 => Set([(755, 1952), (1876, 755), (1882, 756), (611, 1882), (1952, 611),…
719 => Set([(1864, 1146), (1146, 1660), (666, 1335), (1865, 1864), (1335, 18…
1703 => Set([(476, 261), (196, 476), (382, 196), (261, 381), (381, 382)])
1956 => Set([(796, 582), (1748, 796), (804, 797), (797, 1748), (582, 804)])
1028 => Set([(1640, 1562), (1475, 1405), (1405, 1640), (1562, 1160), (1904, 1…
699 => Set([(800, 812), (796, 798), (812, 1705), (1706, 796), (1705, 1706), …
1299 => Set([(1294, 630), (1832, 632), (630, 1832), (632, 629), (1295, 1294),…
1438 => Set([(7, 1437), (1437, 1439), (619, 7), (1439, 1966), (1966, 619)])
-5 => Set([(8, 24), (457, 150), (450, 452), (497, 493), (23, 14), (469, 159…
1074 => Set([(1077, 938), (2034, 1073), (1073, 1077), (674, 2034), (2015, 674…
319 => Set([(346, 320), (229, 228), (228, 321), (321, 318), (156, 346), (318…
687 => Set([(1542, 1543), (1544, 1428), (799, 1542), (1427, 799), (1543, 154…
1812 => Set([(1052, 1053), (1053, 1916), (1871, 1055), (1055, 1052), (1916, 9…
1199 => Set([(1525, 1198), (1066, 1200), (1200, 1377), (1078, 1525), (1377, 1…
185 => Set([(273, 311), (311, 312), (270, 419), (418, 273), (312, 314), (419…
823 => Set([(866, 1799), (1039, 866), (1589, 1588), (1588, 1590), (1590, 103…
1090 => Set([(1092, 903), (914, 1099), (1098, 1092), (1099, 1098), (903, 1089…
⋮ => ⋮
An example of this mapping is:
get_adjacent2vertex(tri, 700)
Set{Tuple{Int64, Int64}} with 6 elements:
(792, 1935)
(790, 787)
(789, 1693)
(1693, 792)
(787, 789)
(1935, 790)
This output means that (700, 792, 1936)
, (700, 790, 787)
, (700, 789, 1693)
, (700, 1693, 792)
, (700, 1936, 790)
, and (700, 787, 789)
are all positively oriented triangles in the triangulation, and these are the only triangles that contain the vertex 700
. In contrast to get_adjacent
, calling get_adjacent2vertex
on a vertex not in the triangulation will throw a KeyError
. For ghost vertices, you will get the set of all edges on the boundary associated with that vertex, for example
get_adjacent2vertex(tri, -1)
Set{Tuple{Int64, Int64}} with 31 elements:
(2, 103)
(61, 492)
(570, 58)
(378, 570)
(523, 2)
(483, 56)
(138, 285)
(244, 136)
(63, 244)
(342, 138)
(1631, 55)
(530, 54)
(492, 99)
(58, 1633)
(295, 63)
(525, 378)
(55, 1447)
(56, 1631)
(1633, 59)
⋮
gives the set of all edges on the boundary associated with the ghost vertex -1
. It is important to note that the edges in this set are not returned in any particular order, and so you should not rely on the order of the edges in the set.
get_graph(tri)
The last field relating to topology is the graph
, which stores the graph information of the underlying triangulation. In particular, it stores the information about which vertices share an edge. All together, we have three different types of connectivity information being stored in a triangulation:
Adjacent
: The map from edges to vertices.Adjacent2Vertex
: The map from vertices to edges.Graph
: The map from vertices to vertices.
For this triangulation, we have:
get_graph(tri)
Graph
Number of edges: 5713
Number of vertices: 1911
This output by itself of course isn't too useful. The returned Graph
does have its own fields,
propertynames(get_graph(tri))
(:vertices, :edges, :neighbours)
which you can expect using DelaunayTriangulation.get_vertices(tri)
, DelaunayTriangulation.get_edges(tri)
, and get_neighbours(tri)
. Let us go through each of these fields one at a time.
- For the vertices, you should not be inspecting this field directly. Instead, we provide
each_vertex(tri)
,each_solid_vertex(tri)
, andeach_ghost_vertex(tri)
to iterate over the vertices in the triangulation. These functions return iterators. For example,
each_solid_vertex(tri)
EachSolidVertex iterator over vertices in a triangulation.
collect(each_solid_vertex(tri))
1903-element Vector{Int64}:
1144
1175
1953
719
1703
1956
1028
699
1299
1438
⋮
641
887
979
1711
65
1827
298
1402
1115
Since these iterators are derived from Set
s, you should not rely on the particular ordering of vertices returned.
- For the edges, again we recommend you do not inspect this field directly. Instead, we provide
each_edge(tri)
,each_solid_edge(tri)
, andeach_ghost_edge(tri)
to iterate over the edges in the triangulation. These functions return iterators. For example,
each_edge(tri)
Set{Tuple{Int64, Int64}} with 5713 elements:
(1390, 785)
(1519, 1463)
(1936, 1571)
(248, 169)
(697, 967)
(1500, 1495)
(294, 63)
(1848, 1068)
(1215, 671)
(1804, 1803)
(1479, 882)
(353, 304)
(36, 73)
(1599, 1319)
(331, 146)
(1782, 549)
(1653, 777)
(1946, 1265)
(1451, 291)
⋮
collect(each_edge(tri))
5713-element Vector{Tuple{Int64, Int64}}:
(1390, 785)
(1519, 1463)
(1936, 1571)
(248, 169)
(697, 967)
(1500, 1495)
(294, 63)
(1848, 1068)
(1215, 671)
(1804, 1803)
⋮
(1714, 888)
(1667, 349)
(432, 429)
(1686, 1417)
(356, 211)
(192, 179)
(1699, 1080)
(1738, 29)
(1798, 1657)
Since these iterators are derived from Set
s, you should not rely on the particular ordering of edges returned.
- The neighbours are typically the more useful part of
Graph
. Looking atget_neighbours
by itself, you obtain a mapping:
get_neighbours(tri)
Dict{Int64, Set{Int64}} with 1911 entries:
1144 => Set([1145, 744, 603, 1955, 1143, 658])
1175 => Set([1316, 1312, 1315, 1323, 1989, 1775])
1953 => Set([755, 1882, 1876, 756, 1952, 611])
719 => Set([666, 1864, 1335, 1146, 1865, 1660])
1703 => Set([476, 261, 382, 196, 381])
1956 => Set([797, 804, 1748, 796, 582])
1028 => Set([1904, 1405, 1640, 1475, 1160, 1562])
699 => Set([812, 796, 1705, 798, 800, 1706])
1299 => Set([1832, 630, 632, 1294, 1295, 629])
1438 => Set([1437, 7, 1966, 1439, 619])
-5 => Set([24, 8, 23, 450, 22, 497, 469, 14, 1596, 452, 457, 493, 150])
1074 => Set([674, 1073, 1077, 938, 2034, 2015])
319 => Set([318, 229, 346, 321, 156, 228, 320])
687 => Set([799, 1428, 1544, 1427, 1542, 1543])
1812 => Set([924, 1871, 1055, 1052, 1916, 1053])
1199 => Set([1198, 1078, 1377, 1200, 1066, 1525])
185 => Set([273, 312, 314, 419, 418, 311, 270])
823 => Set([1588, 1799, 1589, 1039, 866, 1590])
1090 => Set([914, 1092, 1098, 1089, 1099, 903])
⋮ => ⋮
This mapping shows the neighbours for each vertex in the triangulation. For example,
get_neighbours(tri, 1)
Set{Int64} with 4 elements:
-1
18
487
-2
shows that the vertices sharing edge with 1
are -1
, 18
, 487
, and -2
. These two ghost vertices, -1
and -2
, imply that 1
is on the boundary of the triangulation and is also at the corner of the two sections of the boundary associated with the ghost vertices -1
and -2
. Similarly,
get_neighbours(tri, -2)
Set{Int64} with 39 elements:
484
16
105
465
2004
60
488
17
1
340
83
49
388
335
407
2006
539
478
196
⋮
shows the set of all vertices that are on the boundary associated with the ghost vertex -2
. Once again, the vertices in this set are not returned in any particular order, and so you should not rely on the order of the vertices in the set. If you did want to traverse the boundary in some order, you would need to use get_adjacent
to do so, or use the boundary_nodes
; see, for example, the get_triangulation_area
function in this tutorial. (If you have a node on the boundary you are interested in, DelaunayTriangulation.get_left_boundary_node
and DelaunayTriangulation.get_right_boundary_node
are available.)
Boundary Handling Fields
The next set of fields relate to how the boundary is handled in the triangulation.
get_boundary_curves(tri)
This field is relevant only for curve-bounded domains, like the triangulation in this example. It stores the curves that were used to define the boundary of the triangulation. For our triangulation, we have
get_boundary_curves(tri)
(PiecewiseLinear(), EllipticalArc, BSpline, PiecewiseLinear(), BezierCurve, CatmullRomSpline, PiecewiseLinear(), CircularArc)
This output is not just giving the names of the curves - the actual curves themselves are stored. For example,
get_boundary_curves(tri)[3]
(::BSpline) (generic function with 1 method)
is the actual BSpline
that we have provided. The curves are stored in the same order as they were provided, so that the j
th curve is associated with the j
th section (i.e., the ghost vertex -j
) of the boundary. The parts of the boundary that are a sequence of straight lines between vertices are represented by a PiecewiseLinear
curve. For a triangulation that is not curve-bounded, the output is an empty Tuple
. For example:
tri2 = triangulate(rand(2, 50))
get_boundary_curves(tri2)
()
get_boundary_edge_map(tri)
This field is used to give information about where boundary edges are located in the triangulation. The precise definition is a bit cumbersome to describe: For a given boundary edge (u, v)
, the boundary_edge_map
will map (u, v)
to a tuple of the form (pos, ℓ)
, so that pos
is the position of the edge in the boundary, and ℓ
is the length of the boundary up to the edge. In particular, if get_boundary_edge_map(tri, u, v) == (pos, ℓ)
and bn = get_boundary_nodes(tri, pos)
, then get_boundary_nodes(bn, ℓ) = u
and get_boundary_nodes(bn, ℓ + 1) = v
. For our triangulation, we have
get_boundary_edge_map(tri)
Dict{Tuple{Int64, Int64}, Tuple{Tuple{Int64, Int64}, Int64}} with 401 entries:
(913, 950) => ((3, 1), 37)
(1711, 595) => ((3, 1), 52)
(697, 967) => ((6, 1), 28)
(117, 118) => ((4, 2), 33)
(92, 93) => ((4, 2), 27)
(7, 1437) => ((3, 1), 95)
(867, 68) => ((3, 1), 110)
(174, 1677) => ((5, 1), 4)
(487, 59) => ((1, 1), 2)
(354, 65) => ((5, 1), 6)
(36, 73) => ((4, 2), 20)
(33, 1685) => ((6, 1), 40)
(103, 2) => ((1, 1), 15)
(55, 1631) => ((1, 1), 28)
(115, 114) => ((4, 2), 36)
(1385, 695) => ((3, 1), 114)
(82, 444) => ((5, 1), 17)
(111, 108) => ((4, 2), 39)
(382, 196) => ((1, 2), 29)
⋮ => ⋮
Since our domain has multiple curves, the pos
values are Tuple
s of the form (m, n)
, where m
is the curve and n
is the section of that curve. So, for example, we have
pos, ℓ = get_boundary_edge_map(tri, 913, 950)
((3, 1), 37)
This means that (913, 950)
is the 37
th edge of the first section on the third boundary:
bn = get_boundary_nodes(tri, pos) # notice that we can pass Tuples (m, n) as a single argument
get_boundary_nodes(bn, ℓ), get_boundary_nodes(bn, ℓ + 1)
(913, 950)
For simpler domains, pos
may be either a vector of vertices (in the case of a contiguous boundary) or a single vertex (in the case of a single boundary). For the contiguous case, we have:
tri3 = triangulate_rectangle(0, 1, 0, 1, 10, 10, single_boundary = true)
get_boundary_edge_map(tri3)
Dict{Tuple{Int64, Int64}, Tuple{Vector{Int64}, Int64}} with 36 entries:
(91, 81) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(4, 5) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(1, 2) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(96, 95) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(60, 70) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(9, 10) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(98, 97) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(21, 11) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(100, 99) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(95, 94) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(93, 92) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(30, 40) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(31, 21) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(61, 51) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(7, 8) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(71, 61) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(8, 9) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(3, 4) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
(5, 6) => ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, …
⋮ => ⋮
This may seem strange, but we note that get_boundary_nodes(tri, pos)
, when pos
is a vector of vertices, just returns pos
once again so that get_boundary_nodes(get_boundary_nodes(tri, pos), ℓ) == u
is always true, regardless of the boundary type. For example:
pos, ℓ = get_boundary_edge_map(tri3, 1, 2)
bn = get_boundary_nodes(tri3, pos)
37-element Vector{Int64}:
1
2
3
4
5
6
7
8
9
10
⋮
81
71
61
51
41
31
21
11
1
For the sectioned case, we have:
tri4 = triangulate_rectangle(0, 1, 0, 1, 10, 10, single_boundary = false)
get_boundary_edge_map(tri4)
Dict{Tuple{Int64, Int64}, Tuple{Int64, Int64}} with 36 entries:
(91, 81) => (4, 1)
(4, 5) => (1, 4)
(1, 2) => (1, 1)
(96, 95) => (3, 5)
(60, 70) => (2, 6)
(9, 10) => (1, 9)
(98, 97) => (3, 3)
(21, 11) => (4, 8)
(100, 99) => (3, 1)
(95, 94) => (3, 6)
(93, 92) => (3, 8)
(30, 40) => (2, 3)
(31, 21) => (4, 7)
(61, 51) => (4, 4)
(7, 8) => (1, 7)
(71, 61) => (4, 3)
(8, 9) => (1, 8)
(3, 4) => (1, 3)
(5, 6) => (1, 5)
⋮ => ⋮
When there is no constrained boundary, the boundary edge map will be empty:
tri5 = triangulate(rand(2, 50))
get_boundary_edge_map(tri5)
Dict{Tuple{Int64, Int64}, Tuple{Vector{Int64}, Int64}}()
get_ghost_vertex_map(tri)
This field is used to identify to what curve and to what section a ghost vertex is associated. For example, we have
get_ghost_vertex_map(tri)
Dict{Int64, Tuple{Int64, Int64}} with 8 entries:
-5 => (4, 1)
-1 => (1, 1)
-8 => (6, 1)
-7 => (5, 1)
-3 => (2, 1)
-2 => (1, 2)
-4 => (3, 1)
-6 => (4, 2)
We see, for example, that the ghost vertex -7
comes from the first section of the fifth boundary curve. In general, the mappings are of the form g => pos
, where g
is the ghost vertex and pos
is such that get_boundary_nodes(tri, pos)
is the boundary curve associated with g
. For example, using map_ghost_vertex
,
pos = map_ghost_vertex(tri, -7)
bn = get_boundary_nodes(tri, pos)
33-element Vector{Int64}:
12
1925
394
174
1677
354
65
576
302
460
⋮
10
506
199
540
66
565
67
513
12
The forms of pos
for the case of a contiguous boundary, a sectioned boundary, and no boundary are the same as for get_boundary_edge_map
. For example,
get_ghost_vertex_map(tri3) # contiguous
Dict{Int64, Vector{Int64}} with 1 entry:
-1 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 91, 81, 71, 61, 51, 41, 31, 21, 11, …
get_ghost_vertex_map(tri4) # sectioned
Dict{Int64, Int64} with 4 entries:
-1 => 1
-3 => 3
-2 => 2
-4 => 4
get_ghost_vertex_map(tri5) # no boundary
Dict{Int64, Vector{Int64}} with 1 entry:
-1 => []
get_ghost_vertex_ranges(tri)
This field is used to identify all the ghost vertices associated with a curve that has a specific ghost vertex. For example, we have
get_ghost_vertex_ranges(tri)
Dict{Int64, UnitRange{Int64}} with 8 entries:
-5 => -6:-5
-1 => -2:-1
-8 => -8:-8
-7 => -7:-7
-3 => -3:-3
-2 => -2:-1
-4 => -4:-4
-6 => -6:-5
This output means, for example, that the ghost vertex -5
is associated with a curve that has both ghost vertices -6
and -5
associated with it. You would use get_ghost_vertex_range
to get the range of ghost vertices associated with a specific ghost vertex. For example,
get_ghost_vertex_range(tri, -5)
-6:-5
Similarly, the output for the contiguous, sectioned, and no boundary cases are
get_ghost_vertex_ranges(tri3) # contiguous
Dict{Int64, UnitRange{Int64}} with 1 entry:
-1 => -1:-1
get_ghost_vertex_ranges(tri4) # sectioned
Dict{Int64, UnitRange{Int64}} with 4 entries:
-1 => -4:-1
-3 => -4:-1
-2 => -4:-1
-4 => -4:-1
get_ghost_vertex_ranges(tri5) # no boundary
Dict{Int64, UnitRange{Int64}} with 1 entry:
-1 => -1:-1
Other Fields
There are some other more general fields to inspect.
get_convex_hull(tri)
For all triangulations, the convex_hull
field stores the ConvexHull
of the triangulation. For our triangulation, we have
get_convex_hull(tri)
Convex hull.
Vertices:
24-element Vector{Int64}:
84
16
105
62
51
50
49
18
1
7
⋮
53
52
6
3
17
48
60
83
84
To inspect the vertices of the convex hull, you use get_convex_hull_vertices
:
get_convex_hull_vertices(tri)
24-element Vector{Int64}:
84
16
105
62
51
50
49
18
1
7
⋮
53
52
6
3
17
48
60
83
84
If you ever need to reconstruct the convex hull, say after some dynamic updates, you would use convex_hull!
.
get_representative_point_list(tri)
This field is related to the need for a representative point for each curve, as described in the ghost vertices section. For our triangulation, we have
DelaunayTriangulation.get_representative_point_list(tri)
Dict{Int64, DelaunayTriangulation.RepresentativeCoordinates{Int64, Float64}} with 6 entries:
5 => RepresentativeCoordinates{Int64, Float64}(0.2, -2.9, 0)
4 => RepresentativeCoordinates{Int64, Float64}(0.0654276, -3.01177, 0)
6 => RepresentativeCoordinates{Int64, Float64}(0.0, -3.0, 0)
2 => RepresentativeCoordinates{Int64, Float64}(8.65974e-15, 0.275, 0)
3 => RepresentativeCoordinates{Int64, Float64}(0.0, -2.55, 0)
1 => RepresentativeCoordinates{Int64, Float64}(-0.75, 0.25, 0)
This shows, for example, that the sixth boundary curve's representative point, i.e. its pole of inaccessibility, is the point (0, -3)
. You could also review this using DelaunayTriangulation.get_representative_point_coordinates
:
DelaunayTriangulation.get_representative_point_coordinates(tri, 6)
(0.0, -3.0)
In the case of a triangulation with no constrained boundary, the representative point list is simply the centroid of the domain. If you ever need to recompute the representative points, you need DelaunayTriangulation.compute_representative_points!
.
get_polygon_hierarchy(tri)
This field is used to store information about which boundary curves are contained in other boundary curves. For our triangulation, we have
DelaunayTriangulation.get_polygon_hierarchy(tri)
DelaunayTriangulation.PolygonHierarchy{Int64}(Bool[1, 0, 1, 1, 0, 0], DelaunayTriangulation.BoundingBox[[-2.0000000596046448, 2.0000000596046448] × [-7.450580596923828e-9, 0.5000000074505806], [-0.577350284704563, 0.5773502847045628] × [0.14999999627470972, 0.4000000037252903], [-2.0000000596046448, 2.0000000596046448] × [-5.0000000730156895, -0.09999992698431015], [-1.0000000298023222, 1.0000000124171444] × [-4.000000008207955, -1.9999999701976778], [-0.5000000208616256, 0.9000000208616257] × [-3.5000000178813933, -2.2999999821186066], [-1.1000000327825548, 1.1000000327825548] × [-4.100000032782554, -1.8999999672174452]], Dict{Int64, DelaunayTriangulation.PolygonTree{Int64}}(3 => PolygonTree at height 0 with index 3 and 1 children, 1 => PolygonTree at height 0 with index 1 and 1 children), DelaunayTriangulation.PolygonTree{Int64}[PolygonTree at height 2 with index 4 and 1 children])
This field is not intended for public use. One useful method that makes direct use of the polygon hierarchy is find_polygon
, but the hierarchy's main use is in boundary enrichment for curve-bounded domains, and for checking arguments via check_args
.
boundary_enricher(tri)
This field, just as for the polygon_hierarchy
, is not intended for public use. It is the field used for enriching the boundary for initialising the triangulation of a curve-bounded domain.
DelaunayTriangulation.get_boundary_enricher(tri)
BoundaryEnricher with 8 boundary curves and 2042 points
get_cache(tri)
This field is not intended for public use. It is used to provide several caches for use during triangulation, such as reusing arrays for constrained triangulations.
DelaunayTriangulation.get_cache(tri)
TriangulationCache with storage.