Compare commits
No commits in common. "master" and "dev" have entirely different histories.
|
@ -1,2 +0,0 @@
|
|||
[build]
|
||||
rustflags = ["-C", "target-cpu=native"]
|
|
@ -0,0 +1,40 @@
|
|||
{{ if .Versions -}}
|
||||
## [Unreleased]
|
||||
|
||||
{{ if .Unreleased.CommitGroups -}}
|
||||
{{ range .Unreleased.CommitGroups -}}
|
||||
### {{ .Title }}
|
||||
{{ range .Commits -}}
|
||||
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [{{.Hash.Short}}]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Short }})
|
||||
{{ end }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
|
||||
{{ range .Versions }}
|
||||
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
|
||||
{{ range .CommitGroups -}}
|
||||
### {{ .Title }}
|
||||
{{ range .Commits -}}
|
||||
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} [{{.Hash.Short}}]({{ $.Info.RepositoryURL }}/commit/{{ .Hash.Short }})
|
||||
{{ 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,34 @@
|
|||
style: gitlab
|
||||
template: CHANGELOG.tpl.md
|
||||
info:
|
||||
title: CHANGELOG
|
||||
repository_url: https://gitlab.com/Earthnuker/ed_lrr
|
||||
options:
|
||||
commits:
|
||||
filters:
|
||||
Type:
|
||||
- feat
|
||||
- fix
|
||||
- perf
|
||||
- refactor
|
||||
- misc
|
||||
- other
|
||||
- docs
|
||||
commit_groups:
|
||||
title_maps:
|
||||
feat: Features
|
||||
fix: Bug Fixes
|
||||
perf: Performance Improvements
|
||||
refactor: Code Refactoring
|
||||
misc: Miscellaneous
|
||||
other: Other
|
||||
docs: Documentation
|
||||
header:
|
||||
pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$"
|
||||
pattern_maps:
|
||||
- Type
|
||||
- Scope
|
||||
- Subject
|
||||
notes:
|
||||
keywords:
|
||||
- BREAKING CHANGE
|
|
@ -0,0 +1,28 @@
|
|||
on: push
|
||||
env:
|
||||
PYTHON_VERSION: 3.7
|
||||
RUST_TOOLCHAIN: nightly
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
name: Build Rust Extension for ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4.0.0
|
||||
with:
|
||||
python-version: ${{env.PYTHON_VERSION}}
|
||||
cache: pip
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: ${{env.RUST_TOOLCHAIN}}
|
||||
profile: minimal
|
||||
default: true
|
||||
- name: Build Python Extension
|
||||
run: |
|
||||
python -m build .
|
|
@ -1,9 +1,31 @@
|
|||
/target
|
||||
rust/target
|
||||
rust/.history/
|
||||
rust/Cargo.lock
|
||||
rust/ed_lrr_test.log
|
||||
**/*.rs.bk
|
||||
*.tmp
|
||||
*.idx
|
||||
.vscode/**
|
||||
*.csv
|
||||
*.router
|
||||
dumps/*.json
|
||||
plot.py
|
||||
*.tmp
|
||||
*.idx
|
||||
plot.py
|
||||
*.tmp
|
||||
*.idx
|
||||
*.pyd
|
||||
__pycache__
|
||||
*.egg-info
|
||||
build
|
||||
*.pdf
|
||||
.history
|
||||
.tox
|
||||
pip-wheel-metadata
|
||||
.eggs/
|
||||
dist/
|
||||
installer/Output/
|
||||
workspace.code-workspace
|
||||
ed_lrr_gui/web/jobs.db
|
||||
ed_lrr_gui/web/ed_lrr_web_ui.db
|
||||
__version__.py
|
||||
.nox/
|
||||
dist_vis.py
|
||||
img/**
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
# This file is a template, and might need editing before it works on your project.
|
||||
# Official language image. Look for the different tagged releases at:
|
||||
# https://hub.docker.com/r/library/rust/tags/
|
||||
image: "rust:latest"
|
||||
|
||||
# Optional: Pick zero or more services to be used on all builds.
|
||||
# Only needed when using a docker container to run your tests in.
|
||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
||||
# services:
|
||||
# - mysql:latest
|
||||
# - redis:latest
|
||||
# - postgres:latest
|
||||
|
||||
# Optional: Install a C compiler, cmake and git into the container.
|
||||
# You will often need this when you (or any of your dependencies) depends on C code.
|
||||
before_script:
|
||||
- apt-get update -yqq
|
||||
- apt-get install -yqq --no-install-recommends build-essential
|
||||
- rustup update nightly
|
||||
- rustup default nightly
|
||||
|
||||
# Use cargo to test the project
|
||||
test:cargo:
|
||||
script:
|
||||
- rustc --version && cargo --version # Print version info for debugging
|
||||
- cargo build --release
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"cells": [],
|
||||
"metadata": {},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
|
@ -0,0 +1,904 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Populating the interactive namespace from numpy and matplotlib\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pylab notebook"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 131,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<ipython-input-131-1f27bb9e71ac>:5: RuntimeWarning: invalid value encountered in double_scalars\n",
|
||||
" ld = (((a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c))**0.5)/(c*2.0)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/javascript": [
|
||||
"/* Put everything inside the global mpl namespace */\n",
|
||||
"window.mpl = {};\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.get_websocket_type = function() {\n",
|
||||
" if (typeof(WebSocket) !== 'undefined') {\n",
|
||||
" return WebSocket;\n",
|
||||
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
||||
" return MozWebSocket;\n",
|
||||
" } else {\n",
|
||||
" alert('Your browser does not have WebSocket support. ' +\n",
|
||||
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
||||
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
||||
" 'have to enable WebSockets in about:config.');\n",
|
||||
" };\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
||||
" this.id = figure_id;\n",
|
||||
"\n",
|
||||
" this.ws = websocket;\n",
|
||||
"\n",
|
||||
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
||||
"\n",
|
||||
" if (!this.supports_binary) {\n",
|
||||
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
||||
" if (warnings) {\n",
|
||||
" warnings.style.display = 'block';\n",
|
||||
" warnings.textContent = (\n",
|
||||
" \"This browser does not support binary websocket messages. \" +\n",
|
||||
" \"Performance may be slow.\");\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" this.imageObj = new Image();\n",
|
||||
"\n",
|
||||
" this.context = undefined;\n",
|
||||
" this.message = undefined;\n",
|
||||
" this.canvas = undefined;\n",
|
||||
" this.rubberband_canvas = undefined;\n",
|
||||
" this.rubberband_context = undefined;\n",
|
||||
" this.format_dropdown = undefined;\n",
|
||||
"\n",
|
||||
" this.image_mode = 'full';\n",
|
||||
"\n",
|
||||
" this.root = $('<div/>');\n",
|
||||
" this._root_extra_style(this.root)\n",
|
||||
" this.root.attr('style', 'display: inline-block');\n",
|
||||
"\n",
|
||||
" $(parent_element).append(this.root);\n",
|
||||
"\n",
|
||||
" this._init_header(this);\n",
|
||||
" this._init_canvas(this);\n",
|
||||
" this._init_toolbar(this);\n",
|
||||
"\n",
|
||||
" var fig = this;\n",
|
||||
"\n",
|
||||
" this.waiting = false;\n",
|
||||
"\n",
|
||||
" this.ws.onopen = function () {\n",
|
||||
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
||||
" fig.send_message(\"send_image_mode\", {});\n",
|
||||
" if (mpl.ratio != 1) {\n",
|
||||
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
||||
" }\n",
|
||||
" fig.send_message(\"refresh\", {});\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" this.imageObj.onload = function() {\n",
|
||||
" if (fig.image_mode == 'full') {\n",
|
||||
" // Full images could contain transparency (where diff images\n",
|
||||
" // almost always do), so we need to clear the canvas so that\n",
|
||||
" // there is no ghosting.\n",
|
||||
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
||||
" }\n",
|
||||
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
||||
" };\n",
|
||||
"\n",
|
||||
" this.imageObj.onunload = function() {\n",
|
||||
" fig.ws.close();\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
||||
"\n",
|
||||
" this.ondownload = ondownload;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._init_header = function() {\n",
|
||||
" var titlebar = $(\n",
|
||||
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
||||
" 'ui-helper-clearfix\"/>');\n",
|
||||
" var titletext = $(\n",
|
||||
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
||||
" 'text-align: center; padding: 3px;\"/>');\n",
|
||||
" titlebar.append(titletext)\n",
|
||||
" this.root.append(titlebar);\n",
|
||||
" this.header = titletext[0];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
||||
"\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
||||
"\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._init_canvas = function() {\n",
|
||||
" var fig = this;\n",
|
||||
"\n",
|
||||
" var canvas_div = $('<div/>');\n",
|
||||
"\n",
|
||||
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
||||
"\n",
|
||||
" function canvas_keyboard_event(event) {\n",
|
||||
" return fig.key_event(event, event['data']);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
||||
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
||||
" this.canvas_div = canvas_div\n",
|
||||
" this._canvas_extra_style(canvas_div)\n",
|
||||
" this.root.append(canvas_div);\n",
|
||||
"\n",
|
||||
" var canvas = $('<canvas/>');\n",
|
||||
" canvas.addClass('mpl-canvas');\n",
|
||||
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
||||
"\n",
|
||||
" this.canvas = canvas[0];\n",
|
||||
" this.context = canvas[0].getContext(\"2d\");\n",
|
||||
"\n",
|
||||
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
||||
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
||||
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
||||
"\tthis.context.msBackingStorePixelRatio ||\n",
|
||||
"\tthis.context.oBackingStorePixelRatio ||\n",
|
||||
"\tthis.context.backingStorePixelRatio || 1;\n",
|
||||
"\n",
|
||||
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
||||
"\n",
|
||||
" var rubberband = $('<canvas/>');\n",
|
||||
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
||||
"\n",
|
||||
" var pass_mouse_events = true;\n",
|
||||
"\n",
|
||||
" canvas_div.resizable({\n",
|
||||
" start: function(event, ui) {\n",
|
||||
" pass_mouse_events = false;\n",
|
||||
" },\n",
|
||||
" resize: function(event, ui) {\n",
|
||||
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
||||
" },\n",
|
||||
" stop: function(event, ui) {\n",
|
||||
" pass_mouse_events = true;\n",
|
||||
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
||||
" },\n",
|
||||
" });\n",
|
||||
"\n",
|
||||
" function mouse_event_fn(event) {\n",
|
||||
" if (pass_mouse_events)\n",
|
||||
" return fig.mouse_event(event, event['data']);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
||||
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
||||
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
||||
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
||||
"\n",
|
||||
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
||||
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
||||
"\n",
|
||||
" canvas_div.on(\"wheel\", function (event) {\n",
|
||||
" event = event.originalEvent;\n",
|
||||
" event['data'] = 'scroll'\n",
|
||||
" if (event.deltaY < 0) {\n",
|
||||
" event.step = 1;\n",
|
||||
" } else {\n",
|
||||
" event.step = -1;\n",
|
||||
" }\n",
|
||||
" mouse_event_fn(event);\n",
|
||||
" });\n",
|
||||
"\n",
|
||||
" canvas_div.append(canvas);\n",
|
||||
" canvas_div.append(rubberband);\n",
|
||||
"\n",
|
||||
" this.rubberband = rubberband;\n",
|
||||
" this.rubberband_canvas = rubberband[0];\n",
|
||||
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
||||
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
||||
"\n",
|
||||
" this._resize_canvas = function(width, height) {\n",
|
||||
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
||||
" // canvas in synch.\n",
|
||||
" canvas_div.css('width', width)\n",
|
||||
" canvas_div.css('height', height)\n",
|
||||
"\n",
|
||||
" canvas.attr('width', width * mpl.ratio);\n",
|
||||
" canvas.attr('height', height * mpl.ratio);\n",
|
||||
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
||||
"\n",
|
||||
" rubberband.attr('width', width);\n",
|
||||
" rubberband.attr('height', height);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
||||
" // upon first draw.\n",
|
||||
" this._resize_canvas(600, 600);\n",
|
||||
"\n",
|
||||
" // Disable right mouse context menu.\n",
|
||||
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
||||
" return false;\n",
|
||||
" });\n",
|
||||
"\n",
|
||||
" function set_focus () {\n",
|
||||
" canvas.focus();\n",
|
||||
" canvas_div.focus();\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" window.setTimeout(set_focus, 100);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._init_toolbar = function() {\n",
|
||||
" var fig = this;\n",
|
||||
"\n",
|
||||
" var nav_element = $('<div/>');\n",
|
||||
" nav_element.attr('style', 'width: 100%');\n",
|
||||
" this.root.append(nav_element);\n",
|
||||
"\n",
|
||||
" // Define a callback function for later on.\n",
|
||||
" function toolbar_event(event) {\n",
|
||||
" return fig.toolbar_button_onclick(event['data']);\n",
|
||||
" }\n",
|
||||
" function toolbar_mouse_event(event) {\n",
|
||||
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
||||
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
||||
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
||||
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
||||
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
||||
"\n",
|
||||
" if (!name) {\n",
|
||||
" // put a spacer in here.\n",
|
||||
" continue;\n",
|
||||
" }\n",
|
||||
" var button = $('<button/>');\n",
|
||||
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
||||
" 'ui-button-icon-only');\n",
|
||||
" button.attr('role', 'button');\n",
|
||||
" button.attr('aria-disabled', 'false');\n",
|
||||
" button.click(method_name, toolbar_event);\n",
|
||||
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
||||
"\n",
|
||||
" var icon_img = $('<span/>');\n",
|
||||
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
||||
" icon_img.addClass(image);\n",
|
||||
" icon_img.addClass('ui-corner-all');\n",
|
||||
"\n",
|
||||
" var tooltip_span = $('<span/>');\n",
|
||||
" tooltip_span.addClass('ui-button-text');\n",
|
||||
" tooltip_span.html(tooltip);\n",
|
||||
"\n",
|
||||
" button.append(icon_img);\n",
|
||||
" button.append(tooltip_span);\n",
|
||||
"\n",
|
||||
" nav_element.append(button);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var fmt_picker_span = $('<span/>');\n",
|
||||
"\n",
|
||||
" var fmt_picker = $('<select/>');\n",
|
||||
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
||||
" fmt_picker_span.append(fmt_picker);\n",
|
||||
" nav_element.append(fmt_picker_span);\n",
|
||||
" this.format_dropdown = fmt_picker[0];\n",
|
||||
"\n",
|
||||
" for (var ind in mpl.extensions) {\n",
|
||||
" var fmt = mpl.extensions[ind];\n",
|
||||
" var option = $(\n",
|
||||
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
||||
" fmt_picker.append(option);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" // Add hover states to the ui-buttons\n",
|
||||
" $( \".ui-button\" ).hover(\n",
|
||||
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
||||
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
||||
" );\n",
|
||||
"\n",
|
||||
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
||||
" nav_element.append(status_bar);\n",
|
||||
" this.message = status_bar[0];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
||||
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
||||
" // which will in turn request a refresh of the image.\n",
|
||||
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
||||
" properties['type'] = type;\n",
|
||||
" properties['figure_id'] = this.id;\n",
|
||||
" this.ws.send(JSON.stringify(properties));\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.send_draw_message = function() {\n",
|
||||
" if (!this.waiting) {\n",
|
||||
" this.waiting = true;\n",
|
||||
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
||||
" var format_dropdown = fig.format_dropdown;\n",
|
||||
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
||||
" fig.ondownload(fig, format);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
||||
" var size = msg['size'];\n",
|
||||
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
||||
" fig._resize_canvas(size[0], size[1]);\n",
|
||||
" fig.send_message(\"refresh\", {});\n",
|
||||
" };\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
||||
" var x0 = msg['x0'] / mpl.ratio;\n",
|
||||
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
||||
" var x1 = msg['x1'] / mpl.ratio;\n",
|
||||
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
||||
" x0 = Math.floor(x0) + 0.5;\n",
|
||||
" y0 = Math.floor(y0) + 0.5;\n",
|
||||
" x1 = Math.floor(x1) + 0.5;\n",
|
||||
" y1 = Math.floor(y1) + 0.5;\n",
|
||||
" var min_x = Math.min(x0, x1);\n",
|
||||
" var min_y = Math.min(y0, y1);\n",
|
||||
" var width = Math.abs(x1 - x0);\n",
|
||||
" var height = Math.abs(y1 - y0);\n",
|
||||
"\n",
|
||||
" fig.rubberband_context.clearRect(\n",
|
||||
" 0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
|
||||
"\n",
|
||||
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
||||
" // Updates the figure title.\n",
|
||||
" fig.header.textContent = msg['label'];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
||||
" var cursor = msg['cursor'];\n",
|
||||
" switch(cursor)\n",
|
||||
" {\n",
|
||||
" case 0:\n",
|
||||
" cursor = 'pointer';\n",
|
||||
" break;\n",
|
||||
" case 1:\n",
|
||||
" cursor = 'default';\n",
|
||||
" break;\n",
|
||||
" case 2:\n",
|
||||
" cursor = 'crosshair';\n",
|
||||
" break;\n",
|
||||
" case 3:\n",
|
||||
" cursor = 'move';\n",
|
||||
" break;\n",
|
||||
" }\n",
|
||||
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
||||
" fig.message.textContent = msg['message'];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
||||
" // Request the server to send over a new figure.\n",
|
||||
" fig.send_draw_message();\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
||||
" fig.image_mode = msg['mode'];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
||||
" // Called whenever the canvas gets updated.\n",
|
||||
" this.send_message(\"ack\", {});\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"// A function to construct a web socket function for onmessage handling.\n",
|
||||
"// Called in the figure constructor.\n",
|
||||
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
||||
" return function socket_on_message(evt) {\n",
|
||||
" if (evt.data instanceof Blob) {\n",
|
||||
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
||||
" * transferred with MIME type text/plain:\" errors on\n",
|
||||
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
||||
" * to be part of the websocket stream */\n",
|
||||
" evt.data.type = \"image/png\";\n",
|
||||
"\n",
|
||||
" /* Free the memory for the previous frames */\n",
|
||||
" if (fig.imageObj.src) {\n",
|
||||
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
||||
" fig.imageObj.src);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
||||
" evt.data);\n",
|
||||
" fig.updated_canvas_event();\n",
|
||||
" fig.waiting = false;\n",
|
||||
" return;\n",
|
||||
" }\n",
|
||||
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
||||
" fig.imageObj.src = evt.data;\n",
|
||||
" fig.updated_canvas_event();\n",
|
||||
" fig.waiting = false;\n",
|
||||
" return;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var msg = JSON.parse(evt.data);\n",
|
||||
" var msg_type = msg['type'];\n",
|
||||
"\n",
|
||||
" // Call the \"handle_{type}\" callback, which takes\n",
|
||||
" // the figure and JSON message as its only arguments.\n",
|
||||
" try {\n",
|
||||
" var callback = fig[\"handle_\" + msg_type];\n",
|
||||
" } catch (e) {\n",
|
||||
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
||||
" return;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" if (callback) {\n",
|
||||
" try {\n",
|
||||
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
||||
" callback(fig, msg);\n",
|
||||
" } catch (e) {\n",
|
||||
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" };\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
||||
"mpl.findpos = function(e) {\n",
|
||||
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
||||
" var targ;\n",
|
||||
" if (!e)\n",
|
||||
" e = window.event;\n",
|
||||
" if (e.target)\n",
|
||||
" targ = e.target;\n",
|
||||
" else if (e.srcElement)\n",
|
||||
" targ = e.srcElement;\n",
|
||||
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
||||
" targ = targ.parentNode;\n",
|
||||
"\n",
|
||||
" // jQuery normalizes the pageX and pageY\n",
|
||||
" // pageX,Y are the mouse positions relative to the document\n",
|
||||
" // offset() returns the position of the element relative to the document\n",
|
||||
" var x = e.pageX - $(targ).offset().left;\n",
|
||||
" var y = e.pageY - $(targ).offset().top;\n",
|
||||
"\n",
|
||||
" return {\"x\": x, \"y\": y};\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"/*\n",
|
||||
" * return a copy of an object with only non-object keys\n",
|
||||
" * we need this to avoid circular references\n",
|
||||
" * http://stackoverflow.com/a/24161582/3208463\n",
|
||||
" */\n",
|
||||
"function simpleKeys (original) {\n",
|
||||
" return Object.keys(original).reduce(function (obj, key) {\n",
|
||||
" if (typeof original[key] !== 'object')\n",
|
||||
" obj[key] = original[key]\n",
|
||||
" return obj;\n",
|
||||
" }, {});\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
||||
" var canvas_pos = mpl.findpos(event)\n",
|
||||
"\n",
|
||||
" if (name === 'button_press')\n",
|
||||
" {\n",
|
||||
" this.canvas.focus();\n",
|
||||
" this.canvas_div.focus();\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var x = canvas_pos.x * mpl.ratio;\n",
|
||||
" var y = canvas_pos.y * mpl.ratio;\n",
|
||||
"\n",
|
||||
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
||||
" step: event.step,\n",
|
||||
" guiEvent: simpleKeys(event)});\n",
|
||||
"\n",
|
||||
" /* This prevents the web browser from automatically changing to\n",
|
||||
" * the text insertion cursor when the button is pressed. We want\n",
|
||||
" * to control all of the cursor setting manually through the\n",
|
||||
" * 'cursor' event from matplotlib */\n",
|
||||
" event.preventDefault();\n",
|
||||
" return false;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
||||
" // Handle any extra behaviour associated with a key event\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
||||
"\n",
|
||||
" // Prevent repeat events\n",
|
||||
" if (name == 'key_press')\n",
|
||||
" {\n",
|
||||
" if (event.which === this._key)\n",
|
||||
" return;\n",
|
||||
" else\n",
|
||||
" this._key = event.which;\n",
|
||||
" }\n",
|
||||
" if (name == 'key_release')\n",
|
||||
" this._key = null;\n",
|
||||
"\n",
|
||||
" var value = '';\n",
|
||||
" if (event.ctrlKey && event.which != 17)\n",
|
||||
" value += \"ctrl+\";\n",
|
||||
" if (event.altKey && event.which != 18)\n",
|
||||
" value += \"alt+\";\n",
|
||||
" if (event.shiftKey && event.which != 16)\n",
|
||||
" value += \"shift+\";\n",
|
||||
"\n",
|
||||
" value += 'k';\n",
|
||||
" value += event.which.toString();\n",
|
||||
"\n",
|
||||
" this._key_event_extra(event, name);\n",
|
||||
"\n",
|
||||
" this.send_message(name, {key: value,\n",
|
||||
" guiEvent: simpleKeys(event)});\n",
|
||||
" return false;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
||||
" if (name == 'download') {\n",
|
||||
" this.handle_save(this, null);\n",
|
||||
" } else {\n",
|
||||
" this.send_message(\"toolbar_button\", {name: name});\n",
|
||||
" }\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
||||
" this.message.textContent = tooltip;\n",
|
||||
"};\n",
|
||||
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
||||
"\n",
|
||||
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
||||
"\n",
|
||||
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
||||
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
||||
" // object with the appropriate methods. Currently this is a non binary\n",
|
||||
" // socket, so there is still some room for performance tuning.\n",
|
||||
" var ws = {};\n",
|
||||
"\n",
|
||||
" ws.close = function() {\n",
|
||||
" comm.close()\n",
|
||||
" };\n",
|
||||
" ws.send = function(m) {\n",
|
||||
" //console.log('sending', m);\n",
|
||||
" comm.send(m);\n",
|
||||
" };\n",
|
||||
" // Register the callback with on_msg.\n",
|
||||
" comm.on_msg(function(msg) {\n",
|
||||
" //console.log('receiving', msg['content']['data'], msg);\n",
|
||||
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
||||
" ws.onmessage(msg['content']['data'])\n",
|
||||
" });\n",
|
||||
" return ws;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
||||
" // This is the function which gets called when the mpl process\n",
|
||||
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
||||
"\n",
|
||||
" var id = msg.content.data.id;\n",
|
||||
" // Get hold of the div created by the display call when the Comm\n",
|
||||
" // socket was opened in Python.\n",
|
||||
" var element = $(\"#\" + id);\n",
|
||||
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
||||
"\n",
|
||||
" function ondownload(figure, format) {\n",
|
||||
" window.open(figure.imageObj.src);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var fig = new mpl.figure(id, ws_proxy,\n",
|
||||
" ondownload,\n",
|
||||
" element.get(0));\n",
|
||||
"\n",
|
||||
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
||||
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
||||
" ws_proxy.onopen();\n",
|
||||
"\n",
|
||||
" fig.parent_element = element.get(0);\n",
|
||||
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
||||
" if (!fig.cell_info) {\n",
|
||||
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
||||
" return;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var output_index = fig.cell_info[2]\n",
|
||||
" var cell = fig.cell_info[0];\n",
|
||||
"\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
||||
" var width = fig.canvas.width/mpl.ratio\n",
|
||||
" fig.root.unbind('remove')\n",
|
||||
"\n",
|
||||
" // Update the output cell to use the data from the current canvas.\n",
|
||||
" fig.push_to_output();\n",
|
||||
" var dataURL = fig.canvas.toDataURL();\n",
|
||||
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
||||
" // the notebook keyboard shortcuts fail.\n",
|
||||
" IPython.keyboard_manager.enable()\n",
|
||||
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
||||
" fig.close_ws(fig, msg);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
||||
" fig.send_message('closing', msg);\n",
|
||||
" // fig.ws.close()\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
||||
" // Turn the data on the canvas into data in the output cell.\n",
|
||||
" var width = this.canvas.width/mpl.ratio\n",
|
||||
" var dataURL = this.canvas.toDataURL();\n",
|
||||
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
||||
" // Tell IPython that the notebook contents must change.\n",
|
||||
" IPython.notebook.set_dirty(true);\n",
|
||||
" this.send_message(\"ack\", {});\n",
|
||||
" var fig = this;\n",
|
||||
" // Wait a second, then push the new image to the DOM so\n",
|
||||
" // that it is saved nicely (might be nice to debounce this).\n",
|
||||
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._init_toolbar = function() {\n",
|
||||
" var fig = this;\n",
|
||||
"\n",
|
||||
" var nav_element = $('<div/>');\n",
|
||||
" nav_element.attr('style', 'width: 100%');\n",
|
||||
" this.root.append(nav_element);\n",
|
||||
"\n",
|
||||
" // Define a callback function for later on.\n",
|
||||
" function toolbar_event(event) {\n",
|
||||
" return fig.toolbar_button_onclick(event['data']);\n",
|
||||
" }\n",
|
||||
" function toolbar_mouse_event(event) {\n",
|
||||
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
||||
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
||||
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
||||
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
||||
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
||||
"\n",
|
||||
" if (!name) { continue; };\n",
|
||||
"\n",
|
||||
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
||||
" button.click(method_name, toolbar_event);\n",
|
||||
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
||||
" nav_element.append(button);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" // Add the status bar.\n",
|
||||
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
||||
" nav_element.append(status_bar);\n",
|
||||
" this.message = status_bar[0];\n",
|
||||
"\n",
|
||||
" // Add the close button to the window.\n",
|
||||
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
||||
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
||||
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
||||
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
||||
" buttongrp.append(button);\n",
|
||||
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
||||
" titlebar.prepend(buttongrp);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
||||
" var fig = this\n",
|
||||
" el.on(\"remove\", function(){\n",
|
||||
"\tfig.close_ws(fig, {});\n",
|
||||
" });\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
||||
" // this is important to make the div 'focusable\n",
|
||||
" el.attr('tabindex', 0)\n",
|
||||
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
||||
" // off when our div gets focus\n",
|
||||
"\n",
|
||||
" // location in version 3\n",
|
||||
" if (IPython.notebook.keyboard_manager) {\n",
|
||||
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
||||
" }\n",
|
||||
" else {\n",
|
||||
" // location in version 2\n",
|
||||
" IPython.keyboard_manager.register_events(el);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
||||
" var manager = IPython.notebook.keyboard_manager;\n",
|
||||
" if (!manager)\n",
|
||||
" manager = IPython.keyboard_manager;\n",
|
||||
"\n",
|
||||
" // Check for shift+enter\n",
|
||||
" if (event.shiftKey && event.which == 13) {\n",
|
||||
" this.canvas_div.blur();\n",
|
||||
" // select the cell after this one\n",
|
||||
" var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
|
||||
" IPython.notebook.select(index + 1);\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
||||
" fig.ondownload(fig, null);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.find_output_cell = function(html_output) {\n",
|
||||
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
||||
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
||||
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
||||
" // our purposes (turning an active figure into a static one), is too late.\n",
|
||||
" var cells = IPython.notebook.get_cells();\n",
|
||||
" var ncells = cells.length;\n",
|
||||
" for (var i=0; i<ncells; i++) {\n",
|
||||
" var cell = cells[i];\n",
|
||||
" if (cell.cell_type === 'code'){\n",
|
||||
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
||||
" var data = cell.output_area.outputs[j];\n",
|
||||
" if (data.data) {\n",
|
||||
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
||||
" data = data.data;\n",
|
||||
" }\n",
|
||||
" if (data['text/html'] == html_output) {\n",
|
||||
" return [cell, data, j];\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"// Register the function which deals with the matplotlib target/channel.\n",
|
||||
"// The kernel may be null if the page has been refreshed.\n",
|
||||
"if (IPython.notebook.kernel != null) {\n",
|
||||
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
||||
"}\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Javascript object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div id='215b26ff-80fe-45a3-bb52-9351ea1b2ebb'></div>"
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.HTML object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(0, 100)"
|
||||
]
|
||||
},
|
||||
"execution_count": 131,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def h(p,src,dest):\n",
|
||||
" c = (((src-dest)**2).sum())**0.5\n",
|
||||
" a = (((dest-p)**2).sum())**0.5\n",
|
||||
" b = (((src-p)**2).sum())**0.5\n",
|
||||
" ld = (((a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c))**0.5)/(c*2.0)\n",
|
||||
" return a-b\n",
|
||||
"S=np.array([10,10])\n",
|
||||
"D=np.array([20,25])\n",
|
||||
"\n",
|
||||
"Rx=range(100)\n",
|
||||
"Ry=range(100)\n",
|
||||
"grid=np.zeros((len(Rx),len(Ry)))\n",
|
||||
"for px,x in enumerate(Rx):\n",
|
||||
" for py,y in enumerate(Ry):\n",
|
||||
" grid[px,py]=h(np.array([x,y]),S,D)\n",
|
||||
"imshow(grid,cmap='coolwarm_r',origin='lower')\n",
|
||||
"colorbar()\n",
|
||||
"\n",
|
||||
"scatter(*S,color='green')\n",
|
||||
"scatter(*D,color='red')\n",
|
||||
"plt.xlim(0,100)\n",
|
||||
"plt.ylim(0,100)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 38,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Object `np.magnitude` not found.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"np."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.1 64-bit ('anaconda': conda)",
|
||||
"language": "python",
|
||||
"name": "python38164bitanacondaconda2a51168e890d45bd836f654eb2ae46f7"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
|
@ -0,0 +1,505 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'ed_lrr.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 5.15.2
|
||||
##
|
||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
from PySide2.QtCore import *
|
||||
from PySide2.QtGui import *
|
||||
from PySide2.QtWidgets import *
|
||||
|
||||
|
||||
class Ui_ED_LRR(object):
|
||||
def setupUi(self, ED_LRR):
|
||||
if not ED_LRR.objectName():
|
||||
ED_LRR.setObjectName(u"ED_LRR")
|
||||
ED_LRR.setEnabled(True)
|
||||
ED_LRR.resize(577, 500)
|
||||
sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(ED_LRR.sizePolicy().hasHeightForWidth())
|
||||
ED_LRR.setSizePolicy(sizePolicy)
|
||||
ED_LRR.setMinimumSize(QSize(577, 500))
|
||||
ED_LRR.setMaximumSize(QSize(577, 500))
|
||||
ED_LRR.setStyleSheet(u"")
|
||||
ED_LRR.setDocumentMode(False)
|
||||
ED_LRR.setTabShape(QTabWidget.Rounded)
|
||||
self.menu_act_quit = QAction(ED_LRR)
|
||||
self.menu_act_quit.setObjectName(u"menu_act_quit")
|
||||
self.actionA = QAction(ED_LRR)
|
||||
self.actionA.setObjectName(u"actionA")
|
||||
self.actionB = QAction(ED_LRR)
|
||||
self.actionB.setObjectName(u"actionB")
|
||||
self.centralwidget = QWidget(ED_LRR)
|
||||
self.centralwidget.setObjectName(u"centralwidget")
|
||||
sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
|
||||
sizePolicy1.setHorizontalStretch(0)
|
||||
sizePolicy1.setVerticalStretch(0)
|
||||
sizePolicy1.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
|
||||
self.centralwidget.setSizePolicy(sizePolicy1)
|
||||
self.verticalLayout = QVBoxLayout(self.centralwidget)
|
||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||
self.tabs = QTabWidget(self.centralwidget)
|
||||
self.tabs.setObjectName(u"tabs")
|
||||
self.tabs.setEnabled(True)
|
||||
self.tabs.setAutoFillBackground(False)
|
||||
self.tabs.setTabPosition(QTabWidget.North)
|
||||
self.tabs.setTabShape(QTabWidget.Rounded)
|
||||
self.tabs.setElideMode(Qt.ElideNone)
|
||||
self.tabs.setTabsClosable(False)
|
||||
self.tabs.setTabBarAutoHide(False)
|
||||
self.tab_download = QWidget()
|
||||
self.tab_download.setObjectName(u"tab_download")
|
||||
self.formLayout = QFormLayout(self.tab_download)
|
||||
self.formLayout.setObjectName(u"formLayout")
|
||||
self.lbl_bodies_dl = QLabel(self.tab_download)
|
||||
self.lbl_bodies_dl.setObjectName(u"lbl_bodies_dl")
|
||||
|
||||
self.formLayout.setWidget(1, QFormLayout.LabelRole, self.lbl_bodies_dl)
|
||||
|
||||
self.lbl_systems_dl = QLabel(self.tab_download)
|
||||
self.lbl_systems_dl.setObjectName(u"lbl_systems_dl")
|
||||
|
||||
self.formLayout.setWidget(3, QFormLayout.LabelRole, self.lbl_systems_dl)
|
||||
|
||||
self.inp_bodies_dl = QComboBox(self.tab_download)
|
||||
self.inp_bodies_dl.setObjectName(u"inp_bodies_dl")
|
||||
self.inp_bodies_dl.setEditable(True)
|
||||
self.inp_bodies_dl.setInsertPolicy(QComboBox.InsertAtTop)
|
||||
|
||||
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.inp_bodies_dl)
|
||||
|
||||
self.inp_systems_dl = QComboBox(self.tab_download)
|
||||
self.inp_systems_dl.setObjectName(u"inp_systems_dl")
|
||||
self.inp_systems_dl.setEditable(True)
|
||||
self.inp_systems_dl.setInsertPolicy(QComboBox.InsertAtTop)
|
||||
|
||||
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.inp_systems_dl)
|
||||
|
||||
self.gridLayout = QGridLayout()
|
||||
self.gridLayout.setObjectName(u"gridLayout")
|
||||
self.inp_bodies_dest_dl = QComboBox(self.tab_download)
|
||||
self.inp_bodies_dest_dl.setObjectName(u"inp_bodies_dest_dl")
|
||||
sizePolicy2 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
sizePolicy2.setHorizontalStretch(0)
|
||||
sizePolicy2.setVerticalStretch(0)
|
||||
sizePolicy2.setHeightForWidth(self.inp_bodies_dest_dl.sizePolicy().hasHeightForWidth())
|
||||
self.inp_bodies_dest_dl.setSizePolicy(sizePolicy2)
|
||||
self.inp_bodies_dest_dl.setEditable(False)
|
||||
self.inp_bodies_dest_dl.setInsertPolicy(QComboBox.InsertAtTop)
|
||||
|
||||
self.gridLayout.addWidget(self.inp_bodies_dest_dl, 0, 0, 1, 1)
|
||||
|
||||
self.btn_bodies_dest_browse_dl = QPushButton(self.tab_download)
|
||||
self.btn_bodies_dest_browse_dl.setObjectName(u"btn_bodies_dest_browse_dl")
|
||||
|
||||
self.gridLayout.addWidget(self.btn_bodies_dest_browse_dl, 0, 1, 1, 1)
|
||||
|
||||
|
||||
self.formLayout.setLayout(2, QFormLayout.FieldRole, self.gridLayout)
|
||||
|
||||
self.gridLayout_2 = QGridLayout()
|
||||
self.gridLayout_2.setObjectName(u"gridLayout_2")
|
||||
self.btn_systems_dest_browse_dl = QPushButton(self.tab_download)
|
||||
self.btn_systems_dest_browse_dl.setObjectName(u"btn_systems_dest_browse_dl")
|
||||
|
||||
self.gridLayout_2.addWidget(self.btn_systems_dest_browse_dl, 0, 1, 1, 1)
|
||||
|
||||
self.inp_systems_dest_dl = QComboBox(self.tab_download)
|
||||
self.inp_systems_dest_dl.setObjectName(u"inp_systems_dest_dl")
|
||||
sizePolicy2.setHeightForWidth(self.inp_systems_dest_dl.sizePolicy().hasHeightForWidth())
|
||||
self.inp_systems_dest_dl.setSizePolicy(sizePolicy2)
|
||||
self.inp_systems_dest_dl.setEditable(False)
|
||||
self.inp_systems_dest_dl.setInsertPolicy(QComboBox.InsertAtTop)
|
||||
|
||||
self.gridLayout_2.addWidget(self.inp_systems_dest_dl, 0, 0, 1, 1)
|
||||
|
||||
|
||||
self.formLayout.setLayout(4, QFormLayout.FieldRole, self.gridLayout_2)
|
||||
|
||||
self.btn_download = QPushButton(self.tab_download)
|
||||
self.btn_download.setObjectName(u"btn_download")
|
||||
|
||||
self.formLayout.setWidget(5, QFormLayout.LabelRole, self.btn_download)
|
||||
|
||||
self.label = QLabel(self.tab_download)
|
||||
self.label.setObjectName(u"label")
|
||||
|
||||
self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label)
|
||||
|
||||
self.label_2 = QLabel(self.tab_download)
|
||||
self.label_2.setObjectName(u"label_2")
|
||||
|
||||
self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_2)
|
||||
|
||||
self.tabs.addTab(self.tab_download, "")
|
||||
self.tab_preprocess = QWidget()
|
||||
self.tab_preprocess.setObjectName(u"tab_preprocess")
|
||||
self.formLayout_3 = QFormLayout(self.tab_preprocess)
|
||||
self.formLayout_3.setObjectName(u"formLayout_3")
|
||||
self.lbl_bodies_pp = QLabel(self.tab_preprocess)
|
||||
self.lbl_bodies_pp.setObjectName(u"lbl_bodies_pp")
|
||||
|
||||
self.formLayout_3.setWidget(0, QFormLayout.LabelRole, self.lbl_bodies_pp)
|
||||
|
||||
self.gr_bodies_pp = QGridLayout()
|
||||
self.gr_bodies_pp.setObjectName(u"gr_bodies_pp")
|
||||
self.btn_bodies_browse_pp = QPushButton(self.tab_preprocess)
|
||||
self.btn_bodies_browse_pp.setObjectName(u"btn_bodies_browse_pp")
|
||||
|
||||
self.gr_bodies_pp.addWidget(self.btn_bodies_browse_pp, 0, 1, 1, 1)
|
||||
|
||||
self.inp_bodies_pp = QComboBox(self.tab_preprocess)
|
||||
self.inp_bodies_pp.setObjectName(u"inp_bodies_pp")
|
||||
sizePolicy2.setHeightForWidth(self.inp_bodies_pp.sizePolicy().hasHeightForWidth())
|
||||
self.inp_bodies_pp.setSizePolicy(sizePolicy2)
|
||||
self.inp_bodies_pp.setEditable(False)
|
||||
self.inp_bodies_pp.setInsertPolicy(QComboBox.InsertAtTop)
|
||||
|
||||
self.gr_bodies_pp.addWidget(self.inp_bodies_pp, 0, 0, 1, 1)
|
||||
|
||||
|
||||
self.formLayout_3.setLayout(0, QFormLayout.FieldRole, self.gr_bodies_pp)
|
||||
|
||||
self.lbl_systems_pp = QLabel(self.tab_preprocess)
|
||||
self.lbl_systems_pp.setObjectName(u"lbl_systems_pp")
|
||||
|
||||
self.formLayout_3.setWidget(1, QFormLayout.LabelRole, self.lbl_systems_pp)
|
||||
|
||||
self.gr_systems_pp = QGridLayout()
|
||||
self.gr_systems_pp.setObjectName(u"gr_systems_pp")
|
||||
self.btn_systems_browse_pp = QPushButton(self.tab_preprocess)
|
||||
self.btn_systems_browse_pp.setObjectName(u"btn_systems_browse_pp")
|
||||
|
||||
self.gr_systems_pp.addWidget(self.btn_systems_browse_pp, 0, 1, 1, 1)
|
||||
|
||||
self.inp_systems_pp = QComboBox(self.tab_preprocess)
|
||||
self.inp_systems_pp.setObjectName(u"inp_systems_pp")
|
||||
sizePolicy2.setHeightForWidth(self.inp_systems_pp.sizePolicy().hasHeightForWidth())
|
||||
self.inp_systems_pp.setSizePolicy(sizePolicy2)
|
||||
self.inp_systems_pp.setEditable(False)
|
||||
self.inp_systems_pp.setInsertPolicy(QComboBox.InsertAtTop)
|
||||
|
||||
self.gr_systems_pp.addWidget(self.inp_systems_pp, 0, 0, 1, 1)
|
||||
|
||||
|
||||
self.formLayout_3.setLayout(1, QFormLayout.FieldRole, self.gr_systems_pp)
|
||||
|
||||
self.lbl_out_pp = QLabel(self.tab_preprocess)
|
||||
self.lbl_out_pp.setObjectName(u"lbl_out_pp")
|
||||
|
||||
self.formLayout_3.setWidget(2, QFormLayout.LabelRole, self.lbl_out_pp)
|
||||
|
||||
self.gr_out_grid_pp = QGridLayout()
|
||||
self.gr_out_grid_pp.setObjectName(u"gr_out_grid_pp")
|
||||
self.btn_out_browse_pp = QPushButton(self.tab_preprocess)
|
||||
self.btn_out_browse_pp.setObjectName(u"btn_out_browse_pp")
|
||||
sizePolicy3 = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
|
||||
sizePolicy3.setHorizontalStretch(0)
|
||||
sizePolicy3.setVerticalStretch(0)
|
||||
sizePolicy3.setHeightForWidth(self.btn_out_browse_pp.sizePolicy().hasHeightForWidth())
|
||||
self.btn_out_browse_pp.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.gr_out_grid_pp.addWidget(self.btn_out_browse_pp, 0, 1, 1, 1)
|
||||
|
||||
self.inp_out_pp = QComboBox(self.tab_preprocess)
|
||||
self.inp_out_pp.setObjectName(u"inp_out_pp")
|
||||
sizePolicy2.setHeightForWidth(self.inp_out_pp.sizePolicy().hasHeightForWidth())
|
||||
self.inp_out_pp.setSizePolicy(sizePolicy2)
|
||||
self.inp_out_pp.setEditable(False)
|
||||
self.inp_out_pp.setInsertPolicy(QComboBox.InsertAtTop)
|
||||
|
||||
self.gr_out_grid_pp.addWidget(self.inp_out_pp, 0, 0, 1, 1)
|
||||
|
||||
|
||||
self.formLayout_3.setLayout(2, QFormLayout.FieldRole, self.gr_out_grid_pp)
|
||||
|
||||
self.btn_preprocess = QPushButton(self.tab_preprocess)
|
||||
self.btn_preprocess.setObjectName(u"btn_preprocess")
|
||||
|
||||
self.formLayout_3.setWidget(3, QFormLayout.LabelRole, self.btn_preprocess)
|
||||
|
||||
self.tabs.addTab(self.tab_preprocess, "")
|
||||
self.tab_route = QWidget()
|
||||
self.tab_route.setObjectName(u"tab_route")
|
||||
self.formLayout_2 = QFormLayout(self.tab_route)
|
||||
self.formLayout_2.setObjectName(u"formLayout_2")
|
||||
self.lbl_sys_lst = QLabel(self.tab_route)
|
||||
self.lbl_sys_lst.setObjectName(u"lbl_sys_lst")
|
||||
|
||||
self.formLayout_2.setWidget(0, QFormLayout.LabelRole, self.lbl_sys_lst)
|
||||
|
||||
self.gr_sys = QGridLayout()
|
||||
self.gr_sys.setObjectName(u"gr_sys")
|
||||
self.btn_sys_lst_browse = QPushButton(self.tab_route)
|
||||
self.btn_sys_lst_browse.setObjectName(u"btn_sys_lst_browse")
|
||||
|
||||
self.gr_sys.addWidget(self.btn_sys_lst_browse, 0, 1, 1, 1)
|
||||
|
||||
self.inp_sys_lst = QComboBox(self.tab_route)
|
||||
self.inp_sys_lst.setObjectName(u"inp_sys_lst")
|
||||
sizePolicy2.setHeightForWidth(self.inp_sys_lst.sizePolicy().hasHeightForWidth())
|
||||
self.inp_sys_lst.setSizePolicy(sizePolicy2)
|
||||
self.inp_sys_lst.setEditable(False)
|
||||
self.inp_sys_lst.setInsertPolicy(QComboBox.InsertAtTop)
|
||||
self.inp_sys_lst.setFrame(True)
|
||||
self.inp_sys_lst.setModelColumn(0)
|
||||
|
||||
self.gr_sys.addWidget(self.inp_sys_lst, 0, 0, 1, 1)
|
||||
|
||||
|
||||
self.formLayout_2.setLayout(0, QFormLayout.FieldRole, self.gr_sys)
|
||||
|
||||
self.btn_add = QPushButton(self.tab_route)
|
||||
self.btn_add.setObjectName(u"btn_add")
|
||||
|
||||
self.formLayout_2.setWidget(1, QFormLayout.LabelRole, self.btn_add)
|
||||
|
||||
self.inp_sys = QLineEdit(self.tab_route)
|
||||
self.inp_sys.setObjectName(u"inp_sys")
|
||||
|
||||
self.formLayout_2.setWidget(1, QFormLayout.FieldRole, self.inp_sys)
|
||||
|
||||
self.btn_rm = QPushButton(self.tab_route)
|
||||
self.btn_rm.setObjectName(u"btn_rm")
|
||||
|
||||
self.formLayout_2.setWidget(3, QFormLayout.LabelRole, self.btn_rm)
|
||||
|
||||
self.gr_mode = QGridLayout()
|
||||
self.gr_mode.setObjectName(u"gr_mode")
|
||||
self.rd_comp = QRadioButton(self.tab_route)
|
||||
self.rd_comp.setObjectName(u"rd_comp")
|
||||
self.rd_comp.setChecked(True)
|
||||
|
||||
self.gr_mode.addWidget(self.rd_comp, 0, 1, 1, 1)
|
||||
|
||||
self.rd_precomp = QRadioButton(self.tab_route)
|
||||
self.rd_precomp.setObjectName(u"rd_precomp")
|
||||
|
||||
self.gr_mode.addWidget(self.rd_precomp, 0, 2, 1, 1)
|
||||
|
||||
|
||||
self.formLayout_2.setLayout(3, QFormLayout.FieldRole, self.gr_mode)
|
||||
|
||||
self.chk_permute = QCheckBox(self.tab_route)
|
||||
self.chk_permute.setObjectName(u"chk_permute")
|
||||
|
||||
self.formLayout_2.setWidget(4, QFormLayout.LabelRole, self.chk_permute)
|
||||
|
||||
self.gridLayout_4 = QGridLayout()
|
||||
self.gridLayout_4.setObjectName(u"gridLayout_4")
|
||||
self.chk_permute_keep_last = QCheckBox(self.tab_route)
|
||||
self.chk_permute_keep_last.setObjectName(u"chk_permute_keep_last")
|
||||
|
||||
self.gridLayout_4.addWidget(self.chk_permute_keep_last, 0, 3, 1, 1)
|
||||
|
||||
self.chk_permute_keep_first = QCheckBox(self.tab_route)
|
||||
self.chk_permute_keep_first.setObjectName(u"chk_permute_keep_first")
|
||||
sizePolicy3.setHeightForWidth(self.chk_permute_keep_first.sizePolicy().hasHeightForWidth())
|
||||
self.chk_permute_keep_first.setSizePolicy(sizePolicy3)
|
||||
self.chk_permute_keep_first.setTristate(False)
|
||||
|
||||
self.gridLayout_4.addWidget(self.chk_permute_keep_first, 0, 2, 1, 1)
|
||||
|
||||
self.lbl_keep = QLabel(self.tab_route)
|
||||
self.lbl_keep.setObjectName(u"lbl_keep")
|
||||
sizePolicy1.setHeightForWidth(self.lbl_keep.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_keep.setSizePolicy(sizePolicy1)
|
||||
|
||||
self.gridLayout_4.addWidget(self.lbl_keep, 0, 1, 1, 1)
|
||||
|
||||
|
||||
self.formLayout_2.setLayout(4, QFormLayout.FieldRole, self.gridLayout_4)
|
||||
|
||||
self.lst_sys = QTreeWidget(self.tab_route)
|
||||
__qtreewidgetitem = QTreeWidgetItem()
|
||||
__qtreewidgetitem.setText(0, u"Name");
|
||||
self.lst_sys.setHeaderItem(__qtreewidgetitem)
|
||||
self.lst_sys.setObjectName(u"lst_sys")
|
||||
sizePolicy4 = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
sizePolicy4.setHorizontalStretch(0)
|
||||
sizePolicy4.setVerticalStretch(0)
|
||||
sizePolicy4.setHeightForWidth(self.lst_sys.sizePolicy().hasHeightForWidth())
|
||||
self.lst_sys.setSizePolicy(sizePolicy4)
|
||||
self.lst_sys.setMinimumSize(QSize(0, 0))
|
||||
self.lst_sys.setDragEnabled(True)
|
||||
self.lst_sys.setDragDropOverwriteMode(False)
|
||||
self.lst_sys.setDragDropMode(QAbstractItemView.InternalMove)
|
||||
self.lst_sys.setDefaultDropAction(Qt.MoveAction)
|
||||
self.lst_sys.setAlternatingRowColors(True)
|
||||
self.lst_sys.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||
self.lst_sys.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.lst_sys.setHeaderHidden(True)
|
||||
self.lst_sys.header().setVisible(False)
|
||||
|
||||
self.formLayout_2.setWidget(7, QFormLayout.SpanningRole, self.lst_sys)
|
||||
|
||||
self.lbl_range = QLabel(self.tab_route)
|
||||
self.lbl_range.setObjectName(u"lbl_range")
|
||||
|
||||
self.formLayout_2.setWidget(8, QFormLayout.LabelRole, self.lbl_range)
|
||||
|
||||
self.sb_range = QDoubleSpinBox(self.tab_route)
|
||||
self.sb_range.setObjectName(u"sb_range")
|
||||
|
||||
self.formLayout_2.setWidget(8, QFormLayout.FieldRole, self.sb_range)
|
||||
|
||||
self.gr_opts = QGridLayout()
|
||||
self.gr_opts.setObjectName(u"gr_opts")
|
||||
self.cmb_mode = QComboBox(self.tab_route)
|
||||
self.cmb_mode.addItem("")
|
||||
self.cmb_mode.addItem("")
|
||||
self.cmb_mode.addItem("")
|
||||
self.cmb_mode.setObjectName(u"cmb_mode")
|
||||
|
||||
self.gr_opts.addWidget(self.cmb_mode, 0, 2, 1, 1)
|
||||
|
||||
self.lbl_greedyness = QLabel(self.tab_route)
|
||||
self.lbl_greedyness.setObjectName(u"lbl_greedyness")
|
||||
self.lbl_greedyness.setEnabled(True)
|
||||
|
||||
self.gr_opts.addWidget(self.lbl_greedyness, 1, 1, 1, 1)
|
||||
|
||||
self.chk_primary = QCheckBox(self.tab_route)
|
||||
self.chk_primary.setObjectName(u"chk_primary")
|
||||
|
||||
self.gr_opts.addWidget(self.chk_primary, 0, 3, 1, 1)
|
||||
|
||||
self.sld_greedyness = QSlider(self.tab_route)
|
||||
self.sld_greedyness.setObjectName(u"sld_greedyness")
|
||||
self.sld_greedyness.setMaximum(100)
|
||||
self.sld_greedyness.setPageStep(10)
|
||||
self.sld_greedyness.setValue(50)
|
||||
self.sld_greedyness.setOrientation(Qt.Horizontal)
|
||||
self.sld_greedyness.setTickPosition(QSlider.TicksBelow)
|
||||
self.sld_greedyness.setTickInterval(10)
|
||||
|
||||
self.gr_opts.addWidget(self.sld_greedyness, 1, 2, 1, 2)
|
||||
|
||||
self.lbl_mode = QLabel(self.tab_route)
|
||||
self.lbl_mode.setObjectName(u"lbl_mode")
|
||||
|
||||
self.gr_opts.addWidget(self.lbl_mode, 0, 1, 1, 1)
|
||||
|
||||
|
||||
self.formLayout_2.setLayout(9, QFormLayout.SpanningRole, self.gr_opts)
|
||||
|
||||
self.btn_go = QPushButton(self.tab_route)
|
||||
self.btn_go.setObjectName(u"btn_go")
|
||||
self.btn_go.setFlat(False)
|
||||
|
||||
self.formLayout_2.setWidget(10, QFormLayout.LabelRole, self.btn_go)
|
||||
|
||||
self.tabs.addTab(self.tab_route, "")
|
||||
self.tab_log = QWidget()
|
||||
self.tab_log.setObjectName(u"tab_log")
|
||||
self.gridLayout_3 = QGridLayout(self.tab_log)
|
||||
self.gridLayout_3.setObjectName(u"gridLayout_3")
|
||||
self.txt_log = QTextEdit(self.tab_log)
|
||||
self.txt_log.setObjectName(u"txt_log")
|
||||
self.txt_log.setEnabled(True)
|
||||
self.txt_log.setFrameShadow(QFrame.Sunken)
|
||||
self.txt_log.setLineWidth(1)
|
||||
self.txt_log.setReadOnly(True)
|
||||
self.txt_log.setAcceptRichText(False)
|
||||
|
||||
self.gridLayout_3.addWidget(self.txt_log, 0, 0, 1, 1)
|
||||
|
||||
self.tabs.addTab(self.tab_log, "")
|
||||
|
||||
self.verticalLayout.addWidget(self.tabs)
|
||||
|
||||
ED_LRR.setCentralWidget(self.centralwidget)
|
||||
self.menu = QMenuBar(ED_LRR)
|
||||
self.menu.setObjectName(u"menu")
|
||||
self.menu.setGeometry(QRect(0, 0, 577, 21))
|
||||
self.menu_file = QMenu(self.menu)
|
||||
self.menu_file.setObjectName(u"menu_file")
|
||||
self.menuWindow = QMenu(self.menu)
|
||||
self.menuWindow.setObjectName(u"menuWindow")
|
||||
self.menuStyle = QMenu(self.menuWindow)
|
||||
self.menuStyle.setObjectName(u"menuStyle")
|
||||
ED_LRR.setMenuBar(self.menu)
|
||||
self.bar_status = QStatusBar(ED_LRR)
|
||||
self.bar_status.setObjectName(u"bar_status")
|
||||
ED_LRR.setStatusBar(self.bar_status)
|
||||
QWidget.setTabOrder(self.rd_comp, self.cmb_mode)
|
||||
QWidget.setTabOrder(self.cmb_mode, self.chk_primary)
|
||||
QWidget.setTabOrder(self.chk_primary, self.sld_greedyness)
|
||||
QWidget.setTabOrder(self.sld_greedyness, self.rd_precomp)
|
||||
|
||||
self.menu.addAction(self.menu_file.menuAction())
|
||||
self.menu.addAction(self.menuWindow.menuAction())
|
||||
self.menu_file.addAction(self.menu_act_quit)
|
||||
self.menuWindow.addAction(self.menuStyle.menuAction())
|
||||
|
||||
self.retranslateUi(ED_LRR)
|
||||
self.menu_act_quit.triggered.connect(ED_LRR.close)
|
||||
|
||||
self.tabs.setCurrentIndex(2)
|
||||
|
||||
|
||||
QMetaObject.connectSlotsByName(ED_LRR)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, ED_LRR):
|
||||
ED_LRR.setWindowTitle(QCoreApplication.translate("ED_LRR", u"Elite: Dangerous Long Range Route Plotter", None))
|
||||
self.menu_act_quit.setText(QCoreApplication.translate("ED_LRR", u"Quit", None))
|
||||
#if QT_CONFIG(shortcut)
|
||||
self.menu_act_quit.setShortcut(QCoreApplication.translate("ED_LRR", u"Ctrl+Q", None))
|
||||
#endif // QT_CONFIG(shortcut)
|
||||
self.actionA.setText(QCoreApplication.translate("ED_LRR", u"A", None))
|
||||
self.actionB.setText(QCoreApplication.translate("ED_LRR", u"B", None))
|
||||
self.lbl_bodies_dl.setText(QCoreApplication.translate("ED_LRR", u"bodies.json", None))
|
||||
self.lbl_systems_dl.setText(QCoreApplication.translate("ED_LRR", u"systemsWithCoordinates.json", None))
|
||||
self.inp_bodies_dl.setCurrentText(QCoreApplication.translate("ED_LRR", u"https://www.edsm.net/dump/bodies.json", None))
|
||||
self.inp_systems_dl.setCurrentText(QCoreApplication.translate("ED_LRR", u"https://www.edsm.net/dump/systemsWithCoordinates.json", None))
|
||||
self.btn_bodies_dest_browse_dl.setText(QCoreApplication.translate("ED_LRR", u"...", None))
|
||||
self.btn_systems_dest_browse_dl.setText(QCoreApplication.translate("ED_LRR", u"...", None))
|
||||
self.btn_download.setText(QCoreApplication.translate("ED_LRR", u"Download", None))
|
||||
self.label.setText(QCoreApplication.translate("ED_LRR", u"Download path", None))
|
||||
self.label_2.setText(QCoreApplication.translate("ED_LRR", u"Download path", None))
|
||||
self.tabs.setTabText(self.tabs.indexOf(self.tab_download), QCoreApplication.translate("ED_LRR", u"Download", None))
|
||||
self.lbl_bodies_pp.setText(QCoreApplication.translate("ED_LRR", u"bodies.json", None))
|
||||
self.btn_bodies_browse_pp.setText(QCoreApplication.translate("ED_LRR", u"...", None))
|
||||
self.lbl_systems_pp.setText(QCoreApplication.translate("ED_LRR", u"systemsWithCoordinates.json", None))
|
||||
self.btn_systems_browse_pp.setText(QCoreApplication.translate("ED_LRR", u"...", None))
|
||||
self.lbl_out_pp.setText(QCoreApplication.translate("ED_LRR", u"Output", None))
|
||||
self.btn_out_browse_pp.setText(QCoreApplication.translate("ED_LRR", u"...", None))
|
||||
self.btn_preprocess.setText(QCoreApplication.translate("ED_LRR", u"Preprocess", None))
|
||||
self.tabs.setTabText(self.tabs.indexOf(self.tab_preprocess), QCoreApplication.translate("ED_LRR", u"Preprocess", None))
|
||||
self.lbl_sys_lst.setText(QCoreApplication.translate("ED_LRR", u"System List", None))
|
||||
self.btn_sys_lst_browse.setText(QCoreApplication.translate("ED_LRR", u"...", None))
|
||||
self.btn_add.setText(QCoreApplication.translate("ED_LRR", u"Add", None))
|
||||
self.inp_sys.setPlaceholderText(QCoreApplication.translate("ED_LRR", u"System Name", None))
|
||||
self.btn_rm.setText(QCoreApplication.translate("ED_LRR", u"Remove", None))
|
||||
self.rd_comp.setText(QCoreApplication.translate("ED_LRR", u"Compute Route", None))
|
||||
self.rd_precomp.setText(QCoreApplication.translate("ED_LRR", u"Precompute Graph", None))
|
||||
self.chk_permute.setText(QCoreApplication.translate("ED_LRR", u"Permute", None))
|
||||
self.chk_permute_keep_last.setText(QCoreApplication.translate("ED_LRR", u"Last", None))
|
||||
self.chk_permute_keep_first.setText(QCoreApplication.translate("ED_LRR", u"First", None))
|
||||
self.lbl_keep.setText(QCoreApplication.translate("ED_LRR", u"Keep Endpoints:", None))
|
||||
___qtreewidgetitem = self.lst_sys.headerItem()
|
||||
___qtreewidgetitem.setText(1, QCoreApplication.translate("ED_LRR", u"Type", None));
|
||||
self.lbl_range.setText(QCoreApplication.translate("ED_LRR", u"Jump Range (Ly)", None))
|
||||
self.cmb_mode.setItemText(0, QCoreApplication.translate("ED_LRR", u"Breadth-First Search", None))
|
||||
self.cmb_mode.setItemText(1, QCoreApplication.translate("ED_LRR", u"Greedy-Search", None))
|
||||
self.cmb_mode.setItemText(2, QCoreApplication.translate("ED_LRR", u"A*-Search", None))
|
||||
|
||||
self.cmb_mode.setCurrentText(QCoreApplication.translate("ED_LRR", u"Breadth-First Search", None))
|
||||
self.lbl_greedyness.setText(QCoreApplication.translate("ED_LRR", u"Greedyness Factor", None))
|
||||
self.chk_primary.setText(QCoreApplication.translate("ED_LRR", u"Primary Stars Only", None))
|
||||
self.lbl_mode.setText(QCoreApplication.translate("ED_LRR", u"Mode", None))
|
||||
self.btn_go.setText(QCoreApplication.translate("ED_LRR", u"GO!", None))
|
||||
self.tabs.setTabText(self.tabs.indexOf(self.tab_route), QCoreApplication.translate("ED_LRR", u"Route", None))
|
||||
self.tabs.setTabText(self.tabs.indexOf(self.tab_log), QCoreApplication.translate("ED_LRR", u"Log", None))
|
||||
self.menu_file.setTitle(QCoreApplication.translate("ED_LRR", u"File", None))
|
||||
self.menuWindow.setTitle(QCoreApplication.translate("ED_LRR", u"Window", None))
|
||||
self.menuStyle.setTitle(QCoreApplication.translate("ED_LRR", u"Style", None))
|
||||
# retranslateUi
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
################################################################################
|
||||
## Form generated from reading UI file 'widget_route.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 5.15.2
|
||||
##
|
||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
from PySide2.QtCore import *
|
||||
from PySide2.QtGui import *
|
||||
from PySide2.QtWidgets import *
|
||||
|
||||
|
||||
class Ui_diag_route(object):
|
||||
def setupUi(self, diag_route):
|
||||
if not diag_route.objectName():
|
||||
diag_route.setObjectName(u"diag_route")
|
||||
diag_route.setWindowModality(Qt.WindowModal)
|
||||
diag_route.resize(430, 300)
|
||||
self.layout_main = QGridLayout(diag_route)
|
||||
self.layout_main.setObjectName(u"layout_main")
|
||||
self.layout_top = QGridLayout()
|
||||
self.layout_top.setObjectName(u"layout_top")
|
||||
self.lst_route = QTreeWidget(diag_route)
|
||||
self.lst_route.setObjectName(u"lst_route")
|
||||
self.lst_route.setProperty("showDropIndicator", False)
|
||||
self.lst_route.setDefaultDropAction(Qt.IgnoreAction)
|
||||
self.lst_route.setAlternatingRowColors(True)
|
||||
self.lst_route.setSelectionMode(QAbstractItemView.NoSelection)
|
||||
self.lst_route.setVerticalScrollMode(QAbstractItemView.ScrollPerItem)
|
||||
self.lst_route.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
|
||||
self.lst_route.setItemsExpandable(False)
|
||||
self.lst_route.setAllColumnsShowFocus(False)
|
||||
|
||||
self.layout_top.addWidget(self.lst_route, 0, 0, 1, 1)
|
||||
|
||||
|
||||
self.layout_main.addLayout(self.layout_top, 0, 0, 1, 1)
|
||||
|
||||
self.layout_bottom = QGridLayout()
|
||||
self.layout_bottom.setObjectName(u"layout_bottom")
|
||||
self.chk_copy = QCheckBox(diag_route)
|
||||
self.chk_copy.setObjectName(u"chk_copy")
|
||||
|
||||
self.layout_bottom.addWidget(self.chk_copy, 1, 0, 1, 1)
|
||||
|
||||
self.btn_close = QPushButton(diag_route)
|
||||
self.btn_close.setObjectName(u"btn_close")
|
||||
|
||||
self.layout_bottom.addWidget(self.btn_close, 1, 2, 1, 1)
|
||||
|
||||
self.btn_export = QPushButton(diag_route)
|
||||
self.btn_export.setObjectName(u"btn_export")
|
||||
|
||||
self.layout_bottom.addWidget(self.btn_export, 1, 1, 1, 1)
|
||||
|
||||
|
||||
self.layout_main.addLayout(self.layout_bottom, 1, 0, 1, 1)
|
||||
|
||||
|
||||
self.retranslateUi(diag_route)
|
||||
|
||||
QMetaObject.connectSlotsByName(diag_route)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, diag_route):
|
||||
diag_route.setWindowTitle(QCoreApplication.translate("diag_route", u"Route", None))
|
||||
___qtreewidgetitem = self.lst_route.headerItem()
|
||||
___qtreewidgetitem.setText(3, QCoreApplication.translate("diag_route", u"Distance (Ls)", None));
|
||||
___qtreewidgetitem.setText(2, QCoreApplication.translate("diag_route", u"Body", None));
|
||||
___qtreewidgetitem.setText(1, QCoreApplication.translate("diag_route", u"System", None));
|
||||
___qtreewidgetitem.setText(0, QCoreApplication.translate("diag_route", u"Num", None));
|
||||
self.chk_copy.setText(QCoreApplication.translate("diag_route", u"Auto-copy next hop to clipboard", None))
|
||||
self.btn_close.setText(QCoreApplication.translate("diag_route", u"Close", None))
|
||||
self.btn_export.setText(QCoreApplication.translate("diag_route", u"Export", None))
|
||||
# retranslateUi
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
## [Unreleased]
|
||||
|
||||
|
||||
## [v0.14.0] - 2019-10-23
|
||||
### Features
|
||||
- **tests:** Add pytest.ini for pytest-qt [eb43ca8](https://gitlab.com/Earthnuker/ed_lrr/commit/eb43ca8)
|
||||
|
||||
|
||||
## [v0.13.0] - 2019-10-23
|
||||
### Documentation
|
||||
- **readme:** Update README with more infos [634af75](https://gitlab.com/Earthnuker/ed_lrr/commit/634af75)
|
||||
|
||||
### Features
|
||||
- **setup.py:** Update add dependencies for HTTP API [45c11da](https://gitlab.com/Earthnuker/ed_lrr/commit/45c11da)
|
||||
|
||||
|
||||
## [v0.12.0] - 2019-10-23
|
||||
### Features
|
||||
- **tox:** Add more python versions to test against [3115f9d](https://gitlab.com/Earthnuker/ed_lrr/commit/3115f9d)
|
||||
|
||||
|
||||
## [v0.11.0] - 2019-10-23
|
||||
### Features
|
||||
- **html_export:** Add basic HTML-Export using Jinja2 template [bb2ee8c](https://gitlab.com/Earthnuker/ed_lrr/commit/bb2ee8c)
|
||||
|
||||
|
||||
## [v0.10.0] - 2019-10-23
|
||||
### Features
|
||||
- **route_progress:** minor change to progress dialog message [35fc135](https://gitlab.com/Earthnuker/ed_lrr/commit/35fc135)
|
||||
|
||||
|
||||
## [v0.9.4] - 2019-10-23
|
||||
### Bug Fixes
|
||||
- **config:** Add missing defaults [30dbd24](https://gitlab.com/Earthnuker/ed_lrr/commit/30dbd24)
|
||||
|
||||
### Documentation
|
||||
- Update CHANGELOG.md and README.md [38acfc7](https://gitlab.com/Earthnuker/ed_lrr/commit/38acfc7)
|
||||
|
||||
|
||||
## [v0.9.3] - 2019-09-28
|
||||
### Features
|
||||
- **config:** impelemt save and load to GUI [65fe131](https://gitlab.com/Earthnuker/ed_lrr/commit/65fe131)
|
||||
|
||||
|
||||
## [v0.9.2] - 2019-09-28
|
||||
### Features
|
||||
- **GUI:** implement preprocessing [f34d37a](https://gitlab.com/Earthnuker/ed_lrr/commit/f34d37a)
|
||||
|
||||
|
||||
## [v0.9.1] - 2019-09-28
|
||||
### Documentation
|
||||
- Update TODO list in readme [4663d4e](https://gitlab.com/Earthnuker/ed_lrr/commit/4663d4e)
|
||||
|
||||
### Features
|
||||
- **installer:** switch from LZMA to LZMA2 [3ee952e](https://gitlab.com/Earthnuker/ed_lrr/commit/3ee952e)
|
||||
|
||||
|
||||
## [v0.9.0] - 2019-09-28
|
||||
### Features
|
||||
- **build scripts:** skip .history folder when building UI files [92bd1b1](https://gitlab.com/Earthnuker/ed_lrr/commit/92bd1b1)
|
||||
|
||||
|
||||
## [v0.8.1] - 2019-09-28
|
||||
### Features
|
||||
- **config:** update config format [9ec4687](https://gitlab.com/Earthnuker/ed_lrr/commit/9ec4687)
|
||||
|
||||
|
||||
## [v0.8.0] - 2019-09-28
|
||||
### Features
|
||||
- **UI:** made dropdowns non-editable [37f5547](https://gitlab.com/Earthnuker/ed_lrr/commit/37f5547)
|
||||
|
||||
### Miscellaneous
|
||||
- Alsways compile in release mode [e9bcd3e](https://gitlab.com/Earthnuker/ed_lrr/commit/e9bcd3e)
|
||||
|
||||
|
||||
## [v0.7.1] - 2019-09-28
|
||||
### Features
|
||||
- **config:** finish integrating new configuration system [a8d6a32](https://gitlab.com/Earthnuker/ed_lrr/commit/a8d6a32)
|
||||
|
||||
|
||||
## [v0.7.0] - 2019-09-28
|
||||
### Features
|
||||
- **Router:** finish implementing route computation progress display [93f655c](https://gitlab.com/Earthnuker/ed_lrr/commit/93f655c)
|
||||
|
||||
### Miscellaneous
|
||||
- Add VSCode workspace config to gitignore [a91fa73](https://gitlab.com/Earthnuker/ed_lrr/commit/a91fa73)
|
||||
- formatting [f78ec66](https://gitlab.com/Earthnuker/ed_lrr/commit/f78ec66)
|
||||
- **CI:** rename main executable before building installer [dc0e3fc](https://gitlab.com/Earthnuker/ed_lrr/commit/dc0e3fc)
|
||||
|
||||
|
||||
## [v0.6.2] - 2019-09-28
|
||||
### Features
|
||||
- **GUI:** Integrate route computation Job [f4e6bd3](https://gitlab.com/Earthnuker/ed_lrr/commit/f4e6bd3)
|
||||
|
||||
|
||||
## [v0.6.1] - 2019-09-28
|
||||
### Features
|
||||
- **GUI:** Integrate new config system [22ca2d2](https://gitlab.com/Earthnuker/ed_lrr/commit/22ca2d2)
|
||||
|
||||
### Miscellaneous
|
||||
- re-export config from main GUI module [0d89106](https://gitlab.com/Earthnuker/ed_lrr/commit/0d89106)
|
||||
|
||||
|
||||
## [v0.6.0] - 2019-09-28
|
||||
### Features
|
||||
- **cli:** Add config editor [8b0b56f](https://gitlab.com/Earthnuker/ed_lrr/commit/8b0b56f)
|
||||
|
||||
|
||||
## [v0.5.1] - 2019-09-28
|
||||
### Features
|
||||
- **router:** Implement route computation job with progress dualog [2c000da](https://gitlab.com/Earthnuker/ed_lrr/commit/2c000da)
|
||||
|
||||
### Miscellaneous
|
||||
- **setup.py:** Pull version info from git, unify scripts (one script for GUI and CLI) [a630851](https://gitlab.com/Earthnuker/ed_lrr/commit/a630851)
|
||||
|
||||
|
||||
## [v0.5.0] - 2019-09-28
|
||||
### Documentation
|
||||
- Update README.md [66267e7](https://gitlab.com/Earthnuker/ed_lrr/commit/66267e7)
|
||||
|
||||
### Features
|
||||
- **installer:** Download EDSM dumps after install [4237b30](https://gitlab.com/Earthnuker/ed_lrr/commit/4237b30)
|
||||
|
||||
### Miscellaneous
|
||||
- Update icon generator and regenerate icon [7838480](https://gitlab.com/Earthnuker/ed_lrr/commit/7838480)
|
||||
|
||||
|
||||
## [v0.4.1] - 2019-09-28
|
||||
### Features
|
||||
- **router:** Start implementing route pruning support [88a0378](https://gitlab.com/Earthnuker/ed_lrr/commit/88a0378)
|
||||
|
||||
|
||||
## [v0.4.0] - 2019-09-28
|
||||
### Features
|
||||
- **config:** Update config system to use profig [b1143c3](https://gitlab.com/Earthnuker/ed_lrr/commit/b1143c3)
|
||||
|
||||
|
||||
## [v0.3.0] - 2019-09-28
|
||||
### Features
|
||||
- **build system:** Remove build.py (switched to tox), add output to build_gui.py [fb3a130](https://gitlab.com/Earthnuker/ed_lrr/commit/fb3a130)
|
||||
|
||||
|
||||
## [v0.2.0] - 2019-09-28
|
||||
### Features
|
||||
- **router:** Add dialog to display computed route [d498746](https://gitlab.com/Earthnuker/ed_lrr/commit/d498746)
|
||||
|
||||
|
||||
## [v0.1.12] - 2019-09-21
|
||||
### Bug Fixes
|
||||
- switch inno setup version in appveyor [fe3534e](https://gitlab.com/Earthnuker/ed_lrr/commit/fe3534e)
|
||||
|
||||
|
||||
## [v0.1.11] - 2019-09-21
|
||||
### Bug Fixes
|
||||
- typo in appveyor.yml [09e6f0a](https://gitlab.com/Earthnuker/ed_lrr/commit/09e6f0a)
|
||||
|
||||
|
||||
## [v0.1.10] - 2019-09-21
|
||||
### Bug Fixes
|
||||
- disable confirmation for conda install [eaddb18](https://gitlab.com/Earthnuker/ed_lrr/commit/eaddb18)
|
||||
|
||||
|
||||
## [v0.1.9] - 2019-09-21
|
||||
### Bug Fixes
|
||||
- add missing conda channel [6d9f1ef](https://gitlab.com/Earthnuker/ed_lrr/commit/6d9f1ef)
|
||||
|
||||
|
||||
## [v0.1.8] - 2019-09-21
|
||||
### Bug Fixes
|
||||
- add missing dependencies to appveyor.yml [e8beb55](https://gitlab.com/Earthnuker/ed_lrr/commit/e8beb55)
|
||||
|
||||
|
||||
## [v0.1.7] - 2019-09-20
|
||||
### Bug Fixes
|
||||
- fix nuikta call to clean build directory [fa485be](https://gitlab.com/Earthnuker/ed_lrr/commit/fa485be)
|
||||
|
||||
|
||||
## [v0.1.6] - 2019-09-20
|
||||
### Bug Fixes
|
||||
- fix nuikta call to download without confirmation [11efe30](https://gitlab.com/Earthnuker/ed_lrr/commit/11efe30)
|
||||
|
||||
|
||||
## [v0.1.5] - 2019-09-20
|
||||
### Bug Fixes
|
||||
- fixed appveyor.yml [59390fe](https://gitlab.com/Earthnuker/ed_lrr/commit/59390fe)
|
||||
|
||||
|
||||
## [v0.1.4] - 2019-09-20
|
||||
### Bug Fixes
|
||||
- fixed tox.ini [6bb7e1e](https://gitlab.com/Earthnuker/ed_lrr/commit/6bb7e1e)
|
||||
|
||||
### Documentation
|
||||
- small wording change [3779911](https://gitlab.com/Earthnuker/ed_lrr/commit/3779911)
|
||||
|
||||
|
||||
## [v0.1.3] - 2019-08-31
|
||||
### Bug Fixes
|
||||
- **setup:** fix setup script to include subdirectories [4a392d9](https://gitlab.com/Earthnuker/ed_lrr/commit/4a392d9)
|
||||
|
||||
### Documentation
|
||||
- Insert page break after table of contents [f027e02](https://gitlab.com/Earthnuker/ed_lrr/commit/f027e02)
|
||||
- Update documentation to include basic description [714741a](https://gitlab.com/Earthnuker/ed_lrr/commit/714741a)
|
||||
- Rename `doc` folder to `docs`, update outline [fb54bda](https://gitlab.com/Earthnuker/ed_lrr/commit/fb54bda)
|
||||
- Add skeleton for documentation [dbc6f35](https://gitlab.com/Earthnuker/ed_lrr/commit/dbc6f35)
|
||||
|
||||
### Miscellaneous
|
||||
- Update changelog template to make conversion to PDF easier [87550a9](https://gitlab.com/Earthnuker/ed_lrr/commit/87550a9)
|
||||
- **formatting:** ran `cargo fmt` and `cargo clippy`, fixed all warnings [fb3f79b](https://gitlab.com/Earthnuker/ed_lrr/commit/fb3f79b)
|
||||
|
||||
|
||||
## [v0.1.2] - 2019-08-05
|
||||
### Bug Fixes
|
||||
- **router:** Fixed some syntax errors created by botched merge [4b14643](https://gitlab.com/Earthnuker/ed_lrr/commit/4b14643)
|
||||
|
||||
|
||||
## [v0.1.1] - 2019-08-05
|
||||
### Features
|
||||
- **GUI:** Implement route plotting and fuzzy search [c290d5e](https://gitlab.com/Earthnuker/ed_lrr/commit/c290d5e)
|
||||
|
||||
|
||||
## [v0.1.0] - 2019-07-22
|
||||
### Features
|
||||
- **GUI:** Add Download functionality, update Rust code, update Python code [ec3972b](https://gitlab.com/Earthnuker/ed_lrr/commit/ec3972b)
|
||||
|
||||
|
||||
## v0.0.0 - 2019-07-15
|
||||
|
||||
[Unreleased]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.14.0...HEAD
|
||||
[v0.14.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.13.0...v0.14.0
|
||||
[v0.13.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.12.0...v0.13.0
|
||||
[v0.12.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.11.0...v0.12.0
|
||||
[v0.11.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.10.0...v0.11.0
|
||||
[v0.10.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.9.4...v0.10.0
|
||||
[v0.9.4]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.9.3...v0.9.4
|
||||
[v0.9.3]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.9.2...v0.9.3
|
||||
[v0.9.2]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.9.1...v0.9.2
|
||||
[v0.9.1]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.9.0...v0.9.1
|
||||
[v0.9.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.8.1...v0.9.0
|
||||
[v0.8.1]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.8.0...v0.8.1
|
||||
[v0.8.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.7.1...v0.8.0
|
||||
[v0.7.1]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.7.0...v0.7.1
|
||||
[v0.7.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.6.2...v0.7.0
|
||||
[v0.6.2]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.6.1...v0.6.2
|
||||
[v0.6.1]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.6.0...v0.6.1
|
||||
[v0.6.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.5.1...v0.6.0
|
||||
[v0.5.1]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.5.0...v0.5.1
|
||||
[v0.5.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.4.1...v0.5.0
|
||||
[v0.4.1]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.4.0...v0.4.1
|
||||
[v0.4.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.3.0...v0.4.0
|
||||
[v0.3.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.2.0...v0.3.0
|
||||
[v0.2.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.12...v0.2.0
|
||||
[v0.1.12]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.11...v0.1.12
|
||||
[v0.1.11]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.10...v0.1.11
|
||||
[v0.1.10]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.9...v0.1.10
|
||||
[v0.1.9]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.8...v0.1.9
|
||||
[v0.1.8]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.7...v0.1.8
|
||||
[v0.1.7]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.6...v0.1.7
|
||||
[v0.1.6]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.5...v0.1.6
|
||||
[v0.1.5]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.4...v0.1.5
|
||||
[v0.1.4]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.3...v0.1.4
|
||||
[v0.1.3]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.2...v0.1.3
|
||||
[v0.1.2]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.1...v0.1.2
|
||||
[v0.1.1]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.1.0...v0.1.1
|
||||
[v0.1.0]: https://gitlab.com/Earthnuker/ed_lrr/compare/v0.0.0...v0.1.0
|
|
@ -1,825 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-automata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clicolors-control"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bstr 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv-core"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed_lrr"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"csv 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
"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_derive 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)",
|
||||
"structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pdqselect"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "permutohedron"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.12"
|
||||
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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_isaac"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_jitter"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_os"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstar"
|
||||
version = "0.4.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)",
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.94"
|
||||
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 = "serde_json"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.10"
|
||||
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 = "structopt"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"structopt-derive 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "structopt-derive"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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 = "syn"
|
||||
version = "0.15.39"
|
||||
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)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termios"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
|
||||
"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7"
|
||||
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09"
|
||||
"checksum bstr 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc0572e02f76cb335f309b19e0a0d585b4f62788f7d26de2a13a836a637385f"
|
||||
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73abfd4c73d003a674ce5d2933fca6ce6c42480ea84a5ffe0a2dc39ed56300f9"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ca57c2c14b8a2bf3105bc9d15574aad80babf6a9c44b1058034cdf8bd169628"
|
||||
"checksum csv 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a54cd62557f353f140b42305fb4efcff2ae08e32fbabaf5b0929423000febb63"
|
||||
"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c"
|
||||
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
|
||||
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
|
||||
"checksum encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90b2c9496c001e8cb61827acdefad780795c42264c137744cae6f7d9e3450abd"
|
||||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||
"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 heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
"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 itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
|
||||
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||
"checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
|
||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||
"checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319"
|
||||
"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff"
|
||||
"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
|
||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||
"checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee"
|
||||
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
|
||||
"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7"
|
||||
"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"
|
||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
|
||||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
|
||||
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
||||
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
||||
"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
||||
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"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 rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f"
|
||||
"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"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "076a696fdea89c19d3baed462576b8f6d663064414b5c793642da8dfeb99475b"
|
||||
"checksum serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "ef45eb79d6463b22f5f9e16d283798b7c0175ba6050bc25c1a946c122727fe7b"
|
||||
"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
|
||||
"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 strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
"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"
|
||||
"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"
|
||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
||||
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
|
||||
"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
|
||||
"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"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"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"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
26
Cargo.toml
26
Cargo.toml
|
@ -1,26 +0,0 @@
|
|||
[package]
|
||||
name = "ed_lrr"
|
||||
version = "0.1.0"
|
||||
authors = ["Daniel Seiller <earthnuker@gmail.com>"]
|
||||
edition = "2018"
|
||||
repository = "https://gitlab.com/Earthnuker/ed_lrr.git"
|
||||
license = "WTFPL"
|
||||
|
||||
|
||||
[profile.release]
|
||||
# debug=true
|
||||
|
||||
[dependencies]
|
||||
csv = "1.1.0"
|
||||
serde = "1.0.94"
|
||||
serde_derive = "1.0.94"
|
||||
rstar = {version="0.4.0",features=["serde"]}
|
||||
humantime = "1.2.0"
|
||||
structopt = "0.2.18"
|
||||
permutohedron = "0.2.4"
|
||||
serde_json = "1.0.39"
|
||||
indicatif = "0.11.0"
|
||||
fnv = "1.0.6"
|
||||
bincode = "1.1.4"
|
||||
sha3 = "0.8.2"
|
||||
byteorder = "1.3.2"
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 Daniel Seiller
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,11 @@
|
|||
include rust/Cargo.toml
|
||||
include rust/.cargo/config
|
||||
exclude docs_mdbook
|
||||
exclude celery_test
|
||||
exclude installer
|
||||
exclude imgui_test
|
||||
exclude icon
|
||||
recursive-include rust/src *
|
||||
recursive-include ed_lrr_gui *
|
||||
recursive-exclude __pycache__ *.pyc *.pyo
|
||||
global-exclude __pycache__
|
130
README.md
130
README.md
|
@ -1,20 +1,110 @@
|
|||
# Elite: Dangerous Long Range Router (Rust Version)
|
||||
|
||||
## Usage:
|
||||
|
||||
1. download `bodies.json` and `systemsWithCoordinates.json` from https://www.edsm.net/en/nightly-dumps/ and place them in the `dumps` folder
|
||||
2. run `cargo +nightly install --path .` or `cargo +nightly install --git https://gitlab.com/Earthnuker/ed_lrr.git`
|
||||
3. run `ed_lrr_pp --bodies dumps/bodies.json --systems dumps/systemsWithCoordinates.json`
|
||||
- Alternatively run `process.py` in the `dumps` folder
|
||||
4. run `ed_lrr --help`
|
||||
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Working nightly Rust Compiler (tested with `rustc 1.37.0-nightly (5d8f59f4b 2019-06-04)`)
|
||||
- ~8GB of free RAM
|
||||
- Optional:
|
||||
- Python 3.7
|
||||
- Pandas
|
||||
- uJSON
|
||||
# Elite: Dangerous Long-range router
|
||||
|
||||
## Features
|
||||
|
||||
- Five different routing algorithms:
|
||||
- **Breadth-first search:**, always finds the shortest route but is quite slow
|
||||
- **Bidirectional BFS:**: should give similar route quality to BFS but take less time (not yet implemented)
|
||||
- **A-Star:** has an adjustable tradeoff between speed and quality
|
||||
- **Greedy search:** always picks the next reachable star that's closest to the destination, very fast but very poor quality routes
|
||||
- **Economic:** computes route with lowest possible fuel consumption (WIP)
|
||||
- Nice GUI! (made with PyQt5)
|
||||
- Two themes! (Dark and Light)
|
||||
- Uses data from [EDSM](https://edsm.net/) for star data
|
||||
- Only routes through scoopable systems, no more running out of fuel! (assuming you have a fuel scoop)
|
||||
- Take fuel consumption into account and add refueling stops along the route (Not yet implemented)
|
||||
- Precomputing of BFS routing graphs for:
|
||||
- near-instant routing from your home system to any destination!
|
||||
- near-instant routing any source back to your-home system (WIP)!
|
||||
- near-instant routing between any pair of systems (Not yet implemented, see TODO)
|
||||
- Routing code written in Rust, so it's quite speedy!
|
||||
- Export routes as HTML, JSON, CSV and SVG (WIP)
|
||||
- Automagically copy next jump destination into system clipboard (works by monitoring the player journal file) (Not yet implemented)
|
||||
- (optional) Web interface with job queue for route computations and multi-user support
|
||||
|
||||
# Installation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python:
|
||||
- conda ([Miniconda](https://docs.conda.io/en/latest/miniconda.html)/[Anaconda](https://www.anaconda.com/distribution/))
|
||||
- [nox](https://nox.thea.codes/en/stable/index.html)
|
||||
- Inno Setup Compiler
|
||||
- Download from [here](http://www.jrsoftware.org/isdl.php)
|
||||
- or install via [scoop](https://scoop.sh/) `scoop install inno-setup`)
|
||||
- Visual Studio 2019
|
||||
- nightly rust compiler (`x86_64-pc-windows-msvc`)
|
||||
|
||||
## Building an installer
|
||||
|
||||
(Assuming `conda` is in your `PATH`)
|
||||
|
||||
1. Run `nox -k build`
|
||||
2. Grab the installer from `installer/Output/`
|
||||
|
||||
## Manual installation
|
||||
|
||||
(Assuming `conda` is in your `PATH`)
|
||||
|
||||
1. Start a Visual Studio 2019 x64 command prompt
|
||||
2. Run the following commands:
|
||||
|
||||
```
|
||||
conda install pycrypto ujson
|
||||
pip install setuptools_rust
|
||||
pip install .[all]
|
||||
```
|
||||
|
||||
then you can run `ed_lrr -h` from your command prompt to get help
|
||||
|
||||
# Notes on FSD Fuel consumption and jump range
|
||||
|
||||
FSD Fuel consumption ([E:D Wiki](https://elite-dangerous.fandom.com/wiki/Frame_Shift_Drive#Hyperspace_Fuel_Equation)): $$Fuel = 0.001 \cdot l \cdot \left(\frac{dist \cdot \left(m_{fuel} +
|
||||
m_{ship}\right)}{boost \cdot m_{opt}}\right)^{p}$$
|
||||
|
||||
Solving for $dist$ gives the jump range (in Ly) for a given amount of fuel (in tons) as: $$dist = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
Assuming $f_{max}$ tons of available fuel gives us the maximum jump range for a single jump as: $$dist_{max} = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot f_{max}}{l}\right)^{\frac{1}{p}}}{f_{max} + m_{ship}}$$
|
||||
|
||||
Since the guardian FSD booster increases the maximum jump range by $B_g$ Ly we can calculate a correction factor for the fuel consumption as: $$0.001 \cdot l \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{p}$$
|
||||
|
||||
Incorporating $e_{fuel}$ into the distance equation yields $$dist = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot e_{fuel} \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
Expanding $e_{fuel}$ yields $$dist = \frac{boost \cdot m_{opt} \cdot \left(1.0 \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{p} \cdot \min\left(f_{max}, m_{fuel}\right)\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
Finally, Expanding $dist_{max}$ yields the full equation as $$dist = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000000.0 \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{- p} \cdot \min\left(f_{max}, m_{fuel}\right)}{l^{2}}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
Where:
|
||||
- $Fuel$ is the fuel needed to jump (in tons)
|
||||
- $l$ is the linear constant of your FSD (depends on the rating)
|
||||
- $p$ is the power constant of your FSD (depends on the class)
|
||||
- $m_{ship}$ is the mass of your ship (including cargo)
|
||||
- $m_{fuel}$ is the amount of fuel in tons currently stored in your tanks
|
||||
- $m_{opt}$ is the optimized mass of your FSD (in tons)
|
||||
- $f_{max}$ is the maximum amount of fuel your FSD can use per jump
|
||||
- $boost$ is the "boost factor" of your FSD (1.0 when jumping normally, 1.5 when supercharged by a white dwarf, 4.0 for a neutron star, etc)
|
||||
- $dist$ is the distance you can jump with a given fuel amount
|
||||
- $dist_{max}$ is the maximum distance you can jump (when $m_{fuel}=f_{max}$)
|
||||
- $B_{g}$ is the amount of Ly added by your Guardian FSD Booster
|
||||
- $e_{fuel}$ is the efficiency increase added by the Guardian FSD Booster
|
||||
|
||||
# Fuel multiplier (CMDR jonburnage)
|
||||
|
||||
$$F_f = f_{max} \cdot \left(\frac{B_{g} \cdot m_{ship}}{m_{opt}} + l \cdot \left(\frac{f_{max}}{l}\right)^{\frac{1}{p}}\right)^{- p}$$
|
||||
|
||||
# Fuel multiplier (Spansh)
|
||||
|
||||
$$F_f = 0.001 \cdot l \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{\left(B_{g} + \frac{boost^{2} \cdot m_
|
||||
{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}\right) \cdot \left(m_{fuel} + m_{ship}\right)}\right)^{p}$$
|
||||
|
||||
$$ F_f = 0.001 \cdot l \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{p}$$
|
||||
|
||||
$$dist = \frac{boost \cdot m_{opt} \cdot \left(1.0 \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{p} \cdot \min\left(f_{max}, m_{fuel}\right)\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
# Misc Stuff
|
||||
|
||||
---
|
||||
|
||||
$$dist = \frac{- B_{g} \cdot m_{fuel} - B_{g} \cdot m_{ship} + boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot e_{fuel} \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
$$e_{fuel} = \frac{l \cdot \left(\frac{10.0^{- \frac{3.0}{p}} \cdot \left(B_{g} + dist\right) \cdot \left(m_{fuel} + m_{ship}\right)}{boost \cdot m_{opt}}\right)^{p}}{\min\left(f_{max}, m_{fuel}\right)}$$
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
# TODO
|
||||
|
||||
## API
|
||||
|
||||
- expose multiroute TSP optimizer
|
||||
|
||||
## Routing
|
||||
|
||||
- beam stack search, queue for each depth level, ordered by distance to goal node (min first)
|
||||
- GraphSearch trait
|
||||
- Implement Neutron Mode
|
||||
- Filter for neutron stars, plot coarse route, then plot exact router between waypoints
|
||||
- What Jump-Range to use for neutron route? `max_range*4`?
|
||||
- furthest inside jump range, otherwise closest?
|
||||
- Implement Bidirectional BFS
|
||||
- Optimized All-Pairs BFS for graph precomputation
|
||||
- Take fuel consumption into account (WIP) partially implemented
|
||||
- Guardian Booster support (Done?)
|
||||
- Economic routing (minimal fuel, dijkstra)
|
||||
- implemented, needs to be properly exposed
|
||||
- Custom weights and filtering for routing [evalexpr](https://docs.rs/evalexpr/)
|
||||
- pathfinding weighted by $(goal-pos)\cdot(goal-start)$
|
||||
- use vecmat crate for vector distance etc
|
||||
|
||||
|
||||
## GUI
|
||||
|
||||
- Imgui?
|
||||
- Implement estimate time to completion display for route computation and preprocessing (Done?)
|
||||
- Export route as:
|
||||
- JSON
|
||||
- HTML (WIP)
|
||||
- CSV
|
||||
- SVG
|
||||
|
||||
## Installer
|
||||
|
||||
- Update PATH from installer
|
||||
|
||||
## Preprocessing
|
||||
|
||||
- Build index over `systemsWithCoordinates.json` instead of loading it into RAM (reuse modified `LineCache` from `router.rs`)
|
||||
- Finish `galaxy.jsonl` preprocessor (Done?)
|
||||
- Implement Python interface to preprocessor
|
||||
|
||||
## Misc
|
||||
|
||||
- Luigi/Celery/Dask based Task queue for distributed routing
|
|
@ -0,0 +1,32 @@
|
|||
image: Visual Studio 2019
|
||||
platform: x64
|
||||
branches:
|
||||
only:
|
||||
- pyqt_gui
|
||||
- WIP
|
||||
build: off
|
||||
environment:
|
||||
VCVARS: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat
|
||||
VCVARSARG: x64
|
||||
MINICONDA: C:\Miniconda3-x64
|
||||
INNO: C:\Program Files (x86)\Inno Setup 6
|
||||
matrix:
|
||||
- PY_VERSION: "3.5"
|
||||
- PY_VERSION: "3.6"
|
||||
- PY_VERSION: "3.7"
|
||||
- PY_VERSION: "3.8"
|
||||
|
||||
install:
|
||||
- set PATH=%MINICONDA%\\Library\\bin;%MINICONDA%\\Scripts;%USERPROFILE%\\.cargo\\bin;%PATH%
|
||||
- set PATH=%INNO%;%PATH%
|
||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init -y --default-toolchain nightly --default-host x86_64-pc-windows-msvc
|
||||
- if defined VCVARS call "%VCVARS%" %VCVARSARG%
|
||||
- conda activate
|
||||
- conda update -y -n base -c defaults conda
|
||||
- pip install nox
|
||||
|
||||
test_script:
|
||||
- cmd: nox -s test-%PY_VERSION%
|
||||
- ps: Get-ChildItem .\installer\Output\*.exe | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- ps: Get-ChildItem .\dist\* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name.Insert($_.Name.Length,".zip") }
|
|
@ -0,0 +1,6 @@
|
|||
def pruntLayer(l,w):
|
||||
Keep=sorted(Open[l])[:w] # best `w` node from `Open[l]`
|
||||
Prune = [n for n in Open[l] if n not in Keep]
|
||||
beam_stack.top().f_max=min([f(n) for n in Prune])
|
||||
for n in Prune:
|
||||
Open[l].remove(n)
|
|
@ -0,0 +1,84 @@
|
|||
from tqdm import tqdm
|
||||
import time
|
||||
import json
|
||||
import os
|
||||
import statistics
|
||||
|
||||
def stats(values):
|
||||
ret={
|
||||
"max":max(values),
|
||||
"min":min(values),
|
||||
"mean":statistics.mean(values),
|
||||
"range":abs(max(values)-min(values)),
|
||||
"sum":sum(values)
|
||||
}
|
||||
if len(values)>1:
|
||||
ret["stdev"]=statistics.stdev(values)
|
||||
else:
|
||||
ret["stdev"]=0
|
||||
return ret
|
||||
|
||||
last_seen=float('inf')
|
||||
pbar=None
|
||||
def callback(state):
|
||||
try:
|
||||
global pbar,last_seen
|
||||
if state['n_seen']<last_seen:
|
||||
if pbar:
|
||||
pbar.close()
|
||||
pbar=tqdm(total=state['d_total'],unit="Ly",unit_scale=True)
|
||||
last_seen=state['n_seen']
|
||||
pbar.n=round(state['d_total']-state['d_rem'],2)
|
||||
pbar.set_description("[J:{depth} | Q:{queue_size} | D:{d_rem:.2f} Ly | S:{n_seen} ({prc_seen:.2f}%)] {system}".format(**state))
|
||||
pbar.update(0)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
if not 'callback' in vars():
|
||||
callback=None
|
||||
import _ed_lrr
|
||||
callback=None
|
||||
r=_ed_lrr.PyRouter(callback)
|
||||
r.load("stars.csv")
|
||||
res=[]
|
||||
done=set()
|
||||
if os.path.isfile("res.json"):
|
||||
with open("res.json","r") as fh:
|
||||
try:
|
||||
res=json.load(fh)
|
||||
except:
|
||||
pass
|
||||
for v in res:
|
||||
done.add((v[0],v[1]))
|
||||
systems=r.resolve_systems("Sol","Colonia")
|
||||
min_dt_total=60*10
|
||||
print("Warming up...")
|
||||
r.route([systems['Sol'],systems['Sol']],48.0,0.0,0.0,0) # warmup
|
||||
print("Done")
|
||||
|
||||
for g in [0.0, 0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]:
|
||||
for w in [0.0, 0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]:
|
||||
if (g,w) in done:
|
||||
continue
|
||||
dt=[]
|
||||
lr=[]
|
||||
l=0
|
||||
while sum(dt)<min_dt_total:
|
||||
l+=1
|
||||
print({'g':g,'w':w,'l':l})
|
||||
t=time.time()
|
||||
route=r.route([systems['Sol'],systems['Colonia']],48.0,g,w,0)
|
||||
dt.append(time.time()-t)
|
||||
lr.append(len(route))
|
||||
print(stats(dt))
|
||||
dt=sum(dt)/len(dt)
|
||||
res.append((g,w,lr,dt))
|
||||
with open("res.json","w") as of:
|
||||
json.dump(res,of)
|
||||
|
||||
# for n in range(21):
|
||||
# grid=r.get_grid(1<<n)
|
||||
# max_v=max(grid.items(),key=lambda v:v[1])
|
||||
# mean_v=sum(grid.values())/len(grid)
|
||||
# print(1<<n,len(grid),mean_v,max_v)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from PyQt5 import uic
|
||||
import os
|
||||
|
||||
ui_path = os.path.dirname(os.path.abspath(__file__))
|
||||
for root, folders, files in os.walk(ui_path):
|
||||
if "site-packages" in root:
|
||||
continue
|
||||
if ".history" in folders:
|
||||
folders.remove(".history")
|
||||
for file in files:
|
||||
file = os.path.join(root, file)
|
||||
outfile, ext = os.path.splitext(file)
|
||||
if ext == ".ui":
|
||||
outfile = outfile + ".py"
|
||||
print(file, "->", outfile)
|
||||
with open(outfile, "w") as fh_out:
|
||||
uic.compileUi(file, fh_out, from_imports=True)
|
|
@ -0,0 +1,16 @@
|
|||
#RABBITMQ
|
||||
rabbitmqctl stop_app
|
||||
rabbitmqctl reset
|
||||
rabbitmqctl start_app
|
||||
rabbitmqctl add_user ed_lrr ed_lrr
|
||||
rabbitmqctl add_vhost ed_lrr
|
||||
rabbitmqctl set_user_tags ed_lrr ed_lrr
|
||||
rabbitmqctl set_permissions -p ed_lrr ed_lrr ".*" ".*" ".*"
|
||||
rabbitmqctl set_permissions guest ".*" ".*" ".*"
|
||||
rabbitmqctl set_permissions -p ed_lrr guest ".*" ".*" ".*"
|
||||
Write-Host RabbitMQ setup done
|
||||
#Celery
|
||||
Write-Host starting Celery
|
||||
celery worker -l info
|
||||
|
||||
#celery -A celery_test flower --presistent --broker=pyamqp://ed_lrr:ed_lrr@localhost/ed_lrr --broker_api=http://ed_lrr:ed_lrr@localhost:15672/api/
|
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from celery import Celery
|
||||
import _ed_lrr
|
||||
import os
|
||||
|
||||
app = Celery("ed_lrr")
|
||||
app.config_from_object(__import__("celeryconfig"))
|
||||
|
||||
|
||||
@app.task(bind=True)
|
||||
def route(self, hops, jmp_range):
|
||||
def callback(state):
|
||||
print("PRC: ", state.get("prc_done", 0.0))
|
||||
self.update_state(state="PROGRESS", meta=state)
|
||||
|
||||
self.update_state(state="RUNNING", meta={})
|
||||
return _ed_lrr.route(
|
||||
hops,
|
||||
jmp_range,
|
||||
None,
|
||||
"bfs",
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
0.0,
|
||||
None,
|
||||
r"C:\Users\Earthnuker\AppData\Local\ED_LRR\data\stars.csv",
|
||||
0,
|
||||
callback,
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from celery_test import route
|
||||
import sys
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
job = route.AsyncResult(sys.argv[1])
|
||||
if job.ready():
|
||||
print([job, job.state, len(job.info), len(job.result)])
|
||||
else:
|
||||
print([job, job.state, job.info, job.result])
|
||||
exit(0)
|
||||
jobs = [route.delay(["Ix", "Colonia"], 48)]
|
||||
print(jobs)
|
|
@ -0,0 +1,18 @@
|
|||
import os
|
||||
|
||||
os.environ["FORKED_BY_MULTIPROCESSING"] = "1"
|
||||
broker_url = "pyamqp://ed_lrr:ed_lrr@localhost/ed_lrr"
|
||||
broker_api = "http://guest:guest@localhost:15672/api/"
|
||||
imports = ("celery_test",)
|
||||
result_backend = "file://celery_results/"
|
||||
result_persistent = True
|
||||
task_track_started = True
|
||||
task_time_limit = 60 * 60
|
||||
result_extended = True
|
||||
result_expires = None
|
||||
worker_direct = True
|
||||
worker_max_tasks_per_child = 10
|
||||
worker_max_memory_per_child = 4 * 1024 * 1024 # 4GB
|
||||
worker_state_db = "ed_lrr.state"
|
||||
worker_send_task_events = True
|
||||
worker_log_color = True
|
|
@ -0,0 +1 @@
|
|||
book
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"spellright.language": [
|
||||
"de",
|
||||
"en"
|
||||
],
|
||||
"spellright.documentTypes": [
|
||||
"latex",
|
||||
"plaintext",
|
||||
"git-commit"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"todo": [],
|
||||
"in-progress": [],
|
||||
"testing": [],
|
||||
"done": []
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
[book]
|
||||
authors = ["Daniel Seiller"]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
title = "Elite: Dangerous Long Range Router"
|
||||
|
||||
[output.html]
|
||||
preferred-dark-theme = "ayu"
|
||||
mathjax-support = true
|
||||
|
||||
[preprocessor.svgbob]
|
||||
text_width = 8.0
|
||||
text_height = 16.0
|
||||
class = "bob"
|
||||
font_family = "arial"
|
||||
font_size = 14.0
|
||||
stroke_width = 2.0
|
||||
# there's using css-variables from theme:
|
||||
stroke_color = "var(--fg)" # see default theme / variables.css
|
||||
background_color = "transparent" # also useful `var(--bg)`
|
||||
# all properties are optional.
|
|
@ -0,0 +1,12 @@
|
|||
# Summary
|
||||
|
||||
- [Intro](./intro/_index.md)
|
||||
- [Galactic Travel in Elite: Dangerous](./intro/ed_travel.md)
|
||||
- [FSD Fuel consumption and jump range](./intro/fsd_fuel.md)
|
||||
- [Graph Search algorithms](./intro/graph_algos.md)
|
||||
|
||||
- [Elite Dangerous Long Range Router](./ed_lrr/_index.md)
|
||||
- [Graph representation](./ed_lrr/graph.md)
|
||||
- [Search modes](./ed_lrr/modes.md)
|
||||
- [Graph precomputation](./ed_lrr/precomp.md)
|
||||
- [Python API](./ed_lrr/py_api.md)
|
|
@ -0,0 +1 @@
|
|||
# Elite Dangerous Long Range Router
|
|
@ -0,0 +1,4 @@
|
|||
# Graph representation
|
||||
|
||||
ED_LRR uses an implicit graph built on top of an R*-Tree for its route computation.
|
||||
Every node (star system) has edges towards all systems within jump range, edge weights (the distance between star systems) can be computed on the fly when necessary
|
|
@ -0,0 +1,31 @@
|
|||
# Search modes
|
||||
|
||||
## Heuristic
|
||||
|
||||
A*, Greedy and Beam search all use the following heuristic to select candidates to expand on the next layer of the graph
|
||||
|
||||
$$mult(n) =
|
||||
\begin{cases}
|
||||
4 &\text{if $n$ is a neutron star} \\\\
|
||||
1.5 &\text{if $n$ is a white dwarf star} \\\\
|
||||
1 &\text{otherwise}
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
$$d(a,b) = \sqrt{(a_x-b_x)^2+(a_y-b_y)^2+(a_z-b_z)^2}$$
|
||||
|
||||
$$
|
||||
h(\text{node},\text{goal})=
|
||||
\max(
|
||||
d(\text{node},\text{goal})-
|
||||
(
|
||||
\text{range}*mult(\text{node})
|
||||
),0)
|
||||
$$
|
||||
|
||||
potential new heuristic:
|
||||
|
||||
$$
|
||||
h(\text{node},\text{next},\text{goal})=
|
||||
1 - {\cos^{-1}(|(\text{next}-\text{node})| \cdot |(\text{goal}-\text{node})|)\over\pi}
|
||||
$$
|
|
@ -0,0 +1,2 @@
|
|||
# Graph precomputation
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
# Python API
|
||||
|
||||
First the module needs to be imported
|
||||
|
||||
```python
|
||||
from _ed_lrr import PyRouter, PyShip
|
||||
```
|
||||
|
||||
Then we need to instantiate a route plotter
|
||||
|
||||
```python
|
||||
# callback is passed a dict describing the current search state and progress
|
||||
def callback(state):
|
||||
print(state)
|
||||
|
||||
r=PyRouter(callback)
|
||||
```
|
||||
|
||||
Optionally ship loadouts can be loaded from the Elite: Dangerous journal files
|
||||
|
||||
```python
|
||||
ships = PyShip.from_journal()
|
||||
```
|
||||
|
||||
To plot a route we need to load a list of star systems with coordinates
|
||||
|
||||
```python
|
||||
r.load("./stars.csv")
|
||||
```
|
||||
|
||||
After a list has been loaded we can resolve star systems to their IDs
|
||||
|
||||
```python
|
||||
systems = [
|
||||
# resolve by coordinates, needs to build an R*-Tree so uses a few GB of RAM
|
||||
(0,0,0),
|
||||
# resolve by name, does fuzzy search, has to scan the whole list of system names
|
||||
"Colonia",
|
||||
# resolve by ID, fairly fast, but the IDs are ed_lrr specific
|
||||
3553323
|
||||
]
|
||||
systems = r.resolve_systems(*query) # this will return a dict mapping the input key to a dict
|
||||
assert sorted(systems.keys())==sorted(query)
|
||||
sys_ids = {k: v["id"] for k, v in systems.items()}
|
||||
```
|
||||
|
||||
Once the system IDs are known we can compute a route
|
||||
|
||||
```python
|
||||
route = r.route(
|
||||
[sys_ids["Sol"], sys_ids["Colonia"]] # route hops, can be any number of systems (at least 2)
|
||||
48.0, # jump range
|
||||
beam_width=1<<12, # beam width to limit exploration (4096)
|
||||
greedyness=0, # greedyness for A* (0=BFS,0.5 = A*, 1=Greedy)
|
||||
max_dist=500, # maximum deviation from straight line (not yet implemented)
|
||||
num_workers=0 # number of workers to distribute the search accross (0 -> serial mode, >=1 -> spawn worker pool)
|
||||
)
|
||||
```
|
|
@ -0,0 +1,17 @@
|
|||
import graph;
|
||||
size(500,0);
|
||||
real fsd(real m_fuel) {
|
||||
// 4A drive
|
||||
real boost = 1.0;
|
||||
real f_max = 3.0;
|
||||
real m_opt = 525.0;
|
||||
real m_ship = 347.0;
|
||||
real l = 12.0;
|
||||
real p = 2.30;
|
||||
return ((boost*m_opt*(1000.0*min(f_max,m_fuel)/l)^(1/p)))/(m_ship+m_fuel);
|
||||
}
|
||||
|
||||
draw(graph(fsd,-100,100,n=500,Hermite),red);
|
||||
|
||||
xaxis("$m_{fuel}$");
|
||||
yaxis("$range (Ly)$",0);
|
|
@ -0,0 +1 @@
|
|||
# Intro
|
|
@ -0,0 +1,19 @@
|
|||
# Galactic Travel in Elite: Dangerous
|
||||
|
||||
All ships in Elite: Dangerous (E:D) are equipped with a Frame Shift Drive (FSD) which allows them to jumpst vast distances (multiple light years) from one star system to another.
|
||||
|
||||
The maximum range you can traverse in a single jump is limited by the maximum fuel consuption per jump of the specific drive (depends on class and rating) and influenced by the following factors:
|
||||
|
||||
- Rating of the FSD
|
||||
- Class of the FSD
|
||||
- Mass of your ship (Base mass+Cargo mass+Fuel mass)
|
||||
- Amount of fuel available in the tank
|
||||
|
||||
For details see [the chapter detailing FSD fuel consumption](./fsd_fuel.html)
|
||||
|
||||
If the ship is equipped with a Fuel Scoop it can:
|
||||
|
||||
- Scoop hydrogen from the corona of a star to refill its fuel tank (only applies to stars of class K, G, B, F, O, A or M)
|
||||
- Supercharge its FSD to increase the maximum jump range by a factor of:
|
||||
- 4 if supercharging in the jets of a neutron star
|
||||
- 1.5 if supercharging in the jets of a white dwarf star
|
|
@ -0,0 +1,36 @@
|
|||
# Notes on FSD Fuel consumption and jump range
|
||||
|
||||
FSD Fuel consumption ([Elite: Dangerous Wiki](https://elite-dangerous.fandom.com/wiki/Frame_Shift_Drive#Hyperspace_Fuel_Equation)):
|
||||
$$Fuel = 0.001 \cdot l \cdot \left(\frac{dist \cdot \left(m_{fuel} + m_{ship}\right)}{boost \cdot m_{opt}}\right)^{p}$$
|
||||
|
||||
Solving for \\(dist\\) gives the jump range (in Ly) for a given amount of fuel (in tons) as:
|
||||
$$dist = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
Assuming \\(f_{max}\\) tons of available fuel gives us the maximum jump range for a single jump as:
|
||||
$$dist_{max} = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot f_{max}}{l}\right)^{\frac{1}{p}}}{f_{max} + m_{ship}}$$
|
||||
|
||||
Since the guardian FSD booster increases the maximum jump range by \\(B_g\\) light years we can calculate a correction factor for the fuel consumption as:
|
||||
$$ e_{fuel} = 0.001 \cdot l \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{p}$$
|
||||
|
||||
Incorporating \\(e_{fuel}\\) into the distance equation yields
|
||||
$$dist = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot e_{fuel} \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
Expanding \\(e_{fuel}\\) yields
|
||||
$$dist = \frac{boost \cdot m_{opt} \cdot \left(1.0 \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{p} \cdot \min\left(f_{max}, m_{fuel}\right)\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
Finally, Expanding \\(dist_{max}\\) yields the full equation as
|
||||
$$dist = \frac{boost \cdot m_{opt} \cdot \left(\frac{1000000.0 \cdot \left(\frac{boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}{B_{g} \cdot \left(m_{fuel} + m_{ship}\right) + boost^{2} \cdot m_{opt} \cdot \left(\frac{1000.0 \cdot \min\left(f_{max}, m_{fuel}\right)}{l}\right)^{\frac{1}{p}}}\right)^{- p} \cdot \min\left(f_{max}, m_{fuel}\right)}{l^{2}}\right)^{\frac{1}{p}}}{m_{fuel} + m_{ship}}$$
|
||||
|
||||
Where:
|
||||
- \\(Fuel\\) is the fuel needed to jump (in tons)
|
||||
- \\(l\\) is the linear constant of your FSD (depends on the rating)
|
||||
- \\(p\\) is the power constant of your FSD (depends on the class)
|
||||
- \\(m_{ship}\\) is the mass of your ship (including cargo)
|
||||
- \\(m_{fuel}\\) is the amount of fuel in tons currently stored in your tanks
|
||||
- \\(m_{opt}\\) is the optimized mass of your FSD (in tons)
|
||||
- \\(f_{max}\\) is the maximum amount of fuel your FSD can use per jump
|
||||
- \\(boost\\) is the "boost factor" of your FSD (1.0 when jumping normally, 1.5 when supercharged by a white dwarf, 4.0 for a neutron star, etc)
|
||||
- \\(dist\\) is the distance you can jump with a given fuel amount
|
||||
- \\(dist_{max}\\) is the maximum distance you can jump (when \\(m_{fuel}=f_{max}\\))
|
||||
- \\(B_{g}\\) is the amount of Ly added by your Guardian FSD Booster
|
||||
- \\(e_{fuel}\\) is the efficiency increase added by the Guardian FSD Booster
|
|
@ -0,0 +1,25 @@
|
|||
# Graph Search algorithms
|
||||
|
||||
## Breadth-first search (BFS)
|
||||
|
||||
BFS expand node in breadth first order while keeping track of the parent node of each expanded node
|
||||
|
||||
## Beam search
|
||||
|
||||
Beam search is similar to BFS but limits the number of expanded nodes based on a heuristic
|
||||
|
||||
## Greedy search
|
||||
|
||||
Greedy search is essentially Beam search with a beam width of 1
|
||||
|
||||
## Dijkstra
|
||||
|
||||
Dijkstra's algorithm finds the shortest path across a graph based on some edge weight
|
||||
|
||||
## A*
|
||||
|
||||
A* is similar to Dijkstra but uses a heuristic to speed up the search
|
||||
|
||||
## Beam-Stack search (BSS)
|
||||
|
||||
Beam-Stack search is a variation of beam search which keeps a separate priority queue for each layer of the graph to allow backtracking and expand previously unexpanded nodes
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
import graph;
|
||||
import stats;
|
||||
size(500);
|
||||
srand(10);
|
||||
|
||||
struct Star {
|
||||
pair pos;
|
||||
real mult;
|
||||
}
|
||||
|
||||
real range = 48.0;
|
||||
|
||||
int n_stars=1000;
|
||||
Star[] stars=new Star[n_stars];
|
||||
for(int i=0; i < n_stars; ++i) {
|
||||
Star s=new Star;
|
||||
s.pos=(Gaussrand()*range*2,Gaussrand()*range*2);
|
||||
s.mult=1.0;
|
||||
if (unitrand()<0.2) {
|
||||
s.mult=1.5;
|
||||
} else {
|
||||
if (unitrand()<0.1) {
|
||||
s.mult=4.0;
|
||||
}
|
||||
}
|
||||
stars[i]=s;
|
||||
}
|
||||
|
||||
Star origin=new Star;
|
||||
origin.pos=(0,0);
|
||||
origin.mult=1.0;
|
||||
|
||||
draw(circle(origin.pos,range*origin.mult),white);
|
||||
draw(circle(origin.pos,range),white+dashed);
|
||||
draw(circle(origin.pos,range*2),white+dashed);
|
||||
draw(circle(origin.pos,range*4),white+dashed);
|
||||
fill(circle(origin.pos,2),red);
|
||||
|
||||
for (Star s: stars) {
|
||||
if (length(s.pos-origin.pos)<(range*origin.mult)) {
|
||||
draw(s.pos--origin.pos,white+dashed);
|
||||
fill(circle(s.pos,s.mult),green);
|
||||
} else {
|
||||
fill(circle(s.pos,s.mult),white);
|
||||
}
|
||||
};
|
180
dumps/process.py
180
dumps/process.py
|
@ -1,180 +0,0 @@
|
|||
import ujson as json
|
||||
from tqdm import tqdm
|
||||
from pprint import pprint
|
||||
import itertools as ITT
|
||||
import os
|
||||
import sys
|
||||
import csv
|
||||
import sqlite3
|
||||
import pandas as pd
|
||||
from urllib.parse import urljoin
|
||||
|
||||
|
||||
def is_scoopable(entry):
|
||||
first = entry.type.split()[0]
|
||||
return first == "Neutron" or first == "White" or first in "KGBFOAM"
|
||||
|
||||
|
||||
def get_mult(name):
|
||||
try:
|
||||
first = name.split()[0]
|
||||
except:
|
||||
return 1
|
||||
if first == "Neutron":
|
||||
return 4
|
||||
if first == "White":
|
||||
return 1.5
|
||||
return 1
|
||||
|
||||
|
||||
def dict_factory(cursor, row):
|
||||
d = {}
|
||||
for idx, col in enumerate(cursor.description):
|
||||
d[col[0]] = row[idx]
|
||||
return d
|
||||
|
||||
|
||||
def blocks(files, size=65536):
|
||||
while True:
|
||||
b = files.read(size)
|
||||
if not b:
|
||||
break
|
||||
yield b
|
||||
|
||||
|
||||
def getlines(f, fn, show_progbar=False):
|
||||
f.seek(0, 2)
|
||||
size = f.tell()
|
||||
f.seek(0)
|
||||
progbar = tqdm(
|
||||
desc="Processing " + fn,
|
||||
total=size,
|
||||
unit="b",
|
||||
unit_scale=True,
|
||||
unit_divisor=1024,
|
||||
ascii=True,
|
||||
leave=True,
|
||||
disable=(not show_progbar),
|
||||
)
|
||||
buffer = []
|
||||
for block in blocks(f):
|
||||
progbar.n = f.tell()
|
||||
progbar.update(0)
|
||||
if buffer:
|
||||
buffer += (buffer.pop(0) + block).splitlines(keepends=True)
|
||||
else:
|
||||
buffer += block.splitlines(keepends=True)
|
||||
while buffer and buffer[0].endswith("\n"):
|
||||
try:
|
||||
yield json.loads(buffer.pop(0).strip().rstrip(","))
|
||||
except ValueError:
|
||||
pass
|
||||
while buffer:
|
||||
try:
|
||||
yield json.loads(buffer.pop(0).strip().rstrip(","))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
def process_file(fn, show_progbar=False):
|
||||
with open(fn, "r") as f:
|
||||
for line in tqdm(
|
||||
getlines(f, fn, show_progbar),
|
||||
desc=fn,
|
||||
unit=" lines",
|
||||
unit_scale=True,
|
||||
ascii=True,
|
||||
leave=True,
|
||||
disable=(not show_progbar),
|
||||
):
|
||||
yield line
|
||||
|
||||
|
||||
if not (
|
||||
os.path.isfile("bodies.json") and os.path.isfile("systemsWithCoordinates.json")
|
||||
):
|
||||
exit(
|
||||
"Please download bodies.json and systemsWithCoordinates.json from https://www.edsm.net/en/nightly-dumps/"
|
||||
)
|
||||
|
||||
if not os.path.isfile("stars.jl"):
|
||||
print("Filtering for Stars")
|
||||
with open("stars.jl", "w") as neut:
|
||||
for body in process_file("bodies.json", True):
|
||||
T = body.get("type") or ""
|
||||
if "Star" in T:
|
||||
neut.write(json.dumps(body) + "\n")
|
||||
|
||||
|
||||
def load_systems(load=False):
|
||||
load = not os.path.isfile("systems.db")
|
||||
cache = sqlite3.connect("systems.db")
|
||||
cache.row_factory = dict_factory
|
||||
c = cache.cursor()
|
||||
if load:
|
||||
print("Caching Systems")
|
||||
c.execute("DROP TABLE IF EXISTS systems")
|
||||
c.execute(
|
||||
"CREATE TABLE systems (id64 int primary key, name text, x real, y real, z real)"
|
||||
)
|
||||
cache.commit()
|
||||
recs = []
|
||||
for system in process_file("systemsWithCoordinates.json", True):
|
||||
rec = [
|
||||
system["id64"],
|
||||
system["name"],
|
||||
system["coords"]["x"],
|
||||
system["coords"]["y"],
|
||||
system["coords"]["z"],
|
||||
]
|
||||
recs.append(rec)
|
||||
if len(recs) % 1024 * 1024 == 0:
|
||||
c.executemany("INSERT INTO systems VALUES (?,?,?,?,?)", recs)
|
||||
recs.clear()
|
||||
c.executemany("INSERT INTO systems VALUES (?,?,?,?,?)", recs)
|
||||
cache.commit()
|
||||
return cache, c
|
||||
|
||||
|
||||
if not os.path.isfile("stars.csv"):
|
||||
cache, cur = load_systems()
|
||||
rows = []
|
||||
with open("stars.csv", "w", newline="") as sys_csv:
|
||||
csv_writer = csv.writer(sys_csv, dialect="excel")
|
||||
for neut in process_file("stars.jl", True):
|
||||
cur.execute(
|
||||
"SELECT * FROM systems WHERE id64==?", (neut.get("systemId64"),)
|
||||
)
|
||||
system = cur.fetchone()
|
||||
if not system:
|
||||
continue
|
||||
row = [
|
||||
neut["systemId64"],
|
||||
neut["subType"],
|
||||
neut["name"],
|
||||
get_mult(neut["subType"]),
|
||||
system["x"],
|
||||
system["y"],
|
||||
system["z"],
|
||||
]
|
||||
rows.append(row)
|
||||
if len(rows) > 1024:
|
||||
csv_writer.writerows(rows)
|
||||
rows.clear()
|
||||
csv_writer.writerows(rows)
|
||||
print()
|
||||
cache.close()
|
||||
|
||||
if not os.path.isfile("stars.csv"):
|
||||
tqdm.pandas(ascii=True, leave=True)
|
||||
print("Loading data...")
|
||||
data = pd.read_csv(
|
||||
"stars.csv",
|
||||
encoding="utf-8",
|
||||
names=["id", "type", "name", "mult", "x", "y", "z"],
|
||||
)
|
||||
print("Cleaning data...")
|
||||
data.type.fillna("Unknown", inplace=True)
|
||||
data.drop_duplicates("id", inplace=True)
|
||||
print("Writing CSV...")
|
||||
data.to_csv("stars.csv", header=False, index=False)
|
|
@ -1,2 +0,0 @@
|
|||
https://www.edsm.net/dump/systemsWithCoordinates.json
|
||||
https://www.edsm.net/dump/bodies.json
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "rust"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"discord.enabled": true,
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/CVS": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/Thumbs.db": true,
|
||||
"**/.history": true,
|
||||
".history": true
|
||||
},
|
||||
"explorerExclude.backup": null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from _ed_lrr import *
|
||||
|
||||
from .preprocess import Preprocessor
|
||||
from .router import Router
|
||||
from .config import cfg
|
|
@ -0,0 +1,432 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
import multiprocessing as MP
|
||||
import queue
|
||||
import ctypes
|
||||
import os
|
||||
from datetime import datetime
|
||||
import click
|
||||
from tqdm import tqdm
|
||||
import requests as RQ
|
||||
from urllib.parse import urljoin
|
||||
from ed_lrr_gui import Router, Preprocessor, cfg
|
||||
from _ed_lrr import PyRouter
|
||||
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
|
||||
|
||||
stars_path = os.path.join(cfg["folders.data_dir"], "stars.csv")
|
||||
for folder in cfg["history.stars_csv_path"]:
|
||||
file = os.path.join(folder, "stars.csv")
|
||||
if os.path.isfile(file):
|
||||
stars_path = file
|
||||
break
|
||||
|
||||
systems_path = os.path.join(cfg["folders.data_dir"], "systemsWithCoordinates.json")
|
||||
for file in cfg["history.systems_path"]:
|
||||
if os.path.isfile(file):
|
||||
systems_path = file
|
||||
break
|
||||
|
||||
bodies_path = os.path.join(cfg["folders.data_dir"], "bodies.json")
|
||||
for file in cfg["history.bodies_path"][::-1]:
|
||||
if os.path.isfile(file):
|
||||
bodies_path = file
|
||||
break
|
||||
|
||||
|
||||
@click.group(invoke_without_command=True, context_settings=CONTEXT_SETTINGS)
|
||||
@click.pass_context
|
||||
@click.version_option()
|
||||
def main(ctx):
|
||||
"Elite: Dangerous long range router, command line interface."
|
||||
MP.freeze_support()
|
||||
if ctx.invoked_subcommand != "config":
|
||||
os.makedirs(cfg["folders.data_dir"], exist_ok=True)
|
||||
if ctx.invoked_subcommand==None:
|
||||
click.forward(gui_main)
|
||||
return
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option("--port", "-p", help="Port to bind to", type=int, default=3777)
|
||||
@click.option("--host", "-h", help="Address to bind to", type=str, default="0.0.0.0")
|
||||
@click.option("--debug", "-d", is_flag=True, help="Run using debug server")
|
||||
def web(host, port, debug):
|
||||
"Run web interface."
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
from gevent.pywsgi import WSGIServer
|
||||
from ed_lrr_gui.web import app
|
||||
|
||||
with app.test_client() as c:
|
||||
c.get("/") # Force before_first_request hook to run
|
||||
if debug:
|
||||
app.debug = True
|
||||
app.run(host=host, port=port, debug=True)
|
||||
else:
|
||||
print("Listening on {}:{}".format(host, port))
|
||||
server = WSGIServer((host, port), app)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.argument("option", default=None, required=False)
|
||||
@click.argument("value", default=None, required=False)
|
||||
def config(option, value):
|
||||
"""Change configuration.
|
||||
|
||||
If "key" and "value" are both omitted the current configuration is printed
|
||||
"""
|
||||
|
||||
def print_config(key):
|
||||
default = cfg.section(key).default()
|
||||
comment = cfg.section(key).comment
|
||||
value = cfg[key]
|
||||
is_default = value == default
|
||||
if (
|
||||
isinstance(value, list)
|
||||
and all(isinstance(element, str) for element in value)
|
||||
and len(value) != 0
|
||||
):
|
||||
value = "[{}]".format(", ".join(map("'{}'".format, value)))
|
||||
key = click.style("{}".format(key), fg="cyan")
|
||||
value = click.style("{}".format(value), fg="green")
|
||||
default = click.style("{}".format(default), fg="blue")
|
||||
comment = click.style("{}".format(comment), fg="yellow")
|
||||
if is_default:
|
||||
print("{}: {} # {}".format(key, default, comment))
|
||||
else:
|
||||
print("{}: {} (default: {}) # {}".format(key, value, default, comment))
|
||||
|
||||
if option is None and value is None:
|
||||
click.secho("Config path: {}".format(cfg.sources[0]), bold=True)
|
||||
print()
|
||||
for key in cfg:
|
||||
print_config(key)
|
||||
return
|
||||
if value is None:
|
||||
if option in cfg:
|
||||
print_config(option)
|
||||
else:
|
||||
print("Invalid option:", option)
|
||||
return
|
||||
cfg[option] = value
|
||||
cfg.sync()
|
||||
return
|
||||
|
||||
|
||||
@main.command()
|
||||
def explore():
|
||||
"Open file manager in data folder."
|
||||
click.launch(cfg["folders.data_dir"], locate=True)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option("--debug", help="Enable debug output", is_flag=True)
|
||||
def gui(debug):
|
||||
"Run the ED LRR GUI (default)."
|
||||
import ed_lrr_gui.gui as ED_LRR_GUI
|
||||
|
||||
if (not debug) and os.name == "nt":
|
||||
ctypes.windll.kernel32.FreeConsole()
|
||||
sys.stdin = open("NUL", "rt")
|
||||
sys.stdout = open("NUL", "wt")
|
||||
sys.stderr = open("NUL", "wt")
|
||||
sys.exit(ED_LRR_GUI.main())
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option(
|
||||
"--url",
|
||||
"-u",
|
||||
help="Base URL",
|
||||
default="https://www.edsm.net/dump/",
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--folder",
|
||||
"-f",
|
||||
help="Target folder for downloads",
|
||||
default=cfg["folders.data_dir"],
|
||||
type=click.Path(exists=True, dir_okay=True, file_okay=False),
|
||||
show_default=True,
|
||||
)
|
||||
def download(url, folder):
|
||||
"Download EDSM dumps."
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
for file_name in ["systemsWithCoordinates.json", "bodies.json"]:
|
||||
download_url = urljoin(url, file_name)
|
||||
download_path = os.path.join(folder, file_name)
|
||||
if os.path.isfile(download_path):
|
||||
try:
|
||||
if not click.confirm(
|
||||
"{} already exissts, overwrite?".format(file_name)
|
||||
):
|
||||
continue
|
||||
except click.Abort:
|
||||
exit("Canceled!")
|
||||
size = RQ.head(download_url, headers={"Accept-Encoding": "None"})
|
||||
size.raise_for_status()
|
||||
size = int(size.headers.get("Content-Length", 0))
|
||||
with tqdm(
|
||||
total=size,
|
||||
desc="{}".format(file_name),
|
||||
unit="b",
|
||||
unit_divisor=1024,
|
||||
unit_scale=True,
|
||||
ascii=True,
|
||||
smoothing=0,
|
||||
) as pbar:
|
||||
with open(download_path, "wb") as of:
|
||||
resp = RQ.get(
|
||||
download_url, stream=True, headers={"Accept-Encoding": "gzip"}
|
||||
)
|
||||
for chunk in resp.iter_content(1024 * 1024):
|
||||
of.write(chunk)
|
||||
pbar.update(len(chunk))
|
||||
click.pause()
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option(
|
||||
"--systems",
|
||||
"-s",
|
||||
default=systems_path,
|
||||
metavar="<path>",
|
||||
help="Path to systemsWithCoordinates.json",
|
||||
type=click.Path(exists=True, dir_okay=False),
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--bodies",
|
||||
"-b",
|
||||
default=bodies_path,
|
||||
metavar="<path>",
|
||||
help="Path to bodies.json",
|
||||
type=click.Path(exists=True, dir_okay=False),
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--output",
|
||||
"-o",
|
||||
default=stars_path,
|
||||
metavar="<path>",
|
||||
help="Path to stars.csv",
|
||||
type=click.Path(exists=False, dir_okay=False),
|
||||
show_default=True,
|
||||
)
|
||||
def preprocess(systems, bodies, output):
|
||||
"Preprocess EDSM dumps."
|
||||
with click.progressbar(
|
||||
length=100, label="", show_percent=True, item_show_func=lambda v: v, width=50
|
||||
) as pbar:
|
||||
preproc = Preprocessor(systems, bodies, output)
|
||||
preproc.start()
|
||||
state = {}
|
||||
pstate = {}
|
||||
while not (preproc.queue.empty() and not preproc.is_alive()):
|
||||
try:
|
||||
event = preproc.queue.get(True, 0.1)
|
||||
state.update(event)
|
||||
if state != pstate:
|
||||
prc = (state["status"]["done"] / state["status"]["total"]) * 100
|
||||
pbar.pos = prc
|
||||
pbar.update(0)
|
||||
pbar.current_item = state["status"]["message"]
|
||||
pstate = state.copy()
|
||||
except queue.Empty:
|
||||
pass
|
||||
pbar.pos = 100
|
||||
pbar.update(0)
|
||||
print(state.get("result"))
|
||||
print("DONE!")
|
||||
click.pause()
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option(
|
||||
"--path",
|
||||
"-i",
|
||||
required=True,
|
||||
metavar="<path>",
|
||||
help="Path to stars.csv",
|
||||
default=stars_path,
|
||||
type=click.Path(exists=True, dir_okay=False),
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--precomp_file",
|
||||
"-pf",
|
||||
metavar="<path>",
|
||||
help="Precomputed routing graph to use",
|
||||
type=click.Path(exists=True, dir_okay=False),
|
||||
)
|
||||
@click.option(
|
||||
"--range",
|
||||
"-r",
|
||||
default=cfg["route.range"],
|
||||
metavar="<float>",
|
||||
help="Jump range (Ly)",
|
||||
type=click.FloatRange(min=0),
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--prune",
|
||||
"-d",
|
||||
default=(cfg["route.prune.steps"], cfg["route.prune.min_improvement"]),
|
||||
metavar="<n> <m>",
|
||||
help="Prune search branches",
|
||||
nargs=2,
|
||||
type=click.Tuple([click.IntRange(min=0), click.FloatRange(min=0)]),
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--permute",
|
||||
"-p",
|
||||
type=click.Choice(["all", "keep_first", "keep_last", "keep_both"]),
|
||||
default=None,
|
||||
help="Permute hops to find shortest route",
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--primary/--no-primary",
|
||||
"+ps/-ps",
|
||||
is_flag=True,
|
||||
default=cfg["route.primary"],
|
||||
help="Only route through primary stars",
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--factor",
|
||||
"-g",
|
||||
metavar="<float>",
|
||||
default=cfg["route.greediness"],
|
||||
help="Greedyness factor (0.0=BFS, 1.0=Greedy)",
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--workers",
|
||||
"-w",
|
||||
metavar="<int>",
|
||||
default=1,
|
||||
help="Number of worker threads (more is not always better)",
|
||||
show_default=True,
|
||||
)
|
||||
@click.argument("systems", nargs=-1)
|
||||
def route(**kwargs):
|
||||
"Compute a route."
|
||||
if len(kwargs["systems"]) < 2:
|
||||
exit("Need at least two systems to plot a route")
|
||||
if kwargs["prune"] == (0, 0):
|
||||
kwargs["prune"] = None
|
||||
|
||||
def to_string(state):
|
||||
if state:
|
||||
return "{prc_done:.2f}% [N:{depth} | Q:{queue_size} | D:{d_rem:.2f} Ly | S: {n_seen} ({prc_seen:.2f}%)] {system}".format(
|
||||
**state
|
||||
)
|
||||
|
||||
keep_first, keep_last = {
|
||||
"all": (False, False),
|
||||
"keep_first": (True, False),
|
||||
"keep_last": (False, True),
|
||||
"keep_both": (True, True),
|
||||
None: (False, False),
|
||||
}[kwargs["permute"]]
|
||||
print("Resolving systems...")
|
||||
t = datetime.today()
|
||||
matches = find_sys(kwargs["systems"], kwargs["path"])
|
||||
kwargs["systems"] = [str(matches[key][1]["id"]) for key in kwargs["systems"]]
|
||||
print("Done in", datetime.today() - t)
|
||||
args = [
|
||||
kwargs["systems"],
|
||||
kwargs["range"],
|
||||
kwargs["prune"],
|
||||
kwargs["mode"],
|
||||
kwargs["primary"],
|
||||
kwargs["permute"] != None,
|
||||
keep_first,
|
||||
keep_last,
|
||||
kwargs["factor"],
|
||||
None,
|
||||
kwargs["path"],
|
||||
kwargs["workers"],
|
||||
]
|
||||
with click.progressbar(
|
||||
length=100,
|
||||
label="Computing route",
|
||||
show_percent=False,
|
||||
item_show_func=to_string,
|
||||
width=50,
|
||||
) as pbar:
|
||||
router = Router(*args)
|
||||
t = datetime.today()
|
||||
router.start()
|
||||
state = {}
|
||||
pstate = {}
|
||||
while not (router.queue.empty() and router.is_alive() == False):
|
||||
try:
|
||||
event = router.queue.get(True, 0.1)
|
||||
state.update(event)
|
||||
if state != pstate:
|
||||
pbar.current_item = state.get("status")
|
||||
if pbar.current_item:
|
||||
pbar.pos = pbar.current_item["prc_done"]
|
||||
pbar.update(0)
|
||||
pstate = state.copy()
|
||||
except queue.Empty:
|
||||
pass
|
||||
pbar.pos = 100
|
||||
pbar.update(0)
|
||||
for n, jump in enumerate(state.get("return", []), 1):
|
||||
jump["n"] = n
|
||||
if jump["body"].find(jump["system"]) == -1:
|
||||
jump["where"] = "[{body}] in [{system}]".format(**jump)
|
||||
else:
|
||||
jump["where"] = "[{body}]".format(**jump)
|
||||
if jump["distance"] > 0:
|
||||
print("({n}) {where}: {star_type} ({distance} Ls)".format(**jump))
|
||||
else:
|
||||
print("({n}) {where}: {star_type}".format(**jump))
|
||||
print("Done in", datetime.today() - t)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option(
|
||||
"--path",
|
||||
"-i",
|
||||
required=True,
|
||||
help="Path to stars.csv",
|
||||
default=stars_path,
|
||||
type=click.Path(exists=True, dir_okay=False),
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--range", "-r", required=True, help="Jump range (Ly)", type=click.FloatRange(min=0)
|
||||
)
|
||||
@click.option("--primary", "-ps", help="Only route through primary stars")
|
||||
@click.option(
|
||||
"--output",
|
||||
"-o",
|
||||
required=True,
|
||||
help="Output path",
|
||||
default="./stars.idx",
|
||||
type=click.Path(exists=False, dir_okay=False),
|
||||
show_default=True,
|
||||
)
|
||||
@click.argument("systems", nargs=-1)
|
||||
def precompute(*args, **kwargs):
|
||||
"Precompute routing graph"
|
||||
print("PreComp:", args, kwargs)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def gui_main():
|
||||
return gui(False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,65 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import pathlib
|
||||
from collections import namedtuple
|
||||
import profig
|
||||
import appdirs
|
||||
import os
|
||||
|
||||
config_dir = pathlib.Path(appdirs.user_config_dir("ED_LRR", ""))
|
||||
config_dir.mkdir(parents=True, exist_ok=True)
|
||||
config_file = config_dir / "config.ini"
|
||||
config_file.touch()
|
||||
|
||||
config_dir = str(config_dir)
|
||||
|
||||
cfg = profig.Config(str(config_file), strict=True)
|
||||
|
||||
cfg.init(
|
||||
"history.bodies_url",
|
||||
["https://www.edsm.net/dump/bodies.json"],
|
||||
"path_list",
|
||||
comment="history of bodies.json urls",
|
||||
)
|
||||
cfg.init(
|
||||
"history.systems_url",
|
||||
["https://www.edsm.net/dump/systemsWithCoordinates.json"],
|
||||
"path_list",
|
||||
comment="history of systems.json urls",
|
||||
)
|
||||
cfg.init(
|
||||
"history.bodies_path",
|
||||
[os.path.join(config_dir, "data", "bodies.json")],
|
||||
"path_list",
|
||||
comment="history of bodies.json download paths",
|
||||
)
|
||||
cfg.init(
|
||||
"history.systems_path",
|
||||
[os.path.join(config_dir, "data", "systemsWithCoordinates.json")],
|
||||
"path_list",
|
||||
comment="history of systems.json download paths",
|
||||
)
|
||||
cfg.init(
|
||||
"history.stars_csv_path",
|
||||
[os.path.join(config_dir, "data", "stars.csv")],
|
||||
"path_list",
|
||||
comment="history of paths for stars.csv",
|
||||
)
|
||||
cfg.init("route.range", 7.56, comment="jump range")
|
||||
cfg.init("route.primary", False, comment="only route through primary stars")
|
||||
cfg.init("route.mode", "bfs", comment="routing mode")
|
||||
cfg.init(
|
||||
"route.prune.min_improvement",
|
||||
10.0,
|
||||
comment="path needs to improve by at least (jump_range*min_improvement) in route.prune.steps",
|
||||
)
|
||||
cfg.init("route.prune.steps", 5, comment="number of steps before path gets pruned")
|
||||
cfg.init("route.greediness", 0.5, comment="A* greediness")
|
||||
cfg.init("folders.data_dir", os.path.join(config_dir, "data"), comment="Data directory")
|
||||
|
||||
cfg.init("GUI.theme", "dark", comment="GUI theme to use")
|
||||
|
||||
cfg.init("web.port", 3777, comment="Port to bind to")
|
||||
cfg.init("web.host", "0.0.0.0", comment="Address to bind to")
|
||||
cfg.init("web.debug", False, comment="Run using debug server")
|
||||
|
||||
cfg.sync()
|
|
@ -0,0 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from .main import main
|
|
@ -0,0 +1,484 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'D:\devel\rust\ed_lrr_gui\ed_lrr_gui\gui\ed_lrr.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.14.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_ED_LRR(object):
|
||||
def setupUi(self, ED_LRR):
|
||||
ED_LRR.setObjectName("ED_LRR")
|
||||
ED_LRR.setEnabled(True)
|
||||
ED_LRR.resize(577, 500)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(ED_LRR.sizePolicy().hasHeightForWidth())
|
||||
ED_LRR.setSizePolicy(sizePolicy)
|
||||
ED_LRR.setMinimumSize(QtCore.QSize(577, 500))
|
||||
ED_LRR.setMaximumSize(QtCore.QSize(577, 500))
|
||||
ED_LRR.setStyleSheet("")
|
||||
ED_LRR.setDocumentMode(False)
|
||||
ED_LRR.setTabShape(QtWidgets.QTabWidget.Rounded)
|
||||
self.centralwidget = QtWidgets.QWidget(ED_LRR)
|
||||
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)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.tabs = QtWidgets.QTabWidget(self.centralwidget)
|
||||
self.tabs.setEnabled(True)
|
||||
self.tabs.setAutoFillBackground(False)
|
||||
self.tabs.setTabPosition(QtWidgets.QTabWidget.North)
|
||||
self.tabs.setTabShape(QtWidgets.QTabWidget.Rounded)
|
||||
self.tabs.setElideMode(QtCore.Qt.ElideNone)
|
||||
self.tabs.setTabsClosable(False)
|
||||
self.tabs.setTabBarAutoHide(False)
|
||||
self.tabs.setObjectName("tabs")
|
||||
self.tab_download = QtWidgets.QWidget()
|
||||
self.tab_download.setObjectName("tab_download")
|
||||
self.formLayout = QtWidgets.QFormLayout(self.tab_download)
|
||||
self.formLayout.setObjectName("formLayout")
|
||||
self.lbl_bodies_dl = QtWidgets.QLabel(self.tab_download)
|
||||
self.lbl_bodies_dl.setObjectName("lbl_bodies_dl")
|
||||
self.formLayout.setWidget(
|
||||
1, QtWidgets.QFormLayout.LabelRole, self.lbl_bodies_dl
|
||||
)
|
||||
self.lbl_systems_dl = QtWidgets.QLabel(self.tab_download)
|
||||
self.lbl_systems_dl.setObjectName("lbl_systems_dl")
|
||||
self.formLayout.setWidget(
|
||||
3, QtWidgets.QFormLayout.LabelRole, self.lbl_systems_dl
|
||||
)
|
||||
self.inp_bodies_dl = QtWidgets.QComboBox(self.tab_download)
|
||||
self.inp_bodies_dl.setEditable(True)
|
||||
self.inp_bodies_dl.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
|
||||
self.inp_bodies_dl.setObjectName("inp_bodies_dl")
|
||||
self.formLayout.setWidget(
|
||||
1, QtWidgets.QFormLayout.FieldRole, self.inp_bodies_dl
|
||||
)
|
||||
self.inp_systems_dl = QtWidgets.QComboBox(self.tab_download)
|
||||
self.inp_systems_dl.setEditable(True)
|
||||
self.inp_systems_dl.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
|
||||
self.inp_systems_dl.setObjectName("inp_systems_dl")
|
||||
self.formLayout.setWidget(
|
||||
3, QtWidgets.QFormLayout.FieldRole, self.inp_systems_dl
|
||||
)
|
||||
self.gridLayout = QtWidgets.QGridLayout()
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.inp_bodies_dest_dl = QtWidgets.QComboBox(self.tab_download)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.inp_bodies_dest_dl.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.inp_bodies_dest_dl.setSizePolicy(sizePolicy)
|
||||
self.inp_bodies_dest_dl.setEditable(False)
|
||||
self.inp_bodies_dest_dl.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
|
||||
self.inp_bodies_dest_dl.setObjectName("inp_bodies_dest_dl")
|
||||
self.gridLayout.addWidget(self.inp_bodies_dest_dl, 0, 0, 1, 1)
|
||||
self.btn_bodies_dest_browse_dl = QtWidgets.QPushButton(self.tab_download)
|
||||
self.btn_bodies_dest_browse_dl.setObjectName("btn_bodies_dest_browse_dl")
|
||||
self.gridLayout.addWidget(self.btn_bodies_dest_browse_dl, 0, 1, 1, 1)
|
||||
self.formLayout.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.gridLayout)
|
||||
self.gridLayout_2 = QtWidgets.QGridLayout()
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.btn_systems_dest_browse_dl = QtWidgets.QPushButton(self.tab_download)
|
||||
self.btn_systems_dest_browse_dl.setObjectName("btn_systems_dest_browse_dl")
|
||||
self.gridLayout_2.addWidget(self.btn_systems_dest_browse_dl, 0, 1, 1, 1)
|
||||
self.inp_systems_dest_dl = QtWidgets.QComboBox(self.tab_download)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.inp_systems_dest_dl.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.inp_systems_dest_dl.setSizePolicy(sizePolicy)
|
||||
self.inp_systems_dest_dl.setEditable(False)
|
||||
self.inp_systems_dest_dl.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
|
||||
self.inp_systems_dest_dl.setObjectName("inp_systems_dest_dl")
|
||||
self.gridLayout_2.addWidget(self.inp_systems_dest_dl, 0, 0, 1, 1)
|
||||
self.formLayout.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.gridLayout_2)
|
||||
self.btn_download = QtWidgets.QPushButton(self.tab_download)
|
||||
self.btn_download.setObjectName("btn_download")
|
||||
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.btn_download)
|
||||
self.label = QtWidgets.QLabel(self.tab_download)
|
||||
self.label.setObjectName("label")
|
||||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label)
|
||||
self.label_2 = QtWidgets.QLabel(self.tab_download)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_2)
|
||||
self.tabs.addTab(self.tab_download, "")
|
||||
self.tab_preprocess = QtWidgets.QWidget()
|
||||
self.tab_preprocess.setObjectName("tab_preprocess")
|
||||
self.formLayout_3 = QtWidgets.QFormLayout(self.tab_preprocess)
|
||||
self.formLayout_3.setObjectName("formLayout_3")
|
||||
self.lbl_bodies_pp = QtWidgets.QLabel(self.tab_preprocess)
|
||||
self.lbl_bodies_pp.setObjectName("lbl_bodies_pp")
|
||||
self.formLayout_3.setWidget(
|
||||
0, QtWidgets.QFormLayout.LabelRole, self.lbl_bodies_pp
|
||||
)
|
||||
self.gr_bodies_pp = QtWidgets.QGridLayout()
|
||||
self.gr_bodies_pp.setObjectName("gr_bodies_pp")
|
||||
self.btn_bodies_browse_pp = QtWidgets.QPushButton(self.tab_preprocess)
|
||||
self.btn_bodies_browse_pp.setObjectName("btn_bodies_browse_pp")
|
||||
self.gr_bodies_pp.addWidget(self.btn_bodies_browse_pp, 0, 1, 1, 1)
|
||||
self.inp_bodies_pp = QtWidgets.QComboBox(self.tab_preprocess)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.inp_bodies_pp.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.inp_bodies_pp.setSizePolicy(sizePolicy)
|
||||
self.inp_bodies_pp.setEditable(False)
|
||||
self.inp_bodies_pp.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
|
||||
self.inp_bodies_pp.setObjectName("inp_bodies_pp")
|
||||
self.gr_bodies_pp.addWidget(self.inp_bodies_pp, 0, 0, 1, 1)
|
||||
self.formLayout_3.setLayout(
|
||||
0, QtWidgets.QFormLayout.FieldRole, self.gr_bodies_pp
|
||||
)
|
||||
self.lbl_systems_pp = QtWidgets.QLabel(self.tab_preprocess)
|
||||
self.lbl_systems_pp.setObjectName("lbl_systems_pp")
|
||||
self.formLayout_3.setWidget(
|
||||
1, QtWidgets.QFormLayout.LabelRole, self.lbl_systems_pp
|
||||
)
|
||||
self.gr_systems_pp = QtWidgets.QGridLayout()
|
||||
self.gr_systems_pp.setObjectName("gr_systems_pp")
|
||||
self.btn_systems_browse_pp = QtWidgets.QPushButton(self.tab_preprocess)
|
||||
self.btn_systems_browse_pp.setObjectName("btn_systems_browse_pp")
|
||||
self.gr_systems_pp.addWidget(self.btn_systems_browse_pp, 0, 1, 1, 1)
|
||||
self.inp_systems_pp = QtWidgets.QComboBox(self.tab_preprocess)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.inp_systems_pp.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.inp_systems_pp.setSizePolicy(sizePolicy)
|
||||
self.inp_systems_pp.setEditable(False)
|
||||
self.inp_systems_pp.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
|
||||
self.inp_systems_pp.setObjectName("inp_systems_pp")
|
||||
self.gr_systems_pp.addWidget(self.inp_systems_pp, 0, 0, 1, 1)
|
||||
self.formLayout_3.setLayout(
|
||||
1, QtWidgets.QFormLayout.FieldRole, self.gr_systems_pp
|
||||
)
|
||||
self.lbl_out_pp = QtWidgets.QLabel(self.tab_preprocess)
|
||||
self.lbl_out_pp.setObjectName("lbl_out_pp")
|
||||
self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.lbl_out_pp)
|
||||
self.gr_out_grid_pp = QtWidgets.QGridLayout()
|
||||
self.gr_out_grid_pp.setObjectName("gr_out_grid_pp")
|
||||
self.btn_out_browse_pp = QtWidgets.QPushButton(self.tab_preprocess)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.btn_out_browse_pp.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.btn_out_browse_pp.setSizePolicy(sizePolicy)
|
||||
self.btn_out_browse_pp.setObjectName("btn_out_browse_pp")
|
||||
self.gr_out_grid_pp.addWidget(self.btn_out_browse_pp, 0, 1, 1, 1)
|
||||
self.inp_out_pp = QtWidgets.QComboBox(self.tab_preprocess)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.inp_out_pp.sizePolicy().hasHeightForWidth())
|
||||
self.inp_out_pp.setSizePolicy(sizePolicy)
|
||||
self.inp_out_pp.setEditable(False)
|
||||
self.inp_out_pp.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
|
||||
self.inp_out_pp.setObjectName("inp_out_pp")
|
||||
self.gr_out_grid_pp.addWidget(self.inp_out_pp, 0, 0, 1, 1)
|
||||
self.formLayout_3.setLayout(
|
||||
2, QtWidgets.QFormLayout.FieldRole, self.gr_out_grid_pp
|
||||
)
|
||||
self.btn_preprocess = QtWidgets.QPushButton(self.tab_preprocess)
|
||||
self.btn_preprocess.setObjectName("btn_preprocess")
|
||||
self.formLayout_3.setWidget(
|
||||
3, QtWidgets.QFormLayout.LabelRole, self.btn_preprocess
|
||||
)
|
||||
self.tabs.addTab(self.tab_preprocess, "")
|
||||
self.tab_route = QtWidgets.QWidget()
|
||||
self.tab_route.setObjectName("tab_route")
|
||||
self.formLayout_2 = QtWidgets.QFormLayout(self.tab_route)
|
||||
self.formLayout_2.setObjectName("formLayout_2")
|
||||
self.lbl_sys_lst = QtWidgets.QLabel(self.tab_route)
|
||||
self.lbl_sys_lst.setObjectName("lbl_sys_lst")
|
||||
self.formLayout_2.setWidget(
|
||||
0, QtWidgets.QFormLayout.LabelRole, self.lbl_sys_lst
|
||||
)
|
||||
self.gr_sys = QtWidgets.QGridLayout()
|
||||
self.gr_sys.setObjectName("gr_sys")
|
||||
self.btn_sys_lst_browse = QtWidgets.QPushButton(self.tab_route)
|
||||
self.btn_sys_lst_browse.setObjectName("btn_sys_lst_browse")
|
||||
self.gr_sys.addWidget(self.btn_sys_lst_browse, 0, 1, 1, 1)
|
||||
self.inp_sys_lst = QtWidgets.QComboBox(self.tab_route)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.inp_sys_lst.sizePolicy().hasHeightForWidth())
|
||||
self.inp_sys_lst.setSizePolicy(sizePolicy)
|
||||
self.inp_sys_lst.setEditable(False)
|
||||
self.inp_sys_lst.setInsertPolicy(QtWidgets.QComboBox.InsertAtTop)
|
||||
self.inp_sys_lst.setFrame(True)
|
||||
self.inp_sys_lst.setModelColumn(0)
|
||||
self.inp_sys_lst.setObjectName("inp_sys_lst")
|
||||
self.gr_sys.addWidget(self.inp_sys_lst, 0, 0, 1, 1)
|
||||
self.formLayout_2.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.gr_sys)
|
||||
self.btn_add = QtWidgets.QPushButton(self.tab_route)
|
||||
self.btn_add.setObjectName("btn_add")
|
||||
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.btn_add)
|
||||
self.inp_sys = QtWidgets.QLineEdit(self.tab_route)
|
||||
self.inp_sys.setObjectName("inp_sys")
|
||||
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.inp_sys)
|
||||
self.btn_rm = QtWidgets.QPushButton(self.tab_route)
|
||||
self.btn_rm.setObjectName("btn_rm")
|
||||
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.btn_rm)
|
||||
self.gr_mode = QtWidgets.QGridLayout()
|
||||
self.gr_mode.setObjectName("gr_mode")
|
||||
self.rd_comp = QtWidgets.QRadioButton(self.tab_route)
|
||||
self.rd_comp.setChecked(True)
|
||||
self.rd_comp.setObjectName("rd_comp")
|
||||
self.gr_mode.addWidget(self.rd_comp, 0, 1, 1, 1)
|
||||
self.rd_precomp = QtWidgets.QRadioButton(self.tab_route)
|
||||
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)
|
||||
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.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")
|
||||
self.gridLayout_4.addWidget(self.chk_permute_keep_last, 0, 3, 1, 1)
|
||||
self.chk_permute_keep_first = QtWidgets.QCheckBox(self.tab_route)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.chk_permute_keep_first.sizePolicy().hasHeightForWidth()
|
||||
)
|
||||
self.chk_permute_keep_first.setSizePolicy(sizePolicy)
|
||||
self.chk_permute_keep_first.setTristate(False)
|
||||
self.chk_permute_keep_first.setObjectName("chk_permute_keep_first")
|
||||
self.gridLayout_4.addWidget(self.chk_permute_keep_first, 0, 2, 1, 1)
|
||||
self.lbl_keep = QtWidgets.QLabel(self.tab_route)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lbl_keep.sizePolicy().hasHeightForWidth())
|
||||
self.lbl_keep.setSizePolicy(sizePolicy)
|
||||
self.lbl_keep.setObjectName("lbl_keep")
|
||||
self.gridLayout_4.addWidget(self.lbl_keep, 0, 1, 1, 1)
|
||||
self.formLayout_2.setLayout(
|
||||
4, QtWidgets.QFormLayout.FieldRole, self.gridLayout_4
|
||||
)
|
||||
self.lst_sys = QtWidgets.QTreeWidget(self.tab_route)
|
||||
sizePolicy = QtWidgets.QSizePolicy(
|
||||
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
|
||||
)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.lst_sys.sizePolicy().hasHeightForWidth())
|
||||
self.lst_sys.setSizePolicy(sizePolicy)
|
||||
self.lst_sys.setMinimumSize(QtCore.QSize(0, 0))
|
||||
self.lst_sys.setDragEnabled(True)
|
||||
self.lst_sys.setDragDropOverwriteMode(False)
|
||||
self.lst_sys.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
|
||||
self.lst_sys.setDefaultDropAction(QtCore.Qt.MoveAction)
|
||||
self.lst_sys.setAlternatingRowColors(True)
|
||||
self.lst_sys.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||
self.lst_sys.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
self.lst_sys.setHeaderHidden(True)
|
||||
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.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.sb_range = QtWidgets.QDoubleSpinBox(self.tab_route)
|
||||
self.sb_range.setObjectName("sb_range")
|
||||
self.formLayout_2.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.sb_range)
|
||||
self.gr_opts = QtWidgets.QGridLayout()
|
||||
self.gr_opts.setObjectName("gr_opts")
|
||||
self.cmb_mode = QtWidgets.QComboBox(self.tab_route)
|
||||
self.cmb_mode.setObjectName("cmb_mode")
|
||||
self.cmb_mode.addItem("")
|
||||
self.cmb_mode.addItem("")
|
||||
self.cmb_mode.addItem("")
|
||||
self.gr_opts.addWidget(self.cmb_mode, 0, 2, 1, 1)
|
||||
self.lbl_greedyness = QtWidgets.QLabel(self.tab_route)
|
||||
self.lbl_greedyness.setEnabled(True)
|
||||
self.lbl_greedyness.setObjectName("lbl_greedyness")
|
||||
self.gr_opts.addWidget(self.lbl_greedyness, 1, 1, 1, 1)
|
||||
self.chk_primary = QtWidgets.QCheckBox(self.tab_route)
|
||||
self.chk_primary.setObjectName("chk_primary")
|
||||
self.gr_opts.addWidget(self.chk_primary, 0, 3, 1, 1)
|
||||
self.sld_greedyness = QtWidgets.QSlider(self.tab_route)
|
||||
self.sld_greedyness.setMaximum(100)
|
||||
self.sld_greedyness.setPageStep(10)
|
||||
self.sld_greedyness.setProperty("value", 50)
|
||||
self.sld_greedyness.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.sld_greedyness.setTickPosition(QtWidgets.QSlider.TicksBelow)
|
||||
self.sld_greedyness.setTickInterval(10)
|
||||
self.sld_greedyness.setObjectName("sld_greedyness")
|
||||
self.gr_opts.addWidget(self.sld_greedyness, 1, 2, 1, 2)
|
||||
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)
|
||||
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.tabs.addTab(self.tab_route, "")
|
||||
self.tab_log = QtWidgets.QWidget()
|
||||
self.tab_log.setObjectName("tab_log")
|
||||
self.gridLayout_3 = QtWidgets.QGridLayout(self.tab_log)
|
||||
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||
self.txt_log = QtWidgets.QTextEdit(self.tab_log)
|
||||
self.txt_log.setEnabled(True)
|
||||
self.txt_log.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.txt_log.setLineWidth(1)
|
||||
self.txt_log.setReadOnly(True)
|
||||
self.txt_log.setAcceptRichText(False)
|
||||
self.txt_log.setObjectName("txt_log")
|
||||
self.gridLayout_3.addWidget(self.txt_log, 0, 0, 1, 1)
|
||||
self.tabs.addTab(self.tab_log, "")
|
||||
self.verticalLayout.addWidget(self.tabs)
|
||||
ED_LRR.setCentralWidget(self.centralwidget)
|
||||
self.menu = QtWidgets.QMenuBar(ED_LRR)
|
||||
self.menu.setGeometry(QtCore.QRect(0, 0, 577, 21))
|
||||
self.menu.setObjectName("menu")
|
||||
self.menu_file = QtWidgets.QMenu(self.menu)
|
||||
self.menu_file.setObjectName("menu_file")
|
||||
self.menuWindow = QtWidgets.QMenu(self.menu)
|
||||
self.menuWindow.setObjectName("menuWindow")
|
||||
self.menuStyle = QtWidgets.QMenu(self.menuWindow)
|
||||
self.menuStyle.setObjectName("menuStyle")
|
||||
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")
|
||||
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())
|
||||
|
||||
self.retranslateUi(ED_LRR)
|
||||
self.tabs.setCurrentIndex(2)
|
||||
self.menu_act_quit.triggered.connect(ED_LRR.close)
|
||||
QtCore.QMetaObject.connectSlotsByName(ED_LRR)
|
||||
ED_LRR.setTabOrder(self.rd_comp, self.cmb_mode)
|
||||
ED_LRR.setTabOrder(self.cmb_mode, self.chk_primary)
|
||||
ED_LRR.setTabOrder(self.chk_primary, self.sld_greedyness)
|
||||
ED_LRR.setTabOrder(self.sld_greedyness, self.rd_precomp)
|
||||
|
||||
def retranslateUi(self, ED_LRR):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
ED_LRR.setWindowTitle(
|
||||
_translate("ED_LRR", "Elite: Dangerous Long Range Route Plotter")
|
||||
)
|
||||
self.lbl_bodies_dl.setText(_translate("ED_LRR", "bodies.json"))
|
||||
self.lbl_systems_dl.setText(_translate("ED_LRR", "systemsWithCoordinates.json"))
|
||||
self.inp_bodies_dl.setCurrentText(
|
||||
_translate("ED_LRR", "https://www.edsm.net/dump/bodies.json")
|
||||
)
|
||||
self.inp_systems_dl.setCurrentText(
|
||||
_translate(
|
||||
"ED_LRR", "https://www.edsm.net/dump/systemsWithCoordinates.json"
|
||||
)
|
||||
)
|
||||
self.btn_bodies_dest_browse_dl.setText(_translate("ED_LRR", "..."))
|
||||
self.btn_systems_dest_browse_dl.setText(_translate("ED_LRR", "..."))
|
||||
self.btn_download.setText(_translate("ED_LRR", "Download"))
|
||||
self.label.setText(_translate("ED_LRR", "Download path"))
|
||||
self.label_2.setText(_translate("ED_LRR", "Download path"))
|
||||
self.tabs.setTabText(
|
||||
self.tabs.indexOf(self.tab_download), _translate("ED_LRR", "Download")
|
||||
)
|
||||
self.lbl_bodies_pp.setText(_translate("ED_LRR", "bodies.json"))
|
||||
self.btn_bodies_browse_pp.setText(_translate("ED_LRR", "..."))
|
||||
self.lbl_systems_pp.setText(_translate("ED_LRR", "systemsWithCoordinates.json"))
|
||||
self.btn_systems_browse_pp.setText(_translate("ED_LRR", "..."))
|
||||
self.lbl_out_pp.setText(_translate("ED_LRR", "Output"))
|
||||
self.btn_out_browse_pp.setText(_translate("ED_LRR", "..."))
|
||||
self.btn_preprocess.setText(_translate("ED_LRR", "Preprocess"))
|
||||
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", "..."))
|
||||
self.btn_add.setText(_translate("ED_LRR", "Add"))
|
||||
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"))
|
||||
self.chk_permute.setText(_translate("ED_LRR", "Permute"))
|
||||
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:"))
|
||||
self.lst_sys.headerItem().setText(1, _translate("ED_LRR", "Type"))
|
||||
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"))
|
||||
self.cmb_mode.setItemText(1, _translate("ED_LRR", "Greedy-Search"))
|
||||
self.cmb_mode.setItemText(2, _translate("ED_LRR", "A*-Search"))
|
||||
self.lbl_greedyness.setText(_translate("ED_LRR", "Greedyness Factor"))
|
||||
self.chk_primary.setText(_translate("ED_LRR", "Primary Stars Only"))
|
||||
self.lbl_mode.setText(_translate("ED_LRR", "Mode"))
|
||||
self.btn_go.setText(_translate("ED_LRR", "GO!"))
|
||||
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"))
|
|
@ -0,0 +1,710 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ED_LRR</class>
|
||||
<widget class="QMainWindow" name="ED_LRR">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>577</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>577</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>577</width>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Elite: Dangerous Long Range Route Plotter</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::Rounded</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabs">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::North</enum>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::Rounded</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="elideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
</property>
|
||||
<property name="tabsClosable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tabBarAutoHide">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_download">
|
||||
<attribute name="title">
|
||||
<string>Download</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbl_bodies_dl">
|
||||
<property name="text">
|
||||
<string>bodies.json</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="lbl_systems_dl">
|
||||
<property name="text">
|
||||
<string>systemsWithCoordinates.json</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="inp_bodies_dl">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentText">
|
||||
<string>https://www.edsm.net/dump/bodies.json</string>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAtTop</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="inp_systems_dl">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentText">
|
||||
<string>https://www.edsm.net/dump/systemsWithCoordinates.json</string>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAtTop</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="inp_bodies_dest_dl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAtTop</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="btn_bodies_dest_browse_dl">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="btn_systems_dest_browse_dl">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="inp_systems_dest_dl">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAtTop</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QPushButton" name="btn_download">
|
||||
<property name="text">
|
||||
<string>Download</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Download path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Download path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_preprocess">
|
||||
<attribute name="title">
|
||||
<string>Preprocess</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbl_bodies_pp">
|
||||
<property name="text">
|
||||
<string>bodies.json</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QGridLayout" name="gr_bodies_pp">
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="btn_bodies_browse_pp">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="inp_bodies_pp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAtTop</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbl_systems_pp">
|
||||
<property name="text">
|
||||
<string>systemsWithCoordinates.json</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QGridLayout" name="gr_systems_pp">
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="btn_systems_browse_pp">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="inp_systems_pp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAtTop</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="lbl_out_pp">
|
||||
<property name="text">
|
||||
<string>Output</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QGridLayout" name="gr_out_grid_pp">
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="btn_out_browse_pp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="inp_out_pp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAtTop</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QPushButton" name="btn_preprocess">
|
||||
<property name="text">
|
||||
<string>Preprocess</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_route">
|
||||
<attribute name="title">
|
||||
<string>Route</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbl_sys_lst">
|
||||
<property name="text">
|
||||
<string>System List</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QGridLayout" name="gr_sys">
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="btn_sys_lst_browse">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="inp_sys_lst">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAtTop</enum>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="modelColumn">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="btn_add">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="inp_sys">
|
||||
<property name="placeholderText">
|
||||
<string>System Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QPushButton" name="btn_rm">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QGridLayout" name="gr_mode">
|
||||
<item row="0" column="1">
|
||||
<widget class="QRadioButton" name="rd_comp">
|
||||
<property name="text">
|
||||
<string>Compute Route</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QRadioButton" name="rd_precomp">
|
||||
<property name="text">
|
||||
<string>Precompute Graph</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="chk_permute">
|
||||
<property name="text">
|
||||
<string>Permute</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="chk_permute_keep_last">
|
||||
<property name="text">
|
||||
<string>Last</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QCheckBox" name="chk_permute_keep_first">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>First</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="lbl_keep">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Keep Endpoints:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QTreeWidget" name="lst_sys">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropOverwriteMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::MoveAction</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</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>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="lbl_range">
|
||||
<property name="text">
|
||||
<string>Jump Range (Ly)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QDoubleSpinBox" name="sb_range"/>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gr_opts">
|
||||
<item row="0" column="2">
|
||||
<widget class="QComboBox" name="cmb_mode">
|
||||
<property name="currentText">
|
||||
<string>Breadth-First Search</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Breadth-First Search</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Greedy-Search</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>A*-Search</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="lbl_greedyness">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Greedyness Factor</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="chk_primary">
|
||||
<property name="text">
|
||||
<string>Primary Stars Only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2" colspan="2">
|
||||
<widget class="QSlider" name="sld_greedyness">
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="lbl_mode">
|
||||
<property name="text">
|
||||
<string>Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QPushButton" name="btn_go">
|
||||
<property name="text">
|
||||
<string>GO!</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_log">
|
||||
<attribute name="title">
|
||||
<string>Log</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTextEdit" name="txt_log">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="acceptRichText">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menu">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>577</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_file">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="menu_act_quit"/>
|
||||
</widget>
|
||||
<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"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="bar_status"/>
|
||||
<action name="menu_act_quit">
|
||||
<property name="text">
|
||||
<string>Quit</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionA">
|
||||
<property name="text">
|
||||
<string>A</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionB">
|
||||
<property name="text">
|
||||
<string>B</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>rd_comp</tabstop>
|
||||
<tabstop>cmb_mode</tabstop>
|
||||
<tabstop>chk_primary</tabstop>
|
||||
<tabstop>sld_greedyness</tabstop>
|
||||
<tabstop>rd_precomp</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>menu_act_quit</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>ED_LRR</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>288</x>
|
||||
<y>249</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -0,0 +1,685 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import csv
|
||||
import gzip
|
||||
import multiprocessing as MP
|
||||
import os
|
||||
import pathlib
|
||||
import queue
|
||||
import sys
|
||||
from sys import exit
|
||||
from datetime import datetime, timedelta
|
||||
from urllib.request import Request, urlopen
|
||||
import _ed_lrr
|
||||
import ed_lrr_gui
|
||||
import requests as RQ
|
||||
from ed_lrr_gui import Preprocessor, Router, cfg
|
||||
from ed_lrr_gui.gui.ed_lrr import Ui_ED_LRR
|
||||
from ed_lrr_gui.gui.widget_route import Ui_diag_route
|
||||
from PyQt5.QtCore import QObject, Qt, QThread, QTimer, pyqtSignal
|
||||
from PyQt5.QtGui import QColor, QPalette, QIcon
|
||||
from PyQt5.QtWidgets import (
|
||||
QAction,
|
||||
QApplication,
|
||||
QFileDialog,
|
||||
QMainWindow,
|
||||
QMessageBox,
|
||||
QProgressDialog,
|
||||
QTreeWidgetItem,
|
||||
QLabel,
|
||||
QDialog,
|
||||
)
|
||||
|
||||
|
||||
class ProgressDialog(QProgressDialog):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.setWindowModality(Qt.WindowModal)
|
||||
|
||||
|
||||
def sizeof_fmt(num, suffix="B"):
|
||||
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
|
||||
if abs(num) < 1024.0:
|
||||
return "{:.02f} {}{}".format(num, unit, suffix)
|
||||
num /= 1024.0
|
||||
return "{:.02f} {}{}".format(num, "Yi", suffix)
|
||||
|
||||
|
||||
def t_round(dt):
|
||||
return dt - dt % timedelta(seconds=1)
|
||||
|
||||
|
||||
class Job(QObject):
|
||||
progress = pyqtSignal("PyQt_PyObject")
|
||||
|
||||
def __init__(self, app, main_window, cls, *args, **kwargs):
|
||||
super().__init__()
|
||||
self.job = cls(*args, **kwargs)
|
||||
self.timer = QTimer(app)
|
||||
self.app = app
|
||||
self.main_window = main_window
|
||||
self.timer.timeout.connect(self.interval)
|
||||
self.timer.start(100)
|
||||
self.last_val = None
|
||||
self.progress_dialog = None
|
||||
self.state = {}
|
||||
self.setup_progress(self.handle_progess)
|
||||
|
||||
def setup_progress(self, handle_progess):
|
||||
self.progress.connect(lambda *args, **kwargs: handle_progess(*args, **kwargs))
|
||||
|
||||
def handle_progess(self, *args, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
def start(self):
|
||||
if self.progress_dialog is None:
|
||||
self.progress.connect(
|
||||
lambda *args, **kwargs: print("PROGRESS:", *args, **kwargs)
|
||||
)
|
||||
self.started = datetime.today()
|
||||
return self.job.start()
|
||||
|
||||
def cancel(self):
|
||||
self.job.terminate()
|
||||
self.job = None
|
||||
|
||||
def __bool__(self):
|
||||
return not self.done
|
||||
|
||||
@property
|
||||
def done(self):
|
||||
if self.job:
|
||||
return (self.job.is_alive() == False) and (self.job.queue.empty())
|
||||
return True
|
||||
|
||||
def interval(self):
|
||||
while self:
|
||||
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 PreprocessJob(Job):
|
||||
def __init__(self, app, main_window, *args, **kwargs):
|
||||
super().__init__(app, main_window, Preprocessor, *args, **kwargs)
|
||||
self.progress_dialog = ProgressDialog("", "Cancel", 0, 0, self.main_window)
|
||||
self.progress_dialog.setAutoClose(False)
|
||||
self.progress_dialog.canceled.connect(self.cancel)
|
||||
self.progress_dialog.show()
|
||||
self.start()
|
||||
|
||||
def handle_progess(self, state):
|
||||
sent = object()
|
||||
if state.get("return", sent) != sent:
|
||||
self.progress_dialog.close()
|
||||
return
|
||||
msg = "Processed: {}/{}".format(
|
||||
sizeof_fmt(state["status"]["done"]), sizeof_fmt(state["status"]["total"])
|
||||
)
|
||||
state["status"]["prc_done"] = (
|
||||
state["status"]["done"] / state["status"]["total"]
|
||||
) * 100
|
||||
title = "[{prc_done:.2f}%] Processing {file}".format(**state["status"])
|
||||
self.progress_dialog.setMinimum(0)
|
||||
self.progress_dialog.setMaximum(100 * 100)
|
||||
self.progress_dialog.setWindowTitle(title)
|
||||
self.progress_dialog.setLabelText(msg)
|
||||
self.progress_dialog.setValue(int(state["status"]["prc_done"] * 100))
|
||||
|
||||
|
||||
class RouterJob(Job):
|
||||
def __init__(self, app, main_window, *args, **kwargs):
|
||||
super().__init__(app, main_window, Router, *args, **kwargs)
|
||||
self.progress_dialog = ProgressDialog("", "Cancel", 0, 0, self.main_window)
|
||||
self.progress_dialog.setAutoClose(False)
|
||||
self.progress_dialog.setLabelText("Loading data (this will take a bit) ...")
|
||||
self.progress_dialog.setWindowTitle("Loading...")
|
||||
self.progress_dialog.canceled.connect(self.cancel)
|
||||
self.progress_dialog.show()
|
||||
self.start()
|
||||
self.state = {}
|
||||
|
||||
def handle_progess(self, state):
|
||||
sent = object()
|
||||
if state.get("return", sent) != sent:
|
||||
print(state["return"])
|
||||
self.progress_dialog.close()
|
||||
route_win = WRoute(self.main_window, state["return"])
|
||||
return
|
||||
msg = "Depth: {depth}\nBody: {body}\nQueued: {queue_size}\nDistance remaining: {d_rem:.2f} Ly".format(
|
||||
**state["status"]
|
||||
)
|
||||
title = "[{prc_done:.2f}%] Plotting route from [{from}] to [{to}]".format(
|
||||
**state["status"]
|
||||
)
|
||||
self.progress_dialog.setMinimum(0)
|
||||
self.progress_dialog.setMaximum(100 * 100)
|
||||
self.progress_dialog.setWindowTitle(title)
|
||||
self.progress_dialog.setLabelText(msg)
|
||||
self.progress_dialog.setValue(int(state["status"]["prc_done"] * 100))
|
||||
|
||||
|
||||
class DownloadThread(QThread):
|
||||
progress = pyqtSignal("PyQt_PyObject")
|
||||
|
||||
def __init__(self, systems_url, systems_file, bodies_url, bodies_file):
|
||||
super().__init__()
|
||||
self.systems_url = systems_url
|
||||
self.systems_file = systems_file
|
||||
self.bodies_url = bodies_url
|
||||
self.bodies_file = bodies_file
|
||||
self.running = True
|
||||
|
||||
def __del__(self):
|
||||
self.wait()
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
|
||||
def run(self):
|
||||
dl_jobs = [
|
||||
(self.systems_url, self.systems_file),
|
||||
(self.bodies_url, self.bodies_file),
|
||||
]
|
||||
for url, dest in dl_jobs:
|
||||
outfile = url.split("/")[-1]
|
||||
size = RQ.head(url, headers={"Accept-Encoding": "None"})
|
||||
size.raise_for_status()
|
||||
size = int(size.headers.get("Content-Length", 0))
|
||||
with open(dest, "wb") as of:
|
||||
resp = RQ.get(url, stream=True)
|
||||
for chunk in resp.iter_content(1024 * 1024):
|
||||
of.write(chunk)
|
||||
self.progress.emit(
|
||||
{"done": of.tell(), "size": size, "outfile": outfile}
|
||||
)
|
||||
if not self.running:
|
||||
return
|
||||
|
||||
|
||||
class App(QApplication):
|
||||
def __init__(self):
|
||||
super().__init__(sys.argv)
|
||||
self.setStyle("Fusion")
|
||||
self.setup_styles()
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
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 WRoute(Ui_diag_route):
|
||||
def __init__(self, main_window, hops):
|
||||
super().__init__()
|
||||
self.route = hops
|
||||
dialog = QDialog(main_window)
|
||||
self.setupUi(dialog)
|
||||
for n, item in enumerate(hops):
|
||||
if item["body"].startswith(item["system"]):
|
||||
item["body"] = item["body"].replace(item["system"], "").strip()
|
||||
item = QTreeWidgetItem(
|
||||
self.lst_route,
|
||||
[
|
||||
str(n + 1),
|
||||
item["system"],
|
||||
"{body} ({star_type})".format(**item),
|
||||
str(item["distance"]),
|
||||
"<NotImplemented>", # Jump distance
|
||||
],
|
||||
)
|
||||
item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled)
|
||||
dialog.exec_()
|
||||
|
||||
|
||||
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.current_job = None
|
||||
|
||||
def get_open_file(self, filetypes, callback=None):
|
||||
fileName, _ = QFileDialog.getOpenFileName(
|
||||
self.main_window,
|
||||
"Open file",
|
||||
cfg["folders.data_dir"],
|
||||
filetypes,
|
||||
options=QFileDialog.DontUseNativeDialog,
|
||||
)
|
||||
if callback:
|
||||
return callback(fileName)
|
||||
return fileName
|
||||
|
||||
def get_save_file(self, filetypes, callback=None):
|
||||
fileName, _ = QFileDialog.getSaveFileName(
|
||||
self.main_window,
|
||||
"Save file",
|
||||
cfg["folders.data_dir"],
|
||||
filetypes,
|
||||
options=QFileDialog.DontUseNativeDialog,
|
||||
)
|
||||
if callback:
|
||||
return callback(fileName)
|
||||
return fileName
|
||||
|
||||
def preprocess(self):
|
||||
if self.current_job:
|
||||
# ERROR
|
||||
return
|
||||
bodies_json = self.inp_bodies_pp.currentText()
|
||||
systems_json = self.inp_systems_pp.currentText()
|
||||
output_file = self.inp_out_pp.currentText()
|
||||
self.current_job = PreprocessJob(
|
||||
self.app, self.main_window, systems_json, bodies_json, output_file
|
||||
)
|
||||
|
||||
def set_sys_lst(self, path):
|
||||
if path not in cfg["history.stars_csv_path"]:
|
||||
cfg["history.stars_csv_path"].append(path)
|
||||
self.update_dropdowns()
|
||||
|
||||
def set_bodies_file(self, path):
|
||||
if path not in cfg["history.bodies_path"]:
|
||||
cfg["history.bodies_path"].append(path)
|
||||
self.update_dropdowns()
|
||||
|
||||
def set_systems_file(self, path):
|
||||
if path not in cfg["history.systems_path"]:
|
||||
cfg["history.systems_path"].append(path)
|
||||
self.update_dropdowns()
|
||||
|
||||
def update_dropdowns(self):
|
||||
self.inp_systems_pp.clear()
|
||||
self.inp_systems_dest_dl.clear()
|
||||
for path in cfg["history.systems_path"][:]:
|
||||
self.inp_systems_pp.addItem(path)
|
||||
self.inp_systems_pp.setCurrentText(path)
|
||||
self.inp_systems_dest_dl.addItem(path)
|
||||
self.inp_systems_dest_dl.setCurrentText(path)
|
||||
self.inp_bodies_pp.clear()
|
||||
self.inp_bodies_dest_dl.clear()
|
||||
for path in cfg["history.bodies_path"][:]:
|
||||
self.inp_bodies_pp.addItem(path)
|
||||
self.inp_bodies_pp.setCurrentText(path)
|
||||
self.inp_bodies_dest_dl.addItem(path)
|
||||
self.inp_bodies_dest_dl.setCurrentText(path)
|
||||
self.inp_sys_lst.clear()
|
||||
self.inp_out_pp.clear()
|
||||
for path in cfg["history.stars_csv_path"]:
|
||||
self.inp_sys_lst.addItem(path)
|
||||
self.inp_sys_lst.setCurrentText(path)
|
||||
self.inp_out_pp.addItem(path)
|
||||
self.inp_out_pp.setCurrentText(path)
|
||||
return
|
||||
|
||||
def log(self, *args):
|
||||
t = datetime.today()
|
||||
msg_t = "[{}] {}".format(t, str.format(*args))
|
||||
self.txt_log.append(msg_t)
|
||||
|
||||
def set_comp_mode(self, _):
|
||||
if self.rd_comp.isChecked():
|
||||
comp_mode = "Compute Route"
|
||||
self.btn_add.setText("Add")
|
||||
if self.rd_precomp.isChecked():
|
||||
comp_mode = "Precompute Graph"
|
||||
self.btn_add.setText("Select")
|
||||
self.log("COMP_MODE", comp_mode)
|
||||
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.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())
|
||||
self.chk_permute_keep_last.setEnabled(self.rd_comp.isChecked())
|
||||
self.set_route_mode(self.rd_precomp.isChecked() or None)
|
||||
|
||||
def set_route_mode(self, mode=None):
|
||||
if mode == None:
|
||||
mode = self.cmb_mode.currentText()
|
||||
self.lbl_greedyness.setEnabled(mode == "A*-Search")
|
||||
self.sld_greedyness.setEnabled(mode == "A*-Search")
|
||||
|
||||
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)
|
||||
for c in range(self.lst_sys.headerItem().columnCount())
|
||||
]
|
||||
system = [
|
||||
self.lst_sys.topLevelItem(n).data(c, 0)
|
||||
for c in range(self.lst_sys.topLevelItem(n).columnCount())
|
||||
]
|
||||
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, "ED_LRR 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 compute_route(self):
|
||||
self.bar_status.showMessage("Resolving systems...")
|
||||
num_resolved = self.resolve_systems()
|
||||
if num_resolved:
|
||||
if (
|
||||
QMessageBox.question(
|
||||
self.main_window,
|
||||
"ED_LRR",
|
||||
"Resolved {} system(s), are the names correct?".format(
|
||||
num_resolved
|
||||
),
|
||||
)
|
||||
== QMessageBox.No
|
||||
):
|
||||
return
|
||||
self.bar_status.clearMessage()
|
||||
print(self.systems)
|
||||
systems = [s["id"] for s in self.systems]
|
||||
jump_range = self.sb_range.value()
|
||||
if jump_range == 0:
|
||||
self.error(
|
||||
"Your jump range is set to zero, you're not going to get anywhere that way"
|
||||
)
|
||||
return
|
||||
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
|
||||
)
|
||||
path = self.get_sys_list()
|
||||
if path is None:
|
||||
return
|
||||
precomp = None
|
||||
path = str(path)
|
||||
mode = {
|
||||
"Breadth-First Search": "bfs",
|
||||
"A*-Search": "astar",
|
||||
"Greedy-Search": "greedy",
|
||||
}[mode]
|
||||
print(
|
||||
systems,
|
||||
jump_range,
|
||||
None,
|
||||
mode,
|
||||
primary,
|
||||
permute,
|
||||
keep_first,
|
||||
keep_last,
|
||||
greedyness,
|
||||
precomp,
|
||||
path,
|
||||
os.cpu_count() - 1,
|
||||
)
|
||||
if not self.current_job:
|
||||
self.bar_status.showMessage("Computing Route...")
|
||||
self.current_job = RouterJob(
|
||||
self.app,
|
||||
self.main_window,
|
||||
systems,
|
||||
jump_range,
|
||||
None,
|
||||
mode,
|
||||
primary,
|
||||
permute,
|
||||
keep_first,
|
||||
keep_last,
|
||||
greedyness,
|
||||
precomp,
|
||||
path,
|
||||
os.cpu_count() - 1,
|
||||
)
|
||||
else:
|
||||
self.error("there is already a job running!")
|
||||
|
||||
def find_sys_by_names(self, names):
|
||||
t_s = datetime.today()
|
||||
if not self.get_sys_list():
|
||||
return None
|
||||
# TODO: start thread/subprocess
|
||||
ret = _ed_lrr.find_sys(names, self.inp_sys_lst.currentText())
|
||||
print("Took:", datetime.today() - t_s)
|
||||
return ret
|
||||
|
||||
def resolve_systems(self):
|
||||
# TODO: show spinner
|
||||
names = []
|
||||
nums = []
|
||||
for n in range(self.lst_sys.topLevelItemCount()):
|
||||
sys_id = getattr(self.lst_sys.topLevelItem(n), "__id__", None)
|
||||
if sys_id is not None:
|
||||
continue
|
||||
names.append(self.sys_to_dict(n)["Name"])
|
||||
nums.append(n)
|
||||
if not names:
|
||||
return 0
|
||||
systems = self.find_sys_by_names(names)
|
||||
if systems is None:
|
||||
return
|
||||
for i, name in zip(nums, 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"]
|
||||
return len(names)
|
||||
# diff, item = self.find_sys_by_name(name)
|
||||
# print("Found", (diff, item))
|
||||
|
||||
def add_system(self):
|
||||
name = self.inp_sys.text()
|
||||
item = QTreeWidgetItem(self.lst_sys, [name, None])
|
||||
item.resolved = False
|
||||
item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled)
|
||||
|
||||
def remove_system(self):
|
||||
root = self.lst_sys.invisibleRootItem()
|
||||
for item in self.lst_sys.selectedItems():
|
||||
root.removeChild(item)
|
||||
|
||||
def dl_canceled(self):
|
||||
if self.dl_thread:
|
||||
print("Cancel!")
|
||||
try:
|
||||
self.dl_thread.progress.disconnect()
|
||||
except TypeError:
|
||||
pass
|
||||
self.dl_thread.stop()
|
||||
self.dl_thread.wait()
|
||||
self.diag_prog.close()
|
||||
self.dl_thread = None
|
||||
self.diag_prog = None
|
||||
self.dl_started = None
|
||||
|
||||
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)
|
||||
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"])
|
||||
|
||||
def run_download(self):
|
||||
if self.dl_thread:
|
||||
return
|
||||
self.dl_started = datetime.today()
|
||||
self.dl_thread = DownloadThread(
|
||||
self.inp_systems_dl.currentText(),
|
||||
self.inp_systems_dest_dl.currentText(),
|
||||
self.inp_bodies_dl.currentText(),
|
||||
self.inp_bodies_dest_dl.currentText(),
|
||||
)
|
||||
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.set_greedyness(self.sld_greedyness.value())
|
||||
self.cmb_mode.currentTextChanged.connect(self.set_route_mode)
|
||||
self.rd_comp.toggled.connect(self.set_comp_mode)
|
||||
self.rd_precomp.toggled.connect(self.set_comp_mode)
|
||||
self.sld_greedyness.valueChanged.connect(self.set_greedyness)
|
||||
self.btn_go.clicked.connect(self.compute_route)
|
||||
self.btn_add.clicked.connect(self.add_system)
|
||||
self.btn_rm.clicked.connect(self.remove_system)
|
||||
self.btn_preprocess.clicked.connect(self.preprocess)
|
||||
self.chk_permute.stateChanged.connect(self.update_permute_chk)
|
||||
self.btn_out_browse_pp.clicked.connect(
|
||||
lambda: self.get_save_file("CSV File (*.csv)", self.set_sys_lst)
|
||||
)
|
||||
self.btn_sys_lst_browse.clicked.connect(
|
||||
lambda: self.get_open_file("CSV File (*.csv)", self.set_sys_lst)
|
||||
)
|
||||
|
||||
self.btn_bodies_browse_pp.clicked.connect(
|
||||
lambda: self.get_open_file("JSON File (*.json)", self.set_bodies_file)
|
||||
)
|
||||
self.btn_bodies_dest_browse_dl.clicked.connect(
|
||||
lambda: self.get_save_file("JSON File (*.json)", self.set_bodies_file)
|
||||
)
|
||||
self.btn_systems_browse_pp.clicked.connect(
|
||||
lambda: self.get_open_file("JSON File (*.json)", self.set_systems_file)
|
||||
)
|
||||
self.btn_systems_dest_browse_dl.clicked.connect(
|
||||
lambda: self.get_save_file("JSON File (*.json)", self.set_systems_file)
|
||||
)
|
||||
|
||||
def handle_close(self):
|
||||
for key in [
|
||||
"history.stars_csv_path",
|
||||
"history.bodies_path",
|
||||
"history.systems_path",
|
||||
]:
|
||||
for path in cfg[key][:]:
|
||||
if not os.path.isfile(path):
|
||||
cfg[key].remove(path)
|
||||
cfg.write()
|
||||
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()
|
||||
self.main_window = MainWindow
|
||||
self.app = app
|
||||
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():
|
||||
MP.freeze_support()
|
||||
app = App()
|
||||
app.setWindowIcon(QIcon(r"D:\devel\rust\ed_lrr_gui\icon\icon.ico"))
|
||||
MainWindow = QMainWindow()
|
||||
MainWindow.setWindowIcon(QIcon(r"D:\devel\rust\ed_lrr_gui\icon\icon.ico"))
|
||||
ui = ED_LRR()
|
||||
ui.setupUi(MainWindow, app)
|
||||
MainWindow.show()
|
||||
ret = app.exec_()
|
||||
ui.handle_close()
|
||||
exit(ret)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,65 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'D:\devel\rust\ed_lrr_gui\ed_lrr_gui\gui\widget_route.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.14.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_diag_route(object):
|
||||
def setupUi(self, diag_route):
|
||||
diag_route.setObjectName("diag_route")
|
||||
diag_route.setWindowModality(QtCore.Qt.WindowModal)
|
||||
diag_route.resize(430, 300)
|
||||
self.layout_main = QtWidgets.QGridLayout(diag_route)
|
||||
self.layout_main.setObjectName("layout_main")
|
||||
self.layout_top = QtWidgets.QGridLayout()
|
||||
self.layout_top.setObjectName("layout_top")
|
||||
self.lst_route = QtWidgets.QTreeWidget(diag_route)
|
||||
self.lst_route.setProperty("showDropIndicator", False)
|
||||
self.lst_route.setDefaultDropAction(QtCore.Qt.IgnoreAction)
|
||||
self.lst_route.setAlternatingRowColors(True)
|
||||
self.lst_route.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
|
||||
self.lst_route.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem)
|
||||
self.lst_route.setHorizontalScrollMode(
|
||||
QtWidgets.QAbstractItemView.ScrollPerPixel
|
||||
)
|
||||
self.lst_route.setItemsExpandable(False)
|
||||
self.lst_route.setAllColumnsShowFocus(False)
|
||||
self.lst_route.setObjectName("lst_route")
|
||||
self.layout_top.addWidget(self.lst_route, 0, 0, 1, 1)
|
||||
self.layout_main.addLayout(self.layout_top, 0, 0, 1, 1)
|
||||
self.layout_bottom = QtWidgets.QGridLayout()
|
||||
self.layout_bottom.setObjectName("layout_bottom")
|
||||
self.chk_copy = QtWidgets.QCheckBox(diag_route)
|
||||
self.chk_copy.setObjectName("chk_copy")
|
||||
self.layout_bottom.addWidget(self.chk_copy, 1, 0, 1, 1)
|
||||
self.btn_close = QtWidgets.QPushButton(diag_route)
|
||||
self.btn_close.setObjectName("btn_close")
|
||||
self.layout_bottom.addWidget(self.btn_close, 1, 2, 1, 1)
|
||||
self.btn_export = QtWidgets.QPushButton(diag_route)
|
||||
self.btn_export.setObjectName("btn_export")
|
||||
self.layout_bottom.addWidget(self.btn_export, 1, 1, 1, 1)
|
||||
self.layout_main.addLayout(self.layout_bottom, 1, 0, 1, 1)
|
||||
|
||||
self.retranslateUi(diag_route)
|
||||
QtCore.QMetaObject.connectSlotsByName(diag_route)
|
||||
|
||||
def retranslateUi(self, diag_route):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
diag_route.setWindowTitle(_translate("diag_route", "Route"))
|
||||
self.lst_route.headerItem().setText(0, _translate("diag_route", "Num"))
|
||||
self.lst_route.headerItem().setText(1, _translate("diag_route", "System"))
|
||||
self.lst_route.headerItem().setText(2, _translate("diag_route", "Body"))
|
||||
self.lst_route.headerItem().setText(
|
||||
3, _translate("diag_route", "Distance (Ls)")
|
||||
)
|
||||
self.chk_copy.setText(
|
||||
_translate("diag_route", "Auto-copy next hop to clipboard")
|
||||
)
|
||||
self.btn_close.setText(_translate("diag_route", "Close"))
|
||||
self.btn_export.setText(_translate("diag_route", "Export"))
|
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>diag_route</class>
|
||||
<widget class="QDialog" name="diag_route">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>430</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Route</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="layout_main">
|
||||
<item row="0" column="0">
|
||||
<layout class="QGridLayout" name="layout_top">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTreeWidget" name="lst_route">
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::IgnoreAction</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerItem</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="itemsExpandable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Num</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>System</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Body</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Distance (Ls)</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QGridLayout" name="layout_bottom">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="chk_copy">
|
||||
<property name="text">
|
||||
<string>Auto-copy next hop to clipboard</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btn_close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="btn_export">
|
||||
<property name="text">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import jinja2
|
||||
import os
|
||||
|
||||
tmpl_path = os.path.join(os.path.dirname(__file__), "html_export_template.html.jinja2")
|
||||
|
||||
|
||||
def dist(p1, p2):
|
||||
s = 0
|
||||
for c1, c2 in zip(p1, p2):
|
||||
s += (c1 - c2) ** 2
|
||||
return s ** 0.5
|
||||
|
||||
|
||||
colors = {
|
||||
"O": "#0000FF",
|
||||
"B": "#140AF0",
|
||||
"A": "#3C1EDC",
|
||||
"F": "#EEEEEE",
|
||||
"G": "#969646",
|
||||
"K": "#B43C1E",
|
||||
"M": "#FF280A",
|
||||
"L": "#FF1E00",
|
||||
"T": "#800000",
|
||||
"Y": "#800000",
|
||||
"White Dwarf": "#5D67EF",
|
||||
"Neutron": "#99A0FF",
|
||||
}
|
||||
|
||||
entries = []
|
||||
prev = route[0]
|
||||
num = 1
|
||||
for hop in route[1:]:
|
||||
prev["jump_dist"] = "{:.2f} Ly".format(dist(hop["pos"], prev["pos"]))
|
||||
prev["num"] = num
|
||||
prev["color"] = colors.get(prev["star_type"].split()[0], "#eee")
|
||||
prev["distance"] = "{} Ls".format(prev["distance"])
|
||||
entries.append(prev)
|
||||
prev = hop
|
||||
num += 1
|
||||
prev["jump_dist"] = "0 Ly"
|
||||
prev["distance"] = "{} Ls".format(prev["distance"])
|
||||
prev["num"] = num
|
||||
prev["color"] = colors.get(prev["star_type"].split()[0], "#eee")
|
||||
entries.append(prev)
|
||||
tmpl = jinja2.Template(open(tmpl_path).read())
|
||||
open("route.html", "w").write(tmpl.render(route=entries))
|
|
@ -0,0 +1,158 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
h1 {
|
||||
float: left;
|
||||
}
|
||||
body {
|
||||
background: #222;
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
max-width: 50%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#graph {
|
||||
border: 1px solid #eee;
|
||||
float: left;
|
||||
}
|
||||
|
||||
table,
|
||||
td,
|
||||
tr,th {
|
||||
color: #eee;
|
||||
margin: auto;
|
||||
border: 1px solid #eee;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* D3 stuff */
|
||||
|
||||
.d3-tip {
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
padding: 12px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Creates a small triangle extender for the tooltip */
|
||||
.d3-tip:after {
|
||||
box-sizing: border-box;
|
||||
display: inline;
|
||||
font-size: 10px;
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
content: "\25BC";
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Style northward tooltips differently */
|
||||
.d3-tip.n:after {
|
||||
margin: -1px 0 0 0;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
<script src="https://d3js.org/d3.v5.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="graph"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Num</th>
|
||||
<th>Body</th>
|
||||
<th>Type</th>
|
||||
<th>Distance to arrival</th>
|
||||
<th>Jump range</th>
|
||||
</tr>
|
||||
{% for sys in route %}
|
||||
<tr>
|
||||
<td>{{sys.num}}</td>
|
||||
<td>{{sys.body}}</td>
|
||||
<td style="color: {{sys.color}}">{{sys.star_type}}</td>
|
||||
<td>{{sys.distance}}</td>
|
||||
<td>{{sys.jump_dist}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<script type="text/javascript">
|
||||
function dist(a,b) {
|
||||
var sum=0;
|
||||
for (var i=0;i<a.length;++i) {
|
||||
sum+=Math.pow(a[i]-b[i],2)
|
||||
}
|
||||
return Math.pow(sum,0.5);
|
||||
}
|
||||
var width=512;
|
||||
var height=512;
|
||||
var route={{route|tojson}};
|
||||
var vis=d3.select("#graph")
|
||||
.append("svg").attr("viewBox", [0, 0, width, height]);;
|
||||
|
||||
vis.attr("width", width)
|
||||
.attr("height", height);
|
||||
var g=vis.append("g");
|
||||
|
||||
vis.call(d3.zoom()
|
||||
.extent([[0, 0], [width, height]])
|
||||
.on("zoom", () => {
|
||||
g.attr("transform", d3.event.transform);
|
||||
}));
|
||||
|
||||
var lines=[];
|
||||
for (var i=0;i<route.length-1;++i) {
|
||||
lines.push({
|
||||
x1: route[i].pos[1],
|
||||
x2: route[i+1].pos[1],
|
||||
y1: -route[i].pos[2],
|
||||
y2: -route[i+1].pos[2],
|
||||
dist: dist(route[i].pos,route[i+1].pos),
|
||||
color: ({
|
||||
'#99A0FF':'#99A0FF', // Neutron star
|
||||
'#5D67EF':'#5D67EF' // White dwarf
|
||||
}[route[i].color]||'#eee')
|
||||
})
|
||||
}
|
||||
|
||||
g.selectAll(".line")
|
||||
.data(lines)
|
||||
.enter()
|
||||
.append("line")
|
||||
.attr("x1", (l) => l.x1 )
|
||||
.attr("y1", (l) => l.y1 )
|
||||
.attr("x2", (l) => l.x2 )
|
||||
.attr("y2", (l) => l.y2 )
|
||||
.style("stroke", (l) => l.color )
|
||||
.style("stroke-width", 5)
|
||||
.append("title")
|
||||
.text((l) => Math.round(l.dist*100)/100 +" Ly");
|
||||
|
||||
g.selectAll("circle .nodes")
|
||||
.data(route)
|
||||
.enter()
|
||||
.append("svg:circle")
|
||||
.attr("class", "nodes")
|
||||
.attr("cx", (d) => d.pos[1])
|
||||
.attr("cy", (d) => -d.pos[2])
|
||||
.attr("r", 10)
|
||||
.attr("fill", (d) => d.color)
|
||||
.append("title")
|
||||
.text((d) => d.body+" ("+d.star_type+")")
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import queue
|
||||
from collections import namedtuple
|
||||
from datetime import datetime, timedelta
|
||||
from multiprocessing import Process, Queue, freeze_support
|
||||
|
||||
import _ed_lrr
|
||||
|
||||
|
||||
class Preprocessor(Process):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__()
|
||||
self.state = {}
|
||||
self.queue = Queue()
|
||||
self.daemon = True
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.kwargs["callback"] = self.callback
|
||||
|
||||
def callback(self, state):
|
||||
self.queue.put({"status": state})
|
||||
|
||||
def run(self):
|
||||
res = _ed_lrr.preprocess(*self.args, **self.kwargs)
|
||||
self.queue.put({"result": res})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
freeze_support()
|
||||
r = Preprocessor(
|
||||
r"D:\devel\rust\ED_LRR\dumps\systemsWithCoordinates.json",
|
||||
r"D:\devel\rust\ED_LRR\dumps\bodies.json",
|
||||
r"D:\devel\rust\ED_LRR\stars.csv",
|
||||
)
|
||||
for i, e in enumerate(r):
|
||||
print(e)
|
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import queue
|
||||
from collections import namedtuple
|
||||
from datetime import datetime, timedelta
|
||||
from multiprocessing import Process, Queue, freeze_support
|
||||
|
||||
import _ed_lrr
|
||||
|
||||
|
||||
class Router(Process):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__()
|
||||
self.state = {}
|
||||
self.queue = Queue()
|
||||
self.daemon = True
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.kwargs["callback"] = self.callback
|
||||
|
||||
def callback(self, state):
|
||||
self.queue.put({"status": state})
|
||||
|
||||
def run(self):
|
||||
print("Route(): ", self.args, self.kwargs)
|
||||
route = _ed_lrr.route(*self.args, **self.kwargs)
|
||||
self.queue.put({"return": route})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
freeze_support()
|
||||
r = Router(
|
||||
["Ix", "Beagle Point"],
|
||||
48,
|
||||
"BFS",
|
||||
False,
|
||||
False,
|
||||
None,
|
||||
None,
|
||||
r"D:\devel\rust\ED_LRR\stars.csv",
|
||||
)
|
||||
for e in r:
|
||||
print(e)
|
|
@ -0,0 +1,2 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from .app import app, templates, db
|
|
@ -0,0 +1,721 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from flask import (
|
||||
Flask,
|
||||
jsonify,
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
send_from_directory,
|
||||
request,
|
||||
flash,
|
||||
current_app,
|
||||
)
|
||||
from flask.cli import AppGroup
|
||||
import uuid
|
||||
import os
|
||||
import click
|
||||
from functools import wraps
|
||||
from concurrent.futures.process import BrokenProcessPool
|
||||
from datetime import datetime, timedelta
|
||||
from webargs import fields, validate
|
||||
from webargs.flaskparser import use_kwargs
|
||||
|
||||
from flask_executor import Executor
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_bootstrap import Bootstrap
|
||||
|
||||
from flask_nav import Nav, register_renderer
|
||||
from flask_nav.elements import Navbar, View
|
||||
|
||||
from flask_admin import Admin
|
||||
from flask_admin.contrib.sqla import ModelView
|
||||
|
||||
from flask_wtf.csrf import CSRFProtect
|
||||
|
||||
from flask_login import (
|
||||
LoginManager,
|
||||
current_user,
|
||||
logout_user,
|
||||
UserMixin,
|
||||
AnonymousUserMixin,
|
||||
login_user,
|
||||
login_required,
|
||||
)
|
||||
|
||||
from flask_debugtoolbar import DebugToolbarExtension
|
||||
|
||||
from sqlalchemy_utils import generic_repr, JSONType, PasswordType, UUIDType
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.types import DateTime
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
from .forms import RouteForm, LoginForm, RegisterForm, ChangePasswordForm
|
||||
from .utils import prepare_route, BootsrapRenderer, is_safe_url
|
||||
|
||||
import _ed_lrr as ed_lrr
|
||||
|
||||
templates = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
|
||||
app = Flask(__name__, template_folder=templates)
|
||||
app.config.from_pyfile("config.py")
|
||||
|
||||
app.executor = executor = Executor(app)
|
||||
app.db = db = SQLAlchemy(app)
|
||||
app.bootstrap = bootstrap = Bootstrap(app)
|
||||
app.csrf = csfr = CSRFProtect(app)
|
||||
app.nav = nav = Nav(app)
|
||||
app.login_manager = login_manager = LoginManager(app)
|
||||
login_manager.login_view = "login"
|
||||
login_manager.session_protection = "strong"
|
||||
admin = Admin(app, name="ED_LRR", template_mode="bootstrap3")
|
||||
app.debug = True
|
||||
app.toolbar = toolbar = DebugToolbarExtension(app)
|
||||
|
||||
|
||||
def wants_json_response():
|
||||
return (
|
||||
request.accept_mimetypes["application/json"]
|
||||
>= request.accept_mimetypes["text/html"]
|
||||
)
|
||||
|
||||
|
||||
@app.errorhandler(422)
|
||||
@app.errorhandler(400)
|
||||
@app.errorhandler(500)
|
||||
@app.errorhandler(404)
|
||||
def handle_error(err):
|
||||
if wants_json_response():
|
||||
return jsonify(error=str(err), code=err.code), err.code
|
||||
templates = ["error/{}.html".format(err.code), "error/default.html"]
|
||||
try:
|
||||
print(dir(err))
|
||||
return render_template(templates, error=err), err.code
|
||||
except TemplateNotFound:
|
||||
return err.get_response()
|
||||
|
||||
|
||||
def role_required(*roles):
|
||||
def wrapper(fn):
|
||||
@wraps(fn)
|
||||
def decorated_view(*args, **kwargs):
|
||||
if not current_user.is_authenticated():
|
||||
return current_app.login_manager.unauthorized()
|
||||
has_role = False
|
||||
user = current_app.login_manager.reload_user()
|
||||
for role in roles:
|
||||
has_role |= user.has_role(role)
|
||||
if not has_role:
|
||||
return current_app.login_manager.unauthorized()
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
return decorated_view
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_name):
|
||||
return User.query.get(user_name)
|
||||
|
||||
|
||||
@login_manager.request_loader
|
||||
def load_user_from_header(header_val):
|
||||
for api_key in [request.args.get("api_key"), request.headers.get("X-API-Key")]:
|
||||
if api_key:
|
||||
user = User.query.filter_by(api_key=api_key).one_or_none()
|
||||
if user:
|
||||
return user
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def left_nav():
|
||||
links = [
|
||||
View("Home", "index"),
|
||||
View("Route", "route"),
|
||||
View("Jobs", "status", job_id=None),
|
||||
]
|
||||
if current_user.has_role("admin") or current_user.has_role("worker_host"):
|
||||
links.insert(2, View("Workers", "worker"))
|
||||
return Navbar("E:D LRR", *links)
|
||||
|
||||
|
||||
def right_nav():
|
||||
links = [View("Login", "login"), View("Register", "register")]
|
||||
if current_user.is_authenticated:
|
||||
links = [View("Change Password", "change_password"), View("Logout", "logout")]
|
||||
if current_user.has_role("admin"):
|
||||
links = [View("Admin", "admin.index")] + links
|
||||
return Navbar("", *links)
|
||||
|
||||
|
||||
register_renderer(app, "bootstrap4", BootsrapRenderer)
|
||||
nav.register_element("left_nav", left_nav)
|
||||
nav.register_element("right_nav", right_nav)
|
||||
|
||||
|
||||
def compute_route(args, kwargs):
|
||||
return ed_lrr.route(*args, **kwargs)
|
||||
|
||||
|
||||
class AnonymousUser(AnonymousUserMixin):
|
||||
def has_role(self, role):
|
||||
return False
|
||||
|
||||
@property
|
||||
def roles(self):
|
||||
return []
|
||||
|
||||
@roles.setter
|
||||
def __set_roles(self, value):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
login_manager.anonymous_user = AnonymousUser
|
||||
|
||||
|
||||
@generic_repr
|
||||
class Worker(db.Model):
|
||||
id = db.Column(
|
||||
UUIDType(binary=False, native=False), primary_key=True, default=uuid.uuid4
|
||||
)
|
||||
name = db.Column(db.String, unique=True)
|
||||
current_job = db.Column(
|
||||
UUIDType(binary=False, native=False),
|
||||
db.ForeignKey("job.id"),
|
||||
nullable=True,
|
||||
default=None,
|
||||
)
|
||||
job = relationship("Job", backref="workers")
|
||||
last_active = db.Column(DateTime, nullable=True, default=None)
|
||||
owner_name = db.Column(
|
||||
db.String, db.ForeignKey("user.name"), nullable=True, index=True
|
||||
)
|
||||
owner = relationship("User", backref="workers")
|
||||
|
||||
|
||||
user_roles = db.Table(
|
||||
"user_roles",
|
||||
db.Column("user_name", db.String, db.ForeignKey("user.name"), primary_key=True),
|
||||
db.Column("role_name", db.String, db.ForeignKey("role.name"), primary_key=True),
|
||||
)
|
||||
|
||||
|
||||
class Role(db.Model):
|
||||
name = db.Column(db.String, unique=True, index=True, primary_key=True)
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
name = db.Column(db.String, unique=True, index=True, primary_key=True)
|
||||
is_active = db.Column(db.Boolean, default=False)
|
||||
api_key = db.Column(
|
||||
UUIDType(binary=False, native=False),
|
||||
nullable=True,
|
||||
default=uuid.uuid4,
|
||||
index=True,
|
||||
)
|
||||
password = db.Column(PasswordType(schemes=["pbkdf2_sha512"], max_length=256))
|
||||
created = db.Column(DateTime, default=datetime.today)
|
||||
|
||||
roles = db.relationship("Role", secondary="user_roles")
|
||||
|
||||
def add_roles(self, roles):
|
||||
for role_name in roles:
|
||||
role = Role.query.filter_by(name=role_name).one()
|
||||
if role not in self.roles:
|
||||
self.roles.append(role)
|
||||
db.session.commit()
|
||||
|
||||
def has_role(self, role_name):
|
||||
return (
|
||||
Role.query.join(User.roles)
|
||||
.filter(User.name == self.name, Role.name == role_name)
|
||||
.count()
|
||||
> 0
|
||||
)
|
||||
|
||||
def reset_api_key(self):
|
||||
self.api_key = uuid.uuid4()
|
||||
db.session.add(self)
|
||||
db.session.comiit()
|
||||
|
||||
def get_id(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Job(db.Model):
|
||||
id = db.Column(
|
||||
UUIDType(binary=False, native=False), primary_key=True, default=uuid.uuid4
|
||||
)
|
||||
user_name = db.Column(
|
||||
db.String, db.ForeignKey("user.name"), nullable=True, index=True
|
||||
)
|
||||
func = db.Column(db.String)
|
||||
args = db.Column(JSONType)
|
||||
kwargs = db.Column(JSONType)
|
||||
state = db.Column(JSONType, default={})
|
||||
priority = db.Column(db.Integer, default=0, nullable=True)
|
||||
created = db.Column(DateTime, default=datetime.today)
|
||||
finished = db.Column(DateTime, nullable=True, default=None)
|
||||
started = db.Column(DateTime, nullable=True, default=None)
|
||||
last_update = db.Column(DateTime, nullable=True, default=None)
|
||||
user = relationship("User", backref="jobs")
|
||||
# ============================================================
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.id)
|
||||
|
||||
@property
|
||||
def future(self):
|
||||
fut = executor.futures._futures.get(self.id)
|
||||
return fut
|
||||
|
||||
@property
|
||||
def sort_key(self):
|
||||
state_priorities = {
|
||||
"Queued": 0,
|
||||
"Starting": 1,
|
||||
"Error": 1,
|
||||
"Stalled": 1,
|
||||
"Running": 1,
|
||||
}
|
||||
status_key = state_priorities.get(self.status[1], -1) + 1
|
||||
user = 1 - int(self.user is not None)
|
||||
return (user, -status_key, self.priority, self.created)
|
||||
|
||||
@property
|
||||
def age(self):
|
||||
dt = datetime.today() - self.created
|
||||
return dt - dt % timedelta(seconds=1)
|
||||
|
||||
@classmethod
|
||||
def get_next(cls):
|
||||
for job in sorted(cls.query.all(), key=lambda v: v.sort_key):
|
||||
if job.status[1] in ["Done"]:
|
||||
continue
|
||||
return job
|
||||
return None
|
||||
# return cls.query.
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
# [
|
||||
# ("primary", "Done"),
|
||||
# ("danger", "Error"),
|
||||
# ("info", "Stalled"),
|
||||
# ("success", "Running"),
|
||||
# ("secondary", "Starting"),
|
||||
# ("warning", "Queued"),
|
||||
# ]
|
||||
# return states[self.id.int%len(states)]
|
||||
if self.state.get("result"):
|
||||
return ("primary", "Done")
|
||||
if self.state.get("error"):
|
||||
return ("danger", "Error")
|
||||
if self.state.get("progress"):
|
||||
if (datetime.today() - self.last_update).total_seconds() > (60 * 10):
|
||||
return ("info", "Stalled")
|
||||
return ("success", "Running")
|
||||
if self.started is not None:
|
||||
return ("secondary", "Starting")
|
||||
return ("warning", "Queued")
|
||||
|
||||
@status.setter
|
||||
def __set_status(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"args": self.args,
|
||||
"kwargs": self.kwargs,
|
||||
"state": self.state,
|
||||
"finished": self.finished,
|
||||
"created": self.created,
|
||||
"started": self.started,
|
||||
}
|
||||
|
||||
@dict.setter
|
||||
def __set_dict(self, value):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def route(self):
|
||||
try:
|
||||
return prepare_route(self.state["result"])
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def t_rem(self):
|
||||
if self.started is None:
|
||||
return None
|
||||
runtime = datetime.today() - self.started
|
||||
try:
|
||||
prc_done = self.state["progress"]["prc_done"]
|
||||
if prc_done != 0:
|
||||
t_rem = (runtime / prc_done) * (100 - prc_done)
|
||||
return timedelta(seconds=round(t_rem.total_seconds(), 0))
|
||||
return None
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
@t_rem.setter
|
||||
def __set_t_rem(self, value):
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def new(cls, func, args=None, kwargs=None):
|
||||
args = args or ()
|
||||
kwargs = kwargs or {}
|
||||
job = cls(args=args, kwargs=kwargs, func=func.__qualname__)
|
||||
job.__last_upd = 0.0
|
||||
if current_user.is_authenticated:
|
||||
job.user = current_user
|
||||
db.session.add(job)
|
||||
db.session.commit()
|
||||
return job
|
||||
|
||||
def start(self):
|
||||
global executor
|
||||
self.state = {}
|
||||
self.started = None
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
args = self.args + [self.callback]
|
||||
try:
|
||||
future = executor.submit_stored(self.id, compute_route, args, self.kwargs)
|
||||
except (BrokenProcessPool, RuntimeError) as e:
|
||||
print("Error:", e)
|
||||
print("Restarting Executor!")
|
||||
executor = Executor(app)
|
||||
future = executor.submit_stored(self.id, compute_route, args, self.kwargs)
|
||||
future.add_done_callback(self.done)
|
||||
|
||||
def callback(self, cb_state):
|
||||
try:
|
||||
if self.started is None:
|
||||
self.started = datetime.today()
|
||||
if self.last_update is not None:
|
||||
time_since_last_upd = (
|
||||
datetime.today() - self.last_update
|
||||
).total_seconds()
|
||||
if time_since_last_upd < 5.0:
|
||||
return
|
||||
state = {}
|
||||
state.update(self.state)
|
||||
state.update({"progress": cb_state})
|
||||
self.state = state
|
||||
self.last_update = datetime.today()
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def done(self, future):
|
||||
print(self.id, "DONE")
|
||||
state = {}
|
||||
state.update(self.state)
|
||||
executor.futures.pop(self.id)
|
||||
exc = future.exception()
|
||||
if exc:
|
||||
state.update(
|
||||
{"error": {"type": type(exc).__name__, "args": list(exc.args)}}
|
||||
)
|
||||
else:
|
||||
state.update({"result": future.result()})
|
||||
self.state = state
|
||||
self.finished = datetime.now()
|
||||
db.session.add(self)
|
||||
db.session.commit()
|
||||
|
||||
class Ship(db.Model):
|
||||
user_name = db.Column(
|
||||
db.String, db.ForeignKey("user.name"), nullable=True, index=True, primary_key=True
|
||||
)
|
||||
user = relationship("User", backref="ships")
|
||||
ship = db.Column(db.JSONType, nullable=False, index=True, primary_key = True)
|
||||
|
||||
|
||||
db.create_all()
|
||||
for role in ["admin", "user", "worker_host"]:
|
||||
if Role.query.filter_by(name=role).one_or_none() is None:
|
||||
db.session.add(Role(role))
|
||||
|
||||
|
||||
def create_user(name, password, roles, active=False):
|
||||
user = User.query.filter_by(name=name).one_or_none()
|
||||
if user:
|
||||
db.session.delete(user)
|
||||
user = User(name=name, password=password, is_active=active)
|
||||
user.add_roles(roles)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return user
|
||||
|
||||
|
||||
# create_user("admin", "admin", ["admin", "user"], True)
|
||||
# create_user("user", "user", ["user"], True)
|
||||
# create_user("host", "host", ["user", "worker_host"], True)
|
||||
|
||||
|
||||
class SQLAView(ModelView):
|
||||
column_exclude_list = ["password"]
|
||||
column_editable_list = []
|
||||
create_modal = True
|
||||
edit_modal = True
|
||||
can_view_details = True
|
||||
column_display_pk = True
|
||||
|
||||
def is_accessible(self):
|
||||
return current_user.is_authenticated and current_user.has_role("admin")
|
||||
|
||||
def inaccessible_callback(self, name, **kwargs):
|
||||
return redirect(url_for("login"))
|
||||
|
||||
|
||||
class UserView(SQLAView):
|
||||
from wtforms import PasswordField
|
||||
|
||||
column_list = ("name", "active", "password", "api_key", "roles")
|
||||
column_formatters = {
|
||||
"password": lambda view, context, model, name: "",
|
||||
"api_key": lambda view, context, model, name: model.api_key or "",
|
||||
}
|
||||
form_extra_fields = {"password": PasswordField("Password")}
|
||||
|
||||
|
||||
class JobView(SQLAView):
|
||||
# Job.id,Job.user,Job.func,Job.args,Job.kwargs,Job.state,Job.created,Job.finished,Job.started,Job.last_update
|
||||
column_list = ("id", "status", "user", "created", "started", "finished")
|
||||
column_formatters = {"status": lambda view, context, model, name: model.status[1]}
|
||||
|
||||
|
||||
class WorkerView(SQLAView):
|
||||
pass
|
||||
# # Job.id,Job.user,Job.func,Job.args,Job.kwargs,Job.state,Job.created,Job.finished,Job.started,Job.last_update
|
||||
# column_list = ("id", "status", "user", "created", "started", "finished")
|
||||
# column_formatters = {
|
||||
# "user": lambda view, context, model, name: model.user.name
|
||||
# if model.user
|
||||
# else "",
|
||||
# "status": lambda view, context, model, name: model.status[1],
|
||||
# }
|
||||
|
||||
|
||||
admin.add_view(JobView(Job, db.session))
|
||||
admin.add_view(UserView(User, db.session))
|
||||
admin.add_view(SQLAView(Worker, db.session))
|
||||
admin.add_view(SQLAView(Role, db.session))
|
||||
|
||||
|
||||
def submit_job(func, *args, **kwargs):
|
||||
job = Job.new(func, args, kwargs)
|
||||
job.start()
|
||||
return job.id
|
||||
|
||||
|
||||
@app.route("/api/route", methods=["GET", "POST"])
|
||||
@use_kwargs(
|
||||
{
|
||||
"jump_range": fields.Float(required=True),
|
||||
"mode": fields.String(
|
||||
missing="bfs", validate=validate.OneOf(["bfs", "greedy", "a-star"])
|
||||
),
|
||||
"systems": fields.DelimitedList(fields.String, required=True),
|
||||
"permute": fields.String(
|
||||
missing=None,
|
||||
validate=validate.OneOf(
|
||||
["off", "all", "keep_first", "keep_last", "keep_both"]
|
||||
),
|
||||
),
|
||||
"primary": fields.Boolean(missing=False),
|
||||
"factor": fields.Float(missing=0.5),
|
||||
}
|
||||
)
|
||||
def api_route(_=None, **args):
|
||||
if args["permute"] == "off":
|
||||
args["permute"] = None
|
||||
args["systems"] = [s.strip() for s in args["systems"]]
|
||||
args = (
|
||||
args["systems"],
|
||||
args["jump_range"],
|
||||
None,
|
||||
args["mode"],
|
||||
args["primary"],
|
||||
args["permute"] is not None,
|
||||
args["permute"] in ["keep_first", "keep_both"],
|
||||
args["permute"] in ["keep_last", "keep_both"],
|
||||
args["factor"],
|
||||
None,
|
||||
r"D:\devel\rust\ED_LRR\stars.csv",
|
||||
app.config["ROUTE_WORKERS"],
|
||||
)
|
||||
return jsonify({"id": submit_job(ed_lrr.route, *args)})
|
||||
|
||||
|
||||
@app.route("/api/status")
|
||||
def api_status():
|
||||
info = {"queued_jobs": len(executor.futures._futures)}
|
||||
return jsonify(info)
|
||||
|
||||
|
||||
@app.route("/api/whoami")
|
||||
def api_whoami():
|
||||
return jsonify({"name": current_user.name})
|
||||
|
||||
|
||||
@app.route("/api/status/<uuid:job_id>")
|
||||
def api_job_status(job_id):
|
||||
job = Job.query.get_or_404(str(job_id))
|
||||
return jsonify(job.dict)
|
||||
|
||||
|
||||
@app.route("/static/<path:path>")
|
||||
def send_static(path):
|
||||
return send_from_directory("static", path)
|
||||
|
||||
|
||||
@app.route("/route", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def route():
|
||||
form = RouteForm()
|
||||
if form.validate_on_submit():
|
||||
data = dict(form.data)
|
||||
if data["permute"] == "off":
|
||||
data["permute"] = None
|
||||
del data["csrf_token"]
|
||||
del data["submit"]
|
||||
job = api_route(data)
|
||||
return redirect(url_for("status", job_id=job.json["id"]))
|
||||
return render_template("form.html", form=form, title="Plot Route")
|
||||
|
||||
|
||||
@app.route("/status/", defaults={"job_id": None})
|
||||
@app.route("/status/<uuid:job_id>")
|
||||
@login_required
|
||||
def status(job_id=None):
|
||||
if job_id is not None:
|
||||
job = Job.query.get_or_404(str(job_id))
|
||||
return render_template("job.html", job=job)
|
||||
return render_template("status.html", Job=Job, state=request.args.get("state"))
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for("index"))
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = User.query.filter_by(name=form.data["username"]).one_or_none()
|
||||
if (user is None) or (user.password != form.data["password"]):
|
||||
flash("Invalid credentials!", "danger")
|
||||
return redirect(url_for("login"))
|
||||
if not user.is_active:
|
||||
flash("Account is deactivated!", "warning")
|
||||
return redirect(url_for("login"))
|
||||
login_user(user, remember=form.data["remember"])
|
||||
next = request.args.get("next")
|
||||
if not is_safe_url(next):
|
||||
next = None
|
||||
return redirect(next or url_for("status"))
|
||||
return render_template("form.html", form=form, title="Login")
|
||||
|
||||
|
||||
@app.route("/register", methods=["GET", "POST"])
|
||||
def register():
|
||||
form = RegisterForm()
|
||||
if form.validate_on_submit():
|
||||
if User.query.filter_by(name=form.data["username"]).one_or_none() is not None:
|
||||
flash("Username already exists", "danger")
|
||||
return render_template("form.html", form=form, title="Register")
|
||||
user = User()
|
||||
user.name = form.data["username"]
|
||||
user.password = form.data["password"]
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
login_user(user)
|
||||
return redirect(url_for("status"))
|
||||
return render_template("form.html", form=form, title="Register")
|
||||
|
||||
|
||||
@app.route("/change_password", methods=["GET", "POST"])
|
||||
def change_password():
|
||||
if current_user.is_anonymous:
|
||||
return redirect(url_for("index"))
|
||||
form = ChangePasswordForm()
|
||||
if form.validate_on_submit():
|
||||
if form.data["old_password"] == current_user.password:
|
||||
current_user.password = form.data["password"]
|
||||
flash("Password changed!", "success")
|
||||
else:
|
||||
flash("Wrong password!", "danger")
|
||||
return render_template("form.html", form=form, title="Register")
|
||||
return redirect(url_for("status"))
|
||||
return render_template("form.html", form=form, title="Register")
|
||||
|
||||
|
||||
|
||||
@app.route("/workers/", defaults={"worker_id": None})
|
||||
@app.route("/workers/<uuid:worker_id>")
|
||||
@login_required
|
||||
def worker(worker_id):
|
||||
return render_template("workers.html")
|
||||
|
||||
|
||||
@app.route("/logout")
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(url_for("login"))
|
||||
|
||||
|
||||
@app.before_first_request
|
||||
def resume_jobs():
|
||||
print("NEXT:", Job.get_next())
|
||||
with app.test_request_context():
|
||||
for job in Job.query.all():
|
||||
if job.status[1] != "Done":
|
||||
print("Restarting {} with state {}".format(job.id, job.status[1]))
|
||||
job.start()
|
||||
|
||||
|
||||
user_cli = AppGroup('user', help="Manage users")
|
||||
job_cli = AppGroup('job', help="Manage Jobs")
|
||||
worker_cli = AppGroup('worker', help="Manage Workers")
|
||||
|
||||
|
||||
@app.cli.command("gevent")
|
||||
def cmd_gevent():
|
||||
return
|
||||
|
||||
|
||||
@user_cli.command("create")
|
||||
@click.argument("name")
|
||||
@click.option("-i", "--inactive", help="Crate account as inactive", is_flag=True, default=False)
|
||||
@click.option("-r", "--role", help="Assign role to account", default=["user"], multiple=True)
|
||||
@click.password_option("-p", "--password", help="Password for user")
|
||||
def cmd_create_user(name, role, password, inactive):
|
||||
"Create a new user"
|
||||
create_user(name, password, role, not inactive)
|
||||
print("User created!")
|
||||
|
||||
|
||||
app.cli.add_command(user_cli)
|
||||
app.cli.add_command(job_cli)
|
||||
app.cli.add_command(worker_cli)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="127.0.0.1", port=3777, debug=True)
|
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
|
||||
SECRET_KEY = "ED_LRR_WEBAPP"
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///ed_lrr_web_ui.db"
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
ROUTE_WORKERS = 0
|
||||
|
||||
EXECUTOR_TYPE = "process"
|
||||
EXECUTOR_MAX_WORKERS = os.cpu_count() - 1
|
||||
EXECUTOR_FUTURES_MAX_LENGTH = 500
|
||||
|
||||
FLASK_ADMIN_SWATCH = "Darkly"
|
||||
|
||||
DEBUG_TB_TEMPLATE_EDITOR_ENABLED = True
|
||||
|
||||
MAIL_DEFAULT_SENDER = '"ED_LRR Admin" <ed_lrr@gmail.com>'
|
|
@ -0,0 +1,106 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (
|
||||
StringField,
|
||||
PasswordField,
|
||||
FieldList,
|
||||
FloatField,
|
||||
BooleanField,
|
||||
SelectField,
|
||||
SubmitField,
|
||||
validators,
|
||||
Field,
|
||||
)
|
||||
from wtforms.widgets.html5 import NumberInput
|
||||
from wtforms.widgets import TextInput
|
||||
from wtforms.validators import ValidationError
|
||||
|
||||
|
||||
class StringListField(Field):
|
||||
widget = TextInput()
|
||||
|
||||
def _value(self):
|
||||
if self.data:
|
||||
return u",".join(self.data)
|
||||
else:
|
||||
return u""
|
||||
|
||||
def process_formdata(self, valuelist):
|
||||
if valuelist:
|
||||
self.data = [x.strip() for x in valuelist[0].split(",")]
|
||||
else:
|
||||
self.data = []
|
||||
|
||||
|
||||
class RouteForm(FlaskForm):
|
||||
systems = StringListField("Systems", [validators.DataRequired()])
|
||||
jump_range = FloatField(
|
||||
"Jump Range (Ly)",
|
||||
[validators.DataRequired(), validators.NumberRange(0, None)],
|
||||
widget=NumberInput(min=0, step=0.1),
|
||||
)
|
||||
mode = SelectField(
|
||||
"Routing Mode",
|
||||
choices=[
|
||||
("bfs", "Breadth-First Search"),
|
||||
("greedy", "Greedy Search"),
|
||||
("a-star", "A*-Search"),
|
||||
],
|
||||
)
|
||||
permute = SelectField(
|
||||
"Permutation Mode",
|
||||
choices=[
|
||||
("off", "Off"),
|
||||
("keep_first", "Keep starting system"),
|
||||
("keep_last", "Keep destination system"),
|
||||
("keep_both", "Keep both endpoints"),
|
||||
],
|
||||
)
|
||||
primary = BooleanField("Only route through primary stars")
|
||||
factor = FloatField(
|
||||
"Greedyness for A*-Search (%)",
|
||||
[validators.NumberRange(0, 100)],
|
||||
default=50,
|
||||
widget=NumberInput(min=0, max=100, step=1),
|
||||
)
|
||||
|
||||
priority = FloatField(
|
||||
"Priority (0=max, 100=min)",
|
||||
[validators.NumberRange(0, 100)],
|
||||
default=0,
|
||||
widget=NumberInput(min=0, max=100, step=1),
|
||||
)
|
||||
submit = SubmitField("GO!")
|
||||
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
username = StringField("Username", [validators.Required()])
|
||||
password = PasswordField("Password", [validators.Required()])
|
||||
remember = BooleanField("Remember me")
|
||||
submit = SubmitField("Login")
|
||||
|
||||
|
||||
class RegisterForm(FlaskForm):
|
||||
username = StringField("Username", [validators.Required()])
|
||||
password = PasswordField(
|
||||
"Password",
|
||||
[
|
||||
validators.Required(),
|
||||
validators.EqualTo("confirm", message="Passwords must match"),
|
||||
],
|
||||
)
|
||||
confirm = PasswordField("Verify password", [validators.Required()])
|
||||
submit = SubmitField("Login")
|
||||
|
||||
|
||||
class ChangePasswordForm(FlaskForm):
|
||||
old_password = PasswordField("Current Password", [validators.Required()])
|
||||
password = PasswordField(
|
||||
"Password",
|
||||
[
|
||||
validators.Required(),
|
||||
validators.EqualTo("confirm", message="Passwords must match"),
|
||||
],
|
||||
)
|
||||
confirm = PasswordField("Verify password", [validators.Required()])
|
||||
submit = SubmitField("Change")
|
|
@ -0,0 +1,23 @@
|
|||
body,input,select,pre {
|
||||
background-color: #222 !important;
|
||||
color: #eee;
|
||||
}
|
||||
table {
|
||||
line-height: 1;
|
||||
}
|
||||
.progress {
|
||||
background-color: #444;
|
||||
}
|
||||
.progress-bar {
|
||||
background-color: #f70;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
color: #eee !important;
|
||||
}
|
||||
|
||||
#graph {
|
||||
border: 1px solid #eee;
|
||||
width: 512px;
|
||||
height: 512px;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{% extends 'admin/master.html' %}
|
||||
|
||||
{% block body %}
|
||||
<p>Hello world</p>
|
||||
{% endblock %}
|
|
@ -0,0 +1,48 @@
|
|||
{% extends "bootstrap/base.html" %}
|
||||
{% import "bootstrap/utils.html" as utils %}
|
||||
{% block title %}Elite: Dangerous Long Range Router{% endblock %}
|
||||
|
||||
{% block scrips %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
{{super()}}
|
||||
<link rel="stylesheet" href="{{url_for('static', filename='theme.css')}}">
|
||||
{% endblock %}
|
||||
|
||||
{% block navbar %}
|
||||
<nav class="navbar navbar-expand-lg navbar-dark" style="background-color: #222;">
|
||||
<a class="navbar-brand" href="/">E:D LRR</a>
|
||||
<ul class="navbar-nav mr-auto">
|
||||
{{nav.left_nav.render(renderer='bootstrap4')}}
|
||||
</ul>
|
||||
|
||||
<ul class="navbar-nav ml-auto">
|
||||
{% if current_user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link">
|
||||
Logged in as {{current_user.name}}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{{nav.right_nav.render(renderer='bootstrap4')}}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category,message in messages %}
|
||||
<div class="alert alert-{{category}}" role="{{category}}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
{# application content needs to be provided in the app_content block #}
|
||||
{% block app_content %}{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block app_content %}
|
||||
<h1>404 Not Found</h1>
|
||||
<p><a href="{{ url_for('index') }}"><button type="button" class="btn btn-secondary">Back</button></a></p>
|
||||
{% endblock %}
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "base.html" %}
|
||||
{% import "bootstrap/wtf.html" as wtf %}
|
||||
|
||||
{% block app_content %}
|
||||
<h1>{{title}}</h1>
|
||||
{% for field in form %}
|
||||
{% for error in field.errors %}
|
||||
<div class="alert alert-danger" role="danger">{{error}}</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
{{ wtf.quick_form(form) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,10 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block app_content %}
|
||||
<h1>E:D LRR</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
Number of Jobs: {{current_user.jobs|count}}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,131 @@
|
|||
{% extends "base.html" %}
|
||||
{% block app_content %}
|
||||
<h1>Job Status <span class="badge badge-{{job.status[0]}}">{{ job.status[1] }}</span></h1>
|
||||
<div class="row">
|
||||
<div class="col-lg-0">
|
||||
{% if job.state.error %}
|
||||
<ul>
|
||||
{% for err in job.state.error.args %}
|
||||
<li>{{err}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if job.state.progress %}
|
||||
<p class="lead">Routing from <b>{{ job.state.progress.from }}</b> to <b>{{ job.state.progress.to }}</b> using
|
||||
{{ job.state.progress.mode }}</p>
|
||||
<p>Current system: <b>{{ job.state.progress.system }}</b></p>
|
||||
<p>Search queue size: <b>{{"{:,}".format(job.state.progress.queue_size) }}</b></p>
|
||||
<p>Number of systems checked: <b>{{"{:,}".format(job.state.progress.n_seen) }}
|
||||
({{job.state.progress.prc_seen|round(2)}} %)</b></p>
|
||||
<p>Estimated time remaining: <b>{{job.t_rem}}</b></p>
|
||||
<p>Search Depth: <b>{{job.state.progress.depth}}</b></p>
|
||||
<div class="progress" style="width: 100%;">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
||||
style="width: {{job.state.progress.prc_done}}%;" role="progressbar"
|
||||
aria-valuenow="{{job.state.progress.prc_done|round(2)}}" aria-valuemin="0" aria-valuemax="100">
|
||||
{{job.state.progress.prc_done|round(2)}} %
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if job.state.result %}
|
||||
<h2>Result</h2>
|
||||
|
||||
<h3>Map</h3>
|
||||
<div id="graph">
|
||||
</div>
|
||||
<script src="https://d3js.org/d3.v5.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
function dist(a, b) {
|
||||
var sum = 0;
|
||||
for (var i = 0; i < a.length; ++i) {
|
||||
sum += Math.pow(a[i] - b[i], 2)
|
||||
}
|
||||
return Math.pow(sum, 0.5);
|
||||
}
|
||||
var width = 512;
|
||||
var height = 512;
|
||||
var route = {{job.route | tojson}};
|
||||
var vis = d3.select("#graph")
|
||||
.append("svg").attr("viewBox", [0, 0, width, height]);
|
||||
|
||||
vis.attr("width", width)
|
||||
.attr("height", height);
|
||||
var g = vis.append("g");
|
||||
|
||||
vis.call(d3.zoom()
|
||||
.extent([
|
||||
[0, 0],
|
||||
[width, height]
|
||||
])
|
||||
.on("zoom", () => {
|
||||
g.attr("transform", d3.event.transform);
|
||||
}));
|
||||
|
||||
var lines = [];
|
||||
for (var i = 0; i < route.length - 1; ++i) {
|
||||
lines.push({
|
||||
x1: route[i].pos[1],
|
||||
x2: route[i + 1].pos[1],
|
||||
y1: -route[i].pos[2],
|
||||
y2: -route[i + 1].pos[2],
|
||||
dist: dist(route[i].pos, route[i + 1].pos),
|
||||
color: route[i].color || '#eee'
|
||||
})
|
||||
}
|
||||
|
||||
g.selectAll(".line")
|
||||
.data(lines)
|
||||
.enter()
|
||||
.append("line")
|
||||
.attr("x1", (l) => l.x1)
|
||||
.attr("y1", (l) => l.y1)
|
||||
.attr("x2", (l) => l.x2)
|
||||
.attr("y2", (l) => l.y2)
|
||||
.style("stroke", (l) => l.color)
|
||||
.style("stroke-width", 5)
|
||||
.append("title")
|
||||
.text((l) => Math.round(l.dist * 100) / 100 + " Ly");
|
||||
|
||||
g.selectAll("circle .nodes")
|
||||
.data(route)
|
||||
.enter()
|
||||
.append("svg:circle")
|
||||
.attr("class", "nodes")
|
||||
.attr("cx", (d) => d.pos[1])
|
||||
.attr("cy", (d) => -d.pos[2])
|
||||
.attr("r", 10)
|
||||
.attr("fill", (d) => d.color)
|
||||
.append("title")
|
||||
.text((d) => d.body + " (" + d.star_type + ")")
|
||||
</script>
|
||||
<h3>Jumps</h3>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Num</th>
|
||||
<th scope="col">Body</th>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col">Distance from arrival</th>
|
||||
<th scope="col">Jump distance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for sys in job.route %}
|
||||
<tr>
|
||||
<th scope="row">{{sys.num}}</td>
|
||||
<td>{{sys.body}}</td>
|
||||
<td style="color: {{sys.color}}">{{sys.star_type}}</td>
|
||||
<td>{{"{:,}".format(sys.distance)}} Ls</td>
|
||||
<td>{{"{:,}".format(sys.jump_dist|round(2))}} Ly</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<th scope="row" colspan=4>Total Distance</th>
|
||||
<td>{{"{:,}".format(job.route|sum(attribute='jump_dist')|round(2))}} Ly</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,87 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block app_content %}
|
||||
{% if current_user.has_role('admin') %}
|
||||
{% set jobs = Job.query.all() %}
|
||||
{% else %}
|
||||
{% set jobs = current_user.jobs %}
|
||||
{% endif %}
|
||||
<h1>System Status</h1>
|
||||
<div class="row">
|
||||
<h2>Overview</h2>
|
||||
</div>
|
||||
<div class="row">
|
||||
<table class="table table-striped table-bordered" style="width: 1px;">
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<th>Count</th>
|
||||
</tr>
|
||||
{% for group in (jobs|groupby('status')) %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="?state={{group.grouper[1]}}">
|
||||
<span class="badge badge-{{ group.grouper[0] }}">{{ group.grouper[1] }}</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{group.list|count}}
|
||||
</td>
|
||||
<tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="?">
|
||||
Total
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{jobs|count}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h2>Jobs</h2>
|
||||
</div>
|
||||
<div class="row">
|
||||
<!-- Next: {{Job.next().id}} -->
|
||||
<table class="table table-striped table-bordered" style="width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">ID</th>
|
||||
<th scope="col">Systems</th>
|
||||
<th scope="col">Status</th>
|
||||
<th scope="col">User</th>
|
||||
<th scope="col">Priority</th>
|
||||
<th scope="col">Progess</th>
|
||||
<th scope="col">ETC</th>
|
||||
<th scope="col">Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for job in (jobs|sort(attribute='sort_key')) %}
|
||||
{% if (state==None) or job.status[1]==state %}
|
||||
<tr>
|
||||
<td style="width: 1px; white-space: nowrap;"><a href="{{url_for('status',job_id=job.id)}}">{{job.id}}</a></td>
|
||||
<td style="width: 1px; white-space: nowrap;">{{job.args[0]|join(', ')}}</td>
|
||||
<td style="width: 1px; white-space: nowrap;"><span class="badge badge-{{job.status[0]}}">{{ job.status[1] }}</span></td>
|
||||
<td style="width: 1px; white-space: nowrap;">{{job.user.name}}</td>
|
||||
<td style="width: 1px; white-space: nowrap;">{{job.priority}}</td>
|
||||
{% if job.state.progress %}
|
||||
<td>
|
||||
<div class="progress" style="width: 100%;">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: {{job.state.progress.prc_done}}%;" role="progressbar" aria-valuenow="{{job.state.progress.prc_done|round(2)}}" aria-valuemin="0" aria-valuemax="100">
|
||||
{{job.state.progress.prc_done|round(2)}} %
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{% else %}
|
||||
<td>Unknown</td>
|
||||
{% endif %}
|
||||
<td style="width: 1px; white-space: nowrap;">{{job.t_rem}}</td>
|
||||
<td style="width: 1px; white-space: nowrap;">{{job.age}} ago</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,14 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block app_content %}
|
||||
<h1>Workers</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
{% if current_user.is_authenticated %}
|
||||
Hello {{current_user.name}}!
|
||||
{% else %}
|
||||
Nothing to see here!
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,75 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from flask_nav.renderers import Renderer
|
||||
from dominate import tags
|
||||
from urllib.parse import urlparse, urljoin
|
||||
from flask import request
|
||||
|
||||
|
||||
def is_safe_url(target):
|
||||
ref_url = urlparse(request.host_url)
|
||||
test_url = urlparse(urljoin(request.host_url, target))
|
||||
return test_url.scheme in ("http", "https") and ref_url.netloc == test_url.netloc
|
||||
|
||||
|
||||
def dist(p1, p2):
|
||||
s = 0
|
||||
for c1, c2 in zip(p1, p2):
|
||||
s += (c1 - c2) ** 2
|
||||
return s ** 0.5
|
||||
|
||||
|
||||
class BootsrapRenderer(Renderer):
|
||||
def visit_Navbar(self, node):
|
||||
sub = []
|
||||
for item in node.items:
|
||||
sub.append(self.visit(item))
|
||||
return "".join([v.render() for v in sub])
|
||||
|
||||
def visit_View(self, node):
|
||||
classes = ["nav-link"]
|
||||
if node.active:
|
||||
classes.append("active")
|
||||
return tags.li(
|
||||
tags.a(node.text, href=node.get_url(), cls=" ".join(classes)),
|
||||
cls="nav-item",
|
||||
)
|
||||
|
||||
def visit_Subgroup(self, node):
|
||||
# almost the same as visit_Navbar, but written a bit more concise
|
||||
return tags.div(node.title, *[self.visit(item) for item in node.items])
|
||||
|
||||
|
||||
colors = {
|
||||
"O": "#0000FF",
|
||||
"B": "#140AF0",
|
||||
"A": "#3C1EDC",
|
||||
"F": "#EEEEEE",
|
||||
"G": "#969646",
|
||||
"K": "#B43C1E",
|
||||
"M": "#FF280A",
|
||||
"L": "#FF1E00",
|
||||
"T": "#800000",
|
||||
"Y": "#800000",
|
||||
"White Dwarf": "#5D67EF",
|
||||
"Neutron": "#99A0FF",
|
||||
}
|
||||
|
||||
|
||||
def prepare_route(route):
|
||||
entries = []
|
||||
prev = route[0]
|
||||
num = 1
|
||||
for hop in route[1:]:
|
||||
prev["jump_dist"] = dist(hop["pos"], prev["pos"])
|
||||
prev["num"] = num
|
||||
prev["color"] = colors.get(prev["star_type"].split()[0], "#eee")
|
||||
prev["distance"] = prev["distance"]
|
||||
entries.append(prev)
|
||||
prev = hop
|
||||
num += 1
|
||||
prev["jump_dist"] = 0
|
||||
prev["distance"] = prev["distance"]
|
||||
prev["num"] = num
|
||||
prev["color"] = colors.get(prev["star_type"].split()[0], "#eee")
|
||||
entries.append(prev)
|
||||
return entries
|
|
@ -0,0 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import requests as RQ
|
||||
import _ed_lrr as ed_lrr
|
||||
|
||||
funcs = {
|
||||
func: getattr(ed_lrr, func) for func in dir(ed_lrr) if not func.startswith("_")
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
from sympy import *
|
||||
from sympy.utilities.codegen import RustCodeGen
|
||||
|
||||
|
||||
def to_latex(eq, inline=False):
|
||||
mode = "equation"
|
||||
itex = True
|
||||
if inline:
|
||||
mode = "inline"
|
||||
itex = False
|
||||
return latex(eq, mul_symbol=" \\cdot ", mode=mode, itex=itex)
|
||||
|
||||
|
||||
def solve_for(eq, sym):
|
||||
return Eq(sym, solve(eq, sym)[0])
|
||||
|
||||
|
||||
init_printing(use_latex=True, latex_mode="equation", mul_symbol="\\cdot")
|
||||
|
||||
var("m_ship m_fuel m_opt f_useable f_max l p boost dist dist_max Fuel B_g e_fuel")
|
||||
|
||||
mass = m_ship + m_fuel # total mass of ship+fuel
|
||||
m_opt = m_opt * boost # supercharging increases optimized mass
|
||||
|
||||
available_fuel = Min(
|
||||
f_max, m_fuel
|
||||
) # limit maximum fuel consumption to FSD max fuel limit
|
||||
|
||||
eq_fuel = Eq(Fuel, l * 0.001 * (((dist * mass) / m_opt) ** p)) # FSD Fuel equation
|
||||
|
||||
|
||||
|
||||
eq_fuel_boost = eq_fuel.subs({"dist":dist+B_g,"Fuel":available_fuel*e_fuel}) # FSD Booster boosts maximum distance by B_g
|
||||
eq_d_boost = solve_for(eq_fuel_boost, dist) # solve for distance
|
||||
# eq_d_boost = eq_d_boost.subs({"Fuel":f_max,"m_fuel":f_max}) # Assume maximum jump range
|
||||
|
||||
print(to_latex(eq_d_boost))
|
||||
print(to_latex(solve_for(eq_d_boost,e_fuel)))
|
||||
|
||||
exit()
|
||||
|
||||
max_range = eq_d.subs(
|
||||
{m_fuel: f_max, dist: dist_max}
|
||||
) # Compute maximum jump range by assuming f_max tons of fuel in tank
|
||||
|
||||
full_eq = eq_d.subs(
|
||||
Min(f_max, m_fuel), Min(f_max, m_fuel) * fuel_mult
|
||||
).subs(
|
||||
max_range.lhs, max_range.rhs
|
||||
) # substitute everything in
|
||||
|
||||
docs = [
|
||||
(
|
||||
eq_fuel,
|
||||
"FSD Fuel consumption ([E:D Wiki](https://elite-dangerous.fandom.com/wiki/Frame_Shift_Drive#Hyperspace_Fuel_Equation)):",
|
||||
),
|
||||
(
|
||||
eq_d,
|
||||
"Solving for $dist$ gives the jump range (in Ly) for a given amount of fuel (in tons) as:",
|
||||
),
|
||||
(
|
||||
max_range,
|
||||
"Assuming $f_{max}$ tons of available fuel gives us the maximum jump range for a single jump as:",
|
||||
),
|
||||
(
|
||||
fuel_mult,
|
||||
"Since the guardian FSD booster increases the maximum jump range by $B_g$ Ly we can calculate a correction factor for the fuel consumption as:",
|
||||
),
|
||||
(
|
||||
eq_d.subs(Min(f_max, m_fuel), Min(f_max, m_fuel) * e_fuel),
|
||||
"Incorporating $e_{fuel}$ into the distance equation yields",
|
||||
),
|
||||
(
|
||||
eq_d.subs(Min(f_max, m_fuel), Min(f_max, m_fuel) * fuel_mult),
|
||||
"Expanding $e_{fuel}$ yields",
|
||||
),
|
||||
(full_eq, "Finally, Expanding $dist_{max}$ yields the full equation as"),
|
||||
]
|
||||
|
||||
|
||||
var_defs = [
|
||||
("Fuel", "is the fuel needed to jump (in tons)"),
|
||||
("l", "is the linear constant of your FSD (depends on the rating)"),
|
||||
("p", "is the power constant of your FSD (depends on the class)"),
|
||||
("m_ship", "is the mass of your ship (including cargo)"),
|
||||
("m_fuel", "is the amount of fuel in tons currently stored in your tanks"),
|
||||
("m_opt", "is the optimized mass of your FSD (in tons)"),
|
||||
("f_max", "is the maximum amount of fuel your FSD can use per jump"),
|
||||
(
|
||||
"boost",
|
||||
'is the "boost factor" of your FSD (1.0 when jumping normally, 1.5 when supercharged by a white dwarf, 4.0 for a neutron star, etc)',
|
||||
),
|
||||
("dist", "is the distance you can jump with a given fuel amount"),
|
||||
("dist_max", "is the maximum distance you can jump (when $m_{fuel}=f_{max}$)"),
|
||||
("B_g", "is the amount of Ly added by your Guardian FSD Booster"),
|
||||
("e_fuel", "is the efficiency increase added by the Guardian FSD Booster"),
|
||||
]
|
||||
for eq, doc in docs:
|
||||
eq=simplify(eq)
|
||||
if doc:
|
||||
print(doc, to_latex(eq))
|
||||
else:
|
||||
print(to_latex(eq))
|
||||
print()
|
||||
|
||||
print("Where:")
|
||||
for name, desc in var_defs:
|
||||
print("- {} {}".format(to_latex(symbols(name), True), desc))
|
|
@ -0,0 +1,904 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Populating the interactive namespace from numpy and matplotlib\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pylab notebook"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 131,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<ipython-input-131-1f27bb9e71ac>:5: RuntimeWarning: invalid value encountered in double_scalars\n",
|
||||
" ld = (((a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c))**0.5)/(c*2.0)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/javascript": [
|
||||
"/* Put everything inside the global mpl namespace */\n",
|
||||
"window.mpl = {};\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.get_websocket_type = function() {\n",
|
||||
" if (typeof(WebSocket) !== 'undefined') {\n",
|
||||
" return WebSocket;\n",
|
||||
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
|
||||
" return MozWebSocket;\n",
|
||||
" } else {\n",
|
||||
" alert('Your browser does not have WebSocket support. ' +\n",
|
||||
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
|
||||
" 'Firefox 4 and 5 are also supported but you ' +\n",
|
||||
" 'have to enable WebSockets in about:config.');\n",
|
||||
" };\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
|
||||
" this.id = figure_id;\n",
|
||||
"\n",
|
||||
" this.ws = websocket;\n",
|
||||
"\n",
|
||||
" this.supports_binary = (this.ws.binaryType != undefined);\n",
|
||||
"\n",
|
||||
" if (!this.supports_binary) {\n",
|
||||
" var warnings = document.getElementById(\"mpl-warnings\");\n",
|
||||
" if (warnings) {\n",
|
||||
" warnings.style.display = 'block';\n",
|
||||
" warnings.textContent = (\n",
|
||||
" \"This browser does not support binary websocket messages. \" +\n",
|
||||
" \"Performance may be slow.\");\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" this.imageObj = new Image();\n",
|
||||
"\n",
|
||||
" this.context = undefined;\n",
|
||||
" this.message = undefined;\n",
|
||||
" this.canvas = undefined;\n",
|
||||
" this.rubberband_canvas = undefined;\n",
|
||||
" this.rubberband_context = undefined;\n",
|
||||
" this.format_dropdown = undefined;\n",
|
||||
"\n",
|
||||
" this.image_mode = 'full';\n",
|
||||
"\n",
|
||||
" this.root = $('<div/>');\n",
|
||||
" this._root_extra_style(this.root)\n",
|
||||
" this.root.attr('style', 'display: inline-block');\n",
|
||||
"\n",
|
||||
" $(parent_element).append(this.root);\n",
|
||||
"\n",
|
||||
" this._init_header(this);\n",
|
||||
" this._init_canvas(this);\n",
|
||||
" this._init_toolbar(this);\n",
|
||||
"\n",
|
||||
" var fig = this;\n",
|
||||
"\n",
|
||||
" this.waiting = false;\n",
|
||||
"\n",
|
||||
" this.ws.onopen = function () {\n",
|
||||
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
|
||||
" fig.send_message(\"send_image_mode\", {});\n",
|
||||
" if (mpl.ratio != 1) {\n",
|
||||
" fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
|
||||
" }\n",
|
||||
" fig.send_message(\"refresh\", {});\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" this.imageObj.onload = function() {\n",
|
||||
" if (fig.image_mode == 'full') {\n",
|
||||
" // Full images could contain transparency (where diff images\n",
|
||||
" // almost always do), so we need to clear the canvas so that\n",
|
||||
" // there is no ghosting.\n",
|
||||
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
|
||||
" }\n",
|
||||
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
|
||||
" };\n",
|
||||
"\n",
|
||||
" this.imageObj.onunload = function() {\n",
|
||||
" fig.ws.close();\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" this.ws.onmessage = this._make_on_message_function(this);\n",
|
||||
"\n",
|
||||
" this.ondownload = ondownload;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._init_header = function() {\n",
|
||||
" var titlebar = $(\n",
|
||||
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
|
||||
" 'ui-helper-clearfix\"/>');\n",
|
||||
" var titletext = $(\n",
|
||||
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
|
||||
" 'text-align: center; padding: 3px;\"/>');\n",
|
||||
" titlebar.append(titletext)\n",
|
||||
" this.root.append(titlebar);\n",
|
||||
" this.header = titletext[0];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
|
||||
"\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
|
||||
"\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._init_canvas = function() {\n",
|
||||
" var fig = this;\n",
|
||||
"\n",
|
||||
" var canvas_div = $('<div/>');\n",
|
||||
"\n",
|
||||
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
|
||||
"\n",
|
||||
" function canvas_keyboard_event(event) {\n",
|
||||
" return fig.key_event(event, event['data']);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
|
||||
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
|
||||
" this.canvas_div = canvas_div\n",
|
||||
" this._canvas_extra_style(canvas_div)\n",
|
||||
" this.root.append(canvas_div);\n",
|
||||
"\n",
|
||||
" var canvas = $('<canvas/>');\n",
|
||||
" canvas.addClass('mpl-canvas');\n",
|
||||
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
|
||||
"\n",
|
||||
" this.canvas = canvas[0];\n",
|
||||
" this.context = canvas[0].getContext(\"2d\");\n",
|
||||
"\n",
|
||||
" var backingStore = this.context.backingStorePixelRatio ||\n",
|
||||
"\tthis.context.webkitBackingStorePixelRatio ||\n",
|
||||
"\tthis.context.mozBackingStorePixelRatio ||\n",
|
||||
"\tthis.context.msBackingStorePixelRatio ||\n",
|
||||
"\tthis.context.oBackingStorePixelRatio ||\n",
|
||||
"\tthis.context.backingStorePixelRatio || 1;\n",
|
||||
"\n",
|
||||
" mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
|
||||
"\n",
|
||||
" var rubberband = $('<canvas/>');\n",
|
||||
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
|
||||
"\n",
|
||||
" var pass_mouse_events = true;\n",
|
||||
"\n",
|
||||
" canvas_div.resizable({\n",
|
||||
" start: function(event, ui) {\n",
|
||||
" pass_mouse_events = false;\n",
|
||||
" },\n",
|
||||
" resize: function(event, ui) {\n",
|
||||
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
||||
" },\n",
|
||||
" stop: function(event, ui) {\n",
|
||||
" pass_mouse_events = true;\n",
|
||||
" fig.request_resize(ui.size.width, ui.size.height);\n",
|
||||
" },\n",
|
||||
" });\n",
|
||||
"\n",
|
||||
" function mouse_event_fn(event) {\n",
|
||||
" if (pass_mouse_events)\n",
|
||||
" return fig.mouse_event(event, event['data']);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" rubberband.mousedown('button_press', mouse_event_fn);\n",
|
||||
" rubberband.mouseup('button_release', mouse_event_fn);\n",
|
||||
" // Throttle sequential mouse events to 1 every 20ms.\n",
|
||||
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
|
||||
"\n",
|
||||
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
|
||||
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
|
||||
"\n",
|
||||
" canvas_div.on(\"wheel\", function (event) {\n",
|
||||
" event = event.originalEvent;\n",
|
||||
" event['data'] = 'scroll'\n",
|
||||
" if (event.deltaY < 0) {\n",
|
||||
" event.step = 1;\n",
|
||||
" } else {\n",
|
||||
" event.step = -1;\n",
|
||||
" }\n",
|
||||
" mouse_event_fn(event);\n",
|
||||
" });\n",
|
||||
"\n",
|
||||
" canvas_div.append(canvas);\n",
|
||||
" canvas_div.append(rubberband);\n",
|
||||
"\n",
|
||||
" this.rubberband = rubberband;\n",
|
||||
" this.rubberband_canvas = rubberband[0];\n",
|
||||
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
|
||||
" this.rubberband_context.strokeStyle = \"#000000\";\n",
|
||||
"\n",
|
||||
" this._resize_canvas = function(width, height) {\n",
|
||||
" // Keep the size of the canvas, canvas container, and rubber band\n",
|
||||
" // canvas in synch.\n",
|
||||
" canvas_div.css('width', width)\n",
|
||||
" canvas_div.css('height', height)\n",
|
||||
"\n",
|
||||
" canvas.attr('width', width * mpl.ratio);\n",
|
||||
" canvas.attr('height', height * mpl.ratio);\n",
|
||||
" canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
|
||||
"\n",
|
||||
" rubberband.attr('width', width);\n",
|
||||
" rubberband.attr('height', height);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
|
||||
" // upon first draw.\n",
|
||||
" this._resize_canvas(600, 600);\n",
|
||||
"\n",
|
||||
" // Disable right mouse context menu.\n",
|
||||
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
|
||||
" return false;\n",
|
||||
" });\n",
|
||||
"\n",
|
||||
" function set_focus () {\n",
|
||||
" canvas.focus();\n",
|
||||
" canvas_div.focus();\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" window.setTimeout(set_focus, 100);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._init_toolbar = function() {\n",
|
||||
" var fig = this;\n",
|
||||
"\n",
|
||||
" var nav_element = $('<div/>');\n",
|
||||
" nav_element.attr('style', 'width: 100%');\n",
|
||||
" this.root.append(nav_element);\n",
|
||||
"\n",
|
||||
" // Define a callback function for later on.\n",
|
||||
" function toolbar_event(event) {\n",
|
||||
" return fig.toolbar_button_onclick(event['data']);\n",
|
||||
" }\n",
|
||||
" function toolbar_mouse_event(event) {\n",
|
||||
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" for(var toolbar_ind in mpl.toolbar_items) {\n",
|
||||
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
||||
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
||||
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
||||
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
||||
"\n",
|
||||
" if (!name) {\n",
|
||||
" // put a spacer in here.\n",
|
||||
" continue;\n",
|
||||
" }\n",
|
||||
" var button = $('<button/>');\n",
|
||||
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
|
||||
" 'ui-button-icon-only');\n",
|
||||
" button.attr('role', 'button');\n",
|
||||
" button.attr('aria-disabled', 'false');\n",
|
||||
" button.click(method_name, toolbar_event);\n",
|
||||
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
||||
"\n",
|
||||
" var icon_img = $('<span/>');\n",
|
||||
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
|
||||
" icon_img.addClass(image);\n",
|
||||
" icon_img.addClass('ui-corner-all');\n",
|
||||
"\n",
|
||||
" var tooltip_span = $('<span/>');\n",
|
||||
" tooltip_span.addClass('ui-button-text');\n",
|
||||
" tooltip_span.html(tooltip);\n",
|
||||
"\n",
|
||||
" button.append(icon_img);\n",
|
||||
" button.append(tooltip_span);\n",
|
||||
"\n",
|
||||
" nav_element.append(button);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var fmt_picker_span = $('<span/>');\n",
|
||||
"\n",
|
||||
" var fmt_picker = $('<select/>');\n",
|
||||
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
|
||||
" fmt_picker_span.append(fmt_picker);\n",
|
||||
" nav_element.append(fmt_picker_span);\n",
|
||||
" this.format_dropdown = fmt_picker[0];\n",
|
||||
"\n",
|
||||
" for (var ind in mpl.extensions) {\n",
|
||||
" var fmt = mpl.extensions[ind];\n",
|
||||
" var option = $(\n",
|
||||
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
|
||||
" fmt_picker.append(option);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" // Add hover states to the ui-buttons\n",
|
||||
" $( \".ui-button\" ).hover(\n",
|
||||
" function() { $(this).addClass(\"ui-state-hover\");},\n",
|
||||
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
|
||||
" );\n",
|
||||
"\n",
|
||||
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
|
||||
" nav_element.append(status_bar);\n",
|
||||
" this.message = status_bar[0];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
|
||||
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
|
||||
" // which will in turn request a refresh of the image.\n",
|
||||
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.send_message = function(type, properties) {\n",
|
||||
" properties['type'] = type;\n",
|
||||
" properties['figure_id'] = this.id;\n",
|
||||
" this.ws.send(JSON.stringify(properties));\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.send_draw_message = function() {\n",
|
||||
" if (!this.waiting) {\n",
|
||||
" this.waiting = true;\n",
|
||||
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
||||
" var format_dropdown = fig.format_dropdown;\n",
|
||||
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
|
||||
" fig.ondownload(fig, format);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
|
||||
" var size = msg['size'];\n",
|
||||
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
|
||||
" fig._resize_canvas(size[0], size[1]);\n",
|
||||
" fig.send_message(\"refresh\", {});\n",
|
||||
" };\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
|
||||
" var x0 = msg['x0'] / mpl.ratio;\n",
|
||||
" var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
|
||||
" var x1 = msg['x1'] / mpl.ratio;\n",
|
||||
" var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
|
||||
" x0 = Math.floor(x0) + 0.5;\n",
|
||||
" y0 = Math.floor(y0) + 0.5;\n",
|
||||
" x1 = Math.floor(x1) + 0.5;\n",
|
||||
" y1 = Math.floor(y1) + 0.5;\n",
|
||||
" var min_x = Math.min(x0, x1);\n",
|
||||
" var min_y = Math.min(y0, y1);\n",
|
||||
" var width = Math.abs(x1 - x0);\n",
|
||||
" var height = Math.abs(y1 - y0);\n",
|
||||
"\n",
|
||||
" fig.rubberband_context.clearRect(\n",
|
||||
" 0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n",
|
||||
"\n",
|
||||
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
|
||||
" // Updates the figure title.\n",
|
||||
" fig.header.textContent = msg['label'];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
|
||||
" var cursor = msg['cursor'];\n",
|
||||
" switch(cursor)\n",
|
||||
" {\n",
|
||||
" case 0:\n",
|
||||
" cursor = 'pointer';\n",
|
||||
" break;\n",
|
||||
" case 1:\n",
|
||||
" cursor = 'default';\n",
|
||||
" break;\n",
|
||||
" case 2:\n",
|
||||
" cursor = 'crosshair';\n",
|
||||
" break;\n",
|
||||
" case 3:\n",
|
||||
" cursor = 'move';\n",
|
||||
" break;\n",
|
||||
" }\n",
|
||||
" fig.rubberband_canvas.style.cursor = cursor;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
|
||||
" fig.message.textContent = msg['message'];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
|
||||
" // Request the server to send over a new figure.\n",
|
||||
" fig.send_draw_message();\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
|
||||
" fig.image_mode = msg['mode'];\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
||||
" // Called whenever the canvas gets updated.\n",
|
||||
" this.send_message(\"ack\", {});\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"// A function to construct a web socket function for onmessage handling.\n",
|
||||
"// Called in the figure constructor.\n",
|
||||
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
|
||||
" return function socket_on_message(evt) {\n",
|
||||
" if (evt.data instanceof Blob) {\n",
|
||||
" /* FIXME: We get \"Resource interpreted as Image but\n",
|
||||
" * transferred with MIME type text/plain:\" errors on\n",
|
||||
" * Chrome. But how to set the MIME type? It doesn't seem\n",
|
||||
" * to be part of the websocket stream */\n",
|
||||
" evt.data.type = \"image/png\";\n",
|
||||
"\n",
|
||||
" /* Free the memory for the previous frames */\n",
|
||||
" if (fig.imageObj.src) {\n",
|
||||
" (window.URL || window.webkitURL).revokeObjectURL(\n",
|
||||
" fig.imageObj.src);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
|
||||
" evt.data);\n",
|
||||
" fig.updated_canvas_event();\n",
|
||||
" fig.waiting = false;\n",
|
||||
" return;\n",
|
||||
" }\n",
|
||||
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
|
||||
" fig.imageObj.src = evt.data;\n",
|
||||
" fig.updated_canvas_event();\n",
|
||||
" fig.waiting = false;\n",
|
||||
" return;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var msg = JSON.parse(evt.data);\n",
|
||||
" var msg_type = msg['type'];\n",
|
||||
"\n",
|
||||
" // Call the \"handle_{type}\" callback, which takes\n",
|
||||
" // the figure and JSON message as its only arguments.\n",
|
||||
" try {\n",
|
||||
" var callback = fig[\"handle_\" + msg_type];\n",
|
||||
" } catch (e) {\n",
|
||||
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
|
||||
" return;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" if (callback) {\n",
|
||||
" try {\n",
|
||||
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
|
||||
" callback(fig, msg);\n",
|
||||
" } catch (e) {\n",
|
||||
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" };\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
|
||||
"mpl.findpos = function(e) {\n",
|
||||
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
|
||||
" var targ;\n",
|
||||
" if (!e)\n",
|
||||
" e = window.event;\n",
|
||||
" if (e.target)\n",
|
||||
" targ = e.target;\n",
|
||||
" else if (e.srcElement)\n",
|
||||
" targ = e.srcElement;\n",
|
||||
" if (targ.nodeType == 3) // defeat Safari bug\n",
|
||||
" targ = targ.parentNode;\n",
|
||||
"\n",
|
||||
" // jQuery normalizes the pageX and pageY\n",
|
||||
" // pageX,Y are the mouse positions relative to the document\n",
|
||||
" // offset() returns the position of the element relative to the document\n",
|
||||
" var x = e.pageX - $(targ).offset().left;\n",
|
||||
" var y = e.pageY - $(targ).offset().top;\n",
|
||||
"\n",
|
||||
" return {\"x\": x, \"y\": y};\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"/*\n",
|
||||
" * return a copy of an object with only non-object keys\n",
|
||||
" * we need this to avoid circular references\n",
|
||||
" * http://stackoverflow.com/a/24161582/3208463\n",
|
||||
" */\n",
|
||||
"function simpleKeys (original) {\n",
|
||||
" return Object.keys(original).reduce(function (obj, key) {\n",
|
||||
" if (typeof original[key] !== 'object')\n",
|
||||
" obj[key] = original[key]\n",
|
||||
" return obj;\n",
|
||||
" }, {});\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
|
||||
" var canvas_pos = mpl.findpos(event)\n",
|
||||
"\n",
|
||||
" if (name === 'button_press')\n",
|
||||
" {\n",
|
||||
" this.canvas.focus();\n",
|
||||
" this.canvas_div.focus();\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var x = canvas_pos.x * mpl.ratio;\n",
|
||||
" var y = canvas_pos.y * mpl.ratio;\n",
|
||||
"\n",
|
||||
" this.send_message(name, {x: x, y: y, button: event.button,\n",
|
||||
" step: event.step,\n",
|
||||
" guiEvent: simpleKeys(event)});\n",
|
||||
"\n",
|
||||
" /* This prevents the web browser from automatically changing to\n",
|
||||
" * the text insertion cursor when the button is pressed. We want\n",
|
||||
" * to control all of the cursor setting manually through the\n",
|
||||
" * 'cursor' event from matplotlib */\n",
|
||||
" event.preventDefault();\n",
|
||||
" return false;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
||||
" // Handle any extra behaviour associated with a key event\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.key_event = function(event, name) {\n",
|
||||
"\n",
|
||||
" // Prevent repeat events\n",
|
||||
" if (name == 'key_press')\n",
|
||||
" {\n",
|
||||
" if (event.which === this._key)\n",
|
||||
" return;\n",
|
||||
" else\n",
|
||||
" this._key = event.which;\n",
|
||||
" }\n",
|
||||
" if (name == 'key_release')\n",
|
||||
" this._key = null;\n",
|
||||
"\n",
|
||||
" var value = '';\n",
|
||||
" if (event.ctrlKey && event.which != 17)\n",
|
||||
" value += \"ctrl+\";\n",
|
||||
" if (event.altKey && event.which != 18)\n",
|
||||
" value += \"alt+\";\n",
|
||||
" if (event.shiftKey && event.which != 16)\n",
|
||||
" value += \"shift+\";\n",
|
||||
"\n",
|
||||
" value += 'k';\n",
|
||||
" value += event.which.toString();\n",
|
||||
"\n",
|
||||
" this._key_event_extra(event, name);\n",
|
||||
"\n",
|
||||
" this.send_message(name, {key: value,\n",
|
||||
" guiEvent: simpleKeys(event)});\n",
|
||||
" return false;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
|
||||
" if (name == 'download') {\n",
|
||||
" this.handle_save(this, null);\n",
|
||||
" } else {\n",
|
||||
" this.send_message(\"toolbar_button\", {name: name});\n",
|
||||
" }\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
|
||||
" this.message.textContent = tooltip;\n",
|
||||
"};\n",
|
||||
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
|
||||
"\n",
|
||||
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
|
||||
"\n",
|
||||
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
|
||||
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
|
||||
" // object with the appropriate methods. Currently this is a non binary\n",
|
||||
" // socket, so there is still some room for performance tuning.\n",
|
||||
" var ws = {};\n",
|
||||
"\n",
|
||||
" ws.close = function() {\n",
|
||||
" comm.close()\n",
|
||||
" };\n",
|
||||
" ws.send = function(m) {\n",
|
||||
" //console.log('sending', m);\n",
|
||||
" comm.send(m);\n",
|
||||
" };\n",
|
||||
" // Register the callback with on_msg.\n",
|
||||
" comm.on_msg(function(msg) {\n",
|
||||
" //console.log('receiving', msg['content']['data'], msg);\n",
|
||||
" // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
|
||||
" ws.onmessage(msg['content']['data'])\n",
|
||||
" });\n",
|
||||
" return ws;\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.mpl_figure_comm = function(comm, msg) {\n",
|
||||
" // This is the function which gets called when the mpl process\n",
|
||||
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
|
||||
"\n",
|
||||
" var id = msg.content.data.id;\n",
|
||||
" // Get hold of the div created by the display call when the Comm\n",
|
||||
" // socket was opened in Python.\n",
|
||||
" var element = $(\"#\" + id);\n",
|
||||
" var ws_proxy = comm_websocket_adapter(comm)\n",
|
||||
"\n",
|
||||
" function ondownload(figure, format) {\n",
|
||||
" window.open(figure.imageObj.src);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var fig = new mpl.figure(id, ws_proxy,\n",
|
||||
" ondownload,\n",
|
||||
" element.get(0));\n",
|
||||
"\n",
|
||||
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
|
||||
" // web socket which is closed, not our websocket->open comm proxy.\n",
|
||||
" ws_proxy.onopen();\n",
|
||||
"\n",
|
||||
" fig.parent_element = element.get(0);\n",
|
||||
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
|
||||
" if (!fig.cell_info) {\n",
|
||||
" console.error(\"Failed to find cell for figure\", id, fig);\n",
|
||||
" return;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" var output_index = fig.cell_info[2]\n",
|
||||
" var cell = fig.cell_info[0];\n",
|
||||
"\n",
|
||||
"};\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
|
||||
" var width = fig.canvas.width/mpl.ratio\n",
|
||||
" fig.root.unbind('remove')\n",
|
||||
"\n",
|
||||
" // Update the output cell to use the data from the current canvas.\n",
|
||||
" fig.push_to_output();\n",
|
||||
" var dataURL = fig.canvas.toDataURL();\n",
|
||||
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
|
||||
" // the notebook keyboard shortcuts fail.\n",
|
||||
" IPython.keyboard_manager.enable()\n",
|
||||
" $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
|
||||
" fig.close_ws(fig, msg);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
|
||||
" fig.send_message('closing', msg);\n",
|
||||
" // fig.ws.close()\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
|
||||
" // Turn the data on the canvas into data in the output cell.\n",
|
||||
" var width = this.canvas.width/mpl.ratio\n",
|
||||
" var dataURL = this.canvas.toDataURL();\n",
|
||||
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.updated_canvas_event = function() {\n",
|
||||
" // Tell IPython that the notebook contents must change.\n",
|
||||
" IPython.notebook.set_dirty(true);\n",
|
||||
" this.send_message(\"ack\", {});\n",
|
||||
" var fig = this;\n",
|
||||
" // Wait a second, then push the new image to the DOM so\n",
|
||||
" // that it is saved nicely (might be nice to debounce this).\n",
|
||||
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._init_toolbar = function() {\n",
|
||||
" var fig = this;\n",
|
||||
"\n",
|
||||
" var nav_element = $('<div/>');\n",
|
||||
" nav_element.attr('style', 'width: 100%');\n",
|
||||
" this.root.append(nav_element);\n",
|
||||
"\n",
|
||||
" // Define a callback function for later on.\n",
|
||||
" function toolbar_event(event) {\n",
|
||||
" return fig.toolbar_button_onclick(event['data']);\n",
|
||||
" }\n",
|
||||
" function toolbar_mouse_event(event) {\n",
|
||||
" return fig.toolbar_button_onmouseover(event['data']);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" for(var toolbar_ind in mpl.toolbar_items){\n",
|
||||
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
|
||||
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
|
||||
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
|
||||
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
|
||||
"\n",
|
||||
" if (!name) { continue; };\n",
|
||||
"\n",
|
||||
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
|
||||
" button.click(method_name, toolbar_event);\n",
|
||||
" button.mouseover(tooltip, toolbar_mouse_event);\n",
|
||||
" nav_element.append(button);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" // Add the status bar.\n",
|
||||
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
|
||||
" nav_element.append(status_bar);\n",
|
||||
" this.message = status_bar[0];\n",
|
||||
"\n",
|
||||
" // Add the close button to the window.\n",
|
||||
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
|
||||
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
|
||||
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
|
||||
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
|
||||
" buttongrp.append(button);\n",
|
||||
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
|
||||
" titlebar.prepend(buttongrp);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._root_extra_style = function(el){\n",
|
||||
" var fig = this\n",
|
||||
" el.on(\"remove\", function(){\n",
|
||||
"\tfig.close_ws(fig, {});\n",
|
||||
" });\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
|
||||
" // this is important to make the div 'focusable\n",
|
||||
" el.attr('tabindex', 0)\n",
|
||||
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
|
||||
" // off when our div gets focus\n",
|
||||
"\n",
|
||||
" // location in version 3\n",
|
||||
" if (IPython.notebook.keyboard_manager) {\n",
|
||||
" IPython.notebook.keyboard_manager.register_events(el);\n",
|
||||
" }\n",
|
||||
" else {\n",
|
||||
" // location in version 2\n",
|
||||
" IPython.keyboard_manager.register_events(el);\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
|
||||
" var manager = IPython.notebook.keyboard_manager;\n",
|
||||
" if (!manager)\n",
|
||||
" manager = IPython.keyboard_manager;\n",
|
||||
"\n",
|
||||
" // Check for shift+enter\n",
|
||||
" if (event.shiftKey && event.which == 13) {\n",
|
||||
" this.canvas_div.blur();\n",
|
||||
" // select the cell after this one\n",
|
||||
" var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
|
||||
" IPython.notebook.select(index + 1);\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
|
||||
" fig.ondownload(fig, null);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mpl.find_output_cell = function(html_output) {\n",
|
||||
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
|
||||
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
|
||||
" // IPython event is triggered only after the cells have been serialised, which for\n",
|
||||
" // our purposes (turning an active figure into a static one), is too late.\n",
|
||||
" var cells = IPython.notebook.get_cells();\n",
|
||||
" var ncells = cells.length;\n",
|
||||
" for (var i=0; i<ncells; i++) {\n",
|
||||
" var cell = cells[i];\n",
|
||||
" if (cell.cell_type === 'code'){\n",
|
||||
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
|
||||
" var data = cell.output_area.outputs[j];\n",
|
||||
" if (data.data) {\n",
|
||||
" // IPython >= 3 moved mimebundle to data attribute of output\n",
|
||||
" data = data.data;\n",
|
||||
" }\n",
|
||||
" if (data['text/html'] == html_output) {\n",
|
||||
" return [cell, data, j];\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"// Register the function which deals with the matplotlib target/channel.\n",
|
||||
"// The kernel may be null if the page has been refreshed.\n",
|
||||
"if (IPython.notebook.kernel != null) {\n",
|
||||
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
|
||||
"}\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Javascript object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div id='215b26ff-80fe-45a3-bb52-9351ea1b2ebb'></div>"
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.HTML object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(0, 100)"
|
||||
]
|
||||
},
|
||||
"execution_count": 131,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def h(p,src,dest):\n",
|
||||
" c = (((src-dest)**2).sum())**0.5\n",
|
||||
" a = (((dest-p)**2).sum())**0.5\n",
|
||||
" b = (((src-p)**2).sum())**0.5\n",
|
||||
" ld = (((a+b+c)*(-a+b+c)*(a-b+c)*(a+b-c))**0.5)/(c*2.0)\n",
|
||||
" return a-b\n",
|
||||
"S=np.array([10,10])\n",
|
||||
"D=np.array([20,25])\n",
|
||||
"\n",
|
||||
"Rx=range(100)\n",
|
||||
"Ry=range(100)\n",
|
||||
"grid=np.zeros((len(Rx),len(Ry)))\n",
|
||||
"for px,x in enumerate(Rx):\n",
|
||||
" for py,y in enumerate(Ry):\n",
|
||||
" grid[px,py]=h(np.array([x,y]),S,D)\n",
|
||||
"imshow(grid,cmap='coolwarm_r',origin='lower')\n",
|
||||
"colorbar()\n",
|
||||
"\n",
|
||||
"scatter(*S,color='green')\n",
|
||||
"scatter(*D,color='red')\n",
|
||||
"plt.xlim(0,100)\n",
|
||||
"plt.ylim(0,100)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 38,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Object `np.magnitude` not found.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"np."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.1 64-bit ('anaconda': conda)",
|
||||
"language": "python",
|
||||
"name": "python38164bitanacondaconda2a51168e890d45bd836f654eb2ae46f7"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 368 KiB |
|
@ -0,0 +1,154 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import svgwrite
|
||||
import svgpathtools
|
||||
import random
|
||||
import tempfile
|
||||
import os
|
||||
from math import sin, cos, pi
|
||||
import tsp as m_tsp
|
||||
|
||||
|
||||
def dist(p1, p2):
|
||||
return dist2(p1, p2) ** 0.5
|
||||
|
||||
|
||||
def dist2(p1, p2):
|
||||
diff = 0
|
||||
for a, b in zip(p1, p2):
|
||||
diff += (a - b) ** 2
|
||||
return diff
|
||||
|
||||
|
||||
def tsp(points):
|
||||
res = []
|
||||
for idx in m_tsp.tsp(points)[1]:
|
||||
res.append(points[idx])
|
||||
return res
|
||||
|
||||
|
||||
def make_points(n, size, min_dist=0):
|
||||
min_dist *= min_dist
|
||||
points = []
|
||||
while len(points) < n:
|
||||
px, py = random.random(), random.random()
|
||||
px *= size / 2
|
||||
py *= size / 2
|
||||
valid = True
|
||||
for p in points:
|
||||
if dist2(p, (px, py)) < min_dist:
|
||||
valid = False
|
||||
break
|
||||
if valid:
|
||||
points.append((px, py))
|
||||
print("{}/{}".format(len(points), n))
|
||||
return points
|
||||
|
||||
|
||||
def ring_step(v):
|
||||
return 10 + v * 10
|
||||
|
||||
|
||||
def generate(seed, name=None, small=False):
|
||||
sd = 1
|
||||
if small:
|
||||
sd = 2
|
||||
random.seed(seed)
|
||||
w = 2
|
||||
max_rings = 3
|
||||
num_points = 5
|
||||
min_dist = 10 + 10 + 20 * (max_rings + 1)
|
||||
base_r = 10
|
||||
size = 1000
|
||||
if name is None:
|
||||
name = seed
|
||||
out_path = "out/{}.svg".format(name)
|
||||
dwg = svgwrite.Drawing(filename=out_path)
|
||||
dwg.defs.add(dwg.style(".background { fill: #222 }"))
|
||||
print("Generating points...")
|
||||
color = "#eee"
|
||||
pos = make_points(num_points, size, min_dist=min_dist)
|
||||
print("TSP...")
|
||||
pos = tsp(pos)
|
||||
for (x1, y1), (x2, y2) in zip(pos, pos[1:]):
|
||||
if small:
|
||||
x1 /= sd
|
||||
x2 /= sd
|
||||
y1 /= sd
|
||||
y2 /= sd
|
||||
dwg.add(dwg.line((x1, y1), (x2, y2), stroke_width=w, stroke=color))
|
||||
|
||||
for (px, py) in pos:
|
||||
base_r = 3
|
||||
if small:
|
||||
base_r = 5
|
||||
px /= sd
|
||||
py /= sd
|
||||
if random.random() > 0.8:
|
||||
dwg.add(
|
||||
dwg.circle(
|
||||
(px, py),
|
||||
r=base_r + random.random() * base_r,
|
||||
stroke_width=w,
|
||||
stroke="#0ae",
|
||||
)
|
||||
).fill("#0ae")
|
||||
else:
|
||||
dwg.add(
|
||||
dwg.circle(
|
||||
(px, py),
|
||||
r=base_r + random.random() * base_r,
|
||||
stroke_width=w,
|
||||
stroke=color,
|
||||
)
|
||||
).fill(color)
|
||||
r = base_r
|
||||
for _ in range(random.randint(1, max_rings)):
|
||||
if small:
|
||||
random.random()
|
||||
random.random()
|
||||
random.random()
|
||||
random.random()
|
||||
continue
|
||||
r += ring_step(random.random())
|
||||
ring_col = color
|
||||
if random.random() > 0.75:
|
||||
ring_col = "#ea0"
|
||||
circ = dwg.add(dwg.circle((px, py), r=r, stroke_width=w, stroke=ring_col))
|
||||
circ.fill(color, opacity=0)
|
||||
d = random.random() * pi * 2
|
||||
dx = cos(d)
|
||||
dy = sin(d)
|
||||
m = random.random()
|
||||
moon = dwg.add(
|
||||
dwg.circle(
|
||||
(px + dx * r, py + dy * r),
|
||||
r=2 + 2 * m,
|
||||
stroke_width=w,
|
||||
stroke=ring_col,
|
||||
)
|
||||
)
|
||||
moon.fill(ring_col)
|
||||
dwg.fit()
|
||||
path = tempfile.TemporaryDirectory()
|
||||
filename = os.path.join(path.name, "out.svg")
|
||||
dwg.saveas(filename)
|
||||
paths, attrs = svgpathtools.svg2paths(filename)
|
||||
bbox = [float("inf"), float("-inf"), float("inf"), float("-inf")]
|
||||
for path in paths:
|
||||
path_bbox = path.bbox()
|
||||
bbox[0] = min(bbox[0], path_bbox[0]) # xmin
|
||||
bbox[1] = max(bbox[1], path_bbox[1]) # xmax
|
||||
bbox[2] = min(bbox[2], path_bbox[2]) # ymin
|
||||
bbox[3] = max(bbox[3], path_bbox[3]) # ymax
|
||||
px = bbox[0]
|
||||
sx = (bbox[1] - bbox[0])
|
||||
py = bbox[2]
|
||||
sy = (bbox[3] - bbox[2])
|
||||
dwg.add(dwg.rect(x=px, y=px, size=(sx, sy), class_="background"))
|
||||
dwg.elements.insert(1, dwg.elements.pop(-1))
|
||||
dwg.saveas(out_path)
|
||||
|
||||
|
||||
seed = -5
|
||||
generate(seed, "icon_1", small=False)
|
||||
generate(seed, "icon_1_small", small=True)
|
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="100%" preserveAspectRatio="xMidYMid meet" version="1.1" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"><![CDATA[.background { fill: #222 }]]></style></defs><rect class="background" height="517.2983884727208" width="530.2849722660202" x="0" y="0" /><line stroke="#eee" stroke-width="2" x1="311.450847444851" x2="14.502614141807369" y1="370.89349463036467" y2="232.81132718905266" /><line stroke="#eee" stroke-width="2" x1="14.502614141807369" x2="450.45024587531134" y1="232.81132718905266" y2="56.60298232657218" /><line stroke="#eee" stroke-width="2" x1="450.45024587531134" x2="471.67835849915684" y1="56.60298232657218" y2="324.4872765684621" /><line stroke="#eee" stroke-width="2" x1="471.67835849915684" x2="397.5967827828483" y1="324.4872765684621" y2="471.2251418885252" /><circle cx="311.450847444851" cy="370.89349463036467" fill="#eee" r="3.739718497859491" stroke="#eee" stroke-width="2" /><circle cx="311.450847444851" cy="370.89349463036467" fill="#eee" fill-opacity="0" r="14.019744021739154" stroke="#eee" stroke-width="2" /><circle cx="309.7972050350797" cy="356.9716165524724" fill="#eee" r="2.816302109949028" stroke="#eee" stroke-width="2" /><circle cx="311.450847444851" cy="370.89349463036467" fill="#eee" fill-opacity="0" r="25.84050065951694" stroke="#ea0" stroke-width="2" /><circle cx="291.5987578747967" cy="387.4351394711004" fill="#ea0" r="3.5238508551887717" stroke="#ea0" stroke-width="2" /><circle cx="311.450847444851" cy="370.89349463036467" fill="#eee" fill-opacity="0" r="36.559818016667215" stroke="#eee" stroke-width="2" /><circle cx="277.06522171012307" cy="383.3131981792842" fill="#eee" r="2.2644817084338493" stroke="#eee" stroke-width="2" /><circle cx="14.502614141807369" cy="232.81132718905266" fill="#0ae" r="3.015939411732324" stroke="#0ae" stroke-width="2" /><circle cx="14.502614141807369" cy="232.81132718905266" fill="#eee" fill-opacity="0" r="22.601271002174997" stroke="#eee" stroke-width="2" /><circle cx="25.825648341003884" cy="252.3716530410824" fill="#eee" r="2.6273027354699074" stroke="#eee" stroke-width="2" /><circle cx="450.45024587531134" cy="56.60298232657218" fill="#eee" r="5.628356799732829" stroke="#eee" stroke-width="2" /><circle cx="450.45024587531134" cy="56.60298232657218" fill="#eee" fill-opacity="0" r="15.047795145337929" stroke="#ea0" stroke-width="2" /><circle cx="444.96726006047606" cy="42.5896670410885" fill="#ea0" r="3.933128624634391" stroke="#ea0" stroke-width="2" /><circle cx="450.45024587531134" cy="56.60298232657218" fill="#eee" fill-opacity="0" r="33.98521192110272" stroke="#eee" stroke-width="2" /><circle cx="428.59208164282967" cy="82.62634271119818" fill="#eee" r="2.331912114259491" stroke="#eee" stroke-width="2" /><circle cx="450.45024587531134" cy="56.60298232657218" fill="#eee" fill-opacity="0" r="45.44223101650954" stroke="#eee" stroke-width="2" /><circle cx="436.0392802753398" cy="99.69962291762357" fill="#eee" r="3.2062199948153087" stroke="#eee" stroke-width="2" /><circle cx="471.67835849915684" cy="324.4872765684621" fill="#eee" r="5.033802748643073" stroke="#eee" stroke-width="2" /><circle cx="471.67835849915684" cy="324.4872765684621" fill="#eee" fill-opacity="0" r="13.660224505510772" stroke="#eee" stroke-width="2" /><circle cx="466.96528173978857" cy="337.3086899461385" fill="#eee" r="3.3928396152823135" stroke="#eee" stroke-width="2" /><circle cx="471.67835849915684" cy="324.4872765684621" fill="#eee" fill-opacity="0" r="25.507956906495714" stroke="#eee" stroke-width="2" /><circle cx="483.09849262326327" cy="347.2959679410741" fill="#eee" r="2.5123636718854243" stroke="#eee" stroke-width="2" /><circle cx="397.5967827828483" cy="471.2251418885252" fill="#0ae" r="5.847661430011579" stroke="#0ae" stroke-width="2" /><circle cx="397.5967827828483" cy="471.2251418885252" fill="#eee" fill-opacity="0" r="21.448808893881296" stroke="#eee" stroke-width="2" /><circle cx="402.63513027636975" cy="450.3764858766675" fill="#eee" r="2.7323689516837213" stroke="#eee" stroke-width="2" /><circle cx="397.5967827828483" cy="471.2251418885252" fill="#eee" fill-opacity="0" r="37.23399718445004" stroke="#eee" stroke-width="2" /><circle cx="433.2375240152304" cy="482.0004892489591" fill="#eee" r="2.3618389759020957" stroke="#eee" stroke-width="2" /></svg>
|
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<svg baseProfile="full" height="100%" preserveAspectRatio="xMidYMid meet" version="1.1" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"><![CDATA[.background { fill: #222 }]]></style></defs><rect class="background" height="246.4377768305505" width="267.0041091126337" x="0" y="0" /><line stroke="#eee" stroke-width="2" x1="155.7254237224255" x2="7.251307070903684" y1="185.44674731518234" y2="116.40566359452633" /><line stroke="#eee" stroke-width="2" x1="7.251307070903684" x2="225.22512293765567" y1="116.40566359452633" y2="28.30149116328609" /><line stroke="#eee" stroke-width="2" x1="225.22512293765567" x2="235.83917924957842" y1="28.30149116328609" y2="162.24363828423105" /><line stroke="#eee" stroke-width="2" x1="235.83917924957842" x2="198.79839139142416" y1="162.24363828423105" y2="235.6125709442626" /><circle cx="155.7254237224255" cy="185.44674731518234" fill="#eee" r="6.232864163099151" stroke="#eee" stroke-width="2" /><circle cx="7.251307070903684" cy="116.40566359452633" fill="#0ae" r="5.026565686220541" stroke="#0ae" stroke-width="2" /><circle cx="225.22512293765567" cy="28.30149116328609" fill="#eee" r="9.380594666221384" stroke="#eee" stroke-width="2" /><circle cx="235.83917924957842" cy="162.24363828423105" fill="#eee" r="8.389671247738455" stroke="#eee" stroke-width="2" /><circle cx="198.79839139142416" cy="235.6125709442626" fill="#0ae" r="9.74610238335263" stroke="#0ae" stroke-width="2" /></svg>
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,132 @@
|
|||
import dearpygui.core as dpg
|
||||
import dearpygui.simple as sdpg
|
||||
import uuid
|
||||
import logging
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
|
||||
def setup_logging(loglevel="INFO"):
|
||||
import coloredlogs
|
||||
from datetime import timedelta
|
||||
|
||||
coloredlogs.DEFAULT_FIELD_STYLES["delta"] = {"color": "green"}
|
||||
coloredlogs.DEFAULT_FIELD_STYLES["levelname"] = {"color": "yellow"}
|
||||
|
||||
class DeltaTimeFormatter(coloredlogs.ColoredFormatter):
|
||||
def format(self, record):
|
||||
seconds = record.relativeCreated / 1000
|
||||
duration = timedelta(seconds=seconds)
|
||||
record.delta = str(duration)
|
||||
return super().format(record)
|
||||
|
||||
coloredlogs.ColoredFormatter = DeltaTimeFormatter
|
||||
logfmt = " | ".join(
|
||||
["[%(delta)s] %(levelname)s", "%(name)s:%(pathname)s:%(lineno)s", "%(message)s"]
|
||||
)
|
||||
numeric_level = getattr(logging, loglevel.upper(), None)
|
||||
if not isinstance(numeric_level, int):
|
||||
raise ValueError("Invalid log level: %s" % loglevel)
|
||||
coloredlogs.install(level=numeric_level, fmt=logfmt)
|
||||
|
||||
|
||||
setup_logging()
|
||||
|
||||
def resolve_systems(stars, *args):
|
||||
import _ed_lrr
|
||||
|
||||
r = _ed_lrr.PyRouter(None)
|
||||
r.load(stars)
|
||||
return r.resolve_systems(*args)
|
||||
|
||||
|
||||
class EdLrrGui:
|
||||
def __init__(self):
|
||||
self.pool = ProcessPoolExecutor(1)
|
||||
self.systems = []
|
||||
self.__resolve_job = None
|
||||
self.logger = logging.getLogger("GUI")
|
||||
|
||||
def __set_state(self, state):
|
||||
print(state)
|
||||
dpg.set_value("ed_lrr_state", state)
|
||||
|
||||
def __resolve_done(self, fut):
|
||||
data = fut.result()
|
||||
self.logger.info(f"Gor resolver data back: {data}")
|
||||
self.__resolve_job = None
|
||||
for n, system in enumerate(self.systems):
|
||||
if system["name"] in data:
|
||||
self.systems[n] = data[system["name"]]
|
||||
self.systems[n]["resolved"] = True
|
||||
|
||||
def __resolve_systems(self, sender, data):
|
||||
names = []
|
||||
for system in self.systems:
|
||||
if not system.get("resolver", False):
|
||||
names.append(system["name"])
|
||||
if self.__resolve_job is None:
|
||||
job = self.pool.submit(resolve_systems, "../stars.csv", *names)
|
||||
self.logger.info(f"Resolving {len(names)} systems...")
|
||||
self.__resolve_job = job
|
||||
self.__resolve_job.add_done_callback(self.__resolve_done)
|
||||
|
||||
def __render(self, sender, data):
|
||||
dpg.clear_table("Systems")
|
||||
for system in self.systems:
|
||||
row = [
|
||||
system.get("id", ""),
|
||||
system["name"],
|
||||
", ".join(map(str, system.get("pos", []))),
|
||||
]
|
||||
dpg.add_row("Systems", row)
|
||||
|
||||
def __add_system(self, sender, data):
|
||||
system_name = dpg.get_value("sys-name")
|
||||
self.systems.append({"name": system_name})
|
||||
dpg.set_value("sys-name", "")
|
||||
|
||||
def __select_system(self, sender, data):
|
||||
system_row = dpg.get_table_selections("Systems")
|
||||
idx = system_row[0][0]
|
||||
dpg.add_data("selected-system-index", idx)
|
||||
|
||||
def __remove_system(self, sender, data):
|
||||
if self.systems:
|
||||
system_index = dpg.get_data("selected-system-index")
|
||||
self.systems.pop(system_index)
|
||||
|
||||
def __clear_systems(self, sender, data):
|
||||
self.systems = []
|
||||
|
||||
def show(self):
|
||||
with sdpg.window("Main Window"):
|
||||
dpg.set_main_window_size(550, 550)
|
||||
dpg.set_main_window_resizable(False)
|
||||
dpg.set_main_window_title("Elite: Dangerous Long Range Router")
|
||||
|
||||
dpg.add_text("ED_LRR")
|
||||
dpg.add_separator()
|
||||
|
||||
dpg.add_input_text("System name", source="sys-name")
|
||||
dpg.add_button("Add", callback=self.__add_system)
|
||||
dpg.add_separator()
|
||||
|
||||
dpg.add_table(
|
||||
"Systems",
|
||||
["ID", "Name", "Position"],
|
||||
height=200,
|
||||
callback=self.__select_system,
|
||||
)
|
||||
dpg.add_separator()
|
||||
dpg.add_button("Remove", callback=self.__remove_system)
|
||||
dpg.add_button("Clear", callback=self.__clear_systems)
|
||||
dpg.add_button("Resolve", callback=self.__resolve_systems)
|
||||
|
||||
# Render Callback and Start gui
|
||||
dpg.set_render_callback(self.__render)
|
||||
dpg.start_dearpygui(primary_window="Main Window")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
edlrr_gui = EdLrrGui()
|
||||
edlrr_gui.show()
|
|
@ -0,0 +1,25 @@
|
|||
[Setup]
|
||||
AppName = "ED_LRR"
|
||||
AppVersion ="0.1.0"
|
||||
; WizardStyle = modern
|
||||
DefaultDirName = {autopf}\ED_LRR
|
||||
DefaultGroupName=ED_LRR
|
||||
Compression = lzma2/ultra
|
||||
SolidCompression = true
|
||||
InternalCompressLevel = ultra
|
||||
OutputBaseFilename="ED_LRR Setup"
|
||||
ChangesEnvironment = true
|
||||
|
||||
[Files]
|
||||
Source: "{#SourceFolder}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\ED_LRR"; Filename: "{app}\ED_LRR.exe"; WorkingDir: "{app}"
|
||||
Name: "{group}\Uninstall ED_LRR"; Filename: "{uninstallexe}"
|
||||
|
||||
[Tasks]
|
||||
;Name: modifypath; Description: Add application directory to PATH; Flags: unchecked
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\ED_LRR.exe"; Parameters: "download"; StatusMsg: "Downloading EDSM dumps..."; Description: "Download EDSM dumps"; Flags: postinstall
|
||||
Filename: "{app}\ED_LRR.exe"; Description: "Launch ED_LRR"; Flags: postinstall nowait skipifsilent unchecked
|
|
@ -0,0 +1 @@
|
|||
{"route": [], "dt": 292.124997}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,107 @@
|
|||
import nox
|
||||
from nox.logger import logger
|
||||
import os
|
||||
import shutil
|
||||
|
||||
to_append = []
|
||||
|
||||
path = os.environ.get("PATH", "").split(os.pathsep)
|
||||
while True:
|
||||
python = shutil.which("python", path=os.pathsep.join(path))
|
||||
if python is None:
|
||||
break
|
||||
python = os.path.dirname(python)
|
||||
for elem in path:
|
||||
if elem.startswith(python):
|
||||
path.remove(elem)
|
||||
to_append.append(elem)
|
||||
|
||||
path += to_append
|
||||
|
||||
path = os.pathsep.join(path)
|
||||
os.environ["PATH"] = path
|
||||
|
||||
versions = ["3.{}".format(s) for s in [6, 7, 8]]
|
||||
|
||||
versions += ["3"]
|
||||
|
||||
nox.options.keywords = "test"
|
||||
|
||||
|
||||
@nox.session(python=versions,venv_backend="conda")
|
||||
def devenv(session):
|
||||
"""Set up development environment"""
|
||||
global path
|
||||
location = os.path.abspath(session._runner.venv.location_name)
|
||||
session.env["PATH"] = os.pathsep.join([location, path, location])
|
||||
session.conda_install("pycrypto", "ujson")
|
||||
session.install("--no-cache-dir", "-e",".")
|
||||
logger.warning(f'Devenv set up, now run "conda activate {location}"')
|
||||
|
||||
|
||||
@nox.session(python=versions, venv_backend="conda")
|
||||
def test(session):
|
||||
"""Run the test suite."""
|
||||
global path
|
||||
location = os.path.abspath(session._runner.venv.location_name)
|
||||
python = os.path.join(location, "python.exe")
|
||||
# friendly_name = session._runner.friendly_name
|
||||
cargo_args = ["--manifest-path", "./rust/Cargo.toml"]
|
||||
session.env["PATH"] = os.pathsep.join([location, path, location])
|
||||
# ================================================
|
||||
session.run(python, "-VV", external=True)
|
||||
session.run("cargo", "clean", *cargo_args, external=True)
|
||||
session.run("cargo", "clean", *(cargo_args + ["--release"]), external=True)
|
||||
session.run("cargo", "test", *cargo_args, external=True)
|
||||
session.conda_install("pycrypto", "ujson")
|
||||
session.install("--no-cache-dir", "setuptools_rust")
|
||||
session.install("--no-cache-dir", ".[all]")
|
||||
session.run("py.test", "-v", *(session.posargs or []))
|
||||
if session.python:
|
||||
session.notify(f"build-{session.python}")
|
||||
|
||||
|
||||
@nox.session(python=versions, venv_backend="conda")
|
||||
def build(session):
|
||||
"Build installer and zip"
|
||||
global path
|
||||
location = session._runner.venv.location_name
|
||||
python = os.path.join(location, "python.exe")
|
||||
location = os.path.abspath(location)
|
||||
cargo_args = ["--manifest-path", "./rust/Cargo.toml"]
|
||||
session.env["PATH"] = os.pathsep.join([location, path, location])
|
||||
# ================================================
|
||||
session.run(python, "-VV", external=True)
|
||||
session.run("cargo", "clean", *cargo_args, external=True)
|
||||
session.run("cargo", "clean", *(cargo_args + ["--release"]), external=True)
|
||||
session.conda_install("pycrypto", "ujson")
|
||||
session.install("--no-cache-dir", ".[gui,web,build]")
|
||||
session.run(
|
||||
"pyinstaller",
|
||||
"-y",
|
||||
"--console",
|
||||
"--noupx",
|
||||
"--clean",
|
||||
"--hidden-import",
|
||||
"pkg_resources.py2_warn",
|
||||
"--name",
|
||||
f"ED_LRR_{session.python}",
|
||||
"--specpath",
|
||||
"build",
|
||||
"ed_lrr_gui/__main__.py",
|
||||
external=True,
|
||||
silent=True,
|
||||
)
|
||||
if session.python:
|
||||
source_path = os.path.abspath(f"./dist/ED_LRR_{session.python}")
|
||||
else:
|
||||
source_path = os.path.abspath(f"./dist/ED_LRR")
|
||||
source_path = source_path.strip(os.sep)
|
||||
session.run(
|
||||
"iscc",
|
||||
f'/FED_LRR_{session.python}',
|
||||
f'/DSourceFolder={source_path}',
|
||||
"installer/ED_LRR.iss",
|
||||
external=True,
|
||||
silent=True,
|
||||
)
|
|
@ -0,0 +1,64 @@
|
|||
// cargo-deps: crossbeam-channel="0.5.1"
|
||||
extern crate crossbeam_channel;
|
||||
use std::io::{BufReader,BufRead, BufWriter,Write};
|
||||
use std::fs::File;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use crossbeam_channel::unbounded;
|
||||
use crossbeam_channel::Receiver;
|
||||
use std::thread;
|
||||
fn process(rx: Receiver<String>) -> (HashMap<usize,usize>,HashMap<usize,usize>) {
|
||||
let mut hm_min: HashMap<usize,usize> = HashMap::new();
|
||||
let mut hm_max: HashMap<usize,usize> = HashMap::new();
|
||||
while let Ok(line) = rx.recv() {
|
||||
let line: Vec<usize> = line.split(",").map(|s| s.parse::<usize>().unwrap()).collect();
|
||||
let id=line[0];
|
||||
let mut depth=line[1];
|
||||
hm_min.entry(id).and_modify(|e| {
|
||||
*e=*e.min(&mut depth);
|
||||
}).or_insert(depth);
|
||||
hm_max.entry(id).and_modify(|e| {
|
||||
*e=*e.max(&mut depth);
|
||||
}).or_insert(depth);
|
||||
}
|
||||
(hm_min,hm_max)
|
||||
}
|
||||
fn main() {
|
||||
let (tx,rx) = unbounded();
|
||||
let mut threads: Vec<_> = (0..8).map(|_| {
|
||||
let rx=rx.clone();
|
||||
thread::spawn(|| {
|
||||
process(rx)
|
||||
})
|
||||
}).collect();
|
||||
let fh = BufReader::new(File::open(std::env::args().nth(1).unwrap()).unwrap());
|
||||
fh.lines().flatten().for_each(|line| {
|
||||
tx.send(line).unwrap();
|
||||
});
|
||||
drop(tx);
|
||||
let mut hm_min: HashMap<usize,usize> = HashMap::new();
|
||||
let mut hm_max: HashMap<usize,usize> = HashMap::new();
|
||||
for thread in threads.drain(..) {
|
||||
let (min,max)=thread.join().unwrap();
|
||||
println!("Thread: {:?}",(min.len(),max.len()));
|
||||
for (id,depth) in min {
|
||||
hm_min.entry(id).and_modify(|e| {
|
||||
*e=(*e).min(depth);
|
||||
}).or_insert(depth);
|
||||
}
|
||||
for (id,depth) in max {
|
||||
hm_max.entry(id).and_modify(|e| {
|
||||
*e=(*e).max(depth);
|
||||
}).or_insert(depth);
|
||||
}
|
||||
}
|
||||
println!("Final: {:?}",(hm_min.len(),hm_max.len()));
|
||||
let mut fh_max = BufWriter::new(File::create("route_log_max.txt").unwrap());
|
||||
for (id,depth) in hm_max {
|
||||
write!(fh_max,"{},{}\n",id,depth).unwrap();
|
||||
}
|
||||
let mut fh_min = BufWriter::new(File::create("route_log_min.txt").unwrap());
|
||||
for (id,depth) in hm_min {
|
||||
write!(fh_min,"{},{}\n",id,depth).unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
[build-system]
|
||||
requires = ["setuptools", "wheel","setuptools_rust"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.poetry]
|
||||
description = "Elite: Dangerous Long Range Route Plotter"
|
||||
name="ed_lrr"
|
||||
version="0.2.0"
|
||||
authors = ["Daniel Seiller <earthnuker@gmail.com>"]
|
|
@ -0,0 +1,79 @@
|
|||
import sys
|
||||
import datashader as ds
|
||||
import pandas as pd
|
||||
import datashader.transfer_functions as tf
|
||||
from datashader.utils import export_image
|
||||
from datashader.transfer_functions import set_background
|
||||
import subprocess as SP
|
||||
import os
|
||||
import itertools as ITT
|
||||
from glob import glob
|
||||
|
||||
print("Loading stars...")
|
||||
stars = pd.read_csv("stars.csv", usecols=["id", "x", "z", "mult"], index_col=0)
|
||||
stars.loc[stars.mult == 1.0, "mult"] = float("nan")
|
||||
|
||||
steps = int(sys.argv[1])
|
||||
size = 1080
|
||||
mode = "eq_hist"
|
||||
|
||||
cvs = ds.Canvas(plot_width=size, plot_height=size)
|
||||
|
||||
print("Plotting density")
|
||||
density_agg = cvs.points(stars, "x", "z")
|
||||
density = tf.shade(density_agg, cmap=["black", "white"], how=mode)
|
||||
|
||||
print("Plotting neutrons")
|
||||
neutrons_agg = cvs.points(stars, "x", "z", agg=ds.count("mult"))
|
||||
neutrons = tf.shade(neutrons_agg, cmap=["darkblue", "lightblue"], how=mode)
|
||||
|
||||
base = tf.stack(density, neutrons)
|
||||
|
||||
# ffplay = SP.Popen([
|
||||
# "ffplay","-f","image2pipe","-"
|
||||
# ],stdin=SP.PIPE,bufsize=0)
|
||||
|
||||
|
||||
for rh_fn in ITT.chain.from_iterable(map(glob, sys.argv[2:])):
|
||||
basename = os.path.splitext(os.path.split(rh_fn)[-1])[0]
|
||||
filename = "img/{}_{}_{}.mkv".format(basename, size, mode)
|
||||
ffmpeg = SP.Popen(
|
||||
[
|
||||
"ffmpeg",
|
||||
"-y",
|
||||
"-f",
|
||||
"image2pipe",
|
||||
"-i",
|
||||
"-",
|
||||
"-crf",
|
||||
"17",
|
||||
"-r",
|
||||
"25",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
filename,
|
||||
],
|
||||
stdin=SP.PIPE,
|
||||
bufsize=0,
|
||||
)
|
||||
|
||||
print("Loading", rh_fn)
|
||||
route_hist = pd.read_csv(
|
||||
rh_fn, names=["id", "d"], index_col=0, dtype={"d": int}, low_memory=False,
|
||||
)
|
||||
exp_span = [route_hist.d.min(), route_hist.d.max()]
|
||||
stars["d"] = float("nan")
|
||||
rng = range(route_hist.d.min(), route_hist.d.max() + 1, steps)
|
||||
if steps == 0:
|
||||
rng = [route_hist.d.max() + 1]
|
||||
for n in rng:
|
||||
stars['d'] = route_hist[route_hist.d < n] # slow
|
||||
explored_agg = cvs.points(stars, "x", "z", agg=ds.mean("d")) # slow
|
||||
explored = tf.shade(
|
||||
explored_agg, cmap=["darkred", "lightpink"], how="linear", span=exp_span
|
||||
)
|
||||
img = set_background(tf.stack(base, explored), "black").to_pil()
|
||||
img.save(ffmpeg.stdin, "png")
|
||||
img.save("img/current.png")
|
||||
ffmpeg.stdin.close()
|
||||
ffmpeg.wait()
|
|
@ -0,0 +1,79 @@
|
|||
import pandas as pd
|
||||
import vaex as vx
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from skimage import exposure
|
||||
from skimage.util import img_as_ubyte
|
||||
import numpy as np
|
||||
from matplotlib import cm
|
||||
import sys
|
||||
|
||||
base_size = 1080, 1920
|
||||
|
||||
|
||||
def scale_to(width=None, height=None):
|
||||
isnone = (width is None, height is None)
|
||||
ret = {
|
||||
(False, False): lambda w, h: (w, h),
|
||||
(True, True): lambda w, h: (width, height),
|
||||
(False, True): lambda w, h: (width, width * (h / w)),
|
||||
(True, False): lambda w, h: (height * (w / h), height),
|
||||
}
|
||||
return lambda *args: tuple(map(int, ret[isnone](*args)))
|
||||
|
||||
|
||||
# xz -1 1
|
||||
bining = {
|
||||
("zx", -1, 1): scale_to(width=base_size[0]), # main view, top down
|
||||
# ('yx',1,1): lambda size,w,h: (size,int(size*(w/h))), #
|
||||
# ('zy',-1,1): lambda size,w,h: (int(size*(h/w)),size), #
|
||||
}
|
||||
|
||||
print("Loading stars.csv")
|
||||
stars = pd.read_csv(
|
||||
"stars.csv",
|
||||
names=["id", "name", "num_bodies", "has_scoopable", "mult", "x", "y", "z"],
|
||||
usecols=["id", "num_bodies", "x", "y", "z", "mult"],
|
||||
index_col=0,
|
||||
)
|
||||
stars = vx.from_pandas(stars, copy_index=False)
|
||||
|
||||
filename = "heuristic.png"
|
||||
fnt = ImageFont.truetype(r"FiraCode-Regular", 40)
|
||||
|
||||
for (binby_key, m1, m2), calcshape in bining.items():
|
||||
binby = [m1 * stars[binby_key[0]], m2 * stars[binby_key[1]]]
|
||||
|
||||
mm = [binby[0].minmax(), binby[1].minmax()]
|
||||
|
||||
w, h = [mm[0][1] - mm[0][0], mm[1][1] - mm[1][0]]
|
||||
shape = calcshape(w, h)
|
||||
hm_all = stars.sum("num_bodies", binby=binby, shape=shape, limits="minmax")
|
||||
hm_all_mask = hm_all != 0
|
||||
hm_all = exposure.equalize_hist(hm_all)
|
||||
hm_all -= hm_all.min()
|
||||
hm_all /= hm_all.max()
|
||||
|
||||
hm_boost = stars.sum(
|
||||
"astype(mult>1.0,'int')", binby=binby, shape=shape, limits="minmax"
|
||||
)
|
||||
hm_boost_mask = hm_boost != 0
|
||||
|
||||
hm_boost = exposure.equalize_hist(hm_boost)
|
||||
hm_boost -= hm_boost.min()
|
||||
hm_boost /= hm_boost.max()
|
||||
|
||||
# R = cm.Reds_r()
|
||||
G = cm.Greens_r(hm_all)
|
||||
B = cm.Blues_r(hm_boost)
|
||||
|
||||
img = np.zeros((base_size[0], base_size[1], 4))
|
||||
img[:, :, :] = 0.0
|
||||
img[:, :, 3] = 1.0
|
||||
canvas = img[: shape[0], : shape[1], :]
|
||||
canvas[hm_all_mask] = G[hm_all_mask]
|
||||
canvas[hm_boost_mask] = B[hm_boost_mask]
|
||||
pil_img = Image.fromarray(img_as_ubyte(img))
|
||||
draw = ImageDraw.Draw(pil_img)
|
||||
messages = ["Hello World"]
|
||||
draw.multiline_text((shape[0], 0), "\n".join(messages), font=fnt)
|
||||
pil_img.save(filename)
|
|
@ -0,0 +1,208 @@
|
|||
import pandas as pd
|
||||
import vaex as vx
|
||||
import json
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from skimage import exposure
|
||||
from skimage.io import imsave
|
||||
from skimage.util import img_as_ubyte
|
||||
import numpy as np
|
||||
from matplotlib import cm
|
||||
import subprocess as SP
|
||||
import os
|
||||
import sys
|
||||
import gc
|
||||
from datetime import timedelta
|
||||
import itertools as ITT
|
||||
from glob import glob
|
||||
|
||||
base_size = 1080, 1920
|
||||
steps = 1
|
||||
framerate = 25
|
||||
|
||||
rh_fn = sys.argv[1]
|
||||
|
||||
|
||||
def scale_to(width=None, height=None):
|
||||
isnone = (width is None, height is None)
|
||||
ret = {
|
||||
(False, False): lambda w, h: (w, h),
|
||||
(True, True): lambda w, h: (width, height),
|
||||
(False, True): lambda w, h: (width, width * (h / w)),
|
||||
(True, False): lambda w, h: (height * (w / h), height),
|
||||
}
|
||||
return lambda *args: tuple(map(int, ret[isnone](*args)))
|
||||
|
||||
|
||||
# xz -1 1
|
||||
bining = {
|
||||
("zx", -1, 1): scale_to(width=base_size[0]), # main view, top down
|
||||
# ('yx',1,1): lambda size,w,h: (size,int(size*(w/h))), #
|
||||
# ('zy',-1,1): lambda size,w,h: (int(size*(h/w)),size), #
|
||||
}
|
||||
|
||||
|
||||
def apply_depth(stars, rh_fn):
|
||||
print("Loading", rh_fn, flush=True, end=" ")
|
||||
route_hist = pd.read_csv(
|
||||
rh_fn,
|
||||
names=["id", "depth"],
|
||||
index_col=0,
|
||||
dtype={"depth": int},
|
||||
low_memory=False,
|
||||
)
|
||||
print("OK")
|
||||
print("Converting to pandas dataframe", flush=True, end=" ")
|
||||
stars = stars.to_pandas_df()
|
||||
gc.collect()
|
||||
print("OK")
|
||||
print("Applying depth", flush=True, end=" ")
|
||||
stars["depth"] = float("nan")
|
||||
print("...",flush=True,end=" ")
|
||||
stars["depth"] = route_hist.depth + 1.0
|
||||
print("OK")
|
||||
print("Converting to vaex dataframe", flush=True, end=" ")
|
||||
stars = vx.from_pandas(stars, copy_index=False)
|
||||
gc.collect()
|
||||
print("OK")
|
||||
return stars, route_hist.depth.max()
|
||||
|
||||
|
||||
"""
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, IntoPyObject)]
|
||||
pub struct System {
|
||||
/// Unique System id
|
||||
pub id: u32,
|
||||
/// Star system
|
||||
pub name: String,
|
||||
/// Number of bodies
|
||||
pub num_bodies: u8,
|
||||
/// Does the system have a scoopable star?
|
||||
pub has_scoopable: bool,
|
||||
/// Jump range multiplier (1.5 for white dwarfs, 4.0 for neutron stars, 1.0 otherwise)
|
||||
pub mult: f32,
|
||||
/// Position
|
||||
pub pos: [f32; 3],
|
||||
}
|
||||
"""
|
||||
|
||||
print("Loading stars.csv")
|
||||
stars = pd.read_csv(
|
||||
"stars.csv",
|
||||
names=["id", "name", "num_bodies", "has_scoopable", "mult", "x", "y", "z"],
|
||||
usecols=["id", "num_bodies", "x", "y", "z", "mult"],
|
||||
index_col=0,
|
||||
)
|
||||
stars = vx.from_pandas(stars, copy_index=False)
|
||||
|
||||
|
||||
def render(stars, rh_fn):
|
||||
print("Rendering")
|
||||
json_file = os.path.splitext(rh_fn)[0] + ".json"
|
||||
if os.path.isfile(json_file):
|
||||
with open(json_file) as fh:
|
||||
route_info = json.load(fh)
|
||||
route_len = len(route_info["route"])
|
||||
time_taken = str(timedelta(seconds=route_info["dt"]))
|
||||
route_rate = route_len / route_info["dt"]
|
||||
else:
|
||||
time_taken = "N/A"
|
||||
route_len = 0
|
||||
route_rate = 0
|
||||
route_info = {"dt": -1.0}
|
||||
stars, d_max = apply_depth(stars, rh_fn)
|
||||
basename = os.path.splitext(os.path.split(rh_fn)[-1])[0]
|
||||
filename = "img/{}.mkv".format(basename)
|
||||
if os.path.isfile(filename):
|
||||
return
|
||||
ffmpeg = SP.Popen(
|
||||
[
|
||||
"ffmpeg",
|
||||
"-y",
|
||||
"-f",
|
||||
"image2pipe",
|
||||
"-probesize",
|
||||
"128M",
|
||||
"-i",
|
||||
"-",
|
||||
"-crf",
|
||||
"17",
|
||||
"-preset",
|
||||
"veryslow",
|
||||
"-r",
|
||||
str(framerate),
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
filename,
|
||||
],
|
||||
stdin=SP.PIPE,
|
||||
bufsize=0,
|
||||
)
|
||||
|
||||
total = stars.length()
|
||||
fnt = ImageFont.truetype(r"FiraCode-Regular", 40)
|
||||
|
||||
for (binby_key, m1, m2), calcshape in bining.items():
|
||||
binby = [m1 * stars[binby_key[0]], m2 * stars[binby_key[1]]]
|
||||
|
||||
mm = [binby[0].minmax(), binby[1].minmax()]
|
||||
|
||||
w, h = [mm[0][1] - mm[0][0], mm[1][1] - mm[1][0]]
|
||||
shape = calcshape(w, h)
|
||||
hm_all = stars.sum("num_bodies", binby=binby, shape=shape, limits="minmax")
|
||||
hm_all_mask = hm_all != 0
|
||||
hm_all = exposure.equalize_hist(hm_all)
|
||||
hm_all -= hm_all.min()
|
||||
hm_all /= hm_all.max()
|
||||
|
||||
hm_boost = stars.sum(
|
||||
"astype(mult>1.0,'int')", binby=binby, shape=shape, limits="minmax"
|
||||
)
|
||||
hm_boost_mask = hm_boost != 0
|
||||
|
||||
hm_boost = exposure.equalize_hist(hm_boost)
|
||||
hm_boost -= hm_boost.min()
|
||||
hm_boost /= hm_boost.max()
|
||||
|
||||
G = cm.Greens_r(hm_all)
|
||||
B = cm.Blues_r(hm_boost)
|
||||
hm_exp = stars.mean("depth", binby=binby, shape=shape, limits="minmax")
|
||||
hm_exp[np.isnan(hm_exp)] = 0.0
|
||||
|
||||
hm_exp -= hm_exp.min()
|
||||
hm_exp /= d_max
|
||||
R = cm.Reds_r(hm_exp)
|
||||
|
||||
hm_exp_mask_base = hm_exp != 0.0
|
||||
img = np.zeros((base_size[0], base_size[1], 4))
|
||||
d_array = stars[~stars["depth"].isna()]["depth"].values
|
||||
exploration_rate = (d_array <= d_max).sum() / route_info["dt"]
|
||||
print("Total frames:",d_max)
|
||||
for d in range(0, d_max, steps):
|
||||
hm_exp_mask = np.logical_and(hm_exp_mask_base, hm_exp <= (d / d_max))
|
||||
num_explored = (d_array <= d).sum()
|
||||
img[:, :, :] = 0.0
|
||||
img[:, :, 3] = 1.0
|
||||
canvas = img[: shape[0], : shape[1], :]
|
||||
canvas[hm_all_mask] = G[hm_all_mask]
|
||||
canvas[hm_boost_mask] = B[hm_boost_mask]
|
||||
canvas[hm_exp_mask] = R[hm_exp_mask]
|
||||
pil_img = Image.fromarray(img_as_ubyte(img))
|
||||
draw = ImageDraw.Draw(pil_img)
|
||||
messages = [
|
||||
"Filename: {}".format(basename),
|
||||
"Total Stars: {:,}".format(total),
|
||||
"Explored: {:,} ({:.2%})".format(num_explored, num_explored / total),
|
||||
"Search Depth: {:,}/{:,}".format(d, route_len),
|
||||
"Time: {}".format(time_taken),
|
||||
"Rate: {:.3f} waypoints/s".format(route_rate),
|
||||
"Exploration Rate: {:.3f} stars/s".format(exploration_rate),
|
||||
]
|
||||
draw.multiline_text((shape[0], 0), "\n".join(messages), font=fnt)
|
||||
pil_img.save(ffmpeg.stdin, "bmp")
|
||||
ffmpeg.stdin.close()
|
||||
ffmpeg.wait()
|
||||
|
||||
|
||||
for rh_fn in ITT.chain.from_iterable(map(glob, sys.argv[1:])):
|
||||
render(stars, rh_fn)
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
|||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.238.0/containers/docker-existing-dockerfile
|
||||
{
|
||||
"name": "ED_LRR",
|
||||
|
||||
// Sets the run context to one level up instead of the .devcontainer folder.
|
||||
"context": "..",
|
||||
|
||||
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
|
||||
"dockerFile": "../Dockerfile",
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Uncomment the next line to run commands after the container is created - for example installing curl.
|
||||
// "postCreateCommand": "apt-get update && apt-get install -y curl",
|
||||
|
||||
// Uncomment when using a ptrace-based debugger like C++, Go, and Rust
|
||||
"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ]
|
||||
|
||||
// Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker.
|
||||
// "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ],
|
||||
|
||||
// Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
|
||||
// "remoteUser": "vscode"
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"spellright.language": [
|
||||
"de",
|
||||
"en"
|
||||
],
|
||||
"spellright.documentTypes": [
|
||||
"latex",
|
||||
"plaintext",
|
||||
"git-commit"
|
||||
],
|
||||
"discord.enabled": true,
|
||||
"python.pythonPath": "..\\.nox\\devenv-3-8\\python.exe",
|
||||
"jupyter.jupyterServerType": "local",
|
||||
"files.associations": {
|
||||
"*.ksy": "yaml",
|
||||
"*.vpy": "python",
|
||||
"stat.h": "c"
|
||||
},
|
||||
"rust-analyzer.diagnostics.disabled": [
|
||||
"unresolved-import"
|
||||
],
|
||||
"yaml.schemas": {
|
||||
"https://raw.githubusercontent.com/kaitai-io/ksy_schema/master/ksy_schema.json": "*.ksy",
|
||||
"https://json.schemastore.org/github-workflow.json": "file:///d%3A/devel/rust/ed_lrr_gui/.github/workflow/build.yml"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
[package]
|
||||
name = "ed_lrr"
|
||||
version = "0.2.0"
|
||||
authors = ["Daniel Seiller <earthnuker@gmail.com>"]
|
||||
edition = "2018"
|
||||
repository = "https://gitlab.com/Earthnuker/ed_lrr.git"
|
||||
license = "MIT"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
name = "_ed_lrr"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
debug = true
|
||||
lto = "fat"
|
||||
|
||||
|
||||
[dependencies]
|
||||
pyo3 = { version = "0.16.5", features = ["extension-module","eyre","abi3-py37"] }
|
||||
csv = "1.1.6"
|
||||
humantime = "2.1.0"
|
||||
permutohedron = "0.2.4"
|
||||
serde_json = "1.0.81"
|
||||
bincode = "1.3.3"
|
||||
sha3 = "0.10.1"
|
||||
byteorder = "1.4.3"
|
||||
rstar = "0.9.3"
|
||||
crossbeam-channel = "0.5.4"
|
||||
better-panic = "0.3.0"
|
||||
derivative = "2.2.0"
|
||||
dict_derive = "0.4.0"
|
||||
regex = "1.5.6"
|
||||
num_cpus = "1.13.1"
|
||||
eddie = "0.4.2"
|
||||
thiserror = "1.0.31"
|
||||
pyo3-log = "0.6.0"
|
||||
log = "0.4.17"
|
||||
flate2 = "1.0.24"
|
||||
eval = "0.4.3"
|
||||
pythonize = "0.16.0"
|
||||
itertools = "0.10.3"
|
||||
rustc-hash = "1.1.0"
|
||||
rand = "0.8.5"
|
||||
eyre = "0.6.8"
|
||||
memmap = "0.7.0"
|
||||
csv-core = "0.1.10"
|
||||
nohash-hasher = "0.2.0"
|
||||
dashmap = "5.3.4"
|
||||
rayon = "1.5.3"
|
||||
|
||||
stats_alloc = {version="0.1.10", optional=true}
|
||||
tracing = { version = "0.1.34", optional = true }
|
||||
tracing-subscriber = { version = "0.3.11", optional = true }
|
||||
tracing-tracy = { version = "0.10.0", optional = true }
|
||||
# tracing-unwrap = { version = "0.9.2", optional = true }
|
||||
tracy-client = { version = "0.14.0", optional = true }
|
||||
tracing-chrome = "0.6.0"
|
||||
|
||||
|
||||
[features]
|
||||
profiling = ["tracing","tracing-subscriber","tracing-tracy","tracy-client","stats_alloc"]
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.3.5", features = ["real_blackbox"] }
|
||||
rand = "0.8.5"
|
||||
rand_distr = "0.4.3"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.137"
|
||||
features = ["derive"]
|
||||
|
||||
|
||||
[[bench]]
|
||||
name = "dot_bench"
|
||||
harness = false
|
|
@ -0,0 +1,5 @@
|
|||
FROM ghcr.io/pyo3/maturin
|
||||
|
||||
LABEL ed_lrr_dev latest
|
||||
RUN rustup default nightly
|
||||
RUN pip install maturin[zig]
|
|
@ -0,0 +1,29 @@
|
|||
import ujson
|
||||
from glob import glob
|
||||
import pandas as pd
|
||||
from datetime import timedelta
|
||||
|
||||
route_info = {}
|
||||
for log in glob("../logs/route_log*.json"):
|
||||
name = log.split("route_log_")[1].rsplit(".", 1)[0]
|
||||
data = ujson.load(open(log))
|
||||
dt = data["dt"]
|
||||
route_len = len(data["route"])
|
||||
if route_len:
|
||||
route_info[name] = (dt, route_len)
|
||||
dt, route_len = route_info["beam_0"] # BFS as baseline
|
||||
|
||||
data = []
|
||||
|
||||
for name, (dt_o, l_o) in sorted(route_info.items(), key=lambda v: v[1][0] / v[1][1]):
|
||||
dt_s = str(timedelta(seconds=round(dt_o, 2))).rstrip("0")
|
||||
data.append(
|
||||
{
|
||||
"name": name,
|
||||
"time": "{} ({:.2f}x)".format(dt_s, dt / dt_o),
|
||||
"length": "{} (+{:.2%})".format(l_o, (l_o / route_len) - 1),
|
||||
"time/hop": "{:.2} s".format(dt_o / l_o),
|
||||
}
|
||||
)
|
||||
df = pd.DataFrame(data)
|
||||
print(df.to_markdown(index=False))
|
|
@ -0,0 +1,142 @@
|
|||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use rand::Rng;
|
||||
use rand_distr::StandardNormal;
|
||||
|
||||
fn rand_v3() -> [f32; 3] {
|
||||
let mut rng = rand::thread_rng();
|
||||
[
|
||||
rng.sample(StandardNormal),
|
||||
rng.sample(StandardNormal),
|
||||
rng.sample(StandardNormal),
|
||||
]
|
||||
}
|
||||
|
||||
fn arand() -> f32 {
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.sample::<f32, _>(StandardNormal).abs()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn veclen(v: &[f32; 3]) -> f32 {
|
||||
(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn dist2(p1: &[f32; 3], p2: &[f32; 3]) -> f32 {
|
||||
let dx = p1[0] - p2[0];
|
||||
let dy = p1[1] - p2[1];
|
||||
let dz = p1[2] - p2[2];
|
||||
|
||||
dx * dx + dy * dy + dz * dz
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn dist(p1: &[f32; 3], p2: &[f32; 3]) -> f32 {
|
||||
dist2(p1, p2).sqrt()
|
||||
}
|
||||
|
||||
/// Dot product (cosine of angle) between two 3D vectors
|
||||
#[inline(always)]
|
||||
pub fn ndot_vec_dist(u: &[f32; 3], v: &[f32; 3]) -> f32 {
|
||||
let z: [f32; 3] = [0.0; 3];
|
||||
let lm = dist(u, &z) * dist(v, &z);
|
||||
((u[0] * v[0]) + (u[1] * v[1]) + (u[2] * v[2])) / lm
|
||||
}
|
||||
|
||||
/// Dot product (cosine of angle) between two 3D vectors
|
||||
#[inline(always)]
|
||||
pub fn ndot_vec_len(u: &[f32; 3], v: &[f32; 3]) -> f32 {
|
||||
let lm = veclen(u) * veclen(v);
|
||||
((u[0] * v[0]) + (u[1] * v[1]) + (u[2] * v[2])) / lm
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn ndot_iter(u: &[f32; 3], v: &[f32; 3]) -> f32 {
|
||||
let mut l_u = 0.0;
|
||||
let mut l_v = 0.0;
|
||||
let mut l_s = 0.0;
|
||||
for (u, v) in u.iter().zip(v.iter()) {
|
||||
l_s += u * v;
|
||||
l_u += u * u;
|
||||
l_v += v * v;
|
||||
}
|
||||
l_s / (l_u * l_v).sqrt()
|
||||
}
|
||||
|
||||
fn bench_ndot(c: &mut Criterion) {
|
||||
let mut g = c.benchmark_group("ndot");
|
||||
g.bench_function("vec_dist", |b| {
|
||||
b.iter_batched(
|
||||
|| (rand_v3(), rand_v3()),
|
||||
|(v1, v2)| ndot_vec_dist(&v1, &v2),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
g.bench_function("vec_len", |b| {
|
||||
b.iter_batched(
|
||||
|| (rand_v3(), rand_v3()),
|
||||
|(v1, v2)| ndot_vec_len(&v1, &v2),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
g.bench_function("iter", |b| {
|
||||
b.iter_batched(
|
||||
|| (rand_v3(), rand_v3()),
|
||||
|(v1, v2)| ndot_iter(&v1, &v2),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
g.finish();
|
||||
}
|
||||
|
||||
fn bench_dist(c: &mut Criterion) {
|
||||
let mut g = c.benchmark_group("dist");
|
||||
g.bench_function("dist", |b| {
|
||||
b.iter_batched(
|
||||
|| (rand_v3(), rand_v3()),
|
||||
|(v1, v2)| dist(&v1, &v2),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
g.bench_function("dist2", |b| {
|
||||
b.iter_batched(
|
||||
|| (rand_v3(), rand_v3()),
|
||||
|(v1, v2)| dist2(&v1, &v2),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
g.finish();
|
||||
}
|
||||
|
||||
fn vsub(a: &[f32; 3], b: &[f32; 3]) -> [f32; 3] {
|
||||
[a[0] - b[0], a[1] - b[1], a[2] - b[2]]
|
||||
}
|
||||
|
||||
pub fn h_old(node: &[f32; 3], m: f32, goal: &[f32; 3], r: f32) -> f32 {
|
||||
(dist(node, goal) - (r * m)).max(0.0)
|
||||
}
|
||||
|
||||
pub fn h_new(node: &[f32; 3], next: &[f32; 3], goal: &[f32; 3]) -> f32 {
|
||||
-ndot_iter(&vsub(node, goal), &vsub(node, next)).acos()
|
||||
}
|
||||
|
||||
fn bench_new_heur(c: &mut Criterion) {
|
||||
c.bench_function("old_heuristic", |b| {
|
||||
b.iter_batched(
|
||||
|| (rand_v3(), arand(), rand_v3(), arand()),
|
||||
|(node, m, goal, range)| h_old(&node, m, &goal, range),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
|
||||
c.bench_function("new_heuristic", |b| {
|
||||
b.iter_batched(
|
||||
|| (rand_v3(), rand_v3(), rand_v3()),
|
||||
|(v1, v2, v3)| h_new(&v1, &v2, &v3),
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_ndot, bench_dist, bench_new_heur);
|
||||
criterion_main!(benches);
|
|
@ -0,0 +1,19 @@
|
|||
import toml
|
||||
import subprocess as SP
|
||||
import os
|
||||
|
||||
|
||||
def set_version(rev=None):
|
||||
with open("Cargo.toml") as fh:
|
||||
cargo = toml.loads(fh.read())
|
||||
cargo["dependencies"]["pyo3"]["rev"] = rev
|
||||
if rev is None:
|
||||
del cargo["dependencies"]["pyo3"]["rev"]
|
||||
with open("Cargo.toml", "w") as fh:
|
||||
toml.dump(cargo, fh)
|
||||
|
||||
|
||||
for commit in open("ch.txt").readlines():
|
||||
set_version(commit.strip())
|
||||
if os.system("cargo check") == 0:
|
||||
break
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue