Another project
1use bone_types::{FeatureId, SketchId};
2use serde::{Deserialize, Serialize};
3use slotmap::{Key, KeyData};
4
5#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
6pub enum PrincipalPlane {
7 Xy,
8 Yz,
9 Zx,
10}
11
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub enum FeatureNode {
14 Origin,
15 PrincipalPlane(PrincipalPlane),
16 Sketch(SketchId),
17}
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(deny_unknown_fields)]
21struct FeatureEntry {
22 id: FeatureId,
23 node: FeatureNode,
24}
25
26#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
27#[serde(deny_unknown_fields)]
28pub struct FeatureTree {
29 entries: Vec<FeatureEntry>,
30}
31
32impl FeatureTree {
33 #[must_use]
34 pub fn seeded() -> Self {
35 let seeds = [
36 FeatureNode::Origin,
37 FeatureNode::PrincipalPlane(PrincipalPlane::Xy),
38 FeatureNode::PrincipalPlane(PrincipalPlane::Yz),
39 FeatureNode::PrincipalPlane(PrincipalPlane::Zx),
40 ];
41 let entries = seeds
42 .into_iter()
43 .zip(1u32..)
44 .map(|(node, idx)| FeatureEntry {
45 id: feature_id_from_idx(idx),
46 node,
47 })
48 .collect();
49 Self { entries }
50 }
51
52 #[must_use]
53 pub fn node(&self, id: FeatureId) -> Option<FeatureNode> {
54 self.entries.iter().find(|e| e.id == id).map(|e| e.node)
55 }
56
57 pub fn iter(&self) -> impl Iterator<Item = (FeatureId, FeatureNode)> + '_ {
58 self.entries.iter().map(|e| (e.id, e.node))
59 }
60
61 #[must_use]
62 pub fn feature_of_sketch(&self, sketch: SketchId) -> Option<FeatureId> {
63 self.entries
64 .iter()
65 .find(|e| matches!(e.node, FeatureNode::Sketch(s) if s == sketch))
66 .map(|e| e.id)
67 }
68
69 pub fn push_sketch(&mut self, sketch: SketchId) -> FeatureId {
70 if let Some(existing) = self.feature_of_sketch(sketch) {
71 return existing;
72 }
73 let id = self.allocate();
74 self.entries.push(FeatureEntry {
75 id,
76 node: FeatureNode::Sketch(sketch),
77 });
78 id
79 }
80
81 pub fn remove_sketch(&mut self, sketch: SketchId) -> Option<FeatureId> {
82 let id = self.feature_of_sketch(sketch)?;
83 self.entries.retain(|e| e.id != id);
84 Some(id)
85 }
86
87 fn allocate(&self) -> FeatureId {
88 let highest = self.entries.iter().map(|e| idx_of(e.id)).max().unwrap_or(0);
89 let Some(next) = highest.checked_add(1) else {
90 panic!("FeatureTree exhausted 32-bit feature id space");
91 };
92 feature_id_from_idx(next)
93 }
94}
95
96fn feature_id_from_idx(idx: u32) -> FeatureId {
97 FeatureId::from(KeyData::from_ffi((1u64 << 32) | u64::from(idx)))
98}
99
100fn idx_of(id: FeatureId) -> u32 {
101 let Ok(idx) = u32::try_from(id.data().as_ffi() & 0xFFFF_FFFF) else {
102 panic!("lower 32 bits of ffi key fit in u32");
103 };
104 idx
105}