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
//! This module contains implementation of a edge-based visibility graph used
//! in shortest path search on the game map.

use parry2d::shape::Segment;
use tinyvec::ArrayVec;

/// Edge based visibility sub-graph.
///
/// Be careful to distinguish between triangle edges and graph edges:
///
/// * triangle edge - a geometric edge of a triangle (each triangle has 3).
///
/// * graph node - a node in a graph. In the case of `VisibilityGraph`, each
///   graph node represents a triangle edge.
///
/// * graph edge - an edge between two nodes in a graph. In the case of
///   `VisibilityGraph`, each graph edge represents direct visibility between
///   two triangle edges.
///
///   If triangle edges A and B are connected by a graph edge (i.e. are
///   neighbours in `VisibilityGraph`), then any point on triangle edge A is
///   accessible (visible) on straight line from any point on triangle B.
///
///   The opposite statement doesn't always hold: two triangle edges might be
///   fully visible one from another and not share a graph edge. However, such
///   triangles are always connected by a graph path.
pub struct VisibilityGraph {
    nodes: Vec<GraphNode>,
}

impl VisibilityGraph {
    /// Returns a new empty visibility graph.
    pub(super) fn new() -> Self {
        Self { nodes: Vec::new() }
    }

    /// Returns number of nodes in the visibility graph.
    pub(super) fn len(&self) -> usize {
        self.nodes.len()
    }

    /// Prepares a graph node without any neighbours, pushes it to the graph
    /// and returns corresponding edge (node) ID.
    ///
    /// Only single node must be created when multiple triangles share an edge
    /// (have coincidental edge line segment).
    ///
    /// # Arguments
    ///
    /// * `segment` - line segment of the triangle edge.
    pub(super) fn new_node(&mut self, segment: Segment) -> u32 {
        let id = self.nodes.len().try_into().unwrap();
        self.nodes.push(GraphNode::new(segment));
        id
    }

    /// Adds 2 neighbours accessible via one of the adjacent triangles to a
    /// graph node (triangle edge).
    ///
    /// # Arguments
    ///
    /// * `edge_id` - ID of the edge whose neighbors are added.
    ///
    /// * `triangle_id` - ID of the traversed triangle (i.e. the triangle
    ///   containing the source and target edges).
    ///
    /// * `neighbour_a_id` - edge ID of the a neighbor.
    ///
    /// * `neighbour_b_id` - edge ID of the a neighbor.
    ///
    /// # Panics
    ///
    /// Panics if `edge_id` already stores more than two neighbours.
    pub(super) fn add_neighbours(
        &mut self,
        edge_id: u32,
        triangle_id: u32,
        neighbour_a_id: u32,
        neighbour_b_id: u32,
    ) {
        let index: usize = edge_id.try_into().unwrap();
        let node = self.nodes.get_mut(index).unwrap();
        node.add_neighbour(Step::new(neighbour_a_id, triangle_id));
        node.add_neighbour(Step::new(neighbour_b_id, triangle_id));
    }

    /// Returns a geometry of a graph node (triangle edge).
    pub(super) fn segment(&self, edge_id: u32) -> Segment {
        let index: usize = edge_id.try_into().unwrap();
        self.nodes[index].segment()
    }

    /// Returns all neighbors of a graph node (triangle edge).
    pub(super) fn neighbours(&self, edge_id: u32) -> &[Step] {
        let index: usize = edge_id.try_into().unwrap();
        self.nodes[index].neighbours()
    }
}

/// A node in the visibility graph.
struct GraphNode {
    segment: Segment,
    /// Graph steps to reach direct neighbors.
    neighbours: ArrayVec<[Step; 4]>,
}

impl GraphNode {
    fn new(segment: Segment) -> Self {
        Self {
            segment,
            neighbours: ArrayVec::new(),
        }
    }

    fn segment(&self) -> Segment {
        self.segment
    }

    fn neighbours(&self) -> &[Step] {
        self.neighbours.as_slice()
    }

    /// Adds a neighbor to the node.
    ///
    /// Each node can store up to 4 neighbors.
    ///
    /// # Panics
    ///
    /// * If the number of already stored neighbors is 4.
    ///
    /// * If the number of already stored triangles is 2.
    fn add_neighbour(&mut self, step: Step) {
        self.neighbours.push(step);
    }
}

/// A step in the triangle edge neighbor graph. Id est triangle traversal from
/// a set of points in the triangle (one point or a line segment) to (part of)
/// an edge of the triangle.
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
pub(super) struct Step {
    edge_id: u32,
    triangle_id: u32,
}

impl Step {
    pub(super) fn new(edge_id: u32, triangle_id: u32) -> Self {
        Self {
            edge_id,
            triangle_id,
        }
    }

    /// A target edge ID (reached from neighboring edge).
    pub(super) fn edge_id(&self) -> u32 {
        self.edge_id
    }

    /// ID of the traversed triangle (to reach [`Self::edge_id()`].
    pub(super) fn triangle_id(&self) -> u32 {
        self.triangle_id
    }
}

#[cfg(test)]
mod tests {
    use parry2d::math::Point;

    use super::*;

    #[test]
    fn test_graph() {
        let mut graph = VisibilityGraph::new();

        let edge_id_a = graph.new_node(Segment::new(Point::new(1., 2.), Point::new(2., 3.)));
        let edge_id_b = graph.new_node(Segment::new(Point::new(2., 3.), Point::new(5., 6.)));
        let edge_id_c = graph.new_node(Segment::new(Point::new(5., 6.), Point::new(1., 2.)));
        graph.add_neighbours(edge_id_a, 1, edge_id_b, edge_id_c);
        graph.add_neighbours(edge_id_b, 1, edge_id_c, edge_id_a);

        assert_eq!(
            graph.neighbours(edge_id_a),
            &[Step::new(edge_id_b, 1), Step::new(edge_id_c, 1)]
        );
        assert_eq!(
            graph.neighbours(edge_id_b),
            &[Step::new(edge_id_c, 1), Step::new(edge_id_a, 1)]
        );
        assert_eq!(graph.neighbours(edge_id_c), &[] as &[Step]);
    }
}