use kdtree::KdTree;
use std::collections::HashMap;
use std::ops::{Add, Div, DivAssign, Mul, MulAssign, Sub};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ProjectedAxis {
X,
#[default]
Y,
Z,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MapPoint {
pub coords: [f64; 3],
pub id: Option<usize>,
pub name: Option<String>,
pub connections: Vec<(usize, usize)>,
}
impl MapPoint {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Self {
coords: [x, y, z],
id: None,
name: None,
connections: Vec::new(),
}
}
pub fn x(&self) -> f64 {
self.coords[0]
}
pub fn y(&self) -> f64 {
self.coords[1]
}
pub fn z(&self) -> f64 {
self.coords[2]
}
pub fn to_2d(&self, axis: ProjectedAxis) -> [f32; 2] {
match axis {
ProjectedAxis::X => [self.coords[1] as f32, self.coords[2] as f32],
ProjectedAxis::Y => [self.coords[0] as f32, self.coords[2] as f32],
ProjectedAxis::Z => [self.coords[0] as f32, self.coords[1] as f32],
}
}
}
impl Default for MapPoint {
fn default() -> Self {
Self::new(0.0, 0.0, 0.0)
}
}
impl From<[i64; 3]> for MapPoint {
fn from(value: [i64; 3]) -> Self {
Self::new(value[0] as f64, value[1] as f64, value[2] as f64)
}
}
impl From<[f32; 3]> for MapPoint {
fn from(value: [f32; 3]) -> Self {
Self::new(value[0] as f64, value[1] as f64, value[2] as f64)
}
}
impl From<[f64; 3]> for MapPoint {
fn from(value: [f64; 3]) -> Self {
Self::new(value[0], value[1], value[2])
}
}
impl From<MapPoint> for [i64; 3] {
fn from(val: MapPoint) -> Self {
[
val.coords[0].round() as i64,
val.coords[1].round() as i64,
val.coords[2].round() as i64,
]
}
}
impl From<MapPoint> for [f64; 3] {
fn from(val: MapPoint) -> Self {
val.coords
}
}
impl DivAssign<i64> for MapPoint {
fn div_assign(&mut self, rhs: i64) {
self.coords[0] /= rhs as f64;
self.coords[1] /= rhs as f64;
self.coords[2] /= rhs as f64;
}
}
impl MulAssign<i64> for MapPoint {
fn mul_assign(&mut self, rhs: i64) {
self.coords[0] *= rhs as f64;
self.coords[1] *= rhs as f64;
self.coords[2] *= rhs as f64;
}
}
impl Mul<f64> for MapPoint {
type Output = Self;
fn mul(mut self, rhs: f64) -> Self::Output {
self.coords[0] *= rhs;
self.coords[1] *= rhs;
self.coords[2] *= rhs;
self
}
}
impl Div<f64> for MapPoint {
type Output = Self;
fn div(mut self, rhs: f64) -> Self::Output {
self.coords[0] /= rhs;
self.coords[1] /= rhs;
self.coords[2] /= rhs;
self
}
}
impl Add<MapPoint> for MapPoint {
type Output = MapPoint;
fn add(self, rhs: MapPoint) -> Self::Output {
MapPoint::new(
self.coords[0] + rhs.coords[0],
self.coords[1] + rhs.coords[1],
self.coords[2] + rhs.coords[2],
)
}
}
impl Sub<MapPoint> for MapPoint {
type Output = MapPoint;
fn sub(self, rhs: MapPoint) -> Self::Output {
MapPoint::new(
self.coords[0] - rhs.coords[0],
self.coords[1] - rhs.coords[1],
self.coords[2] - rhs.coords[2],
)
}
}
impl Add<&MapPoint> for MapPoint {
type Output = MapPoint;
fn add(self, rhs: &MapPoint) -> Self::Output {
MapPoint::new(
self.coords[0] + rhs.coords[0],
self.coords[1] + rhs.coords[1],
self.coords[2] + rhs.coords[2],
)
}
}
impl Sub<&MapPoint> for MapPoint {
type Output = MapPoint;
fn sub(self, rhs: &MapPoint) -> Self::Output {
MapPoint::new(
self.coords[0] - rhs.coords[0],
self.coords[1] - rhs.coords[1],
self.coords[2] - rhs.coords[2],
)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MapSegment {
pub id: (usize, usize),
pub point1: [f32; 2],
pub point2: [f32; 2],
}
pub fn map_points_to_vec(tree: &KdTree<f64, MapPoint, [f64; 3]>) -> Vec<&MapPoint> {
tree.bounding_box(&[f64::MIN, f64::MIN, f64::MIN], &[f64::MAX, f64::MAX, f64::MAX])
.expect("bounding_box with f64::MIN/f64::MAX bounds should never fail (3 fixed dimensions, always-finite bounds)")
}
#[derive(PartialEq, Clone, Debug)]
pub struct EveRegionArea {
pub region_id: i64,
pub name: String,
pub min: MapPoint,
pub max: MapPoint,
}
impl Default for EveRegionArea {
fn default() -> Self {
Self::new()
}
}
impl EveRegionArea {
pub fn new() -> Self {
EveRegionArea {
region_id: 0,
name: String::new(),
min: MapPoint::default(),
max: MapPoint::default(),
}
}
}
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
pub struct Moon {
pub id: u32,
pub planet: u32,
pub index: u8,
pub solar_system: u32,
}
impl Moon {
pub fn new() -> Self {
Moon {
id: 0,
planet: 0,
index: 0,
solar_system: 0,
}
}
}
impl Default for Moon {
fn default() -> Self {
Self::new()
}
}
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
pub struct Planet {
pub id: u32,
pub solar_system: u32,
pub index: u8,
}
impl Planet {
pub fn new() -> Self {
Planet {
id: 0,
solar_system: 0,
index: 0,
}
}
}
impl Default for Planet {
fn default() -> Self {
Self::new()
}
}
#[derive(PartialEq, Clone, Debug)]
pub struct SolarSystem {
pub id: u32,
pub name: String,
pub region: u32,
pub constellation: u32,
pub planets: Vec<u32>,
pub connections: Vec<u32>,
pub real_coords: MapPoint,
pub projected_coords: MapPoint,
pub factor: i64,
}
impl SolarSystem {
pub fn new(factor: i64) -> Self {
SolarSystem {
id: 0,
name: String::new(),
region: 0,
constellation: 0,
planets: Vec::new(),
connections: Vec::new(),
real_coords: MapPoint::default(),
projected_coords: MapPoint::default(),
factor,
}
}
}
impl Default for SolarSystem {
fn default() -> Self {
Self::new(1)
}
}
#[derive(PartialEq, Clone, Debug)]
pub struct Constellation {
pub id: u32,
pub name: String,
pub region: u32,
pub solar_systems: Vec<u32>,
pub projected_coords: MapPoint,
}
impl Constellation {
pub fn new() -> Self {
Constellation {
id: 0,
name: String::new(),
region: 0,
solar_systems: Vec::new(),
projected_coords: MapPoint::default(),
}
}
}
impl Default for Constellation {
fn default() -> Self {
Self::new()
}
}
#[derive(PartialEq, Clone, Debug)]
pub struct Region {
pub id: u32,
pub name: String,
pub constellations: Vec<u32>,
pub projected_coords: MapPoint,
}
impl Region {
pub fn new() -> Self {
Region {
id: 0,
name: String::new(),
constellations: Vec::new(),
projected_coords: MapPoint::default(),
}
}
}
impl Default for Region {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)]
pub struct Universe {
pub regions: HashMap<u32, Region>,
pub constellations: HashMap<u32, Constellation>,
pub solar_systems: HashMap<u32, SolarSystem>,
pub planets: HashMap<u32, Planet>,
pub moons: HashMap<u32, Moon>,
pub factor: i64,
}
impl Universe {
pub fn new(factor: i64) -> Universe {
Universe {
regions: HashMap::new(),
constellations: HashMap::new(),
solar_systems: HashMap::new(),
planets: HashMap::new(),
moons: HashMap::new(),
factor,
}
}
}
impl Default for Universe {
fn default() -> Self {
Self::new(1)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mappoint_new_sets_coordinates() {
let point = MapPoint::new(10.0, -20.0, 30.0);
assert_eq!(point.x(), 10.0);
assert_eq!(point.y(), -20.0);
assert_eq!(point.z(), 30.0);
assert_eq!(point.id, None);
assert_eq!(point.name, None);
assert!(point.connections.is_empty());
}
#[test]
fn mappoint_default_is_origin() {
let point = MapPoint::default();
assert_eq!(point.coords, [0.0, 0.0, 0.0]);
assert_eq!(point, MapPoint::new(0.0, 0.0, 0.0));
}
#[test]
fn mappoint_from_i64_array() {
let point = MapPoint::from([1i64, 2, 3]);
assert_eq!(point, MapPoint::new(1.0, 2.0, 3.0));
}
#[test]
fn mappoint_from_f32_array() {
let point = MapPoint::from([1.4f32, 1.5, -1.5]);
assert_eq!(point, MapPoint::new(1.4f32 as f64, 1.5, -1.5));
}
#[test]
fn mappoint_from_f64_array() {
let point = MapPoint::from([1.4, 1.5, -1.5]);
assert_eq!(point, MapPoint::new(1.4, 1.5, -1.5));
}
#[test]
fn mappoint_into_i64_array_rounds() {
let values: [i64; 3] = MapPoint::new(7.4, 7.5, -7.5).into();
assert_eq!(values, [7, 8, -8]);
}
#[test]
fn mappoint_into_f64_array() {
let values: [f64; 3] = MapPoint::new(7.0, 8.0, 9.0).into();
assert_eq!(values, [7.0, 8.0, 9.0]);
}
#[test]
fn mappoint_to_2d_drops_x() {
let point = MapPoint::new(10.0, 20.0, 30.0);
assert_eq!(point.to_2d(ProjectedAxis::X), [20.0, 30.0]);
}
#[test]
fn mappoint_to_2d_drops_y() {
let point = MapPoint::new(10.0, 20.0, 30.0);
assert_eq!(point.to_2d(ProjectedAxis::Y), [10.0, 30.0]);
}
#[test]
fn mappoint_to_2d_drops_z() {
let point = MapPoint::new(10.0, 20.0, 30.0);
assert_eq!(point.to_2d(ProjectedAxis::Z), [10.0, 20.0]);
}
#[test]
fn mappoint_to_2d_never_fails_even_at_the_pivot_values_that_used_to_panic() {
let point = MapPoint::new(
1_003_094_336_444_825.0,
-2_005_029_375_317_114.0,
3_001_839_229_715_087.0,
);
let _ = point.to_2d(ProjectedAxis::Y); }
#[test]
fn mappoint_add_owned() {
let sum = MapPoint::new(1.0, 2.0, 3.0) + MapPoint::new(10.0, 20.0, 30.0);
assert_eq!(sum, MapPoint::new(11.0, 22.0, 33.0));
}
#[test]
fn mappoint_add_reference() {
let sum = MapPoint::new(1.0, 2.0, 3.0) + &MapPoint::new(-1.0, -2.0, -3.0);
assert_eq!(sum, MapPoint::new(0.0, 0.0, 0.0));
}
#[test]
fn mappoint_sub_owned() {
let diff = MapPoint::new(10.0, 20.0, 30.0) - MapPoint::new(1.0, 2.0, 3.0);
assert_eq!(diff, MapPoint::new(9.0, 18.0, 27.0));
}
#[test]
fn mappoint_sub_reference() {
let diff = MapPoint::new(10.0, 20.0, 30.0) - &MapPoint::new(10.0, 20.0, 30.0);
assert_eq!(diff, MapPoint::new(0.0, 0.0, 0.0));
}
#[test]
fn mappoint_mul_assign_i64() {
let mut point = MapPoint::new(1.0, 2.0, 3.0);
point *= 3i64;
assert_eq!(point, MapPoint::new(3.0, 6.0, 9.0));
}
#[test]
fn mappoint_div_assign_i64() {
let mut point = MapPoint::new(24.0, 48.0, 96.0);
point /= 2i64;
assert_eq!(point, MapPoint::new(12.0, 24.0, 48.0));
}
#[test]
fn mappoint_mul_f64() {
let product = MapPoint::new(1.0, -2.0, 3.0) * 2.5;
assert_eq!(product, MapPoint::new(2.5, -5.0, 7.5));
}
#[test]
fn mappoint_div_f64() {
let quotient = MapPoint::new(10.0, -20.0, 30.0) / 4.0;
assert_eq!(quotient, MapPoint::new(2.5, -5.0, 7.5));
}
#[test]
fn everegionarea_new_is_empty() {
let area = EveRegionArea::new();
assert_eq!(area.region_id, 0);
assert_eq!(area.name, String::new());
assert_eq!(area.min, MapPoint::default());
assert_eq!(area.max, MapPoint::default());
assert_eq!(area, EveRegionArea::default());
}
#[test]
fn moon_new_is_zeroed() {
let moon = Moon::new();
assert_eq!(moon.id, 0);
assert_eq!(moon.planet, 0);
assert_eq!(moon.index, 0);
assert_eq!(moon.solar_system, 0);
assert_eq!(moon, Moon::default());
}
#[test]
fn planet_new_is_zeroed() {
let planet = Planet::new();
assert_eq!(planet.id, 0);
assert_eq!(planet.solar_system, 0);
assert_eq!(planet.index, 0);
assert_eq!(planet, Planet::default());
}
#[test]
fn solarsystem_new_initializes_with_factor() {
let system = SolarSystem::new(1000);
assert_eq!(system.id, 0);
assert_eq!(system.name, String::new());
assert_eq!(system.region, 0);
assert_eq!(system.constellation, 0);
assert!(system.planets.is_empty());
assert!(system.connections.is_empty());
assert_eq!(system.real_coords, MapPoint::default());
assert_eq!(system.projected_coords, MapPoint::default());
assert_eq!(system.factor, 1000);
}
#[test]
fn solarsystem_default_factor_is_one() {
assert_eq!(SolarSystem::default().factor, 1);
}
#[test]
fn constellation_new_is_empty() {
let constellation = Constellation::new();
assert_eq!(constellation.id, 0);
assert_eq!(constellation.name, String::new());
assert_eq!(constellation.region, 0);
assert!(constellation.solar_systems.is_empty());
assert_eq!(constellation.projected_coords, MapPoint::default());
assert_eq!(constellation, Constellation::default());
}
#[test]
fn region_new_is_empty() {
let region = Region::new();
assert_eq!(region.id, 0);
assert_eq!(region.name, String::new());
assert!(region.constellations.is_empty());
assert_eq!(region.projected_coords, MapPoint::default());
assert_eq!(region, Region::default());
}
#[test]
fn universe_new_initializes_with_factor() {
let universe = Universe::new(42);
assert!(universe.regions.is_empty());
assert!(universe.constellations.is_empty());
assert!(universe.solar_systems.is_empty());
assert!(universe.planets.is_empty());
assert!(universe.moons.is_empty());
assert_eq!(universe.factor, 42);
}
#[test]
fn universe_default_factor_is_one() {
assert_eq!(Universe::default().factor, 1);
}
}