## Boundary Nodes

As mentioned at the start of this section, the interface for representing boundary nodes allows for support for a contiguous boundary, a segmented boundary, and multiple separate boundaries. (There is also support for more complex geometries, as described in the constrained triangulation section, although features like point location have limited support for e.g. non-convex domains and regions with holes inside holes.) This interface is customisable, and we define the following methods for this.

### Necessary Methods

`DelaunayTriangulation.has_multiple_curves`

— Function`has_multiple_curves(bn::A) where {A}`

Returns `true`

if the given set of boundary nodes `bn`

defines multiple curves, meaning disjoint boundary curves. We currently define the methods

```
has_multiple_curves(::AAA) where {F<:Number,A<:AV{F},AA<:AV{A},AAA<:AV{AA}}
has_multiple_curves(::AA) where {F<:Number,A<:AV{F},AA<:AV{A}}
has_multiple_curves(::A) where {F<:Number,A<:AV{F}}
```

with the first method returning `true`

, while the last two methods return `false`

, and `AV = AbstractVector`

. You can extend this function as you need.

`DelaunayTriangulation.has_multiple_segments`

— Function`has_multiple_segments(bn::A) where {A}`

Returns `true`

if the given set of boundary nodes `bn`

contains multiple segments, meaning disjoint boundary curves. We currently define the methods

```
has_multiple_segments(::AAA) where {F<:Number,A<:AV{F},AA<:AV{A},AAA<:AV{AA}}
has_multiple_segments(::AA) where {F<:Number,A<:AV{F},AA<:AV{A}}
has_multiple_segments(::A) where {F<:Number,A<:AV{F}}
```

with the first and second methods returning `true`

, while the last method returns `false`

, and `AV = AbstractVector`

. You can extend this function as you need.

`DelaunayTriangulation.num_curves`

— Function`num_curves(bn::A)`

Returns the number of `curves`

defined by the boundary nodes `bn`

. We currently define the methods

`num_curves(bn::AAA) where {F<:Number,A<:AV{F},AA<:AV{A},AAA<:AV{AA}}`

which simply returns `bn`

.

`DelaunayTriangulation.num_segments`

— Function`num_segments(bn::A)`

Returns the number of `segments`

defined by the boundary nodes `bn`

. We currently define the method

`num_segments(bn::AA) where {F<:Number,A<:AV{F},AA<:AV{A}}`

which simply returns `length(bn)`

.

`DelaunayTriangulation.num_boundary_edges`

— Function`num_boundary_edges(bn)`

Given a collection of boundary nodes `bn`

, returns the number of edges. This only needs to be defined for individual segments. We define the method

`num_boundary_edges(bn::A) where {A<:AbstractVector}`

which returns `length(bn) - 1`

(`-1`

because it is assumed that `bn[begin] == bn[end]`

). This is the only method that needs to be extended.

See also `getboundarynodes`

.

`DelaunayTriangulation.getboundarynodes`

— Function`getboundarynodes(bn::A, mnℓ...)`

Given a collection of boundary nodes `bn`

, returns the specified component of the collection. There are several forms for the methods. In these methods, it is assumed that one-based indexing is used for accessing all the boundary nodes. If you want to use offsets, for example, then define `getboundarynodes`

appropriately (e.g. maybe `getboundarynodes(bn, m)`

could map `m`

to `m-4`

if `4`

is your offset).

The methods that you need to define are those that go down a level, i.e. from a set of curves to a curve, from a set of segments to a set of nodes, and from a set of nodes to a node. Of course, if you only ever use e.g. a set of nodes, then you need only define that method. The methods that we define for this are

```
getboundarynodes(bn::AAA, m::Integer) where {F<:Number,A<:AV{F},AA<:AV{A},AAA<:AV{AA}}
getboundarynodes(bn::AA, n::Integer) where {F<:Number,A<:AV{F},AA<:AV{A}}
getboundarynodes(bn::A, ℓ::Integer) where {F<:Number,A<:AV{F}}
```

The first method takes a set of curves to the `m`

th curve, the second takes a set of segments to the `n`

th segment, and the third takes a set of nodes to the `ℓ`

th node. These are the only methods that need to be extended. For the set of curves case, we also define

```
getboundarynodes(bn, m::Integer, n::Integer)
getboundarynodes(bn, (m, n)::NTuple{2,Integer})
```

which calls `getboundarynodes(getboundarynodes(bn, m), n)`

. This does not need to be extended. Lastly, we also define

`getboundarynodes(bn::A, ::A) where {A}`

which simply returns `bn`

. This is useful when using the result of `construct_boundary_map`

.

`DelaunayTriangulation.each_boundary_node`

— Function`each_boundary_node(bn::A)`

Returns an iterator that goes over each node in `bn`

. Only defined for single segments so that `bn`

acts like a vector of numbers. The only method currently defined is

`each_boundary_node(bn::A) where {F<:Number,A<:AbstractVector{F}}`

which just returns `bn`

. You can extend this function as you need. If you really want to loop over every boundary node, you can make use of the result from `construct_boundary_map`

.

### Generic Methods

`DelaunayTriangulation.get_boundary_nodes`

— Method`get_boundary_nodes(bn, mnℓ...)`

Get the boundary nodes from `bn`

corresponding to the specified indices. See `getboundarynodes`

.

`DelaunayTriangulation.construct_boundary_map`

— Function`construct_boundary_map(bn; IntegerType::Type{I} = Int) where {I}`

Given a set of boundary nodes `bn`

, returns a `OrderedDict`

that maps boundary indices to their position in `bn`

. In particular:

`has_multiple_curves(bn)`

In this case, the result is a `dict = OrderedDict{I, NTuple{2, I}}`

. The results will be of the form `dict[i] = (m, n)`

, so that boundary indices with value `i`

correspond to nodes at `get_boundary_nodes(bn, m, n)`

, i.e. the `n`

th segment of the `m`

th curve.

`has_multiple_segments(bn)`

In this case, the result is a `dict = OrderedDict{I, I}`

. The results will be of the form `dict[i] = n`

, so that boundary indices with value `i`

correspond to nodes at `get_boundary_nodes(bn, n)`

, i.e. the `n`

th segment.

`else`

Here, the result is a `dict = OrderedDict{I, F}`

, mapping `-1`

back to `bn`

and `F = typeof(bn)`

.

**Iteration Tips**

This dict can be useful for iterating over all boundary nodes. For example, you could do

```
bn_map = construct_boundary_map(bn)
for segment_index in values(bn_map)
nodes = get_boundary_nodes(bn, segment_index)
## Do something with the nodes
end
```

The above will work for any form of `bn`

also.

`DelaunayTriangulation.construct_boundary_index_ranges`

— Function`construct_boundary_index_ranges(boundary_nodes; IntegerType::Type{I}=Int) where {I}`

Given a set of `boundary_nodes`

, creates an `OrderedDict`

that maps boundary indices to the range of all boundary indices that the corresponding boundary curve could correspond to. For example, suppose we have

```
julia> boundary_nodes = [
[
[1, 2, 3, 4], [4, 5, 6, 1]
],
[
[18, 19, 20, 25, 26, 30]
],
[
[50, 51, 52, 53, 54, 55], [55, 56, 57, 58], [58, 101, 103, 105, 107, 120], [120, 121, 122, 50]
]
]
```

Then the first curve, `[[1, 2, 3, 4], [4, 5, 6, 1]]`

has boundary indices `-1`

and `-2`

, so the range would be `-2:-1`

. The full `Dict`

we obtain will be

```
julia> construct_boundary_index_ranges(boundary_nodes)
OrderedDict{Int, UnitRange{Int}} with 7 entries:
-1 => -2:-1
-2 => -2:-1
-3 => -3:-3
-4 => -7:-4
-5 => -7:-4
-6 => -7:-4
-7 => -7:-4
```

`DelaunayTriangulation.map_boundary_index`

— Method`map_boundary_index(dict, i)`

Given a `dict`

from `construct_boundary_map`

, returns `dict[i]`

. Also works for a `dict`

from `construct_boundary_index_ranges`

.

`DelaunayTriangulation.get_curve_index`

— Function```
get_curve_index(dict, i)
get_curve_index(i)
```

Given a `dict`

from `construct_boundary_map`

and a boundary index `i`

, returns the index of the curve corresponding to that boundary index. The second method maps `i`

to `1`

if it is an integer, and `i[1]`

if it is a `Tuple`

.

`DelaunayTriangulation.get_segment_index`

— Function```
get_segment_index(dict, i)
get_segment_index(i)
```

Given a `dict`

from `construct_boundary_map`

and a boundary index `i`

, returns the index of the segment corresponding to that boundary index. The second method maps `i`

to `i`

if it is an integer, `1`

if it is a vector, and `i[2]`

if it is a `Tuple`

.

`DelaunayTriangulation.num_outer_boundary_segments`

— Function`num_outer_boundary_segments(boundary_nodes)`

Given a set of `boundary_nodes`

, returns the number of segments that correspond to the outer boundary. Note that this also gives the range of outer boundary indices, i.e. `-1:-1:-num_outer_boundary_segments(boundary_nodes)`

.

`DelaunayTriangulation.construct_boundary_edge_map`

— Function`construct_boundary_edge_map(bn::A; IntegerType::Type{I}=Int, EdgeType::Type{E}=NTuple{2,IntegerType}) where {A,I,E}`

Constructs a map that takes boundary edges `(i,j)`

to a `Tuple`

giving the edge's position in the corresponding set of boundary nodes.

`DelaunayTriangulation.insert_boundary_node!`

— Function`insert_boundary_node!(bn, pos, node)`

Inserts a boundary node `node`

into the set of boundary nodes `bn`

at the position pos. The first element of `pos`

finds the set of boundary nodes that lie on the segment corresponding to this first element, and then the second element of `pos`

gives the position of the array to insert `node`

into. In particular,

`insert_boundary_node!(bn, pos, node)`

is the same as

`insert!(get_boundary_nodes(bn, pos[1]), pos[2], node)`

`DelaunayTriangulation.delete_boundary_node!`

— Function`delete_boundary_node!(bn, pos)`

Deletes a boundary node from the set of boundary nodes `bn`

at the position pos. The first element of `pos`

finds the set of boundary nodes that lie on the segment corresponding to this first element, and then the second element of `pos`

gives the position of the array to delete. In particular,

`delete_boundary_node!(bn, pos)`

is the same as

`deleteat!(get_boundary_nodes(bn, pos[1]), pos[2])`