Skip to main content

kasane_logic/spatial_id/flex_id/
ops.rs

1use crate::{FlexId, Side, SpatialId};
2
3impl FlexId {
4    /// 相手の[FlexId]との差集合(self - other)を計算し、イテレータとして返します。
5    /// 空間と時間の両方を考慮し、相手にくり抜かれた「残りの領域」を過不足なく細かい FlexId に分割して返します。
6    pub fn difference(&self, other: &FlexId) -> impl Iterator<Item = FlexId> {
7        let mut results = Vec::new();
8
9        let intersect = match self.intersection(other) {
10            Some(i) => i,
11            None => {
12                results.push(self.clone());
13                return results.into_iter();
14            }
15        };
16
17        if self == &intersect {
18            return results.into_iter();
19        }
20
21        let mut current = self.clone();
22
23        while current.f_zoomlevel < intersect.f_zoomlevel {
24            let lower = current.f_split(Side::Lower).unwrap();
25            let upper = current.f_split(Side::Upper).unwrap();
26            if lower.intersection(&intersect).is_some() {
27                results.push(upper);
28                current = lower;
29            } else {
30                results.push(lower);
31                current = upper;
32            }
33        }
34
35        // X軸の分割
36        while current.x_zoomlevel < intersect.x_zoomlevel {
37            let lower = current.x_split(Side::Lower).unwrap();
38            let upper = current.x_split(Side::Upper).unwrap();
39
40            if lower.intersection(&intersect).is_some() {
41                results.push(upper);
42                current = lower;
43            } else {
44                results.push(lower);
45                current = upper;
46            }
47        }
48
49        // Y軸の分割
50        while current.y_zoomlevel < intersect.y_zoomlevel {
51            let lower = current.y_split(Side::Lower).unwrap();
52            let upper = current.y_split(Side::Upper).unwrap();
53
54            if lower.intersection(&intersect).is_some() {
55                results.push(upper);
56                current = lower;
57            } else {
58                results.push(lower);
59                current = upper;
60            }
61        }
62
63        for t_diff in current.temporal().difference(other.temporal()) {
64            results.push(FlexId {
65                f_zoomlevel: current.f_zoomlevel,
66                f_index: current.f_index,
67                x_zoomlevel: current.x_zoomlevel,
68                x_index: current.x_index,
69                y_zoomlevel: current.y_zoomlevel,
70                y_index: current.y_index,
71                temporal_id: t_diff,
72            });
73        }
74
75        results.into_iter()
76    }
77
78    /// 2つのFlexIdの重なっている領域(Intersection)を計算して返します。
79    /// 重なりがない場合は None を返します。
80    pub fn intersection(&self, other: &FlexId) -> Option<FlexId> {
81        let (f_z, f_i) = Self::intersect_axis_i32(
82            self.f_zoomlevel,
83            self.f_index,
84            other.f_zoomlevel,
85            other.f_index,
86        )?;
87
88        let (x_z, x_i) = Self::intersect_axis_u32(
89            self.x_zoomlevel,
90            self.x_index,
91            other.x_zoomlevel,
92            other.x_index,
93        )?;
94
95        let (y_z, y_i) = Self::intersect_axis_u32(
96            self.y_zoomlevel,
97            self.y_index,
98            other.y_zoomlevel,
99            other.y_index,
100        )?;
101
102        let temporal_id = self.temporal().intersection(other.temporal())?;
103
104        Some(FlexId {
105            f_zoomlevel: f_z,
106            f_index: f_i,
107            x_zoomlevel: x_z,
108            x_index: x_i,
109            y_zoomlevel: y_z,
110            y_index: y_i,
111            temporal_id,
112        })
113    }
114
115    fn intersect_axis_i32(z1: u8, i1: i32, z2: u8, i2: i32) -> Option<(u8, i32)> {
116        let (deep_z, deep_i, shallow_z, shallow_i) = if z1 > z2 {
117            (z1, i1, z2, i2)
118        } else {
119            (z2, i2, z1, i1)
120        };
121
122        let shift = deep_z - shallow_z;
123
124        if (deep_i >> shift) == shallow_i {
125            Some((deep_z, deep_i))
126        } else {
127            None
128        }
129    }
130
131    fn intersect_axis_u32(z1: u8, i1: u32, z2: u8, i2: u32) -> Option<(u8, u32)> {
132        let (deep_z, deep_i, shallow_z, shallow_i) = if z1 > z2 {
133            (z1, i1, z2, i2)
134        } else {
135            (z2, i2, z1, i1)
136        };
137
138        let shift = deep_z - shallow_z;
139        if (deep_i >> shift) == shallow_i {
140            Some((deep_z, deep_i))
141        } else {
142            None
143        }
144    }
145}