From c290d5eb1223e2db92205c31d28c12614ec2d91a Mon Sep 17 00:00:00 2001 From: Daniel Seiller Date: Mon, 5 Aug 2019 00:05:44 +0200 Subject: [PATCH] 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 --- .chglog/CHANGELOG.tpl.md | 40 +++ .chglog/config.yml | 13 + .gitignore | 15 +- MANIFEST.in | 3 +- build.bat | 11 + ed_lrr_gui/__init__.py | 2 + ed_lrr_gui/__main__.py | 313 +++++++++++++++++++---- ed_lrr_gui/gui/ed_lrr.py | 92 +++++++ ed_lrr_gui/gui/ed_lrr.ui | 105 ++++++++ ed_lrr_gui/preprocess.py | 15 +- ed_lrr_gui/router.py | 12 - rust/Cargo.lock | 88 ++++++- rust/Cargo.toml | 5 +- rust/src/common.rs | 14 + rust/src/lib.rs | 188 +++++++++----- rust/src/route.rs | 537 ++++++++++++++++++++++----------------- setup.py | 9 +- test.bat | 4 + 18 files changed, 1075 insertions(+), 391 deletions(-) create mode 100644 .chglog/CHANGELOG.tpl.md create mode 100644 .chglog/config.yml create mode 100644 build.bat create mode 100644 test.bat diff --git a/.chglog/CHANGELOG.tpl.md b/.chglog/CHANGELOG.tpl.md new file mode 100644 index 0000000..1f731b6 --- /dev/null +++ b/.chglog/CHANGELOG.tpl.md @@ -0,0 +1,40 @@ +{{ if .Versions -}} + +## [Unreleased] + +{{ if .Unreleased.CommitGroups -}} +{{ range .Unreleased.CommitGroups -}} +{{ range .Commits -}} +- {{ .Header }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} + +{{ range .Versions }} + +## {{ 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 -}} \ No newline at end of file diff --git a/.chglog/config.yml b/.chglog/config.yml new file mode 100644 index 0000000..65dde53 --- /dev/null +++ b/.chglog/config.yml @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore index f8b9d2b..cfec1c9 100644 --- a/.gitignore +++ b/.gitignore @@ -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/* \ No newline at end of file +build diff --git a/MANIFEST.in b/MANIFEST.in index 900cc3b..fe1501c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ -include rust/Cargo.toml +include rust/Cargo.toml +include rust/.cargo/config recursive-include rust/src * \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..9487f53 --- /dev/null +++ b/build.bat @@ -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 .. \ No newline at end of file diff --git a/ed_lrr_gui/__init__.py b/ed_lrr_gui/__init__.py index 2d1a9d6..9bce23f 100644 --- a/ed_lrr_gui/__init__.py +++ b/ed_lrr_gui/__init__.py @@ -1,2 +1,4 @@ from _ed_lrr import * from . import gui +from .router import Router +from .preprocess import Preprocessor diff --git a/ed_lrr_gui/__main__.py b/ed_lrr_gui/__main__.py index 71c6b13..c03221f 100644 --- a/ed_lrr_gui/__main__.py +++ b/ed_lrr_gui/__main__.py @@ -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(): diff --git a/ed_lrr_gui/gui/ed_lrr.py b/ed_lrr_gui/gui/ed_lrr.py index 76ba5f8..721546f 100644 --- a/ed_lrr_gui/gui/ed_lrr.py +++ b/ed_lrr_gui/gui/ed_lrr.py @@ -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 diff --git a/ed_lrr_gui/gui/ed_lrr.ui b/ed_lrr_gui/gui/ed_lrr.ui index 7034a30..4685221 100644 --- a/ed_lrr_gui/gui/ed_lrr.ui +++ b/ed_lrr_gui/gui/ed_lrr.ui @@ -44,6 +44,15 @@ QTabWidget::Rounded +<<<<<<< Updated upstream +======= + + + 0 + 0 + + +>>>>>>> Stashed changes @@ -363,7 +372,11 @@ +<<<<<<< Updated upstream Search+Add +======= + Add +>>>>>>> Stashed changes @@ -402,6 +415,7 @@ +<<<<<<< Updated upstream @@ -409,6 +423,8 @@ +======= +>>>>>>> Stashed changes @@ -445,26 +461,53 @@ QAbstractItemView::SelectRows +<<<<<<< Updated upstream true 1 +======= + false + + + false + + + + Name + + + + + Type +>>>>>>> Stashed changes +<<<<<<< Updated upstream +======= + + + + +>>>>>>> Stashed changes Jump Range (Ly) +<<<<<<< Updated upstream +======= + +>>>>>>> Stashed changes @@ -536,7 +579,11 @@ +<<<<<<< Updated upstream +======= + +>>>>>>> Stashed changes GO! @@ -548,14 +595,22 @@ +<<<<<<< Updated upstream +======= + +>>>>>>> Stashed changes Last +<<<<<<< Updated upstream +======= + +>>>>>>> Stashed changes @@ -571,7 +626,11 @@ +<<<<<<< Updated upstream +======= + +>>>>>>> Stashed changes @@ -586,6 +645,23 @@ +<<<<<<< Updated upstream +======= + + + + Permute + + + + + + + Search All + + + +>>>>>>> Stashed changes @@ -633,7 +709,23 @@ +<<<<<<< Updated upstream +======= + + + Window + + + + Style + + + + + + +>>>>>>> Stashed changes @@ -644,6 +736,19 @@ Ctrl+Q +<<<<<<< Updated upstream +======= + + + A + + + + + B + + +>>>>>>> Stashed changes rd_comp diff --git a/ed_lrr_gui/preprocess.py b/ed_lrr_gui/preprocess.py index b7c8675..f59b2b4 100644 --- a/ed_lrr_gui/preprocess.py +++ b/ed_lrr_gui/preprocess.py @@ -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__": diff --git a/ed_lrr_gui/router.py b/ed_lrr_gui/router.py index 773f939..4e5bbfe 100644 --- a/ed_lrr_gui/router.py +++ b/ed_lrr_gui/router.py @@ -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}) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index d65855d..4ef4323 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -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" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d97be65..2a579f7 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -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" diff --git a/rust/src/common.rs b/rust/src/common.rs index 6e563f4..74d4d1f 100644 --- a/rust/src/common.rs +++ b/rust/src/common.rs @@ -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 { + return Some(self.cmp(other)); + } +} + diff --git a/rust/src/lib.rs b/rust/src/lib.rs index fdc0fb4..ee39dd4 100644 --- a/rust/src/lib.rs +++ b/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) -> Result)>,String> { + let mut best: HashMap)> = 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::(); + 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,sys_list:String) -> PyResult { + 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::(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, range: f32, + radius_mult: f32, mode: String, primary: bool, permute: bool, + keep_first: bool, + keep_last: bool, greedyness: Option, precomp: Option, path: String, callback: PyObject, ) -> PyResult { use route::*; - // TODO: Verify Parameters let mode = match Mode::parse(&mode) { Ok(val) => val, Err(e) => { - return Err(PyErr::new::(e)); + return Err(PyErr::new::(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::(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::(e)), + } } Ok(()) } diff --git a/rust/src/route.rs b/rust/src/route.rs index d206501..9da946b 100644 --- a/rust/src/route.rs +++ b/rust/src/route.rs @@ -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, @@ -41,8 +55,9 @@ pub struct RouteOpts { pub keep_last: bool, pub factor: Option, pub mode: Mode, - pub systems: Vec, - pub callback: Box PyResult> + pub radius_mult: f32, + pub systems: Vec, + pub callback: Box PyResult>, } #[derive(Debug)] @@ -52,9 +67,9 @@ pub enum Mode { AStar, } -impl Mode { +impl Mode { pub fn parse(s: &str) -> Result { - 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 { 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 PyResult> + radius_mult: f32, + callback: Box PyResult>, } impl Router { - pub fn new(path: &PathBuf, range: f32, primary_only: bool,callback: Box PyResult>) -> Result { + pub fn new( + path: &PathBuf, + range: f32, + radius_mult: f32, + primary_only: bool, + callback: Box PyResult>, + ) -> Result { 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 = reader .deserialize::() @@ -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 PyResult>) -> Result<(PathBuf, Self),String> { - let mut reader = BufReader::new(File::open(&filename).unwrap()); + pub fn from_file( + filename: &PathBuf, + callback: Box PyResult>, + ) -> 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, String, FnvHashMap, - ) = 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 { 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, range: f32, - keep: (bool,bool), + keep: (bool, bool), mode: Mode, factor: f32, - ) -> Result,String> { + ) -> Result, 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,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, range: f32, mode: Mode, factor: f32, - ) -> Result,String> { + ) -> Result, String> { let mut route: Vec = 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,String> { + fn resolve_systems(&self, systems: &[SysEntry]) -> Result, String> { + let mut ret = Vec::new(); + let mut sys_by_id: FnvHashMap = FnvHashMap::default(); + let mut sys_by_name: FnvHashMap = 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,String> { + ) -> Result, 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 = 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,String> { - let (src_name, src) = src; - let (dst_name, dst) = dst; - let start_sys = self.closest(src); - let goal_sys = self.closest(dst); + ) -> Result, 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 = 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,String> { + fn get_systems_by_ids( + &mut self, + path: &PathBuf, + ids: &[u32], + ) -> Result, 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::() .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 { - // 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::() - .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,String> { + pub fn route_to( + &mut self, + dst: &System, + systems_path: &PathBuf, + ) -> Result, 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,String> { + ) -> Result, 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 = Vec::new(); let mut curr_sys = goal_sys; @@ -830,54 +882,71 @@ impl Router { Ok(v) } } -pub fn route(opts: RouteOpts) -> Result>,String> { - +pub fn route(opts: RouteOpts) -> Result>, 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 = 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)) } diff --git a/setup.py b/setup.py index 6211685..8137fb5 100644 --- a/setup.py +++ b/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, ) diff --git a/test.bat b/test.bat new file mode 100644 index 0000000..b7cddb8 --- /dev/null +++ b/test.bat @@ -0,0 +1,4 @@ +python build_gui.py +pip uninstall -y ed_lrr_gui +pip install -e . +ed_lrr_gui