Skip to main content

physdes/
rdllist.rs

1//! Circular doubly-linked list for polygon decomposition algorithms.
2//!
3//! Provides `RDllist`, a circular doubly-linked list specialized for
4//! `Dllink<usize>` nodes. Used internally by rectilinear polygon cut
5//! and hull operations.
6
7use crate::dllink::Dllink;
8
9/// Iterator over the nodes in an `RDllist`.
10///
11/// Traverses the circular list starting from a given node and
12/// stops when it returns to the starting node.
13pub struct RDllIterator {
14    current: *mut Dllink<usize>,
15    stop: *mut Dllink<usize>,
16    started: bool,
17}
18
19impl RDllIterator {
20    fn new(node: *mut Dllink<usize>) -> Self {
21        RDllIterator {
22            current: node,
23            stop: node,
24            started: false,
25        }
26    }
27}
28
29impl Iterator for RDllIterator {
30    type Item = *mut Dllink<usize>;
31
32    fn next(&mut self) -> Option<Self::Item> {
33        if self.current.is_null() {
34            return None;
35        }
36        if !self.started {
37            self.started = true;
38            Some(self.current)
39        } else {
40            unsafe {
41                self.current = (*self.current).next;
42            }
43            if self.current == self.stop {
44                None
45            } else {
46                Some(self.current)
47            }
48        }
49    }
50}
51
52/// A circular doubly-linked list of `Dllink<usize>` nodes.
53///
54/// Nodes are stored in a contiguous `Vec` for cache efficiency.
55/// The list is constructed as a circular chain where each node's
56/// `next` and `prev` point to adjacent nodes, forming a ring.
57///
58/// This structure is used internally by `rpolygon_cut` and
59/// `rpolygon_hull` algorithms which need to dynamically insert
60/// and remove nodes during recursive decomposition.
61pub struct RDllist {
62    /// Storage for all list nodes. The vector is pre-reserved with
63    /// extra capacity (3x initial size) to allow expansion during
64    /// recursive cut operations.
65    pub cycle: Vec<Dllink<usize>>,
66}
67
68impl RDllist {
69    /// Creates a new circular doubly-linked list with `num_nodes` nodes.
70    ///
71    /// The nodes are labeled `0..num_nodes` and linked in a circular chain.
72    /// If `reverse` is `true`, the chain is built in reverse order.
73    ///
74    /// The internal vector is pre-reserved with `3 * num_nodes` capacity
75    /// to accommodate node insertions during recursive decomposition.
76    pub fn new(num_nodes: usize, reverse: bool) -> Self {
77        let mut cycle: Vec<Dllink<usize>> = Vec::with_capacity(3 * num_nodes);
78        for k in 0..num_nodes {
79            cycle.push(Dllink::new_linked(k));
80        }
81
82        let len = cycle.len();
83        if len == 0 {
84            return RDllist { cycle };
85        }
86
87        // Link nodes in a circular chain
88        if !reverse {
89            // Forward order: 0 -> 1 -> 2 -> ... -> N-1 -> 0
90            for i in 0..len {
91                let prev_i = if i == 0 { len - 1 } else { i - 1 };
92                let next_i = if i == len - 1 { 0 } else { i + 1 };
93                let next_ptr: *mut Dllink<usize> = &mut cycle[next_i];
94                let prev_ptr: *mut Dllink<usize> = &mut cycle[prev_i];
95                cycle[i].next = next_ptr;
96                cycle[i].prev = prev_ptr;
97            }
98        } else {
99            // Reverse order: 0 -> N-1 -> N-2 -> ... -> 1 -> 0
100            for i in 0..len {
101                let prev_i = if i == 0 { len - 1 } else { i - 1 };
102                let next_i = if i == len - 1 { 0 } else { i + 1 };
103                // In reverse ordering, prev and next are swapped relative to forward
104                let rev_prev_ptr: *mut Dllink<usize> = &mut cycle[next_i];
105                let rev_next_ptr: *mut Dllink<usize> = &mut cycle[prev_i];
106                cycle[i].next = rev_next_ptr;
107                cycle[i].prev = rev_prev_ptr;
108            }
109        }
110
111        RDllist { cycle }
112    }
113
114    /// Returns a shared reference to the node at index `k`.
115    #[inline]
116    pub fn get(&self, k: usize) -> &Dllink<usize> {
117        &self.cycle[k]
118    }
119
120    /// Returns a mutable reference to the node at index `k`.
121    #[inline]
122    pub fn get_mut(&mut self, k: usize) -> &mut Dllink<usize> {
123        &mut self.cycle[k]
124    }
125
126    /// Creates an iterator starting from node `k`.
127    #[inline]
128    pub fn from_node(&self, k: usize) -> RDllIterator {
129        let ptr: *mut Dllink<usize> = &self.cycle[k] as *const Dllink<usize> as *mut Dllink<usize>;
130        RDllIterator::new(ptr)
131    }
132
133    /// Returns an iterator starting from node 0.
134    #[inline]
135    pub fn iter(&self) -> RDllIterator {
136        self.from_node(0)
137    }
138
139    /// Adds a new linked node with the given data, returning its index.
140    ///
141    /// The node is in locked (self-pointing) state and must be
142    /// spliced into the list by the caller.
143    pub fn push_linked(&mut self, data: usize) -> usize {
144        let idx = self.cycle.len();
145        self.cycle.push(Dllink::new_linked(data));
146        idx
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn test_empty_list() {
156        let list = RDllist::new(0, false);
157        assert_eq!(list.cycle.len(), 0);
158    }
159
160    #[test]
161    fn test_forward_list() {
162        let list = RDllist::new(3, false);
163        let n0 = list.get(0);
164        let n1 = list.get(1);
165        let n2 = list.get(2);
166
167        // Check forward links: 0 -> 1 -> 2 -> 0
168        assert_eq!(unsafe { (*n0.next).data }, 1);
169        assert_eq!(unsafe { (*n1.next).data }, 2);
170        assert_eq!(unsafe { (*n2.next).data }, 0);
171
172        // Check backward links: 0 -> 2 -> 1 -> 0
173        assert_eq!(unsafe { (*n0.prev).data }, 2);
174        assert_eq!(unsafe { (*n1.prev).data }, 0);
175        assert_eq!(unsafe { (*n2.prev).data }, 1);
176    }
177
178    #[test]
179    fn test_reverse_list() {
180        let list = RDllist::new(3, true);
181        let n0 = list.get(0);
182        let n1 = list.get(1);
183        let n2 = list.get(2);
184
185        // Reverse order: 0 -> 2 -> 1 -> 0
186        assert_eq!(unsafe { (*n0.next).data }, 2);
187        assert_eq!(unsafe { (*n1.next).data }, 0);
188        assert_eq!(unsafe { (*n2.next).data }, 1);
189    }
190
191    #[test]
192    fn test_push_linked() {
193        let mut list = RDllist::new(2, false);
194        let idx = list.push_linked(99);
195        assert_eq!(idx, 2);
196        assert!(list.get(2).is_locked());
197        assert_eq!(list.get(2).data, 99);
198    }
199
200    #[test]
201    fn test_capacity_reserved() {
202        let list = RDllist::new(5, false);
203        assert!(list.cycle.capacity() >= 15); // 3 * 5
204    }
205
206    #[test]
207    fn test_iterator_yields_all_nodes() {
208        let list = RDllist::new(5, false);
209        let count: usize = list.from_node(0).count();
210        assert_eq!(
211            count, 5,
212            "Iterator should yield 5 nodes for a 5-element list"
213        );
214    }
215
216    #[test]
217    fn test_iterator_yields_all_nodes_from_middle() {
218        let list = RDllist::new(5, false);
219        let count: usize = list.from_node(2).count();
220        assert_eq!(
221            count, 5,
222            "Iterator from middle should still yield all 5 nodes"
223        );
224    }
225
226    #[test]
227    fn test_detach_and_iterate() {
228        let mut list = RDllist::new(5, false);
229
230        // Detach node at index 2
231        list.cycle[2].detach();
232
233        // Iterate should now yield 4 nodes (index 2 is detached)
234        let count: usize = list.from_node(0).count();
235        assert_eq!(count, 4, "After detaching one node, should yield 4");
236    }
237
238    #[test]
239    fn test_detach_middle_and_check_links() {
240        let mut list = RDllist::new(5, false);
241
242        // Verify links before detach
243        assert_eq!(unsafe { (*list.cycle[2].next).data }, 3);
244        assert_eq!(unsafe { (*list.cycle[2].prev).data }, 1);
245
246        // Detach node at index 2
247        list.cycle[2].detach();
248        assert!(list.cycle[2].is_locked());
249
250        // Verify node 1 now points to node 3
251        assert_eq!(unsafe { (*list.cycle[1].next).data }, 3);
252        // Verify node 3 now points to node 1
253        assert_eq!(unsafe { (*list.cycle[3].prev).data }, 1);
254    }
255
256    #[test]
257    fn test_iter_method() {
258        let list: RDllist = RDllist::new(3, false);
259        let mut count = 0;
260        for _ in list.iter() {
261            count += 1;
262        }
263        assert_eq!(count, 3);
264    }
265}