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:
parent
ec3972b06c
commit
c290d5eb12
|
@ -0,0 +1,40 @@
|
|||
{{ if .Versions -}}
|
||||
<a name="unreleased"></a>
|
||||
## [Unreleased]
|
||||
|
||||
{{ if .Unreleased.CommitGroups -}}
|
||||
{{ range .Unreleased.CommitGroups -}}
|
||||
{{ range .Commits -}}
|
||||
- {{ .Header }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
{{ range .Versions }}
|
||||
<a name="{{ .Tag.Name }}"></a>
|
||||
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
|
||||
{{ range .CommitGroups -}}
|
||||
{{ range .Commits -}}
|
||||
- {{ .Header }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
|
||||
{{- if .NoteGroups -}}
|
||||
{{ range .NoteGroups -}}
|
||||
### {{ .Title }}
|
||||
{{ range .Notes }}
|
||||
{{ .Body }}
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
{{- if .Versions }}
|
||||
[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
|
||||
{{ range .Versions -}}
|
||||
{{ if .Tag.Previous -}}
|
||||
[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
|
@ -0,0 +1,13 @@
|
|||
style: gitlab
|
||||
template: CHANGELOG.tpl.md
|
||||
info:
|
||||
title: CHANGELOG
|
||||
repository_url: https://gitlab.com/Earthnuker/ed_lrr
|
||||
options:
|
||||
header:
|
||||
pattern: "^(.*)$"
|
||||
pattern_maps:
|
||||
- Subject
|
||||
notes:
|
||||
keywords:
|
||||
- BREAKING CHANGE
|
|
@ -1,15 +1,14 @@
|
|||
/target
|
||||
/dist
|
||||
/build
|
||||
rust/target
|
||||
**/*.rs.bk
|
||||
*.tmp
|
||||
*.idx
|
||||
.vscode/**
|
||||
*.csv
|
||||
*.router
|
||||
plot.py
|
||||
*.tmp
|
||||
*.idx
|
||||
*.pyd
|
||||
build.bat
|
||||
test.bat
|
||||
__pycache__
|
||||
DL
|
||||
*.egg-info
|
||||
pip-wheel-metadata
|
||||
rust/target/*
|
||||
build
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
include rust/Cargo.toml
|
||||
include rust/Cargo.toml
|
||||
include rust/.cargo/config
|
||||
recursive-include rust/src *
|
|
@ -0,0 +1,11 @@
|
|||
rm -rf build dist *.egg-info exe
|
||||
python build_gui.py
|
||||
pip uninstall -y ed_lrr_gui
|
||||
pip install -I . setuptools_rust
|
||||
python setup.py build
|
||||
python setup.py bdist_wheel
|
||||
python setup.py sdist
|
||||
mkdir exe
|
||||
cd exe
|
||||
pyinstaller --noupx --noconsole --key="ED_LRR_GUI" --name ED_LRR_GUI ..\ed_lrr_gui\__main__.py
|
||||
cd ..
|
|
@ -1,2 +1,4 @@
|
|||
from _ed_lrr import *
|
||||
from . import gui
|
||||
from .router import Router
|
||||
from .preprocess import Preprocessor
|
||||
|
|
|
@ -2,21 +2,28 @@ import sys
|
|||
import os
|
||||
import requests as RQ
|
||||
from datetime import datetime, timedelta
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, Qt
|
||||
from PyQt5.QtCore import QTimer, QThread, pyqtSignal, Qt, QObject
|
||||
from PyQt5.QtWidgets import (
|
||||
QMainWindow,
|
||||
QApplication,
|
||||
QFileDialog,
|
||||
QProgressDialog,
|
||||
QTreeWidgetItem,
|
||||
QAction,
|
||||
QMessageBox,
|
||||
)
|
||||
from urllib.request import Request, urlopen
|
||||
import gzip
|
||||
import pathlib
|
||||
from PyQt5.QtGui import QPalette, QColor
|
||||
import ed_lrr_gui
|
||||
import ed_lrr_gui.config as cfg
|
||||
from ed_lrr_gui.gui.ed_lrr import Ui_ED_LRR
|
||||
from ed_lrr_gui import Router, Preprocessor
|
||||
import multiprocessing as MP
|
||||
import queue
|
||||
import csv
|
||||
import _ed_lrr
|
||||
|
||||
|
||||
def sizeof_fmt(num, suffix="B"):
|
||||
|
@ -37,6 +44,85 @@ class ProgressDialog(QProgressDialog):
|
|||
self.setWindowModality(Qt.WindowModal)
|
||||
|
||||
|
||||
class Job(QObject):
|
||||
progress = pyqtSignal("PyQt_PyObject")
|
||||
|
||||
def __init__(self, app, cls, *args, build_progress=None, **kwargs):
|
||||
super().__init__()
|
||||
self.job = cls(*args, **kwargs)
|
||||
self.timer = QTimer(app)
|
||||
self.app = app
|
||||
self.timer.timeout.connect(self.interval)
|
||||
self.timer.start(100)
|
||||
self.last_val = None
|
||||
self.build_progress = build_progress
|
||||
self.progress_dialog = None
|
||||
self.state = {}
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
if self.diag_prog is None:
|
||||
self.diag_prog = ProgressDialog("", "Cancel", 0, 1000, self.main_window)
|
||||
if self.dl_thread:
|
||||
self.diag_prog.canceled.connect(self.dl_canceled)
|
||||
self.diag_prog.show()
|
||||
t_elapsed = datetime.today() - self.dl_started
|
||||
rate = args["done"] / t_elapsed.total_seconds()
|
||||
remaining = (args["size"] - args["done"]) / rate
|
||||
rate = round(rate, 2)
|
||||
# print(rate, remaining)
|
||||
try:
|
||||
t_rem = timedelta(seconds=remaining)
|
||||
except OverflowError:
|
||||
t_rem = "-"
|
||||
msg = "Downloading {} [{}/{}] ({}/s)\n[{}/{}]".format(
|
||||
filename,
|
||||
sizeof_fmt(args["done"]),
|
||||
sizeof_fmt(args["size"]),
|
||||
sizeof_fmt(rate),
|
||||
t_round(t_elapsed),
|
||||
t_round(t_rem),
|
||||
)
|
||||
self.diag_prog.setLabelText(msg)
|
||||
self.diag_prog.setWindowTitle("Downloading EDSM Dumps")
|
||||
self.diag_prog.setValue((args["done"] * 1000) // args["size"])
|
||||
"""
|
||||
self.started = datetime.today()
|
||||
if self.build_progress:
|
||||
self.progress_dialog = ProgressDialog("", "Cancel", 0, 1000, self.app)
|
||||
self.progress_dialog.canceled.connect(self.cancel)
|
||||
self.progress_dialog.show()
|
||||
self.progress.connect(self.__build_progress)
|
||||
else:
|
||||
self.progress.connect(
|
||||
lambda *args, **kwargs: print("PROGRESS:", *args, **kwargs)
|
||||
)
|
||||
return self.job.start()
|
||||
|
||||
def __build_progress(self, *args, **kwargs):
|
||||
kwargs["self"] = self
|
||||
return self.build_progress(*args, **kwargs)
|
||||
|
||||
def cancel(self):
|
||||
self.job.terminate()
|
||||
self.job = None
|
||||
|
||||
def done(self):
|
||||
return (self.job.is_alive() == False) and (self.job.queue.empty())
|
||||
|
||||
def interval(self):
|
||||
while True:
|
||||
try:
|
||||
res = self.job.queue.get(True, 0.1)
|
||||
except queue.Empty:
|
||||
return
|
||||
if res == self.last_val:
|
||||
continue
|
||||
self.state.update(res)
|
||||
self.progress.emit(self.state)
|
||||
self.last_val = res
|
||||
|
||||
|
||||
class DownloadThread(QThread):
|
||||
progress = pyqtSignal("PyQt_PyObject")
|
||||
|
||||
|
@ -79,45 +165,65 @@ class App(QApplication):
|
|||
def __init__(self):
|
||||
super().__init__(sys.argv)
|
||||
self.setStyle("Fusion")
|
||||
self.set_pallete()
|
||||
self.setup_styles()
|
||||
|
||||
def set_pallete(self):
|
||||
colors = {
|
||||
"Window": QColor(53, 53, 53),
|
||||
"WindowText": Qt.white,
|
||||
"Base": QColor(15, 15, 15),
|
||||
"AlternateBase": QColor(53, 53, 53),
|
||||
"ToolTipBase": Qt.white,
|
||||
"ToolTipText": Qt.white,
|
||||
"Text": Qt.white,
|
||||
"Button": QColor(53, 53, 53),
|
||||
"ButtonText": Qt.white,
|
||||
"BrightText": Qt.red,
|
||||
"Highlight": QColor(255, 128, 0),
|
||||
"HighlightedText": Qt.black,
|
||||
def set_style(self, style):
|
||||
print("LOAD:", style)
|
||||
self.setPalette(self.styles[style])
|
||||
|
||||
def setup_styles(self):
|
||||
self.styles = {}
|
||||
styles = {
|
||||
"Dark": {
|
||||
"Window": QColor(53, 53, 53),
|
||||
"WindowText": Qt.white,
|
||||
"Base": QColor(15, 15, 15),
|
||||
"AlternateBase": QColor(53, 53, 53),
|
||||
"ToolTipBase": Qt.white,
|
||||
"ToolTipText": Qt.white,
|
||||
"Text": Qt.white,
|
||||
"Button": QColor(53, 53, 53),
|
||||
"ButtonText": Qt.white,
|
||||
"BrightText": Qt.red,
|
||||
"Highlight": QColor(255, 128, 0),
|
||||
"HighlightedText": Qt.black,
|
||||
}
|
||||
}
|
||||
palette = QPalette()
|
||||
for entry, color in colors.items():
|
||||
palette.setColor(getattr(QPalette, entry), color)
|
||||
if color == Qt.darkGray:
|
||||
palette.setColor(
|
||||
QPalette.Disabled, getattr(QPalette, entry), QColor(53, 53, 53)
|
||||
)
|
||||
else:
|
||||
palette.setColor(
|
||||
QPalette.Disabled, getattr(QPalette, entry), Qt.darkGray
|
||||
)
|
||||
self.setPalette(palette)
|
||||
for style, colors in styles.items():
|
||||
palette = QPalette()
|
||||
for entry, color in colors.items():
|
||||
palette.setColor(getattr(QPalette, entry), color)
|
||||
if color == Qt.darkGray:
|
||||
palette.setColor(
|
||||
QPalette.Disabled, getattr(QPalette, entry), QColor(53, 53, 53)
|
||||
)
|
||||
else:
|
||||
palette.setColor(
|
||||
QPalette.Disabled, getattr(QPalette, entry), Qt.darkGray
|
||||
)
|
||||
self.styles[style] = palette
|
||||
self.styles["Light"] = self.style().standardPalette()
|
||||
|
||||
|
||||
class ED_LRR(Ui_ED_LRR):
|
||||
dl_thread = None
|
||||
diag_prog = None
|
||||
dl_started = None
|
||||
system_found = pyqtSignal("PyQt_PyObject")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.config = cfg.load()
|
||||
self.jobs = {}
|
||||
|
||||
def start_job(self, cls, *args, **kwargs):
|
||||
print("START JOB:", cls, args, kwargs)
|
||||
name = cls.__name__
|
||||
if name in self.jobs and self.jobs[name].done():
|
||||
del self.jobs[name]
|
||||
if not name in self.jobs:
|
||||
self.jobs[name] = Job(self.app, cls, *args, **kwargs)
|
||||
self.jobs[name].start()
|
||||
|
||||
def get_open_file(self, filetypes, callback=None):
|
||||
fileName, _ = QFileDialog.getOpenFileName(
|
||||
|
@ -148,6 +254,8 @@ class ED_LRR(Ui_ED_LRR):
|
|||
self.config.history_out_path.append(path)
|
||||
self.inp_sys_lst.addItem(path)
|
||||
self.inp_out_pp.addItem(path)
|
||||
self.inp_sys_lst.setCurrentText(path)
|
||||
self.inp_out_pp.setCurrentText(path)
|
||||
|
||||
def set_bodies_file(self, path):
|
||||
if path not in self.config.history_bodies_path:
|
||||
|
@ -170,7 +278,7 @@ class ED_LRR(Ui_ED_LRR):
|
|||
def set_comp_mode(self, _):
|
||||
if self.rd_comp.isChecked():
|
||||
comp_mode = "Compute Route"
|
||||
self.btn_add.setText("Search+Add")
|
||||
self.btn_add.setText("Add")
|
||||
if self.rd_precomp.isChecked():
|
||||
comp_mode = "Precompute Graph"
|
||||
self.btn_add.setText("Select")
|
||||
|
@ -178,7 +286,7 @@ class ED_LRR(Ui_ED_LRR):
|
|||
self.lst_sys.setEnabled(self.rd_comp.isChecked())
|
||||
self.btn_rm.setEnabled(self.rd_comp.isChecked())
|
||||
self.cmb_mode.setEnabled(self.rd_comp.isChecked())
|
||||
self.btn_permute.setEnabled(self.rd_comp.isChecked())
|
||||
self.chk_permute.setEnabled(self.rd_comp.isChecked())
|
||||
self.lbl_keep.setEnabled(self.rd_comp.isChecked())
|
||||
self.lbl_mode.setEnabled(self.rd_comp.isChecked())
|
||||
self.chk_permute_keep_first.setEnabled(self.rd_comp.isChecked())
|
||||
|
@ -190,11 +298,17 @@ class ED_LRR(Ui_ED_LRR):
|
|||
mode = self.cmb_mode.currentText()
|
||||
self.lbl_greedyness.setEnabled(mode == "A*-Search")
|
||||
self.sld_greedyness.setEnabled(mode == "A*-Search")
|
||||
self.log("ROUTE_MODE", mode)
|
||||
|
||||
def set_greedyness(self, value):
|
||||
self.lbl_greedyness.setText("Greedyness Factor ({:.0%})".format(value / 100))
|
||||
|
||||
@property
|
||||
def systems(self):
|
||||
ret = []
|
||||
for n in range(self.lst_sys.topLevelItemCount()):
|
||||
ret.append(self.sys_to_dict(n))
|
||||
return ret
|
||||
|
||||
def sys_to_dict(self, n):
|
||||
header = [
|
||||
self.lst_sys.headerItem().data(c, 0)
|
||||
|
@ -204,34 +318,108 @@ class ED_LRR(Ui_ED_LRR):
|
|||
self.lst_sys.topLevelItem(n).data(c, 0)
|
||||
for c in range(self.lst_sys.topLevelItem(n).columnCount())
|
||||
]
|
||||
return dict(zip(header, system))
|
||||
ret = dict(zip(header, system))
|
||||
ret["id"] = getattr(self.lst_sys.topLevelItem(n), "__id__", None)
|
||||
ret.pop(None, None)
|
||||
return ret
|
||||
|
||||
def error(self, msg):
|
||||
QMessageBox.critical(self.main_window, "ERROR!", msg)
|
||||
|
||||
def get_sys_list(self):
|
||||
if not self.inp_sys_lst.currentText():
|
||||
self.error("System list is required!")
|
||||
return
|
||||
path = pathlib.Path(self.inp_sys_lst.currentText())
|
||||
if not path.exists():
|
||||
self.error("System list does not exist, run download and preprocess first!")
|
||||
return
|
||||
return path
|
||||
|
||||
def run(self):
|
||||
settings = {}
|
||||
settings["permute"] = (
|
||||
self.chk_permute_keep_first.checkState(),
|
||||
self.chk_permute_keep_last.checkState(),
|
||||
if not all(s["Type"] for s in self.systems):
|
||||
self.error('Not all systens have been resolved, please click "Search All"')
|
||||
return
|
||||
print(self.systems)
|
||||
systems = [str(s["id"]) for s in self.systems]
|
||||
jump_range = self.sb_range.value()
|
||||
mode = self.cmb_mode.currentText()
|
||||
primary = self.chk_primary.isChecked()
|
||||
keep_first = self.chk_permute_keep_first.isChecked()
|
||||
keep_last = self.chk_permute_keep_last.isChecked()
|
||||
permute = self.chk_permute.isChecked()
|
||||
greedyness = (
|
||||
self.sld_greedyness.value() / 100
|
||||
if self.sld_greedyness.isEnabled()
|
||||
else None
|
||||
)
|
||||
settings["range"] = self.sb_range.value()
|
||||
settings["systems"] = [
|
||||
self.sys_to_dict(i) for i in range(self.lst_sys.topLevelItemCount())
|
||||
]
|
||||
print(settings)
|
||||
progress = ProgressDialog(
|
||||
"(Not actually computing)", "Cancel", 0, 0, self.main_window
|
||||
path = self.get_sys_list()
|
||||
if path is None:
|
||||
return
|
||||
precomp = path.with_suffix(".idx")
|
||||
path = str(path)
|
||||
if not precomp.exists():
|
||||
precomp = None
|
||||
else:
|
||||
precomp = str(precomp)
|
||||
mode = {
|
||||
"Breadth-First Search": "bfs",
|
||||
"A*-Search": "astar",
|
||||
"Greedy-Search": "greedy",
|
||||
}[mode]
|
||||
print(
|
||||
systems,
|
||||
jump_range,
|
||||
mode,
|
||||
primary,
|
||||
permute,
|
||||
(keep_first, keep_last),
|
||||
greedyness,
|
||||
path,
|
||||
precomp,
|
||||
)
|
||||
progress.setWindowTitle("Computing Route")
|
||||
progress.setFixedSize(400, 100)
|
||||
progress.setRange(0, 0)
|
||||
progress.show()
|
||||
self.start_job(
|
||||
Router,
|
||||
systems,
|
||||
jump_range,
|
||||
0.1,
|
||||
mode,
|
||||
primary,
|
||||
permute,
|
||||
keep_first,
|
||||
keep_last,
|
||||
greedyness,
|
||||
precomp,
|
||||
path,
|
||||
)
|
||||
|
||||
def find_sys_by_names(self, names):
|
||||
t_s = datetime.today()
|
||||
if not self.get_sys_list():
|
||||
return None
|
||||
ret = _ed_lrr.find_sys(names, self.inp_sys_lst.currentText())
|
||||
print("Took:", datetime.today() - t_s)
|
||||
return ret
|
||||
|
||||
def resolve_systems(self):
|
||||
names = []
|
||||
for n in range(self.lst_sys.topLevelItemCount()):
|
||||
names.append(self.sys_to_dict(n)["Name"])
|
||||
systems = self.find_sys_by_names(names)
|
||||
if systems is None:
|
||||
return
|
||||
for i, name in enumerate(names):
|
||||
_, system = systems[name]
|
||||
self.lst_sys.topLevelItem(i).setData(0, 0, system["system"])
|
||||
self.lst_sys.topLevelItem(i).setData(1, 0, system["star_type"])
|
||||
self.lst_sys.topLevelItem(i).__id__ = system["id"]
|
||||
# diff, item = self.find_sys_by_name(name)
|
||||
# print("Found", (diff, item))
|
||||
|
||||
def add_system(self):
|
||||
n = self.lst_sys.topLevelItemCount() + 1
|
||||
name = self.inp_sys.text()
|
||||
item = QTreeWidgetItem(
|
||||
self.lst_sys, [str(n) + ". Name: " + name, "Type: " + name[::-1]]
|
||||
)
|
||||
item.sys_id = "SYS_ID_HERE"
|
||||
item = QTreeWidgetItem(self.lst_sys, [name, None])
|
||||
item.resolved = False
|
||||
item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled)
|
||||
|
||||
def remove_system(self):
|
||||
|
@ -253,7 +441,7 @@ class ED_LRR(Ui_ED_LRR):
|
|||
self.diag_prog = None
|
||||
self.dl_started = None
|
||||
|
||||
def handle_progress(self, args):
|
||||
def handle_dl_progress(self, args):
|
||||
filename = os.path.split(args["outfile"])[-1]
|
||||
if self.diag_prog is None:
|
||||
self.diag_prog = ProgressDialog("", "Cancel", 0, 1000, self.main_window)
|
||||
|
@ -291,10 +479,15 @@ class ED_LRR(Ui_ED_LRR):
|
|||
self.inp_bodies_dl.currentText(),
|
||||
self.inp_bodies_dest_dl.currentText(),
|
||||
)
|
||||
self.dl_thread.progress.connect(self.handle_progress)
|
||||
self.dl_thread.progress.connect(self.handle_dl_progress)
|
||||
self.dl_thread.start()
|
||||
print(".")
|
||||
|
||||
def update_permute_chk(self, state):
|
||||
self.chk_permute_keep_first.setEnabled(state)
|
||||
self.chk_permute_keep_last.setEnabled(state)
|
||||
self.lbl_keep.setEnabled(state)
|
||||
|
||||
def setup_signals(self):
|
||||
self.btn_download.clicked.connect(self.run_download)
|
||||
self.inp_systems_dest_dl.setCurrentText(r"D:\devel\rust\ed_lrr_gui\DL\s.json")
|
||||
|
@ -307,6 +500,8 @@ class ED_LRR(Ui_ED_LRR):
|
|||
self.btn_go.clicked.connect(self.run)
|
||||
self.btn_add.clicked.connect(self.add_system)
|
||||
self.btn_rm.clicked.connect(self.remove_system)
|
||||
self.chk_permute.stateChanged.connect(self.update_permute_chk)
|
||||
self.btn_search.clicked.connect(self.resolve_systems)
|
||||
self.btn_out_browse_pp.clicked.connect(
|
||||
lambda: self.get_save_file("CSV File (*.csv)", self.set_sys_lst)
|
||||
)
|
||||
|
@ -331,6 +526,14 @@ class ED_LRR(Ui_ED_LRR):
|
|||
cfg.write(self.config)
|
||||
print("BYEEEEEE!")
|
||||
|
||||
def setup_styles(self, win, app):
|
||||
for name in app.styles:
|
||||
action = QAction(app)
|
||||
action.setObjectName("action_load_style_" + name)
|
||||
action.setText(name)
|
||||
action.triggered.connect(lambda _, name=name: app.set_style(name))
|
||||
self.menuStyle.addAction(action)
|
||||
|
||||
def setupUi(self, MainWindow, app):
|
||||
super().setupUi(MainWindow)
|
||||
self.update_dropdowns()
|
||||
|
@ -339,6 +542,8 @@ class ED_LRR(Ui_ED_LRR):
|
|||
self.setup_signals()
|
||||
self.lst_sys.setHeaderLabels(["Name", "Type"])
|
||||
self.set_route_mode()
|
||||
self.update_permute_chk(self.chk_permute.isChecked())
|
||||
self.setup_styles(MainWindow, app)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -26,6 +26,14 @@ class Ui_ED_LRR(object):
|
|||
ED_LRR.setDocumentMode(False)
|
||||
ED_LRR.setTabShape(QtWidgets.QTabWidget.Rounded)
|
||||
self.centralwidget = QtWidgets.QWidget(ED_LRR)
|
||||
<<<<<<< Updated upstream
|
||||
=======
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
|
||||
self.centralwidget.setSizePolicy(sizePolicy)
|
||||
>>>>>>> Stashed changes
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
|
@ -214,9 +222,12 @@ class Ui_ED_LRR(object):
|
|||
self.rd_precomp.setObjectName("rd_precomp")
|
||||
self.gr_mode.addWidget(self.rd_precomp, 0, 2, 1, 1)
|
||||
self.formLayout_2.setLayout(3, QtWidgets.QFormLayout.FieldRole, self.gr_mode)
|
||||
<<<<<<< Updated upstream
|
||||
self.btn_permute = QtWidgets.QPushButton(self.tab_route)
|
||||
self.btn_permute.setObjectName("btn_permute")
|
||||
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.btn_permute)
|
||||
=======
|
||||
>>>>>>> Stashed changes
|
||||
self.lst_sys = QtWidgets.QTreeWidget(self.tab_route)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
|
@ -231,6 +242,7 @@ class Ui_ED_LRR(object):
|
|||
self.lst_sys.setAlternatingRowColors(True)
|
||||
self.lst_sys.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
self.lst_sys.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
<<<<<<< Updated upstream
|
||||
self.lst_sys.setHeaderHidden(True)
|
||||
self.lst_sys.setObjectName("lst_sys")
|
||||
self.lst_sys.headerItem().setText(0, "1")
|
||||
|
@ -241,6 +253,19 @@ class Ui_ED_LRR(object):
|
|||
self.lbl_range = QtWidgets.QLabel(self.tab_route)
|
||||
self.lbl_range.setObjectName("lbl_range")
|
||||
self.formLayout_2.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.lbl_range)
|
||||
=======
|
||||
self.lst_sys.setHeaderHidden(False)
|
||||
self.lst_sys.setObjectName("lst_sys")
|
||||
self.lst_sys.headerItem().setText(0, "Name")
|
||||
self.lst_sys.header().setVisible(False)
|
||||
self.formLayout_2.setWidget(7, QtWidgets.QFormLayout.SpanningRole, self.lst_sys)
|
||||
self.sb_range = QtWidgets.QDoubleSpinBox(self.tab_route)
|
||||
self.sb_range.setObjectName("sb_range")
|
||||
self.formLayout_2.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.sb_range)
|
||||
self.lbl_range = QtWidgets.QLabel(self.tab_route)
|
||||
self.lbl_range.setObjectName("lbl_range")
|
||||
self.formLayout_2.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.lbl_range)
|
||||
>>>>>>> Stashed changes
|
||||
self.gr_opts = QtWidgets.QGridLayout()
|
||||
self.gr_opts.setObjectName("gr_opts")
|
||||
self.cmb_mode = QtWidgets.QComboBox(self.tab_route)
|
||||
|
@ -268,16 +293,28 @@ class Ui_ED_LRR(object):
|
|||
self.lbl_mode = QtWidgets.QLabel(self.tab_route)
|
||||
self.lbl_mode.setObjectName("lbl_mode")
|
||||
self.gr_opts.addWidget(self.lbl_mode, 0, 1, 1, 1)
|
||||
<<<<<<< Updated upstream
|
||||
self.formLayout_2.setLayout(9, QtWidgets.QFormLayout.SpanningRole, self.gr_opts)
|
||||
self.btn_go = QtWidgets.QPushButton(self.tab_route)
|
||||
self.btn_go.setFlat(False)
|
||||
self.btn_go.setObjectName("btn_go")
|
||||
self.formLayout_2.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.btn_go)
|
||||
=======
|
||||
self.formLayout_2.setLayout(10, QtWidgets.QFormLayout.SpanningRole, self.gr_opts)
|
||||
self.btn_go = QtWidgets.QPushButton(self.tab_route)
|
||||
self.btn_go.setFlat(False)
|
||||
self.btn_go.setObjectName("btn_go")
|
||||
self.formLayout_2.setWidget(11, QtWidgets.QFormLayout.LabelRole, self.btn_go)
|
||||
>>>>>>> Stashed changes
|
||||
self.gridLayout_4 = QtWidgets.QGridLayout()
|
||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
||||
self.chk_permute_keep_last = QtWidgets.QCheckBox(self.tab_route)
|
||||
self.chk_permute_keep_last.setObjectName("chk_permute_keep_last")
|
||||
<<<<<<< Updated upstream
|
||||
self.gridLayout_4.addWidget(self.chk_permute_keep_last, 0, 2, 1, 1)
|
||||
=======
|
||||
self.gridLayout_4.addWidget(self.chk_permute_keep_last, 0, 3, 1, 1)
|
||||
>>>>>>> Stashed changes
|
||||
self.chk_permute_keep_first = QtWidgets.QCheckBox(self.tab_route)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
|
@ -286,7 +323,11 @@ class Ui_ED_LRR(object):
|
|||
self.chk_permute_keep_first.setSizePolicy(sizePolicy)
|
||||
self.chk_permute_keep_first.setTristate(False)
|
||||
self.chk_permute_keep_first.setObjectName("chk_permute_keep_first")
|
||||
<<<<<<< Updated upstream
|
||||
self.gridLayout_4.addWidget(self.chk_permute_keep_first, 0, 1, 1, 1)
|
||||
=======
|
||||
self.gridLayout_4.addWidget(self.chk_permute_keep_first, 0, 2, 1, 1)
|
||||
>>>>>>> Stashed changes
|
||||
self.lbl_keep = QtWidgets.QLabel(self.tab_route)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
|
@ -294,8 +335,19 @@ class Ui_ED_LRR(object):
|
|||
sizePolicy.setHeightForWidth(self.lbl_keep.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_keep.setSizePolicy(sizePolicy)
|
||||
self.lbl_keep.setObjectName("lbl_keep")
|
||||
<<<<<<< Updated upstream
|
||||
self.gridLayout_4.addWidget(self.lbl_keep, 0, 0, 1, 1)
|
||||
self.formLayout_2.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.gridLayout_4)
|
||||
=======
|
||||
self.gridLayout_4.addWidget(self.lbl_keep, 0, 1, 1, 1)
|
||||
self.formLayout_2.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.gridLayout_4)
|
||||
self.chk_permute = QtWidgets.QCheckBox(self.tab_route)
|
||||
self.chk_permute.setObjectName("chk_permute")
|
||||
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.chk_permute)
|
||||
self.btn_search = QtWidgets.QPushButton(self.tab_route)
|
||||
self.btn_search.setObjectName("btn_search")
|
||||
self.formLayout_2.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.btn_search)
|
||||
>>>>>>> Stashed changes
|
||||
self.tabs.addTab(self.tab_route, "")
|
||||
self.tab_log = QtWidgets.QWidget()
|
||||
self.tab_log.setObjectName("tab_log")
|
||||
|
@ -317,14 +369,32 @@ class Ui_ED_LRR(object):
|
|||
self.menu.setObjectName("menu")
|
||||
self.menu_file = QtWidgets.QMenu(self.menu)
|
||||
self.menu_file.setObjectName("menu_file")
|
||||
<<<<<<< Updated upstream
|
||||
=======
|
||||
self.menuWindow = QtWidgets.QMenu(self.menu)
|
||||
self.menuWindow.setObjectName("menuWindow")
|
||||
self.menuStyle = QtWidgets.QMenu(self.menuWindow)
|
||||
self.menuStyle.setObjectName("menuStyle")
|
||||
>>>>>>> Stashed changes
|
||||
ED_LRR.setMenuBar(self.menu)
|
||||
self.bar_status = QtWidgets.QStatusBar(ED_LRR)
|
||||
self.bar_status.setObjectName("bar_status")
|
||||
ED_LRR.setStatusBar(self.bar_status)
|
||||
self.menu_act_quit = QtWidgets.QAction(ED_LRR)
|
||||
self.menu_act_quit.setObjectName("menu_act_quit")
|
||||
<<<<<<< Updated upstream
|
||||
self.menu_file.addAction(self.menu_act_quit)
|
||||
self.menu.addAction(self.menu_file.menuAction())
|
||||
=======
|
||||
self.actionA = QtWidgets.QAction(ED_LRR)
|
||||
self.actionA.setObjectName("actionA")
|
||||
self.actionB = QtWidgets.QAction(ED_LRR)
|
||||
self.actionB.setObjectName("actionB")
|
||||
self.menu_file.addAction(self.menu_act_quit)
|
||||
self.menuWindow.addAction(self.menuStyle.menuAction())
|
||||
self.menu.addAction(self.menu_file.menuAction())
|
||||
self.menu.addAction(self.menuWindow.menuAction())
|
||||
>>>>>>> Stashed changes
|
||||
|
||||
self.retranslateUi(ED_LRR)
|
||||
self.tabs.setCurrentIndex(2)
|
||||
|
@ -358,12 +428,20 @@ class Ui_ED_LRR(object):
|
|||
self.tabs.setTabText(self.tabs.indexOf(self.tab_preprocess), _translate("ED_LRR", "Preprocess"))
|
||||
self.lbl_sys_lst.setText(_translate("ED_LRR", "System List"))
|
||||
self.btn_sys_lst_browse.setText(_translate("ED_LRR", "..."))
|
||||
<<<<<<< Updated upstream
|
||||
self.btn_add.setText(_translate("ED_LRR", "Search+Add"))
|
||||
=======
|
||||
self.btn_add.setText(_translate("ED_LRR", "Add"))
|
||||
>>>>>>> Stashed changes
|
||||
self.inp_sys.setPlaceholderText(_translate("ED_LRR", "System Name"))
|
||||
self.btn_rm.setText(_translate("ED_LRR", "Remove"))
|
||||
self.rd_comp.setText(_translate("ED_LRR", "Compute Route"))
|
||||
self.rd_precomp.setText(_translate("ED_LRR", "Precompute Graph"))
|
||||
<<<<<<< Updated upstream
|
||||
self.btn_permute.setText(_translate("ED_LRR", "Permute"))
|
||||
=======
|
||||
self.lst_sys.headerItem().setText(1, _translate("ED_LRR", "Type"))
|
||||
>>>>>>> Stashed changes
|
||||
self.lbl_range.setText(_translate("ED_LRR", "Jump Range (Ly)"))
|
||||
self.cmb_mode.setCurrentText(_translate("ED_LRR", "Breadth-First Search"))
|
||||
self.cmb_mode.setItemText(0, _translate("ED_LRR", "Breadth-First Search"))
|
||||
|
@ -376,8 +454,22 @@ class Ui_ED_LRR(object):
|
|||
self.chk_permute_keep_last.setText(_translate("ED_LRR", "Last"))
|
||||
self.chk_permute_keep_first.setText(_translate("ED_LRR", "First"))
|
||||
self.lbl_keep.setText(_translate("ED_LRR", "Keep Endpoints:"))
|
||||
<<<<<<< Updated upstream
|
||||
self.tabs.setTabText(self.tabs.indexOf(self.tab_route), _translate("ED_LRR", "Route"))
|
||||
self.tabs.setTabText(self.tabs.indexOf(self.tab_log), _translate("ED_LRR", "Log"))
|
||||
self.menu_file.setTitle(_translate("ED_LRR", "File"))
|
||||
self.menu_act_quit.setText(_translate("ED_LRR", "Quit"))
|
||||
self.menu_act_quit.setShortcut(_translate("ED_LRR", "Ctrl+Q"))
|
||||
=======
|
||||
self.chk_permute.setText(_translate("ED_LRR", "Permute"))
|
||||
self.btn_search.setText(_translate("ED_LRR", "Search All"))
|
||||
self.tabs.setTabText(self.tabs.indexOf(self.tab_route), _translate("ED_LRR", "Route"))
|
||||
self.tabs.setTabText(self.tabs.indexOf(self.tab_log), _translate("ED_LRR", "Log"))
|
||||
self.menu_file.setTitle(_translate("ED_LRR", "File"))
|
||||
self.menuWindow.setTitle(_translate("ED_LRR", "Window"))
|
||||
self.menuStyle.setTitle(_translate("ED_LRR", "Style"))
|
||||
self.menu_act_quit.setText(_translate("ED_LRR", "Quit"))
|
||||
self.menu_act_quit.setShortcut(_translate("ED_LRR", "Ctrl+Q"))
|
||||
self.actionA.setText(_translate("ED_LRR", "A"))
|
||||
self.actionB.setText(_translate("ED_LRR", "B"))
|
||||
>>>>>>> Stashed changes
|
||||
|
|
|
@ -44,6 +44,15 @@
|
|||
<enum>QTabWidget::Rounded</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<<<<<<< Updated upstream
|
||||
=======
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
>>>>>>> Stashed changes
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabs">
|
||||
|
@ -363,7 +372,11 @@
|
|||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="btn_add">
|
||||
<property name="text">
|
||||
<<<<<<< Updated upstream
|
||||
<string>Search+Add</string>
|
||||
=======
|
||||
<string>Add</string>
|
||||
>>>>>>> Stashed changes
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -402,6 +415,7 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<<<<<<< Updated upstream
|
||||
<item row="4" column="0">
|
||||
<widget class="QPushButton" name="btn_permute">
|
||||
<property name="text">
|
||||
|
@ -409,6 +423,8 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
=======
|
||||
>>>>>>> Stashed changes
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QTreeWidget" name="lst_sys">
|
||||
<property name="sizePolicy">
|
||||
|
@ -445,26 +461,53 @@
|
|||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<<<<<<< Updated upstream
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
=======
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
>>>>>>> Stashed changes
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<<<<<<< Updated upstream
|
||||
<item row="8" column="1">
|
||||
<widget class="QDoubleSpinBox" name="sb_range"/>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
=======
|
||||
<item row="9" column="1">
|
||||
<widget class="QDoubleSpinBox" name="sb_range"/>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
>>>>>>> Stashed changes
|
||||
<widget class="QLabel" name="lbl_range">
|
||||
<property name="text">
|
||||
<string>Jump Range (Ly)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<<<<<<< Updated upstream
|
||||
<item row="9" column="0" colspan="2">
|
||||
=======
|
||||
<item row="10" column="0" colspan="2">
|
||||
>>>>>>> Stashed changes
|
||||
<layout class="QGridLayout" name="gr_opts">
|
||||
<item row="0" column="2">
|
||||
<widget class="QComboBox" name="cmb_mode">
|
||||
|
@ -536,7 +579,11 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<<<<<<< Updated upstream
|
||||
<item row="10" column="0">
|
||||
=======
|
||||
<item row="11" column="0">
|
||||
>>>>>>> Stashed changes
|
||||
<widget class="QPushButton" name="btn_go">
|
||||
<property name="text">
|
||||
<string>GO!</string>
|
||||
|
@ -548,14 +595,22 @@
|
|||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<<<<<<< Updated upstream
|
||||
<item row="0" column="2">
|
||||
=======
|
||||
<item row="0" column="3">
|
||||
>>>>>>> Stashed changes
|
||||
<widget class="QCheckBox" name="chk_permute_keep_last">
|
||||
<property name="text">
|
||||
<string>Last</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<<<<<<< Updated upstream
|
||||
<item row="0" column="1">
|
||||
=======
|
||||
<item row="0" column="2">
|
||||
>>>>>>> Stashed changes
|
||||
<widget class="QCheckBox" name="chk_permute_keep_first">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
|
@ -571,7 +626,11 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<<<<<<< Updated upstream
|
||||
<item row="0" column="0">
|
||||
=======
|
||||
<item row="0" column="1">
|
||||
>>>>>>> Stashed changes
|
||||
<widget class="QLabel" name="lbl_keep">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
|
@ -586,6 +645,23 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<<<<<<< Updated upstream
|
||||
=======
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="chk_permute">
|
||||
<property name="text">
|
||||
<string>Permute</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QPushButton" name="btn_search">
|
||||
<property name="text">
|
||||
<string>Search All</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
>>>>>>> Stashed changes
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_log">
|
||||
|
@ -633,7 +709,23 @@
|
|||
</property>
|
||||
<addaction name="menu_act_quit"/>
|
||||
</widget>
|
||||
<<<<<<< Updated upstream
|
||||
<addaction name="menu_file"/>
|
||||
=======
|
||||
<widget class="QMenu" name="menuWindow">
|
||||
<property name="title">
|
||||
<string>Window</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuStyle">
|
||||
<property name="title">
|
||||
<string>Style</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menuStyle"/>
|
||||
</widget>
|
||||
<addaction name="menu_file"/>
|
||||
<addaction name="menuWindow"/>
|
||||
>>>>>>> Stashed changes
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="bar_status"/>
|
||||
<action name="menu_act_quit">
|
||||
|
@ -644,6 +736,19 @@
|
|||
<string>Ctrl+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<<<<<<< Updated upstream
|
||||
=======
|
||||
<action name="actionA">
|
||||
<property name="text">
|
||||
<string>A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionB">
|
||||
<property name="text">
|
||||
<string>B</string>
|
||||
</property>
|
||||
</action>
|
||||
>>>>>>> Stashed changes
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>rd_comp</tabstop>
|
||||
|
|
|
@ -14,24 +14,13 @@ class Preprocessor(Process):
|
|||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.kwargs["callback"] = self.callback
|
||||
self.start()
|
||||
|
||||
def __iter__(self):
|
||||
while self.is_alive():
|
||||
try:
|
||||
self.state.update(self.queue.get(True, 0.5))
|
||||
yield self.state
|
||||
except queue.Empty:
|
||||
pass
|
||||
while not self.queue.empty():
|
||||
self.state.update(self.queue.get(True, 0.5))
|
||||
yield self.state
|
||||
|
||||
def callback(self, state):
|
||||
self.queue.put({"status": state})
|
||||
|
||||
def run(self):
|
||||
_ed_lrr.preprocess(*self.args, **self.kwargs)
|
||||
res = _ed_lrr.preprocess(*self.args, **self.kwargs)
|
||||
self.queue.put({"result": res})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -14,18 +14,6 @@ class Router(Process):
|
|||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.kwargs["callback"] = self.callback
|
||||
self.start()
|
||||
|
||||
def __iter__(self):
|
||||
while self.is_alive():
|
||||
try:
|
||||
self.state.update(self.queue.get(True, 0.5))
|
||||
yield self.state
|
||||
except queue.Empty:
|
||||
pass
|
||||
while not self.queue.empty():
|
||||
self.state.update(self.queue.get(True, 0.5))
|
||||
yield self.state
|
||||
|
||||
def callback(self, state):
|
||||
self.queue.put({"status": state})
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
188
rust/src/lib.rs
188
rust/src/lib.rs
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
9
setup.py
9
setup.py
|
@ -18,7 +18,14 @@ setup(
|
|||
],
|
||||
packages=["ed_lrr_gui"],
|
||||
entry_points={"console_scripts": ["ed_lrr_gui=ed_lrr_gui.__main__:main"]},
|
||||
install_requires=["PyQt5", "appdirs", "PyYAML", "requests", "python-dateutil"],
|
||||
install_requires=[
|
||||
"PyQt5",
|
||||
"appdirs",
|
||||
"PyYAML",
|
||||
"requests",
|
||||
"python-dateutil",
|
||||
"pyperclip",
|
||||
],
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue