Much progress
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -107,6 +107,7 @@ name = "edalyze"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"byteorder",
|
||||||
"rstar",
|
"rstar",
|
||||||
"simd-json",
|
"simd-json",
|
||||||
"structopt",
|
"structopt",
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ structopt = "0.3"
|
|||||||
simd-json = "0.10"
|
simd-json = "0.10"
|
||||||
rstar = "0.11"
|
rstar = "0.11"
|
||||||
zstd = "0.11"
|
zstd = "0.11"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
byteorder = "1"
|
||||||
|
|||||||
@@ -3,18 +3,12 @@ use anyhow::anyhow;
|
|||||||
use simd_json::Node;
|
use simd_json::Node;
|
||||||
|
|
||||||
use edalyze::tapetool::{TapeCursor, TapeObject, Value};
|
use edalyze::tapetool::{TapeCursor, TapeObject, Value};
|
||||||
|
use edalyze::bindata::{System, Point, Box};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
pub struct Point(i32, i32, i32);
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct Box(Point, Point);
|
|
||||||
|
|
||||||
const IS_FUEL: u8 = 1;
|
|
||||||
const IS_WDRF: u8 = 2;
|
|
||||||
const IS_NEUT: u8 = 4;
|
|
||||||
|
|
||||||
fn star_typ(sub_type: &str) -> u8 {
|
fn star_typ(sub_type: &str) -> u8 {
|
||||||
|
use edalyze::bindata::{IS_FUEL, IS_WDRF, IS_NEUT};
|
||||||
if sub_type.starts_with("Wh") {
|
if sub_type.starts_with("Wh") {
|
||||||
return IS_WDRF;
|
return IS_WDRF;
|
||||||
} else if sub_type.starts_with("N") {
|
} else if sub_type.starts_with("N") {
|
||||||
@@ -29,38 +23,6 @@ fn star_typ(sub_type: &str) -> u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn isortp(a: i32, b: i32) -> (i32, i32) {
|
|
||||||
if a < b { (a,b) } else { (b,a) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Box {
|
|
||||||
pub const fn from_corners(x: Point, y: Point) -> Box {
|
|
||||||
let (ax, bx) = isortp(x.0, y.0);
|
|
||||||
let (ay, by) = isortp(x.1, y.1);
|
|
||||||
let (az, bz) = isortp(x.2, y.2);
|
|
||||||
|
|
||||||
let pt1 = Point(ax, ay, az);
|
|
||||||
let pt2 = Point(bx, by, bz);
|
|
||||||
|
|
||||||
Box(pt1, pt2)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(&self, x: Point) -> bool {
|
|
||||||
return
|
|
||||||
(self.0 .0 .. self.1 .0).contains(&x.0) &&
|
|
||||||
(self.0 .1 .. self.1 .1).contains(&x.1) &&
|
|
||||||
(self.0 .2 .. self.1 .2).contains(&x.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct System {
|
|
||||||
coords: Point,
|
|
||||||
name: String,
|
|
||||||
stars: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_coords(c: TapeObject) -> Result<Point, &'static str> {
|
fn parse_coords(c: TapeObject) -> Result<Point, &'static str> {
|
||||||
let mut pt = Point::default();
|
let mut pt = Point::default();
|
||||||
|
|
||||||
@@ -113,7 +75,7 @@ fn parse_system<'a>(tape: &'a [Node<'a>]) -> Result<System, &'static str> {
|
|||||||
"name" => system.name = v.as_str().map(str::to_owned).ok_or("name must be str")?,
|
"name" => system.name = v.as_str().map(str::to_owned).ok_or("name must be str")?,
|
||||||
"coords" => system.coords = parse_coords(v.as_object().ok_or("coords must be object")?)?,
|
"coords" => system.coords = parse_coords(v.as_object().ok_or("coords must be object")?)?,
|
||||||
"bodies" => {
|
"bodies" => {
|
||||||
system.stars = v.as_array().ok_or("bodies should be array")?
|
system.star_flags = v.as_array().ok_or("bodies should be array")?
|
||||||
.filter_map(parse_body)
|
.filter_map(parse_body)
|
||||||
.map(|x| x.1)
|
.map(|x| x.1)
|
||||||
.fold(0, |x,y| x|y)
|
.fold(0, |x,y| x|y)
|
||||||
@@ -149,28 +111,37 @@ static FILENAME: &str = "/space/data/ed-utils/spansh/galaxy.json.zst";
|
|||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let f = edalyze::ioutil::Processor::open_file(FILENAME, processor)?;
|
let f = edalyze::ioutil::Processor::open_file(FILENAME, processor)?;
|
||||||
|
|
||||||
|
// Small region
|
||||||
|
#[cfg(ignore)]
|
||||||
let region = Box::from_corners(
|
let region = Box::from_corners(
|
||||||
Point(11900*32, -3800*32, -11510*32),
|
Point(11900*32, -3800*32, -11510*32),
|
||||||
Point(17400*32, 3800*32, -6500*32),
|
Point(17400*32, 3800*32, -6500*32),
|
||||||
);
|
);
|
||||||
|
// Everything southeast of Haffner 18 LSS 27
|
||||||
|
let region = Box::from_corners(
|
||||||
|
Point(11900*32, -3800*32, -18510*32),
|
||||||
|
Point(64400*32, 3800*32, -6500*32),
|
||||||
|
);
|
||||||
|
|
||||||
let mut reg_name = std::io::BufWriter::new(std::fs::File::create("region.nam")?);
|
let mut reg_name = std::io::BufWriter::new(std::fs::File::create("region.nam")?);
|
||||||
let mut reg_data = std::io::BufWriter::new(std::fs::File::create("region.dat")?);
|
let mut reg_data = std::io::BufWriter::new(std::fs::File::create("region.dat")?);
|
||||||
let mut name_pos = 0;
|
let mut name_pos = 0;
|
||||||
|
|
||||||
for sys in f.filter_map(id).filter(|sys| region.contains(sys.coords)) {
|
for mut sys in f.filter_map(id).filter(|sys| region.contains(sys.coords)) {
|
||||||
|
sys.name_off = name_pos as usize;
|
||||||
let hdr = [sys.name.len().min(255) as u8];
|
let hdr = [sys.name.len().min(255) as u8];
|
||||||
if hdr[0] == 255 {
|
if hdr[0] == 255 {
|
||||||
eprintln!("WARNING: Long system name {}", sys.name);
|
eprintln!("WARNING: Long system name {}", sys.name);
|
||||||
}
|
}
|
||||||
reg_name.write(&hdr[..])?;
|
reg_name.write(&hdr[..])?;
|
||||||
reg_name.write(&sys.name.as_bytes()[..hdr[0] as usize])?;
|
reg_name.write(&sys.name.as_bytes()[..hdr[0] as usize])?;
|
||||||
|
name_pos += hdr[0] as u32 + 1;
|
||||||
|
|
||||||
reg_data.write(&sys.coords.0 .to_be_bytes()[..])?;
|
reg_data.write(&sys.coords.0 .to_be_bytes()[..])?;
|
||||||
reg_data.write(&sys.coords.1 .to_be_bytes()[..])?;
|
reg_data.write(&sys.coords.1 .to_be_bytes()[..])?;
|
||||||
reg_data.write(&sys.coords.2 .to_be_bytes()[..])?;
|
reg_data.write(&sys.coords.2 .to_be_bytes()[..])?;
|
||||||
|
|
||||||
let rest = (sys.stars as u32) + (name_pos << 4);
|
let rest = (sys.star_flags as u32) + (name_pos << 4);
|
||||||
name_pos += hdr[0] as u32 + 1;
|
|
||||||
reg_data.write(&rest.to_be_bytes()[..])?;
|
reg_data.write(&rest.to_be_bytes()[..])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
197
src/bin/routefinder.rs
Normal file
197
src/bin/routefinder.rs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
|
||||||
|
use std::{io::Cursor, cmp::{Ordering, Reverse}, collections::{VecDeque, BinaryHeap}};
|
||||||
|
|
||||||
|
use edalyze::bindata::*;
|
||||||
|
use rstar::primitives::{PointWithData, GeomWithData};
|
||||||
|
|
||||||
|
struct IndexData {
|
||||||
|
cost: f32,
|
||||||
|
reachable: bool,
|
||||||
|
last: usize,
|
||||||
|
sys_data: System,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<System> for IndexData {
|
||||||
|
fn from(sys: System) -> Self {
|
||||||
|
Self {
|
||||||
|
cost: f32::INFINITY,
|
||||||
|
reachable: false,
|
||||||
|
last: 0,
|
||||||
|
sys_data: sys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SearchNode {
|
||||||
|
id: usize,
|
||||||
|
cost: f32,
|
||||||
|
heuristic: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait SearchState {
|
||||||
|
fn cost(&self) -> f32;
|
||||||
|
fn heuristic(&self) -> f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ordering tuned for BinaryHeap
|
||||||
|
impl Ord for SearchNode {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
f32::total_cmp(&(self.cost + self.heuristic), &(other.cost + other.heuristic))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for SearchNode {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for SearchNode {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
(self.cost + self.heuristic) == (other.cost + other.heuristic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for SearchNode {}
|
||||||
|
|
||||||
|
trait SearchQueue<T> {
|
||||||
|
fn q_next(&mut self) -> Option<T>;
|
||||||
|
fn q_empty(&self) -> bool;
|
||||||
|
fn q_push(&mut self, item: T);
|
||||||
|
fn q_clear(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SearchEnv<Q> {
|
||||||
|
rst: rstar::RTree<GeomWithData<Point, usize>>,
|
||||||
|
meta: Vec<IndexData>,
|
||||||
|
base_jump: f32,
|
||||||
|
init_system: usize,
|
||||||
|
target_system: usize,
|
||||||
|
jumponium_cost_factor: f32,
|
||||||
|
queue: Q,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Q: SearchQueue<SearchNode>> SearchEnv<Q> {
|
||||||
|
fn reset(&mut self) {
|
||||||
|
for item in self.meta.iter_mut() {
|
||||||
|
item.cost = f32::INFINITY;
|
||||||
|
item.reachable = false;
|
||||||
|
item.last = 0;
|
||||||
|
}
|
||||||
|
self.queue.q_clear();
|
||||||
|
let init_node = SearchNode{
|
||||||
|
id: self.init_system,
|
||||||
|
cost: 0,
|
||||||
|
heuristic: self.heuristic(self.init_system),
|
||||||
|
};
|
||||||
|
self.queue.q_push(init_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_done(&self) -> bool {
|
||||||
|
self.queue.q_empty() || self.meta[self.target_system].reachable
|
||||||
|
}
|
||||||
|
fn search(&mut self) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
fn heuristic(&self, sys_id: usize) -> f32 {
|
||||||
|
let tcoord = self.meta[self.target_system].sys_data.coords;
|
||||||
|
let scoord = self.meta[sys_id].sys_data.coords;
|
||||||
|
let dist = tcoord.distance(scoord);
|
||||||
|
(dist - self.jdist(sys_id)) / (4. * self.base_jump)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_star(&self, sys_id: usize, base_cost: f32) {
|
||||||
|
let star_jump = self.jdist(sys_id);
|
||||||
|
let star_jump_sq = self.boosted_jump * self.boosted_jump;
|
||||||
|
let base_jump_sq = self.base_jump * self.base_jump;
|
||||||
|
let augm_jump_sq = base_jump_sq * 4.;
|
||||||
|
|
||||||
|
let max_jump_sq = augm_jump_sq.max(star_jump_sq);
|
||||||
|
|
||||||
|
let cur_pos = self.meta[sys_id].sys_data.coords;
|
||||||
|
for n_star in self.rst.locate_within_distance(cur_pos, (max_jump_sq * 1024.).ceil() as i32) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jdist(&self, sys_id: usize) -> f32 {
|
||||||
|
self.base_jump * self.meta[sys_id].sys_data.jump_scale()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BFS
|
||||||
|
struct BFS<T>(VecDeque<T>);
|
||||||
|
impl<T> SearchQueue<T> for BFS<T> {
|
||||||
|
fn q_next(&mut self) -> Option<T> { self.0.pop_front() }
|
||||||
|
fn q_empty(&self) -> bool { self.0.is_empty() }
|
||||||
|
fn q_push(&mut self, item: T) { self.0.push_back(item); }
|
||||||
|
fn q_clear(&mut self) { self.0.clear(); }
|
||||||
|
}
|
||||||
|
impl<T> Default for BFS<T> {
|
||||||
|
fn default() -> Self { BFS(VecDeque::new()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DFS<T>(Vec<T>);
|
||||||
|
impl<T> SearchQueue<T> for DFS<T> {
|
||||||
|
fn q_next(&mut self) -> Option<T> { self.0.pop() }
|
||||||
|
fn q_empty(&self) -> bool { self.0.is_empty() }
|
||||||
|
fn q_push(&mut self, item: T) { self.0.push(item); }
|
||||||
|
fn q_clear(&mut self) { self.0.clear(); }
|
||||||
|
}
|
||||||
|
impl<T> Default for DFS<T> {
|
||||||
|
fn default() -> Self { DFS(Vec::new()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AStar<T>(BinaryHeap<Reverse<T>>);
|
||||||
|
impl<T: Ord> SearchQueue<T> for AStar<T> {
|
||||||
|
fn q_next(&mut self) -> Option<T> { self.0.pop().map(|x| x.0) }
|
||||||
|
fn q_empty(&self) -> bool { self.0.is_empty() }
|
||||||
|
fn q_push(&mut self, item: T) { self.0.push(Reverse(item)); }
|
||||||
|
fn q_clear(&mut self) { self.0.clear(); }
|
||||||
|
}
|
||||||
|
impl<T: Ord> Default for AStar<T> {
|
||||||
|
fn default() -> Self { AStar(BinaryHeap::new()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let mut reg_dat = Cursor::new(std::fs::read("region.dat")?);
|
||||||
|
let mut reg_nam = Cursor::new(std::fs::read("region.nam")?);
|
||||||
|
|
||||||
|
let mut systems = Vec::with_capacity(reg_dat.get_ref().len() / 16);
|
||||||
|
while let Ok(mut system) = System::read_from(&mut reg_dat) {
|
||||||
|
system.read_name(&mut reg_nam).ok();
|
||||||
|
systems.push(IndexData::from(system));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let rst = rstar::RTree::bulk_load(
|
||||||
|
systems.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i,md)| GeomWithData::new(md.sys_data.coords, i))
|
||||||
|
.collect()
|
||||||
|
);
|
||||||
|
|
||||||
|
eprintln!("Done reading {} systems", systems.len());
|
||||||
|
let init_system = systems.iter().enumerate()
|
||||||
|
.find(|(_,sys)| sys.sys_data.name == "Haffner 18 LSS 27")
|
||||||
|
.expect("init system should exist")
|
||||||
|
.0;
|
||||||
|
let target_system = systems.iter().enumerate()
|
||||||
|
.find(|(_,sys)| sys.sys_data.name == "Angosk OM-W d1-0")
|
||||||
|
.expect("init system should exist")
|
||||||
|
.0;
|
||||||
|
|
||||||
|
let mut env = SearchEnv {
|
||||||
|
rst,meta: systems,base_jump: 10., init_system, target_system,
|
||||||
|
queue: BFS::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
env.queue.q_push(SearchNode{
|
||||||
|
heuristic: 0., cost: 0., id: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
146
src/bindata.rs
Normal file
146
src/bindata.rs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
use std::io::{Read, Write, self, Seek};
|
||||||
|
|
||||||
|
use byteorder::{WriteBytesExt, BE, ReadBytesExt};
|
||||||
|
|
||||||
|
pub const IS_FUEL: u8 = 1;
|
||||||
|
pub const IS_WDRF: u8 = 2;
|
||||||
|
pub const IS_NEUT: u8 = 4;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct Point(pub i32, pub i32, pub i32);
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Box(pub Point, pub Point);
|
||||||
|
|
||||||
|
const fn isortp(a: i32, b: i32) -> (i32, i32) {
|
||||||
|
if a < b { (a,b) } else { (b,a) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Box {
|
||||||
|
pub const fn from_corners(x: Point, y: Point) -> Box {
|
||||||
|
let (ax, bx) = isortp(x.0, y.0);
|
||||||
|
let (ay, by) = isortp(x.1, y.1);
|
||||||
|
let (az, bz) = isortp(x.2, y.2);
|
||||||
|
|
||||||
|
let pt1 = Point(ax, ay, az);
|
||||||
|
let pt2 = Point(bx, by, bz);
|
||||||
|
|
||||||
|
Box(pt1, pt2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, x: Point) -> bool {
|
||||||
|
return
|
||||||
|
(self.0 .0 .. self.1 .0).contains(&x.0) &&
|
||||||
|
(self.0 .1 .. self.1 .1).contains(&x.1) &&
|
||||||
|
(self.0 .2 .. self.1 .2).contains(&x.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct System {
|
||||||
|
pub coords: Point,
|
||||||
|
pub name: String,
|
||||||
|
pub name_off: usize,
|
||||||
|
pub star_flags: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System {
|
||||||
|
pub fn write_name(&self, writer: &mut dyn Write) -> io::Result<usize> {
|
||||||
|
let name_len = self.name.len().min(255);
|
||||||
|
writer.write_u8(name_len as u8)?;
|
||||||
|
writer.write(&self.name.as_bytes()[..name_len])?;
|
||||||
|
|
||||||
|
return Ok(name_len + 1);
|
||||||
|
}
|
||||||
|
pub fn write_to(&self, writer: &mut dyn Write) -> io::Result<()> {
|
||||||
|
writer.write_i32::<BE>(self.coords.0)?;
|
||||||
|
writer.write_i32::<BE>(self.coords.1)?;
|
||||||
|
writer.write_i32::<BE>(self.coords.2)?;
|
||||||
|
let rest = self.star_flags as u32 + ((self.name_off as u32) << 4);
|
||||||
|
writer.write_u32::<BE>(rest)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_from(r: &mut dyn Read) -> io::Result<Self> {
|
||||||
|
let x = r.read_i32::<BE>()?;
|
||||||
|
let y = r.read_i32::<BE>()?;
|
||||||
|
let z = r.read_i32::<BE>()?;
|
||||||
|
let rest = r.read_u32::<BE>()?;
|
||||||
|
let name_off = (rest >> 4) as usize;
|
||||||
|
let star_flags = (rest & 0xF) as u8;
|
||||||
|
Ok(System{
|
||||||
|
coords: Point(x,y,z),
|
||||||
|
name: String::new(),
|
||||||
|
name_off,
|
||||||
|
star_flags,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_name<R: Read + Seek>(&mut self, r: &mut R) -> io::Result<()> {
|
||||||
|
//eprint!(".");
|
||||||
|
r.seek(io::SeekFrom::Start(self.name_off as u64))?;
|
||||||
|
let mut buf = vec![];
|
||||||
|
let len = r.read_u8()? as usize;
|
||||||
|
//eprintln!("Reading {len} bytes from {}", self.name_off);
|
||||||
|
buf.resize(len, 0);
|
||||||
|
r.read_exact(buf.as_mut_slice())?;
|
||||||
|
self.name = String::from_utf8(buf)
|
||||||
|
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jump_scale(&self) -> f32 {
|
||||||
|
if self.star_flags & IS_NEUT != 0 {
|
||||||
|
4.0
|
||||||
|
} else if self.star_flags & IS_WDRF != 0 {
|
||||||
|
1.5
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl rstar::Point for Point {
|
||||||
|
type Scalar = i32;
|
||||||
|
const DIMENSIONS: usize = 3;
|
||||||
|
fn generate(mut gen: impl FnMut(usize) -> Self::Scalar) -> Self {
|
||||||
|
let x = gen(0);
|
||||||
|
let y = gen(1);
|
||||||
|
let z = gen(2);
|
||||||
|
Self(x,y,z)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nth(&self, index: usize) -> Self::Scalar {
|
||||||
|
match index {
|
||||||
|
0 => self.0,
|
||||||
|
1 => self.1,
|
||||||
|
2 => self.2,
|
||||||
|
_ => 0 as Self::Scalar,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar {
|
||||||
|
match index {
|
||||||
|
0 => &mut self.0,
|
||||||
|
1 => &mut self.1,
|
||||||
|
_ => &mut self.2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Point {
|
||||||
|
pub fn distance_sq(&self, other: Self) -> f32 {
|
||||||
|
let Point(sx, sy, sz) = *self;
|
||||||
|
let Point(tx, ty, tz) = other;
|
||||||
|
let dx = sx as f32 - tx as f32;
|
||||||
|
let dy = sy as f32 - ty as f32;
|
||||||
|
let dz = sz as f32 - tz as f32;
|
||||||
|
(dx*dx) + (dy*dy) + (dz*dz)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn distance(&self, other: Self) -> f32 {
|
||||||
|
self.distance_sq(other).sqrt()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod tapetool;
|
pub mod tapetool;
|
||||||
pub mod ioutil;
|
pub mod ioutil;
|
||||||
|
pub mod bindata;
|
||||||
|
|||||||
Reference in New Issue
Block a user