feat(GUI): Implement route plotting and fuzzy search

Implement route plotting in GUI
Use batch fuzzy search to find systems
search all systems at once after adding
added some error checking
This commit is contained in:
Daniel S. 2019-08-05 00:05:44 +02:00
parent ec3972b06c
commit c290d5eb12
18 changed files with 1075 additions and 391 deletions

88
rust/Cargo.lock generated
View file

@ -168,10 +168,19 @@ dependencies = [
"indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"pyo3 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
<<<<<<< Updated upstream:rust/Cargo.lock
"rstar 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
=======
"rstar 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
>>>>>>> Stashed changes:Cargo.lock
]
[[package]]
@ -205,6 +214,19 @@ dependencies = [
[[package]]
name = "ghost"
version = "0.1.0"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "heck"
version = "0.3.1"
>>>>>>> Stashed changes:Cargo.lock
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
@ -294,7 +316,11 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"mashup-impl 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
<<<<<<< Updated upstream:rust/Cargo.lock
"proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
=======
"proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
>>>>>>> Stashed changes:Cargo.lock
]
[[package]]
@ -302,7 +328,11 @@ name = "mashup-impl"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
<<<<<<< Updated upstream:rust/Cargo.lock
"proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
=======
"proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
>>>>>>> Stashed changes:Cargo.lock
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -377,15 +407,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro-hack"
<<<<<<< Updated upstream:rust/Cargo.lock
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
=======
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
>>>>>>> Stashed changes:Cargo.lock
]
[[package]]
name = "proc-macro-hack-impl"
<<<<<<< Updated upstream:rust/Cargo.lock
version = "0.4.1"
=======
version = "0.4.2"
>>>>>>> Stashed changes:Cargo.lock
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -592,7 +633,7 @@ dependencies = [
[[package]]
name = "rstar"
version = "0.4.0"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -679,6 +720,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "spin"
version = "0.5.0"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "structopt"
version = "0.2.18"
>>>>>>> Stashed changes:Cargo.lock
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -748,6 +807,11 @@ name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.7"
@ -792,6 +856,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum ghost 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5297b71943dc9fea26a3241b178c140ee215798b7f79f7773fd61683e25bca74"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
>>>>>>> Stashed changes:Cargo.lock
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c60da1c9abea75996b70a931bba6c750730399005b61ccd853cee50ef3d0d0c"
"checksum inventory 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21df85981fe094480bc2267723d3dc0fd1ae0d1f136affc659b7398be615d922"
@ -813,8 +881,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c"
"checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27"
"checksum permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c"
<<<<<<< Updated upstream:rust/Cargo.lock
"checksum proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c725b36c99df7af7bf9324e9c999b9e37d92c8f8caf106d82e1d7953218d2d8"
"checksum proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b753ad9ed99dd8efeaa7d2fb8453c8f6bc3e54b97966d35f1bc77ca6865254a"
=======
"checksum proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b"
"checksum proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842"
>>>>>>> Stashed changes:Cargo.lock
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum pyo3 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d09e6e2d3fa5ae1a8af694f865e03e763e730768b16e3097851ff0b7f2276086"
"checksum pyo3-derive-backend 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d7ae8ab3017515cd7c82d88ce49b55e12a56c602dc69993e123da45c91b186"
@ -837,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b2f0808e7d7e4fb1cb07feb6ff2f4bc827938f24f8c2e6a3beb7370af544bdd"
"checksum regex-automata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3ed09217220c272b29ef237a974ad58515bde75f194e3ffa7e6d0bf0f3b01f86"
"checksum regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d76410686f9e3a17f06128962e0ecc5755870bb890c34820c7af7f1db2e1d48"
"checksum rstar 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd08ae4f9661517777346592956ea6cdbba2895a28037af7daa600382f4b4001"
"checksum rstar 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90bb34cd8efef7ed3ebfb29e713e51301c3e60fba37c3e9185a1afaf9ce643a"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
@ -849,6 +922,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf"
"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7"
"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "032c03039aae92b350aad2e3779c352e104d919cb192ba2fabbd7b831ce4f0f6"
"checksum structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7"
"checksum structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107"
>>>>>>> Stashed changes:Cargo.lock
"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c"
"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330"
"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
@ -858,6 +938,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d50aa7650df78abf942826607c62468ce18d9019673d4a2ebe1865dbb96ffde"
<<<<<<< Updated upstream:rust/Cargo.lock
=======
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
>>>>>>> Stashed changes:Cargo.lock
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

View file

@ -11,12 +11,12 @@ license = "WTFPL"
crate-type = ["cdylib"]
[profile.release]
debug=true
#debug=true
[dependencies]
csv = "1.1.1"
serde = { version = "1.0", features = ["derive"] }
rstar = "0.4.0"
rstar = "0.5.0"
humantime = "1.2.0"
permutohedron = "0.2.4"
serde_json = "1.0.40"
@ -25,6 +25,7 @@ fnv = "1.0.6"
bincode = "1.1.4"
sha3 = "0.8.2"
byteorder = "1.3.2"
strsim = "0.9.2"
[dependencies.pyo3]
version = "0.7.0"

View file

@ -27,6 +27,7 @@ impl SystemSerde {
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct System {
pub id: u32,
@ -37,3 +38,16 @@ pub struct System {
pub distance: u32,
pub pos: [f32; 3],
}
impl Ord for System {
fn cmp(&self, other: &Self) -> Ordering {
return self.id.cmp(&other.id);
}
}
impl PartialOrd for System {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
return Some(self.cmp(other));
}
}

View file

@ -1,14 +1,43 @@
extern crate strsim;
mod common;
mod preprocess;
mod route;
use pyo3::prelude::*;
use pyo3::types::{PyDict,PyList};
use std::collections::HashMap;
use pyo3::exceptions::*;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList};
use std::path::PathBuf;
use common::{System,SystemSerde};
fn find_matches(path:&PathBuf,names:Vec<String>) -> Result<HashMap<String,(f64,Option<System>)>,String> {
let mut best: HashMap<String,(f64,Option<System>)> = HashMap::new();
for name in &names {
best.insert(name.to_string(),(0.0,None));
};
let mut reader = match csv::ReaderBuilder::new().from_path(path) {
Ok(rdr) => rdr,
Err(e) => {
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e).to_string());
}
};
let systems=reader
.deserialize::<SystemSerde>();
for sys in systems {
let sys=sys.unwrap();
for name in &names {
best.entry(name.clone()).and_modify(|ent| {
let d1=strsim::normalized_levenshtein(&sys.system,&name);
if d1>ent.0 {
*ent=(d1,Some(sys.build()))
}
});
}
}
return Ok(best);
}
#[pymodule]
pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
/// preprocess(infile_systems, infile_bodies, outfile, callback)
/// --
///
@ -24,108 +53,139 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> {
use preprocess::*;
let state = PyDict::new(py);
let state_dict = PyDict::new(py);
callback.call(py,(state_dict,),None).unwrap();
callback.call(py, (state_dict,), None).unwrap();
let callback_wrapped = move |state: &PreprocessState| {
// println!("SEND: {:?}",state);
state_dict.set_item("file",state.file.clone())?;
state_dict.set_item("total",state.total)?;
state_dict.set_item("done",state.done)?;
callback.call(py,(state_dict,),None)
state_dict.set_item("file", state.file.clone())?;
state_dict.set_item("total", state.total)?;
state_dict.set_item("count", state.count)?;
state_dict.set_item("done", state.done)?;
state_dict.set_item("message", state.message.clone())?;
callback.call(py, (state_dict,), None)
};
preprocess_files(&PathBuf::from(infile_bodies), &PathBuf::from(infile_systems), &PathBuf::from(outfile), Box::new(callback_wrapped)).unwrap();
return Ok(state.to_object(py));
preprocess_files(
&PathBuf::from(infile_bodies),
&PathBuf::from(infile_systems),
&PathBuf::from(outfile),
Box::new(callback_wrapped),
)
.unwrap();
Ok(state.to_object(py))
}
/// route(infile, hops, range, mode,primary, greedyness, precomp, callback)
///find_sys(sys_names,sys_list)
/// --
///
/// Find system by name
#[pyfn(m, "find_sys")]
fn find_sys(py:Python,sys_names:Vec<String>,sys_list:String) -> PyResult<PyObject> {
let path=PathBuf::from(sys_list);
match find_matches(&path,sys_names) {
Ok(vals) => {
let ret=PyDict::new(py);
for (key,(diff,sys)) in vals {
let ret_dict=PyDict::new(py);
if let Some(val)= sys {
let pos = PyList::new(py, val.pos.iter());
ret_dict.set_item("star_type", val.star_type.clone())?;
ret_dict.set_item("system", val.system.clone())?;
ret_dict.set_item("body", val.body.clone())?;
ret_dict.set_item("distance", val.distance)?;
ret_dict.set_item("pos", pos)?;
ret_dict.set_item("id", val.id)?;
ret.set_item(key,(diff,ret_dict).to_object(py))?;
}
}
return Ok(ret.to_object(py));
},
Err(e) => {
return Err(PyErr::new::<ValueError, _>(e));
}
}
}
/// route(infile, hops, range, radius_mult, mode,primary, greedyness, precomp, callback)
/// --
///
/// Compute a Route using the suplied parameters
#[pyfn(m, "route")]
#[allow(clippy::too_many_arguments)]
fn route(
py: Python<'static>,
hops: Vec<String>,
range: f32,
radius_mult: f32,
mode: String,
primary: bool,
permute: bool,
keep_first: bool,
keep_last: bool,
greedyness: Option<f32>,
precomp: Option<String>,
path: String,
callback: PyObject,
) -> PyResult<PyObject> {
use route::*;
// TODO: Verify Parameters
let mode = match Mode::parse(&mode) {
Ok(val) => val,
Err(e) => {
return Err(PyErr::new::<ValueError,_>(e));
return Err(PyErr::new::<ValueError, _>(e));
}
};
let state_dict = PyDict::new(py);
callback.call(py,(state_dict,),None).unwrap();
callback.call(py, (state_dict,), None).unwrap();
let callback_wrapped = move |state: &SearchState| {
println!("SEND: {:?}",state);
state_dict.set_item("mode",state.mode.clone())?;
state_dict.set_item("system",state.system.clone())?;
state_dict.set_item("body",state.body.clone())?;
state_dict.set_item("depth",state.depth)?;
state_dict.set_item("queue_size",state.queue_size)?;
state_dict.set_item("d_rem",state.d_rem)?;
state_dict.set_item("d_total",state.d_total)?;
state_dict.set_item("prc_done",state.prc_done)?;
state_dict.set_item("n_seen",state.n_seen)?;
state_dict.set_item("prc_seen",state.prc_seen)?;
callback.call(py,(state_dict,),None)
state_dict.set_item("mode", state.mode.clone())?;
state_dict.set_item("system", state.system.clone())?;
state_dict.set_item("body", state.body.clone())?;
state_dict.set_item("depth", state.depth)?;
state_dict.set_item("queue_size", state.queue_size)?;
state_dict.set_item("d_rem", state.d_rem)?;
state_dict.set_item("d_total", state.d_total)?;
state_dict.set_item("prc_done", state.prc_done)?;
state_dict.set_item("n_seen", state.n_seen)?;
state_dict.set_item("prc_seen", state.prc_seen)?;
callback.call(py, (state_dict,), None)
};
let opts=RouteOpts{
systems:hops,
range:Some(range),
let mut systems=Vec::new();
for sys in hops {
systems.push(route::SysEntry::parse(&sys))
}
println!("SYSTEMS: {:?}",systems);
let opts = RouteOpts {
systems,
range: Some(range),
file_path: PathBuf::from(path),
precomp_file: precomp.map(PathBuf::from),
callback: Box::new(callback_wrapped),
mode,
factor: greedyness,
precompute: false,
permute: permute,
keep_first: true,
keep_last: true,
primary
permute,
keep_first,
keep_last,
primary,
radius_mult
};
let none=().to_object(py);
let none = ().to_object(py);
match route(opts) {
Ok(Some(route)) => {
let hops=route.iter().map(|hop| {
let pos=PyList::new(py,hop.pos.iter());
let elem=PyDict::new(py);
elem.set_item("star_type",hop.star_type.clone()).unwrap();
elem.set_item("system",hop.system.clone()).unwrap();
elem.set_item("body",hop.body.clone()).unwrap();
elem.set_item("distance",hop.distance).unwrap();
elem.set_item("pos",pos).unwrap();
return elem;
let hops = route.iter().map(|hop| {
let pos = PyList::new(py, hop.pos.iter());
let elem = PyDict::new(py);
elem.set_item("star_type", hop.star_type.clone()).unwrap();
elem.set_item("system", hop.system.clone()).unwrap();
elem.set_item("body", hop.body.clone()).unwrap();
elem.set_item("distance", hop.distance).unwrap();
elem.set_item("pos", pos).unwrap();
elem
});
let lst=PyList::new(py,hops);
return Ok(lst.to_object(py));
let lst = PyList::new(py, hops);
Ok(lst.to_object(py))
}
Ok(None) => {
return Ok(none);
},
Err(e) => {
return Err(PyErr::new::<ValueError,_>(e));
}
};
/*
let state = PyDict::new(py);
state.set_item("infile", infile)?;
state.set_item("source", source)?;
state.set_item("dest", dest)?;
state.set_item("range", range)?;
state.set_item("mode", mode)?;
state.set_item("greedyness", greedyness)?;
state.set_item("precomp", precomp)?;
state.set_item("callback", callback)?;
return callback.call(py,(state.to_object(py),),None);
*/
Ok(None) => Ok(none),
Err(e) => Err(PyErr::new::<ValueError, _>(e)),
}
}
Ok(())
}

View file

@ -1,8 +1,10 @@
use crate::common::{System, SystemSerde};
use core::cmp::Ordering;
use csv::StringRecord;
use fnv::{FnvHashMap, FnvHashSet};
use humantime::format_duration;
use permutohedron::LexicalPermutation;
use pyo3::prelude::*;
use rstar::{PointDistance, RTree, RTreeObject, AABB};
use sha3::{Digest, Sha3_256};
use std::collections::VecDeque;
@ -12,8 +14,6 @@ use std::io::Seek;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::path::PathBuf;
use std::time::Instant;
use pyo3::prelude::*;
use crate::common::{System, SystemSerde};
#[derive(Debug)]
pub struct SearchState {
@ -26,9 +26,23 @@ pub struct SearchState {
pub d_total: f32,
pub prc_done: f32,
pub n_seen: usize,
pub prc_seen: f32
pub prc_seen: f32,
}
#[derive(Debug, Clone)]
pub enum SysEntry {
ID(u32),
Name(String),
}
impl SysEntry {
pub fn parse(s: &str) -> SysEntry {
match s.parse() {
Ok(n) => SysEntry::ID(n),
_ => SysEntry::Name(s.to_owned()),
}
}
}
pub struct RouteOpts {
pub range: Option<f32>,
@ -41,8 +55,9 @@ pub struct RouteOpts {
pub keep_last: bool,
pub factor: Option<f32>,
pub mode: Mode,
pub systems: Vec<String>,
pub callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>
pub radius_mult: f32,
pub systems: Vec<SysEntry>,
pub callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>,
}
#[derive(Debug)]
@ -52,9 +67,9 @@ pub enum Mode {
AStar,
}
impl Mode {
impl Mode {
pub fn parse(s: &str) -> Result<Mode, String> {
let s=s.to_lowercase();
let s = s.to_lowercase();
match s.as_ref() {
"bfs" => Ok(Mode::BFS),
"greedy" => Ok(Mode::Greedy),
@ -160,8 +175,8 @@ impl LineCache {
}
pub fn get(&mut self, id: u32) -> Option<System> {
let pos = self.cache[id as usize];
self.file.seek(std::io::SeekFrom::Start(pos)).unwrap();
let rec = Self::read_record(&mut self.file).unwrap();
self.file.seek(std::io::SeekFrom::Start(pos)).ok()?;
let rec = Self::read_record(&mut self.file)?;
let sys: SystemSerde = rec.deserialize(self.header.as_ref()).unwrap();
Some(sys.build())
}
@ -175,18 +190,26 @@ pub struct Router {
range: f32,
primary_only: bool,
path: PathBuf,
callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>
radius_mult: f32,
callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>,
}
impl Router {
pub fn new(path: &PathBuf, range: f32, primary_only: bool,callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>) -> Result<Self,String> {
pub fn new(
path: &PathBuf,
range: f32,
radius_mult: f32,
primary_only: bool,
callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>,
) -> Result<Self, String> {
let mut scoopable = FnvHashSet::default();
let mut reader = match csv::ReaderBuilder::new().from_path(path) {
Ok(rdr) => rdr,
Err(e) => {
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e).to_string());
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e));
}
};
let t_load = Instant::now();
println!("Loading {}...", path.to_str().unwrap());
let systems: Vec<System> = reader
.deserialize::<SystemSerde>()
@ -220,25 +243,54 @@ impl Router {
primary_only,
cache: LineCache::new(path),
path: path.clone(),
callback: callback,
callback,
radius_mult,
};
println!(
"{} Systems loaded in {}",
ret.tree.size(),
format_duration(t_load.elapsed())
);
Ok(ret)
}
pub fn from_file(filename: &PathBuf,callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>) -> Result<(PathBuf, Self),String> {
let mut reader = BufReader::new(File::open(&filename).unwrap());
pub fn from_file(
filename: &PathBuf,
callback: Box<dyn Fn(&SearchState) -> PyResult<PyObject>>,
) -> Result<(PathBuf, Self), String> {
let t_load = Instant::now();
let mut reader = BufReader::new(match File::open(&filename) {
Ok(fh) => fh,
Err(e) => {
return Err(format!(
"Error opening file {}: {}",
filename.to_str().unwrap(),
e
))
}
});
println!("Loading {}", filename.to_str().unwrap());
let (primary, range, file_hash, path, route_tree): (
bool,
f32,
Vec<u8>,
String,
FnvHashMap<u32, u32>,
) = bincode::deserialize_from(&mut reader).unwrap();
) = match bincode::deserialize_from(&mut reader) {
Ok(res) => res,
Err(e) => {
return Err(format!(
"Error loading file {}: {}",
filename.to_str().unwrap(),
e
))
}
};
let path = PathBuf::from(path);
if hash_file(&path) != file_hash {
return Err("File hash mismatch!".to_string());
}
let cache = LineCache::new(&path);
let cache = LineCache::new(&path).ok();
Ok((
path.clone(),
Self {
@ -249,14 +301,12 @@ impl Router {
cache,
primary_only: primary,
path,
callback
callback,
radius_mult: 0f32,
},
))
}
fn closest(&self, point: &[f32; 3]) -> &System {
self.tree.nearest_neighbor(point).unwrap()
}
fn points_in_sphere(&self, center: &[f32; 3], radius: f32) -> impl Iterator<Item = &System> {
self.tree.locate_within_distance(*center, radius * radius)
}
@ -265,18 +315,23 @@ impl Router {
self.points_in_sphere(&sys.pos, sys.mult * r)
}
fn valid(&self, sys: &System) -> bool {
self.scoopable.contains(&sys.id)
fn valid(&self, sys: &System, src: &System, dst: &System) -> bool {
let scoopable = self.scoopable.contains(&sys.id);
if self.radius_mult == 0.0 {
return scoopable;
}
let df = src.distp(dst);
return (sys.distp(src) + sys.distp(dst)) < (df * (1.0 + self.radius_mult));
}
pub fn best_name_multiroute(
pub fn best_multiroute(
&self,
waypoints: &[String],
waypoints: &Vec<System>,
range: f32,
keep: (bool,bool),
keep: (bool, bool),
mode: Mode,
factor: f32,
) -> Result<Vec<System>,String> {
) -> Result<Vec<System>, String> {
let mut best_score: f32 = std::f32::MAX;
let mut waypoints = waypoints.to_owned();
let mut best_permutation_waypoints = waypoints.to_owned();
@ -287,17 +342,12 @@ impl Router {
loop {
let c_first = waypoints.first().cloned();
let c_last = waypoints.last().cloned();
let valid = (keep.0 && (c_first == first)) && (keep.1 && (c_last==last));
let valid = (keep.0 && (c_first == first)) && (keep.1 && (c_last == last));
if valid {
let mut total_d = 0.0;
for pair in waypoints.windows(2) {
match pair {
[src, dst] => {
let (mut src, dst) =
(self.name_to_systems(&src)?, self.name_to_systems(&dst)?);
src.sort_by_key(|&p| (p.mult * 10.0) as u8);
let src = src.last().unwrap();
let dst = dst.last().unwrap();
total_d += src.distp2(dst);
}
_ => return Err("Invalid routing parameters!".to_string()),
@ -313,35 +363,19 @@ impl Router {
}
}
println!("Best permutation: {:?}", best_permutation_waypoints);
self.name_multiroute(&best_permutation_waypoints, range, mode, factor)
self.multiroute(&best_permutation_waypoints, range, mode, factor)
}
pub fn name_multiroute(
&self,
waypoints: &[String],
range: f32,
mode: Mode,
factor: f32,
) -> Result<Vec<System>,String> {
let mut coords = Vec::new();
for p_name in waypoints {
let mut p_l = self.name_to_systems(p_name)?;
p_l.sort_by_key(|&p| (p.mult * 10.0) as u8);
let p = p_l.last().unwrap();
coords.push((p_name, p.pos));
}
self.multiroute(coords.as_slice(), range, mode, factor)
}
pub fn multiroute(
&self,
waypoints: &[(&String, [f32; 3])],
waypoints: &Vec<System>,
range: f32,
mode: Mode,
factor: f32,
) -> Result<Vec<System>,String> {
) -> Result<Vec<System>, String> {
let mut route: Vec<System> = Vec::new();
for pair in waypoints.windows(2) {
match *pair {
match pair.clone() {
[src, dst] => {
let block = match mode {
Mode::BFS => self.route_bfs(&src, &dst, range)?,
@ -358,56 +392,88 @@ impl Router {
}
}
}
_ => return Err("Invalid routing parameters!".to_string()),
_ => {
return Err("Invalid routing parameters!".to_owned());
}
}
}
Ok(route)
}
fn name_to_systems(&self, name: &str) -> Result<Vec<&System>,String> {
fn resolve_systems(&self, systems: &[SysEntry]) -> Result<Vec<System>, String> {
let mut ret = Vec::new();
let mut sys_by_id: FnvHashMap<u32, &System> = FnvHashMap::default();
let mut sys_by_name: FnvHashMap<String, &System> = FnvHashMap::default();
for sys in &self.tree {
if sys.system == name {
return Ok(self.neighbours(&sys, 0.0).collect());
for ent in systems {
match ent {
SysEntry::ID(i) => {
let i = *i;
if sys.id == i {
sys_by_id.insert(i, sys);
}
}
SysEntry::Name(n) => {
if &sys.body == n || &sys.system == n {
sys_by_name.insert(n.to_string(), sys);
}
}
}
}
}
Err(format!("System not found: \"{}\"", name))
for ent in systems {
match ent {
SysEntry::ID(i) => match sys_by_id.get(i) {
Some(sys) => ret.push(sys.clone().clone()),
None => {
return Err(format!("System: {:?} not found", ent));
}
},
SysEntry::Name(n) => match sys_by_name.get(n) {
Some(sys) => ret.push(sys.clone().clone()),
None => {
return Err(format!("System: {:?} not found", ent));
}
},
}
}
Ok(ret)
}
pub fn route_astar(
&self,
src: &(&String, [f32; 3]),
dst: &(&String, [f32; 3]),
src: &System,
dst: &System,
range: f32,
factor: f32,
) -> Result<Vec<System>,String> {
) -> Result<Vec<System>, String> {
if factor == 0.0 {
return self.route_bfs(src, dst, range);
}
let (src_name, src) = src;
let (dst_name, dst) = dst;
let start_sys = self.closest(src);
let goal_sys = self.closest(dst);
let d_total = dist(&start_sys.pos,&goal_sys.pos);
let mut d_rem=d_total;
let mut state=SearchState {
mode:"A-Star".into(),
depth:0,
queue_size:0,
let src_name = src.system.clone();
let dst_name = dst.system.clone();
let start_sys = src;
let goal_sys = dst;
let d_total = dist(&start_sys.pos, &goal_sys.pos);
let mut d_rem = d_total;
let mut state = SearchState {
mode: "A-Star".into(),
depth: 0,
queue_size: 0,
d_rem: d_total,
d_total: d_total,
d_total,
prc_done: 0.0,
n_seen:0,
n_seen: 0,
prc_seen: 0.0,
body: start_sys.body.clone(),
system: start_sys.system.clone()
system: start_sys.system.clone(),
};
let total = self.tree.size() as f32;
let t_start = Instant::now();
let mut t_last = Instant::now();
let mut prev = FnvHashMap::default();
let mut seen = FnvHashSet::default();
let mut found = false;
let mut maxd = 0;
let mut queue: Vec<(usize, usize, &System)> = Vec::new();
queue.push((
0, // depth
@ -417,31 +483,22 @@ impl Router {
seen.insert(start_sys.id);
while !(queue.is_empty() || found) {
while let Some((depth, _, sys)) = queue.pop() {
if (depth+1) > maxd {
maxd = depth+1;
state.depth=depth;
state.queue_size=queue.len();
state.prc_done=((d_total-d_rem)*100f32) / d_total;
state.d_rem=d_rem;
state.n_seen=seen.len();
state.prc_seen=((seen.len()*100) as f32) / total;
state.body=sys.body.clone();
state.system=sys.system.clone();
if t_last.elapsed().as_millis() > 100 {
t_last = Instant::now();
state.depth = depth;
state.queue_size = queue.len();
state.prc_done = ((d_total - d_rem) * 100f32) / d_total;
state.d_rem = d_rem;
state.n_seen = seen.len();
state.prc_seen = ((seen.len() * 100) as f32) / total;
state.body = sys.body.clone();
state.system = sys.system.clone();
match (self.callback)(&state) {
Ok(_) => (),
Err(e) => {
return Err(format!("{:?}",e).to_string());
return Err(format!("{:?}", e).to_string());
}
};
print!(
"[{}] Depth: {}, Queue: {}, Seen: {} ({:.02}%) \r",
format_duration(t_start.elapsed()),
depth,
queue.len(),
seen.len(),
((seen.len() * 100) as f32) / total
);
std::io::stdout().flush().unwrap();
}
if sys.id == goal_sys.id {
found = true;
@ -449,16 +506,15 @@ impl Router {
}
queue.extend(
self.neighbours(&sys, range)
.filter(|&nb| (self.valid(nb) || (nb.id == goal_sys.id)))
.filter(|&nb| (self.valid(nb, &src, &dst) || (nb.id == goal_sys.id)))
.filter(|&nb| seen.insert(nb.id))
.map(|nb| {
prev.insert(nb.id, sys);
let d_g = (nb.distp(goal_sys) / range) as usize;
let dist = dist(&nb.pos, &goal_sys.pos);
if dist < d_rem {
d_rem = dist;
let d_g=nb.distp(goal_sys);
if d_g<d_rem {
d_rem=d_g;
}
(depth + 1, d_g, nb)
(depth + 1, (d_g / range) as usize, nb)
}),
);
queue.sort_by(|b, a| {
@ -475,7 +531,7 @@ impl Router {
println!();
if !found {
return Err(format!("No route from {} to {} found!", src_name, dst_name).to_string());
return Err(format!("No route from {} to {} found!", src_name, dst_name));
}
let mut v: Vec<System> = Vec::new();
let mut curr_sys = goal_sys;
@ -494,54 +550,54 @@ impl Router {
pub fn route_greedy(
&self,
src: &(&String, [f32; 3]),
dst: &(&String, [f32; 3]),
src: &System,
dst: &System,
range: f32,
) -> Result<Vec<System>,String> {
let (src_name, src) = src;
let (dst_name, dst) = dst;
let start_sys = self.closest(src);
let goal_sys = self.closest(dst);
) -> Result<Vec<System>, String> {
let src_name = src.system.clone();
let dst_name = dst.system.clone();
let start_sys = src;
let goal_sys = dst;
let d_total = dist(&start_sys.pos, &goal_sys.pos);
let mut d_rem=d_total;
let mut state=SearchState {
mode:"Greedy".into(),
depth:0,
queue_size:0,
let mut d_rem = d_total;
let mut state = SearchState {
mode: "Greedy".into(),
depth: 0,
queue_size: 0,
d_rem: d_total,
d_total: d_total,
d_total,
prc_done: 0.0,
n_seen:0,
n_seen: 0,
prc_seen: 0.0,
body:start_sys.body.clone(),
system:start_sys.system.clone(),
body: start_sys.body.clone(),
system: start_sys.system.clone(),
};
let total = self.tree.size() as f32;
let mut t_last = Instant::now();
let mut prev = FnvHashMap::default();
let mut seen = FnvHashSet::default();
let mut found = false;
let mut maxd = 0;
let mut queue: Vec<(f32, f32, usize, &System)> = Vec::new();
queue.push((-goal_sys.mult, start_sys.distp2(goal_sys), 0, &start_sys));
queue.push((-goal_sys.mult, start_sys.distp(goal_sys), 0, &start_sys));
seen.insert(start_sys.id);
while !(queue.is_empty() || found) {
while let Some((_, _, depth, sys)) = queue.pop() {
if (depth+1) > maxd {
state.depth=depth;
state.queue_size=queue.len();
state.prc_done=((d_total-d_rem)*100f32) / d_total;
state.d_rem=d_rem;
state.n_seen=seen.len();
state.prc_seen=((seen.len()*100) as f32) / total;
state.body=sys.body.clone();
state.system=sys.system.clone();
if t_last.elapsed().as_millis() > 100 {
t_last = Instant::now();
state.depth = depth;
state.queue_size = queue.len();
state.prc_done = ((d_total - d_rem) * 100f32) / d_total;
state.d_rem = d_rem;
state.n_seen = seen.len();
state.prc_seen = ((seen.len() * 100) as f32) / total;
state.body = sys.body.clone();
state.system = sys.system.clone();
match (self.callback)(&state) {
Ok(_) => (),
Err(e) => {
return Err(format!("{:?}",e).to_string());
return Err(format!("{:?}", e).to_string());
}
};
maxd = depth+1;
}
if sys.id == goal_sys.id {
found = true;
@ -549,15 +605,15 @@ impl Router {
}
queue.extend(
self.neighbours(&sys, range)
.filter(|&nb| (self.valid(nb) || (nb.id == goal_sys.id)))
.filter(|&nb| (self.valid(nb, &src, &dst) || (nb.id == goal_sys.id)))
.filter(|&nb| seen.insert(nb.id))
.map(|nb| {
prev.insert(nb.id, sys);
let dist = dist(&nb.pos, &goal_sys.pos);
if dist < d_rem {
d_rem = dist;
let d_g=nb.distp(goal_sys);
if d_g<d_rem {
d_rem=d_g;
}
(-nb.mult, nb.distp2(goal_sys), depth + 1, nb)
(-nb.mult, d_g, depth + 1, nb)
}),
);
queue.sort_by(|a, b| fcmp(a.0, b.0).then(fcmp(a.1, b.1)));
@ -565,7 +621,7 @@ impl Router {
}
}
if !found {
return Err(format!("No route from {} to {} found!", src_name, dst_name).to_string());
return Err(format!("No route from {} to {} found!", src_name, dst_name));
}
let mut v: Vec<System> = Vec::new();
let mut curr_sys = goal_sys;
@ -582,10 +638,7 @@ impl Router {
Ok(v)
}
pub fn precompute(&mut self, src: &str) -> Result<(),String> {
let mut sys_l = self.name_to_systems(src)?;
sys_l.sort_by_key(|&sys| (sys.mult * 10.0) as u8);
let sys = sys_l.last().unwrap();
pub fn precompute(&mut self, src: &System) -> Result<(), String> {
let total = self.tree.size() as f32;
let t_start = Instant::now();
let mut prev = FnvHashMap::default();
@ -593,8 +646,8 @@ impl Router {
let mut depth = 0;
let mut queue: VecDeque<(usize, &System)> = VecDeque::new();
let mut queue_next: VecDeque<(usize, &System)> = VecDeque::new();
queue.push_front((0, &sys));
seen.insert(sys.id);
queue.push_front((0, &src));
seen.insert(src.id);
while !queue.is_empty() {
print!(
"[{}] Depth: {}, Queue: {}, Seen: {} ({:.02}%) \r",
@ -622,7 +675,7 @@ impl Router {
self.route_tree = Some(prev);
let ofn = format!(
"{}_{}{}.router",
src.replace("*", "").replace(" ", "_"),
src.system.replace("*", "").replace(" ", "_"),
self.range,
if self.primary_only { "_primary" } else { "" }
);
@ -634,13 +687,17 @@ impl Router {
String::from(self.path.to_str().unwrap()),
self.route_tree.as_ref().unwrap(),
);
return match bincode::serialize_into(&mut out_fh, &data) {
match bincode::serialize_into(&mut out_fh, &data) {
Ok(_) => Ok(()),
Err(e) => Err(format!("Error: {}",e).to_string())
};
Err(e) => Err(format!("Error: {}", e).to_string()),
}
}
fn get_systems_by_ids(&mut self, path: &PathBuf, ids: &[u32]) -> Result<FnvHashMap<u32, System>,String> {
fn get_systems_by_ids(
&mut self,
path: &PathBuf,
ids: &[u32],
) -> Result<FnvHashMap<u32, System>, String> {
let mut ret = FnvHashMap::default();
if let Some(c) = &mut self.cache.as_mut() {
let mut missing = false;
@ -659,13 +716,12 @@ impl Router {
return Ok(ret);
}
}
let mut reader = match csv::ReaderBuilder::new()
.from_path(path) {
Ok(reader) => reader,
Err(e) => {
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e));
}
};
let mut reader = match csv::ReaderBuilder::new().from_path(path) {
Ok(reader) => reader,
Err(e) => {
return Err(format!("Error opening {}: {}", path.to_str().unwrap(), e));
}
};
reader
.deserialize::<SystemSerde>()
.map(|res| res.unwrap())
@ -674,30 +730,20 @@ impl Router {
ret.insert(sys.id, sys.build());
})
.count();
for id in ids {
if !ret.contains_key(&id) {
return Err(format!("ID {} not found", id));
}
}
Ok(ret)
}
fn get_system_by_name(path: &PathBuf, name: &str) -> Result<System,String> {
// TODO: Fuzzy search (https://github.com/andylokandy/simsearch-rs)
let mut reader = csv::ReaderBuilder::new()
.from_path(path)
.unwrap_or_else(|e| {
eprintln!("Error opening {}: {}", path.to_str().unwrap(), e);
std::process::exit(1);
});
let sys = reader
.deserialize::<SystemSerde>()
.map(|res| res.unwrap())
.find(|sys| sys.system == name);
match sys {
Some(system) => Ok(system.build()),
None => Err(format!("System {} not found!",name).to_string())
}
}
pub fn route_to(&mut self, dst: &str, systems_path: &PathBuf) -> Result<Vec<System>,String> {
pub fn route_to(
&mut self,
dst: &System,
systems_path: &PathBuf,
) -> Result<Vec<System>, String> {
let prev = self.route_tree.as_ref().unwrap();
let dst = Self::get_system_by_name(&systems_path, dst)?;
if !prev.contains_key(&dst.id) {
return Err(format!("System-ID {} not found", dst.id).to_string());
};
@ -719,7 +765,7 @@ impl Router {
let sys = match id_map.get(&sys_id) {
Some(sys) => sys,
None => {
return Err(format!("System-ID {} not found!",sys_id));
return Err(format!("System-ID {} not found!", sys_id));
}
};
v.push(sys.clone())
@ -729,29 +775,29 @@ impl Router {
pub fn route_bfs(
&self,
src: &(&String, [f32; 3]),
dst: &(&String, [f32; 3]),
start_sys: &System,
goal_sys: &System,
range: f32,
) -> Result<Vec<System>,String> {
) -> Result<Vec<System>, String> {
println!("Running BFS");
let (src_name, src) = src;
let (dst_name, dst) = dst;
let start_sys = self.closest(src);
let goal_sys = self.closest(dst);
let src_name = start_sys.system.clone();
let dst_name = goal_sys.system.clone();
let d_total = dist(&start_sys.pos, &goal_sys.pos);
let mut state=SearchState {
mode:"BFS".into(),
depth:0,
queue_size:0,
d_rem: d_total,
d_total: d_total,
let mut d_rem = d_total;
let mut state = SearchState {
mode: "BFS".into(),
depth: 0,
queue_size: 0,
d_rem,
d_total,
prc_done: 0.0,
n_seen:0,
n_seen: 0,
prc_seen: 0.0,
system: start_sys.system.clone(),
body: start_sys.body.clone()
body: start_sys.body.clone(),
};
{
let d = dist(&start_sys.pos, &goal_sys.pos);
println!("Plotting route from {} to {}...", src_name, dst_name);
println!(
"Jump Range: {} Ly, Distance: {} Ly, Estimated Jumps: {}",
@ -765,41 +811,47 @@ impl Router {
let mut seen = FnvHashSet::default();
let mut depth = 0;
let mut found = false;
let mut t_last = Instant::now();
let mut queue: VecDeque<(usize, &System)> = VecDeque::new();
let mut queue_next: VecDeque<(usize, &System)> = VecDeque::new();
let mut d_rem = dist(&start_sys.pos, &goal_sys.pos);
queue.push_front((0, &start_sys));
seen.insert(start_sys.id);
while !(queue.is_empty() || found) {
state.depth=depth;
state.queue_size=queue.len();
state.prc_done=((d_total-d_rem)*100f32) / d_total;
state.d_rem=d_rem;
state.n_seen=seen.len();
state.prc_seen=((seen.len()*100) as f32) / total;
{
let s=queue.get(0).unwrap().1;
state.system=s.system.clone();
state.body=s.body.clone();
}
match (self.callback)(&state) {
Ok(_) => (),
Err(e) => {
return Err(format!("{:?}",e).to_string());
}
};
while let Some((d, sys)) = queue.pop_front() {
if sys.id == goal_sys.id {
found = true;
break;
}
if t_last.elapsed().as_millis() > 100 {
state.depth = depth;
state.queue_size = queue.len();
state.prc_done = ((d_total - d_rem) * 100f32) / d_total;
state.d_rem = d_rem;
state.n_seen = seen.len();
state.prc_seen = ((seen.len() * 100) as f32) / total;
{
let s = queue.get(0).unwrap().1;
state.system = s.system.clone();
state.body = s.body.clone();
}
match (self.callback)(&state) {
Ok(_) => (),
Err(e) => {
return Err(format!("{:?}", e).to_string());
}
};
t_last = Instant::now();
}
queue_next.extend(
self.neighbours(&sys, range)
.filter(|&nb| (self.valid(nb) || (nb.id == goal_sys.id)))
.filter(|&nb| {
(self.valid(nb, &start_sys, &goal_sys) || (nb.id == goal_sys.id))
})
.filter(|&nb| seen.insert(nb.id))
.map(|nb| {
prev.insert(nb.id, sys);
let dist = dist(&nb.pos, &goal_sys.pos);
let dist = nb.distp(goal_sys);
if dist < d_rem {
d_rem = dist;
}
@ -813,7 +865,7 @@ impl Router {
println!();
println!();
if !found {
return Err(format!("No route from {} to {} found!", src_name, dst_name).to_string());
return Err(format!("No route from {} to {} found!", src_name, dst_name));
}
let mut v: Vec<System> = Vec::new();
let mut curr_sys = goal_sys;
@ -830,54 +882,71 @@ impl Router {
Ok(v)
}
}
pub fn route(opts: RouteOpts) -> Result<Option<Vec<System>>,String> {
pub fn route(opts: RouteOpts) -> Result<Option<Vec<System>>, String> {
if opts.systems.is_empty() {
return if opts.precomp_file.is_some() {
Err("Error: Please specify exatly one system".to_string())
if opts.precomp_file.is_some() {
return Err("Error: Please specify exatly one system".to_owned());
} else if opts.precompute {
Err("Error: Please specify at least one system".to_string())
return Err("Error: Please specify at least one system".to_owned());
} else {
Err("Error: Please specify at least two systems".to_string())
}
return Err("Error: Please specify at least two systems".to_owned());
};
}
let mut path = opts.file_path;
let mut router: Router = if opts.precomp_file.is_some() {
let (path_,ret) = Router::from_file(&opts.precomp_file.clone().unwrap(),Box::new(opts.callback))?;
path=path_;
let (path_, ret) =
Router::from_file(&opts.precomp_file.clone().unwrap(), Box::new(opts.callback))?;
path = path_;
ret
} else if opts.range.is_some() {
Router::new(&path, opts.range.unwrap(), opts.primary, Box::new(opts.callback))?
Router::new(
&path,
opts.range.unwrap(),
opts.radius_mult,
opts.primary,
Box::new(opts.callback),
)?
} else {
return Err("Please specify a jump range!".to_string());
Router::new(
&path,
opts.range.unwrap(),
opts.radius_mult,
opts.primary,
opts.callback,
)?
};
let systems: Vec<System> = router
.resolve_systems(&opts.systems)?
.iter()
.map(|sys| sys.clone())
.collect();
if opts.precompute {
for sys in opts.systems {
for sys in systems {
router.route_tree = None;
router.precompute(&sys)?;
}
return Ok(None)
return Ok(None);
}
let route = if router.route_tree.is_some() {
router.route_to(opts.systems.first().unwrap(), &path)?
router.route_to(systems.first().unwrap(), &path)?
} else if opts.permute {
router.best_name_multiroute(
&opts.systems,
router.best_multiroute(
&systems,
opts.range.unwrap(),
(opts.keep_first,opts.keep_last),
(opts.keep_first, opts.keep_last),
opts.mode,
opts.factor.unwrap_or(1.0),
)?
} else {
router.name_multiroute(
&opts.systems,
router.multiroute(
&systems,
opts.range.unwrap(),
opts.mode,
opts.factor.unwrap_or(1.0),
)?
};
if route.is_empty() {
return Err("No route found!".to_string())
return Err("No route found!".to_string());
}
return Ok(Some(route));
Ok(Some(route))
}