veloren_world/site/settlement/building/
skeleton.rs1use crate::site::BlockMask;
2use vek::*;
3
4#[derive(Copy, Clone, PartialEq, Eq)]
5pub enum Ori {
6 East,
7 North,
8}
9
10impl Ori {
11 #[must_use]
12 pub fn flip(self) -> Self {
13 match self {
14 Ori::East => Ori::North,
15 Ori::North => Ori::East,
16 }
17 }
18
19 pub fn dir(self) -> Vec2<i32> {
20 match self {
21 Ori::East => Vec2::unit_x(),
22 Ori::North => Vec2::unit_y(),
23 }
24 }
25}
26
27pub struct Branch<T> {
28 pub len: i32,
29 pub attr: T,
30 pub locus: i32,
31 pub border: i32,
32 pub children: Vec<(i32, Branch<T>)>,
33}
34
35impl<T> Branch<T> {
36 fn for_each<'a>(
37 &'a self,
38 node: Vec2<i32>,
39 ori: Ori,
40 is_child: bool,
41 parent_locus: i32,
42 f: &mut impl FnMut(Vec2<i32>, Ori, &'a Branch<T>, bool, i32),
43 ) {
44 f(node, ori, self, is_child, parent_locus);
45 for (offset, child) in &self.children {
46 child.for_each(node + ori.dir() * *offset, ori.flip(), true, self.locus, f);
47 }
48 }
49}
50
51pub struct Skeleton<T> {
52 pub offset: i32,
53 pub ori: Ori,
54 pub root: Branch<T>,
55}
56
57impl<T> Skeleton<T> {
58 pub fn for_each<'a>(&'a self, mut f: impl FnMut(Vec2<i32>, Ori, &'a Branch<T>, bool, i32)) {
59 self.root
60 .for_each(self.ori.dir() * self.offset, self.ori, false, 0, &mut f);
61 }
62
63 pub fn bounds(&self) -> Aabr<i32> {
64 let mut bounds = Aabr::new_empty(self.ori.dir() * self.offset);
65 self.for_each(|node, ori, branch, _, _| {
66 let node2 = node + ori.dir() * branch.len;
67
68 let a = node.map2(node2, |a, b| a.min(b)) - (branch.locus + branch.border);
69 let b = node.map2(node2, |a, b| a.max(b)) + (branch.locus + branch.border);
70 bounds.expand_to_contain_point(a);
71 bounds.expand_to_contain_point(b);
72 });
73 bounds
74 }
75
76 pub fn sample_closest(
77 &self,
78 pos: Vec3<i32>,
79 mut f: impl FnMut(Vec3<i32>, i32, Vec2<i32>, Vec2<i32>, Ori, &Branch<T>) -> BlockMask,
80 ) -> BlockMask {
81 let mut min = None::<(_, BlockMask)>;
82 self.for_each(|node, ori, branch, is_child, parent_locus| {
83 let node2 = node + ori.dir() * branch.len;
84 let node = node
85 + if is_child {
86 ori.dir()
87 * branch.len.signum()
88 * (branch.locus - parent_locus).clamped(0, branch.len.abs())
89 } else {
90 Vec2::zero()
91 };
92 let bounds = Aabr::new_empty(node).expanded_to_contain_point(node2);
93 let bound_offset = if ori == Ori::East {
94 Vec2::new(
95 node.y - pos.y,
96 pos.x - pos.x.clamped(bounds.min.x, bounds.max.x),
97 )
98 } else {
99 Vec2::new(
100 node.x - pos.x,
101 pos.y - pos.y.clamped(bounds.min.y, bounds.max.y),
102 )
103 }
104 .map(|e| e.abs());
105 let center_offset = if ori == Ori::East {
106 Vec2::new(pos.y - bounds.center().y, pos.x - bounds.center().x)
107 } else {
108 Vec2::new(pos.x - bounds.center().x, pos.y - bounds.center().y)
109 };
110 let dist = bound_offset.reduce_max();
111 let dist_locus = dist - branch.locus;
112 let new_bm = f(pos, dist, bound_offset, center_offset, ori, branch);
113 min = min
114 .map(|(_, bm)| (dist_locus, bm.resolve_with(new_bm)))
115 .or(Some((dist_locus, new_bm)));
116 });
117 min.map(|(_, bm)| bm).unwrap_or_else(BlockMask::nothing)
118 }
119}