1use rstar::{AABB, PointDistance, RTreeObject};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct SystemId(pub u64);
10
11impl SystemId {
12 pub fn from_address(addr: &nms_core::address::GalacticAddress) -> Self {
14 let packed = addr.packed() & 0x0FFF_FFFF_FFFF;
16 SystemId(packed)
17 }
18}
19
20#[derive(Debug, Clone, Copy, PartialEq)]
22pub struct SystemPoint {
23 pub id: SystemId,
24 pub point: [f64; 3],
25}
26
27impl SystemPoint {
28 pub fn new(id: SystemId, x: f64, y: f64, z: f64) -> Self {
29 Self {
30 id,
31 point: [x, y, z],
32 }
33 }
34
35 pub fn from_address(addr: &nms_core::address::GalacticAddress) -> Self {
37 let id = SystemId::from_address(addr);
38 Self::new(
39 id,
40 addr.voxel_x() as f64,
41 addr.voxel_y() as f64,
42 addr.voxel_z() as f64,
43 )
44 }
45}
46
47impl RTreeObject for SystemPoint {
48 type Envelope = AABB<[f64; 3]>;
49
50 fn envelope(&self) -> Self::Envelope {
51 AABB::from_point(self.point)
52 }
53}
54
55impl PointDistance for SystemPoint {
56 fn distance_2(&self, point: &[f64; 3]) -> f64 {
57 let dx = self.point[0] - point[0];
58 let dy = self.point[1] - point[1];
59 let dz = self.point[2] - point[2];
60 dx * dx + dy * dy + dz * dz
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use nms_core::address::GalacticAddress;
68
69 #[test]
70 fn test_system_id_from_address_zeroes_planet() {
71 let addr1 = GalacticAddress::new(100, 50, 200, 0x123, 0, 0);
72 let addr2 = GalacticAddress::new(100, 50, 200, 0x123, 5, 0);
73 assert_eq!(
74 SystemId::from_address(&addr1),
75 SystemId::from_address(&addr2)
76 );
77 }
78
79 #[test]
80 fn test_system_id_different_ssi_differs() {
81 let addr1 = GalacticAddress::new(100, 50, 200, 0x123, 0, 0);
82 let addr2 = GalacticAddress::new(100, 50, 200, 0x456, 0, 0);
83 assert_ne!(
84 SystemId::from_address(&addr1),
85 SystemId::from_address(&addr2)
86 );
87 }
88
89 #[test]
90 fn test_system_point_from_address_coordinates() {
91 let addr = GalacticAddress::new(100, -50, 200, 0x123, 3, 0);
92 let point = SystemPoint::from_address(&addr);
93 assert_eq!(point.point, [100.0, -50.0, 200.0]);
94 }
95
96 #[test]
97 fn test_system_point_distance_squared() {
98 use rstar::PointDistance;
99 let p = SystemPoint::new(SystemId(0), 0.0, 0.0, 0.0);
100 let target = [3.0, 4.0, 0.0];
101 assert!((p.distance_2(&target) - 25.0).abs() < 1e-10);
102 }
103
104 #[test]
105 fn test_rtree_nearest_neighbor() {
106 use rstar::RTree;
107 let points = vec![
108 SystemPoint::new(SystemId(1), 0.0, 0.0, 0.0),
109 SystemPoint::new(SystemId(2), 10.0, 0.0, 0.0),
110 SystemPoint::new(SystemId(3), 100.0, 0.0, 0.0),
111 ];
112 let tree = RTree::bulk_load(points);
113 let nearest = tree.nearest_neighbor(&[1.0, 0.0, 0.0]).unwrap();
114 assert_eq!(nearest.id, SystemId(1));
115 }
116
117 #[test]
118 fn test_rtree_bulk_load_size() {
119 use rstar::RTree;
120 let points = vec![
121 SystemPoint::new(SystemId(1), 0.0, 0.0, 0.0),
122 SystemPoint::new(SystemId(2), 5.0, 5.0, 5.0),
123 ];
124 let tree = RTree::bulk_load(points);
125 assert_eq!(tree.size(), 2);
126 }
127
128 #[test]
129 fn test_system_point_envelope() {
130 use rstar::RTreeObject;
131 let p = SystemPoint::new(SystemId(1), 3.0, 4.0, 5.0);
132 let env = p.envelope();
133 assert_eq!(env.lower(), [3.0, 4.0, 5.0]);
134 assert_eq!(env.upper(), [3.0, 4.0, 5.0]);
135 }
136}