Skip to main content

tess2_rust/
dict.rs

1// Copyright 2025 Lars Brubaker
2// License: SGI Free Software License B (MIT-compatible)
3//
4// Port of libtess2 dict.c/h
5//
6// A sorted doubly-linked list (dictionary) used by the sweep algorithm
7// to maintain the active edge set ordered by the comparison function.
8//
9// In C, keys are ActiveRegion*. Here keys are u32 (ActiveRegion index).
10// INVALID = u32::MAX represents a null key (sentinel nodes).
11
12use crate::mesh::INVALID;
13
14/// Index into Dict::nodes
15pub type NodeIdx = u32;
16
17#[derive(Clone, Debug)]
18pub struct DictNode {
19    pub key: u32, // ActiveRegion index, or INVALID for sentinel
20    pub next: NodeIdx,
21    pub prev: NodeIdx,
22}
23
24impl Default for DictNode {
25    fn default() -> Self {
26        DictNode {
27            key: INVALID,
28            next: INVALID,
29            prev: INVALID,
30        }
31    }
32}
33
34/// A sorted doubly-linked list dictionary.
35/// The comparison function takes (frame_data, key1, key2) and returns key1 <= key2.
36// The "head" sentinel node is always at index 0.
37// It forms a circular list: head.prev == head.next == head when empty.
38pub struct Dict {
39    pub nodes: Vec<DictNode>,
40}
41
42/// Index of the head sentinel node.
43pub const DICT_HEAD: NodeIdx = 0;
44
45impl Dict {
46    pub fn new() -> Self {
47        let mut head = DictNode::default();
48        head.key = INVALID;
49        head.next = DICT_HEAD;
50        head.prev = DICT_HEAD;
51
52        Dict { nodes: vec![head] }
53    }
54
55    /// dictInsert: insert a key at the back (before the head sentinel).
56    pub fn insert<F>(&mut self, key: u32, leq: &F) -> NodeIdx
57    where
58        F: Fn(u32, u32) -> bool,
59    {
60        self.insert_before(DICT_HEAD, key, leq)
61    }
62
63    /// dictInsertBefore: insert key before `node`, walking backward to find the
64    /// correct sorted position.
65    pub fn insert_before<F>(&mut self, mut node: NodeIdx, key: u32, leq: &F) -> NodeIdx
66    where
67        F: Fn(u32, u32) -> bool,
68    {
69        // Walk backward until we find a node whose key <= key, or hit the sentinel
70        loop {
71            node = self.nodes[node as usize].prev;
72            let node_key = self.nodes[node as usize].key;
73            if node_key == INVALID || leq(node_key, key) {
74                break;
75            }
76        }
77
78        let new_idx = self.nodes.len() as NodeIdx;
79        let next_node = self.nodes[node as usize].next;
80
81        let new_node = DictNode {
82            key,
83            next: next_node,
84            prev: node,
85        };
86
87        self.nodes.push(new_node);
88        self.nodes[node as usize].next = new_idx;
89        self.nodes[next_node as usize].prev = new_idx;
90
91        new_idx
92    }
93
94    /// dictDelete: remove a node from the dictionary.
95    pub fn delete(&mut self, node: NodeIdx) {
96        let next = self.nodes[node as usize].next;
97        let prev = self.nodes[node as usize].prev;
98        self.nodes[next as usize].prev = prev;
99        self.nodes[prev as usize].next = next;
100        // Mark as deleted
101        self.nodes[node as usize].next = INVALID;
102        self.nodes[node as usize].prev = INVALID;
103        self.nodes[node as usize].key = INVALID;
104    }
105
106    /// dictSearch: find the first node with key >= given key.
107    pub fn search<F>(&self, key: u32, leq: &F) -> NodeIdx
108    where
109        F: Fn(u32, u32) -> bool,
110    {
111        let mut node = DICT_HEAD;
112        loop {
113            node = self.nodes[node as usize].next;
114            let node_key = self.nodes[node as usize].key;
115            if node_key == INVALID || leq(key, node_key) {
116                return node;
117            }
118        }
119    }
120
121    /// dictKey: get the key of a node.
122    #[inline]
123    pub fn key(&self, node: NodeIdx) -> u32 {
124        self.nodes[node as usize].key
125    }
126
127    /// dictMin: first real node (after sentinel).
128    #[inline]
129    pub fn min(&self) -> NodeIdx {
130        self.nodes[DICT_HEAD as usize].next
131    }
132
133    /// dictMax: last real node (before sentinel, via prev).
134    #[inline]
135    pub fn max(&self) -> NodeIdx {
136        self.nodes[DICT_HEAD as usize].prev
137    }
138
139    /// dictSucc: successor of a node.
140    #[inline]
141    pub fn succ(&self, node: NodeIdx) -> NodeIdx {
142        self.nodes[node as usize].next
143    }
144
145    /// dictPred: predecessor of a node.
146    #[inline]
147    pub fn pred(&self, node: NodeIdx) -> NodeIdx {
148        self.nodes[node as usize].prev
149    }
150}
151
152impl Default for Dict {
153    fn default() -> Self {
154        Self::new()
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    fn leq(a: u32, b: u32) -> bool {
163        a <= b
164    }
165
166    #[test]
167    fn empty_dict() {
168        let d = Dict::new();
169        assert_eq!(d.min(), DICT_HEAD);
170        assert_eq!(d.max(), DICT_HEAD);
171    }
172
173    #[test]
174    fn insert_and_order() {
175        let mut d = Dict::new();
176        d.insert(3, &leq);
177        d.insert(1, &leq);
178        d.insert(2, &leq);
179
180        // Should be in ascending order: 1, 2, 3
181        let n1 = d.min();
182        assert_eq!(d.key(n1), 1);
183        let n2 = d.succ(n1);
184        assert_eq!(d.key(n2), 2);
185        let n3 = d.succ(n2);
186        assert_eq!(d.key(n3), 3);
187        let n_end = d.succ(n3);
188        assert_eq!(n_end, DICT_HEAD);
189    }
190
191    #[test]
192    fn delete_node() {
193        let mut d = Dict::new();
194        d.insert(1, &leq);
195        let n2 = d.insert(2, &leq);
196        d.insert(3, &leq);
197
198        d.delete(n2);
199
200        let n1 = d.min();
201        assert_eq!(d.key(n1), 1);
202        let n3 = d.succ(n1);
203        assert_eq!(d.key(n3), 3);
204        assert_eq!(d.succ(n3), DICT_HEAD);
205    }
206
207    #[test]
208    fn search_finds_first_geq() {
209        let mut d = Dict::new();
210        d.insert(1, &leq);
211        d.insert(3, &leq);
212        d.insert(5, &leq);
213
214        let n = d.search(2, &leq);
215        assert_eq!(d.key(n), 3);
216
217        let n2 = d.search(3, &leq);
218        assert_eq!(d.key(n2), 3);
219
220        let n3 = d.search(6, &leq);
221        assert_eq!(n3, DICT_HEAD); // Not found → sentinel
222    }
223}