Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
e3537e148b | |||
440d4bd83a | |||
90d0eacca6 | |||
1681188214 | |||
68860aad80 | |||
d1fe41cbee | |||
b6b1b8eeed | |||
3f366c4e31 | |||
f481105fd9 | |||
bda259cf14 | |||
4c598da07a | |||
83fa2c6864 | |||
f389faef03 | |||
80e2e6da9e | |||
ad8eca522f | |||
ac4bd3c0e2 | |||
3558d2b34e | |||
dfcbd9cecf |
28 changed files with 4373 additions and 1386 deletions
118
.gitignore
vendored
118
.gitignore
vendored
|
@ -1,99 +1,3 @@
|
|||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
#*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# Images
|
||||
*.jpg
|
||||
*.jpeg
|
||||
|
@ -102,3 +6,25 @@ ENV/
|
|||
# Others
|
||||
*.db
|
||||
*.db:encryptable
|
||||
|
||||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# IDE
|
||||
.idea
|
||||
|
||||
# Git
|
||||
.git
|
||||
|
||||
# Dist
|
||||
/dist
|
||||
|
||||
|
|
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
4033
Cargo.lock
generated
Normal file
4033
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "simple_panorama_viewer"
|
||||
version = "2.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
log = "0.4"
|
||||
simplelog = "0.12"
|
||||
wry = "0.34"
|
||||
native-dialog = "0.7"
|
||||
rust-embed = "8"
|
||||
directories = "5"
|
||||
clap = "4"
|
||||
warp = "0.3"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
portpicker = "0.1"
|
||||
rand = "0.8"
|
24
Justfile
Normal file
24
Justfile
Normal file
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env -S just --justfile
|
||||
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
|
||||
|
||||
alias b := build
|
||||
|
||||
build:
|
||||
cargo build
|
||||
|
||||
dist_linux:
|
||||
rm -Rf ./dist
|
||||
mkdir dist
|
||||
cargo build --release
|
||||
smc bin_linux files
|
||||
smc sources
|
||||
|
||||
dist_win:
|
||||
coreutils rm -Rf ./dist
|
||||
coreutils mkdir dist
|
||||
cargo build --release
|
||||
smc bin_win files
|
||||
smc sources
|
||||
pandoc -i LICENSE.md -s -o LICENSE.rtf
|
||||
Start-Process -FilePath "C:\Program Files (x86)\Inno Setup 6\compil32.exe" -ArgumentList "/cc", "win-setup.iss" -Wait
|
||||
Start-Process -WorkingDirectory dist coreutils -ArgumentList sha512sum, -b, "SimplePanoramaViewer-2.0.0-x86_64-pc-windows-msvc-setup.exe" -RedirectStandardOutput "dist/SimplePanoramaViewer-2.0.0-x86_64-pc-windows-msvc-setup.exe.sha512sum"
|
|
@ -1,7 +1,7 @@
|
|||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © 2018 Rémi BERTHO
|
||||
Copyright © 2018-2022 Rémi BERTHO
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# SimplePanoramaViewer
|
||||
|
||||
This is a simple panorama viewer based on [Photo Sphere Viewer](http://photo-sphere-viewer.js.org) which is based on [Three.js](https://threejs.org/), [D.js](https://malko.github.io/D.js/), [uEvenet](https://github.com/mistic100/uEvent) and [doT.js](https://olado.github.io/doT/).
|
||||
This is a simple panorama viewer based on [Photo Sphere Viewer](http://photo-sphere-viewer.js.org) which is based on [Three.js](https://threejs.org/).
|
|
@ -1,49 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
from os import path
|
||||
from pathlib import Path
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5.QtWebEngine import *
|
||||
from PyQt5.QtWebEngineWidgets import *
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
app.setApplicationName("SimplePanoramaViewer")
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
img_dialog = QFileDialog.getOpenFileName(None, "Open image", str(Path.home()), "JPG Images (*.jpg *.JPG *.jpeg *.pjpeg *.pjpg *.PJPG)")
|
||||
img = img_dialog[0]
|
||||
else:
|
||||
img = path.abspath(sys.argv[1])
|
||||
|
||||
if img == "":
|
||||
exit(0)
|
||||
|
||||
img = img.replace("\\", "/")
|
||||
img = img.replace("'", "\\'")
|
||||
|
||||
if getattr( sys, 'frozen', False ) :
|
||||
exe_dir = path.realpath(sys._MEIPASS)
|
||||
else :
|
||||
exe_dir = path.dirname(path.realpath(__file__))
|
||||
os.chdir(exe_dir)
|
||||
|
||||
try:
|
||||
with open("html/index.html", "r", encoding="utf8") as file:
|
||||
html = file.read()
|
||||
except OSError as exception:
|
||||
print("Error: HTML file not found: " + str(exception))
|
||||
html = html.replace("__IMG_PATH__", img)
|
||||
|
||||
web = QWebEngineView()
|
||||
url = QUrl.fromLocalFile(exe_dir + "/index.html")
|
||||
web.setHtml(html,url)
|
||||
web.setWindowTitle(img)
|
||||
web.showMaximized()
|
||||
web.show()
|
||||
|
||||
sys.exit(app.exec_())
|
|
@ -1,39 +0,0 @@
|
|||
# -*- mode: python -*-
|
||||
|
||||
block_cipher = None
|
||||
|
||||
added_files = [
|
||||
( 'README.md', '.' ),
|
||||
( 'LICENSE.md', '.' ),
|
||||
( 'css', 'css' ),
|
||||
( 'html', 'html' ),
|
||||
( 'js', 'js' )
|
||||
]
|
||||
|
||||
a = Analysis(['SimplePanoramaViewer'],
|
||||
pathex=['./SimplePanoramaViewer'],
|
||||
binaries=[],
|
||||
datas=added_files,
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='SimplePanoramaViewer',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
runtime_tmpdir=None,
|
||||
console=False )
|
|
@ -1,43 +0,0 @@
|
|||
# -*- mode: python -*-
|
||||
|
||||
block_cipher = None
|
||||
|
||||
added_files = [
|
||||
( 'README.md', '.' ),
|
||||
( 'LICENSE.md', '.' ),
|
||||
( 'css', 'css' ),
|
||||
( 'html', 'html' ),
|
||||
( 'js', 'js' )
|
||||
]
|
||||
|
||||
a = Analysis(['SimplePanoramaViewer'],
|
||||
pathex=['./SimplePanoramaViewer'],
|
||||
binaries=[],
|
||||
datas=added_files,
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='SimplePanoramaViewer',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False )
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
name='SimplePanoramaViewer')
|
7
css/photo-sphere-viewer.min.css
vendored
7
css/photo-sphere-viewer.min.css
vendored
File diff suppressed because one or more lines are too long
14
embed/css/photo-sphere-viewer.min.css
vendored
Normal file
14
embed/css/photo-sphere-viewer.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
43
embed/index.html
Normal file
43
embed/index.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Panorama viewer</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link rel="stylesheet" href="css/photo-sphere-viewer.min.css">
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%; height: 100%; overflow: hidden; margin: 0; padding:
|
||||
0;
|
||||
}
|
||||
|
||||
#photosphere {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<!-- the viewer container must have a defined size -->
|
||||
<div id="photosphere" style="width: 100vw; height: 100vh;"></div>
|
||||
|
||||
<script src="js/three.min.js"></script>
|
||||
<script src="js/photo-sphere-viewer-core.min.js"></script>
|
||||
|
||||
<script>
|
||||
var PSV = new PhotoSphereViewer.Viewer({
|
||||
container: 'photosphere',
|
||||
panorama: 'img',
|
||||
caption: 'Panorama displayed with Photo Sphere Viewer V5.5.0',
|
||||
defaultZoomLvl: 40,
|
||||
minFov: 5,
|
||||
maxFov: 75,
|
||||
navbar: [
|
||||
'zoom',
|
||||
'caption'
|
||||
]
|
||||
});
|
||||
</script>
|
||||
</html>
|
8
embed/js/photo-sphere-viewer-core.min.js
vendored
Normal file
8
embed/js/photo-sphere-viewer-core.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
embed/js/three.min.js
vendored
Normal file
7
embed/js/three.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
20
generate.ps1
20
generate.ps1
|
@ -1,20 +0,0 @@
|
|||
$version="1.1"
|
||||
|
||||
if (Test-Path dist\"SimplePanoramaViewer-$version-win64.exe")
|
||||
{
|
||||
Remove-Item dist\"SimplePanoramaViewer-$version-win64.exe" -Force -Recurse
|
||||
}
|
||||
if (Test-Path dist\SimplePanoramaViewer)
|
||||
{
|
||||
Remove-Item dist\SimplePanoramaViewer -Force -Recurse
|
||||
}
|
||||
|
||||
pandoc -i LICENSE.md -s -o LICENSE.rtf
|
||||
pyinstaller SimplePanoramaViewer_folder.spec
|
||||
Start-Process -FilePath "C:\Program Files (x86)\Inno Setup 6\compil32.exe" -ArgumentList "/cc", "win-setup.iss" -Wait
|
||||
Remove-Item dist\SimplePanoramaViewer -Force -Recurse
|
||||
Get-FileHash "dist/SimplePanoramaViewer-$version-win64-setup.exe" -Algorithm SHA512 | Select-Object -ExpandProperty Hash | Out-File "dist/SimplePanoramaViewer-$version-win64-setup.exe.sha512sum"
|
||||
|
||||
pyinstaller SimplePanoramaViewer_file.spec
|
||||
Rename-Item -Path "dist\SimplePanoramaViewer.exe" -NewName "SimplePanoramaViewer-$version-win64.exe"
|
||||
Get-FileHash "dist/SimplePanoramaViewer-$version-win64.exe" -Algorithm SHA512 | Select-Object -ExpandProperty Hash | Out-File "dist/SimplePanoramaViewer-$version-win64.exe.sha512sum"
|
|
@ -1,66 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Panorama viewer</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link rel="stylesheet" href="css/photo-sphere-viewer.min.css">
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%; height: 100%; overflow: hidden; margin: 0; padding:
|
||||
0;
|
||||
}
|
||||
|
||||
#photosphere {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.psv-button.custom-button {
|
||||
font-size: 22px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.demo-label {
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
font-family: Helvetica, sans-serif;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
border: 1px solid white;
|
||||
background: rgba(0,0,0,0.4);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="photosphere"></div>
|
||||
|
||||
<script src="js/three.min.js"></script>
|
||||
<script src="js/D.min.js"></script>
|
||||
<script src="js/uevent.min.js"></script>
|
||||
<script src="js/doT.min.js"></script>
|
||||
<script src="js/photo-sphere-viewer.min.js"></script>
|
||||
|
||||
<h1>Panorama</h1>>
|
||||
<script>
|
||||
var PSV = new PhotoSphereViewer({
|
||||
container: 'photosphere',
|
||||
panorama: '__IMG_PATH__',
|
||||
caption: 'Panorama displayed with Photo Sphere Viewer V3.5.1',
|
||||
time_anim: false,
|
||||
default_fov: 40,
|
||||
min_fov: 5,
|
||||
max_fov: 75,
|
||||
navbar: [
|
||||
'autorotate',
|
||||
'zoom',
|
||||
'caption'
|
||||
]
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -4,9 +4,9 @@ Type=Application
|
|||
Name=SimplePanoramaViewer
|
||||
Comment=Simple web based panoram viewer
|
||||
Icon=emblem-photos
|
||||
Exec=SimplePanoramaViewer
|
||||
Exec=simple_panorama_viewer
|
||||
NoDisplay=false
|
||||
Categories=Graphics;Viewer
|
||||
StartupNotify=false
|
||||
Terminal=false
|
||||
MimeType=image/jpeg;image/pjpg
|
||||
MimeType=image/jpeg;image/pjpg;image/webp;image/avif
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="image/pjpg">
|
||||
<comment>Panoramic JPEG image</comment>
|
||||
<comment xml:lang="fr">Image JPEG panoramique</comment>
|
||||
<glob pattern="*.pjpeg"/>
|
||||
<glob pattern="*.pjpg"/>
|
||||
<glob pattern="*.pjpe"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
2
js/D.min.js
vendored
2
js/D.min.js
vendored
|
@ -1,2 +0,0 @@
|
|||
/* (c) Jonathan Gotti - licence: https://github.com/malko/d.js/LICENCE.txt @version 0.7.5*/
|
||||
!function(a){"use strict";function b(a){l(function(){throw a})}function c(b){return this.then(b,a)}function d(b){return this.then(a,b)}function e(b,c){return this.then(function(a){return m(b)?b.apply(null,n(a)?a:[a]):v.onlyFuncs?a:b},c||a)}function f(a){function b(){a()}return this.then(b,b),this}function g(a){return this.then(function(b){return m(a)?a.apply(null,n(b)?b.splice(0,0,void 0)&&b:[void 0,b]):v.onlyFuncs?b:a},function(b){return a(b)})}function h(c){return this.then(a,c?function(a){throw c(a),a}:b)}function i(a,b){var c=q(a);if(1===c.length&&n(c[0])){if(!c[0].length)return v.fulfilled([]);c=c[0]}var d=[],e=v(),f=c.length;if(f)for(var g=function(a){c[a]=v.promisify(c[a]),c[a].then(function(g){d[a]=b?c[a]:g,--f||e.resolve(d)},function(g){b?(d[a]=c[a],--f||e.resolve(d)):e.reject(g)})},h=0,i=f;i>h;h++)g(h);else e.resolve(d);return e.promise}function j(a,b){return a.then(m(b)?b:function(){return b})}function k(a){var b=q(a);1===b.length&&n(b[0])&&(b=b[0]);for(var c=v(),d=0,e=b.length,f=v.resolved();e>d;d++)f=j(f,b[d]);return c.resolve(f),c.promise}var l,m=function(a){return"function"==typeof a},n=function(a){return Array.isArray?Array.isArray(a):a instanceof Array},o=function(a){return!(!a||!(typeof a).match(/function|object/))},p=function(b){return b===!1||b===a||null===b},q=function(a,b){return[].slice.call(a,b)},r="undefined",s=typeof TypeError===r?Error:TypeError;if(typeof process!==r&&process.nextTick)l=process.nextTick;else if(typeof MessageChannel!==r){var t=new MessageChannel,u=[];t.port1.onmessage=function(){u.length&&u.shift()()},l=function(a){u.push(a),t.port2.postMessage(0)}}else l=function(a){setTimeout(a,0)};var v=function(b){function i(){if(0!==r){var a,b=t,c=0,d=b.length,e=~r?0:1;for(t=[];d>c;c++)(a=b[c][e])&&a(n)}}function j(a){function b(a){return function(b){return c?void 0:(c=!0,a(b))}}var c=!1;if(r)return this;try{var d=o(a)&&a.then;if(m(d)){if(a===u)throw new s("Promise can't resolve itself");return d.call(a,b(j),b(k)),this}}catch(e){return b(k)(e),this}return q(function(){n=a,r=1,i()}),this}function k(a){return r||q(function(){try{throw a}catch(b){n=b}r=-1,i()}),this}var n,q=(a!==b?b:v.alwaysAsync)?l:function(a){a()},r=0,t=[],u={then:function(a,b){var c=v();return t.push([function(b){try{p(a)?c.resolve(b):c.resolve(m(a)?a(b):v.onlyFuncs?b:a)}catch(d){c.reject(d)}},function(a){if((p(b)||!m(b)&&v.onlyFuncs)&&c.reject(a),b)try{c.resolve(m(b)?b(a):b)}catch(d){c.reject(d)}}]),0!==r&&q(i),c.promise},success:c,error:d,otherwise:d,apply:e,spread:e,ensure:f,nodify:g,rethrow:h,isPending:function(){return 0===r},getStatus:function(){return r}};return u.toSource=u.toString=u.valueOf=function(){return n===a?this:n},{promise:u,resolve:j,fulfill:j,reject:k}};if(v.deferred=v.defer=v,v.nextTick=l,v.alwaysAsync=!0,v.onlyFuncs=!0,v.resolve=v.resolved=v.fulfilled=function(a){return v(!0).resolve(a).promise},v.reject=v.rejected=function(a){return v(!0).reject(a).promise},v.wait=function(a){var b=v();return setTimeout(b.resolve,a||0),b.promise},v.delay=function(a,b){var c=v();return setTimeout(function(){try{c.resolve(m(a)?a.apply(null):a)}catch(b){c.reject(b)}},b||0),c.promise},v.promisify=function(a){return a&&m(a.then)?a:v.resolved(a)},v.all=function(){return i(arguments,!1)},v.resolveAll=function(){return i(arguments,!0)},v.sequence=function(){return k(arguments)},v.nodeCapsule=function(a,b){return b||(b=a,a=void 0),function(){var c=v(),d=q(arguments);d.push(function(a,b){a?c.reject(a):c.resolve(arguments.length>2?q(arguments,1):b)});try{b.apply(a,d)}catch(e){c.reject(e)}return c.promise}},"function"==typeof define&&define.amd)define("D.js",[],function(){return v});else if(typeof module!==r&&module.exports)module.exports=v;else if(typeof window!==r){var w=window.D;v.noConflict=function(){return window.D=w,v},window.D=v}}();
|
2
js/doT.min.js
vendored
2
js/doT.min.js
vendored
|
@ -1,2 +0,0 @@
|
|||
/* Laura Doktorova https://github.com/olado/doT */
|
||||
!function(){"use strict";function e(n,t,r){return("string"==typeof t?t:t.toString()).replace(n.define||a,function(e,t,o,a){return 0===t.indexOf("def.")&&(t=t.substring(4)),t in r||(":"===o?(n.defineParams&&a.replace(n.defineParams,function(e,n,o){r[t]={arg:n,text:o}}),t in r||(r[t]=a)):new Function("def","def['"+t+"']="+a)(r)),""}).replace(n.use||a,function(t,o){n.useParams&&(o=o.replace(n.useParams,function(e,n,t,o){if(r[t]&&r[t].arg&&o){var a=(t+":"+o).replace(/'|\\/g,"_");return r.__exp=r.__exp||{},r.__exp[a]=r[t].text.replace(new RegExp("(^|[^\\w$])"+r[t].arg+"([^\\w$])","g"),"$1"+o+"$2"),n+"def.__exp['"+a+"']"}}));var a=new Function("def","return "+o)(r);return a?e(n,a,r):a})}function n(e){return e.replace(/\\('|\\)/g,"$1").replace(/[\r\t\n]/g," ")}var t,r={name:"doT",version:"1.1.1",templateSettings:{evaluate:/\{\{([\s\S]+?(\}?)+)\}\}/g,interpolate:/\{\{=([\s\S]+?)\}\}/g,encode:/\{\{!([\s\S]+?)\}\}/g,use:/\{\{#([\s\S]+?)\}\}/g,useParams:/(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g,define:/\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,defineParams:/^\s*([\w$]+):([\s\S]+)/,conditional:/\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,iterate:/\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,varname:"it",strip:!0,append:!0,selfcontained:!1,doNotSkipEncoded:!1},template:void 0,compile:void 0,log:!0};r.encodeHTMLSource=function(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},t=e?/[&<>"'\/]/g:/&(?!#?\w+;)|<|>|"|'|\//g;return function(e){return e?e.toString().replace(t,function(e){return n[e]||e}):""}},t=function(){return this||(0,eval)("this")}(),"undefined"!=typeof module&&module.exports?module.exports=r:"function"==typeof define&&define.amd?define(function(){return r}):t.doT=r;var o={append:{start:"'+(",end:")+'",startencode:"'+encodeHTML("},split:{start:"';out+=(",end:");out+='",startencode:"';out+=encodeHTML("}},a=/$^/;r.template=function(c,i,u){i=i||r.templateSettings;var d,s,p=i.append?o.append:o.split,l=0,f=i.use||i.define?e(i,c,u||{}):c;f=("var out='"+(i.strip?f.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ").replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""):f).replace(/'|\\/g,"\\$&").replace(i.interpolate||a,function(e,t){return p.start+n(t)+p.end}).replace(i.encode||a,function(e,t){return d=!0,p.startencode+n(t)+p.end}).replace(i.conditional||a,function(e,t,r){return t?r?"';}else if("+n(r)+"){out+='":"';}else{out+='":r?"';if("+n(r)+"){out+='":"';}out+='"}).replace(i.iterate||a,function(e,t,r,o){return t?(l+=1,s=o||"i"+l,t=n(t),"';var arr"+l+"="+t+";if(arr"+l+"){var "+r+","+s+"=-1,l"+l+"=arr"+l+".length-1;while("+s+"<l"+l+"){"+r+"=arr"+l+"["+s+"+=1];out+='"):"';} } out+='"}).replace(i.evaluate||a,function(e,t){return"';"+n(t)+"out+='"})+"';return out;").replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/\r/g,"\\r").replace(/(\s|;|\}|^|\{)out\+='';/g,"$1").replace(/\+''/g,""),d&&(i.selfcontained||!t||t._encodeHTML||(t._encodeHTML=r.encodeHTMLSource(i.doNotSkipEncoded)),f="var encodeHTML = typeof _encodeHTML !== 'undefined' ? _encodeHTML : ("+r.encodeHTMLSource.toString()+"("+(i.doNotSkipEncoded||"")+"));"+f);try{return new Function(i.varname,f)}catch(e){throw"undefined"!=typeof console&&console.log("Could not create a template function: "+f),e}},r.compile=function(e,n){return r.template(e,null,n)}}();
|
7
js/photo-sphere-viewer.min.js
vendored
7
js/photo-sphere-viewer.min.js
vendored
File diff suppressed because one or more lines are too long
927
js/three.min.js
vendored
927
js/three.min.js
vendored
File diff suppressed because one or more lines are too long
7
js/uevent.min.js
vendored
7
js/uevent.min.js
vendored
|
@ -1,7 +0,0 @@
|
|||
/*!
|
||||
* uEvent 1.0.0 - to make any js object an event emitter
|
||||
* Copyright 2011 Jerome Etienne (http://jetienne.com)
|
||||
* Copyright 2015-2016 Damien "Mistic" Sorel (http://www.strangeplanet.fr)
|
||||
* Licensed under MIT (http://opensource.org/licenses/MIT)
|
||||
*/
|
||||
!function(a,b){"undefined"!=typeof module&&module.exports?module.exports=b():"function"==typeof define&&define.amd?define([],b):a.uEvent=b()}(this,function(){"use strict";var a=function(){return!0},b=function(){return!1},c=function(){};return c.Event=function(a,b){var c=a,d=b;Object.defineProperties(this,{type:{get:function(){return c},set:function(a){},enumerable:!0},args:{get:function(){return d},set:function(a){},enumerable:!0}})},c.Event.prototype={constructor:c.Event,isDefaultPrevented:b,isPropagationStopped:b,preventDefault:function(){this.isDefaultPrevented=a},stopPropagation:function(){this.isPropagationStopped=a}},c.prototype={constructor:c,on:function(a,b){if(this.__events=this.__events||{},"object"==typeof a)for(var c in a)a.hasOwnProperty(c)&&(this.__events[c]=this.__events[c]||[],this.__events[c].push(a[c]));else a.split(" ").forEach(function(a){this.__events[a]=this.__events[a]||[],this.__events[a].push(b)},this);return this},off:function(a,b){if(this.__events=this.__events||{},"object"==typeof a){for(var c in a)if(a.hasOwnProperty(c)&&c in this.__events){var d=this.__events[c].indexOf(a[c]);-1!==d&&this.__events[c].splice(d,1)}}else a?a.split(" ").forEach(function(a){if(a in this.__events)if(b){var c=this.__events[a].indexOf(b);-1!==c&&this.__events[a].splice(c,1)}else this.__events[a].length=0},this):this.__events={};return this},once:function(a,b){if(this.__once=this.__once||{},"object"==typeof a)for(var c in a)a.hasOwnProperty(c)&&(this.__once[c]=this.__once[c]||[],this.__once[c].push(a[c]));else a.split(" ").forEach(function(a){this.__once[a]=this.__once[a]||[],this.__once[a].push(b)},this);return this},trigger:function(a){var b,d,e,f=Array.prototype.slice.call(arguments,1),g=new c.Event(a,f);if(f.push(g),this.__events&&a in this.__events)for(b=0,d=this.__events[a].length;d>b;b++)if(e=this.__events[a][b],"object"==typeof e?e.handleEvent(g):e.apply(this,f),g.isPropagationStopped())return g;if(this.__once&&a in this.__once){for(b=0,d=this.__once[a].length;d>b;b++)if(e=this.__once[a][b],"object"==typeof e?e.handleEvent(g):e.apply(this,f),g.isPropagationStopped())return delete this.__once[a],g;delete this.__once[a]}return g},change:function(a,b){var d,e,f,g=Array.prototype.slice.call(arguments,1),h=new c.Event(a,g);if(g.push(h),this.__events&&a in this.__events)for(d=0,e=this.__events[a].length;e>d;d++)if(g[0]=b,f=this.__events[a][d],b="object"==typeof f?f.handleEvent(h):f.apply(this,g),h.isPropagationStopped())return b;return b}},c.mixin=function(a,b){b=b||{},a="function"==typeof a?a.prototype:a,["on","off","once","trigger","change"].forEach(function(d){var e=b[d]||d;a[e]=c.prototype[d]}),Object.defineProperties(a,{__events:{value:null,writable:!0},__once:{value:null,writable:!0}})},c});
|
30
smc.toml
Normal file
30
smc.toml
Normal file
|
@ -0,0 +1,30 @@
|
|||
[profiles]
|
||||
[profiles.bin_linux]
|
||||
output = "./dist/SimplePanoramaViewer-2.0.0-x86_64-unknown-linux-gnu$EXT"
|
||||
directory = "target/release/"
|
||||
files = ["simple_panorama_viewer"]
|
||||
container = "Tar"
|
||||
compression = "Zstd"
|
||||
compression_level = 15
|
||||
signatures = ["Sha512"]
|
||||
|
||||
[profiles.bin_win]
|
||||
output = "./dist/SimplePanoramaViewer-2.0.0-x86_64-pc-windows-msvc$EXT"
|
||||
directory = "target/release/"
|
||||
files = ["simple_panorama_viewer.exe"]
|
||||
container = "Zip"
|
||||
compression = "Deflate"
|
||||
compression_level = 9
|
||||
signatures = ["Sha512"]
|
||||
|
||||
[profiles.files]
|
||||
files = ["README.md", "LICENSE.md"]
|
||||
|
||||
[profiles.sources]
|
||||
paths = ["./"]
|
||||
output = "./dist/SimplePanoramaViewer-2.0.0-src$EXT"
|
||||
container = "Tar"
|
||||
compression = "Zstd"
|
||||
compression_level = 15
|
||||
hidden = true
|
||||
signatures = ["Sha512"]
|
164
src/main.rs
Normal file
164
src/main.rs
Normal file
|
@ -0,0 +1,164 @@
|
|||
#![windows_subsystem = "windows"]
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use clap::{Arg, Command};
|
||||
use log::info;
|
||||
use native_dialog::{FileDialog, MessageDialog};
|
||||
use rand::distributions::DistString;
|
||||
use rust_embed::RustEmbed;
|
||||
use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode};
|
||||
use std::{borrow::Cow, path::PathBuf, thread::JoinHandle};
|
||||
use warp::Filter;
|
||||
use wry::{
|
||||
application::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
},
|
||||
webview::{WebContext, WebViewBuilder},
|
||||
};
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "embed/"]
|
||||
struct Embed;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Init log
|
||||
TermLogger::init(
|
||||
LevelFilter::Info,
|
||||
ConfigBuilder::new()
|
||||
.set_time_offset_to_local()
|
||||
.unwrap()
|
||||
.build(),
|
||||
TerminalMode::Mixed,
|
||||
ColorChoice::Auto,
|
||||
)?;
|
||||
|
||||
let cmd = Command::new("Simple panorama viewer")
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.author(env!("CARGO_PKG_AUTHORS"))
|
||||
.about(env!("CARGO_PKG_DESCRIPTION"))
|
||||
.arg(
|
||||
Arg::new("filename")
|
||||
.value_parser(clap::value_parser!(PathBuf))
|
||||
.help("Image path"),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let img_path = if let Some(img_path) = cmd.get_one::<PathBuf>("filename") {
|
||||
Cow::Borrowed(img_path)
|
||||
} else {
|
||||
let user_dirs = directories::UserDirs::new().unwrap();
|
||||
let dir = if let Some(img_dir) = user_dirs.picture_dir() {
|
||||
img_dir
|
||||
} else {
|
||||
user_dirs.home_dir()
|
||||
};
|
||||
Cow::Owned(
|
||||
FileDialog::new()
|
||||
.add_filter(
|
||||
"Images",
|
||||
&[
|
||||
"jpg", "JPG", "jpeg", "pjpeg", "pjpg", "PJPG", "webp", "avif",
|
||||
],
|
||||
)
|
||||
.set_location(dir)
|
||||
.show_open_single_file()?
|
||||
.ok_or_else(|| anyhow!("No file"))?,
|
||||
)
|
||||
};
|
||||
|
||||
if !img_path.exists() {
|
||||
MessageDialog::new()
|
||||
.set_title("Image error")
|
||||
.set_text(&format!("Image `{}` does not exist", img_path.display()))
|
||||
.set_type(native_dialog::MessageType::Error)
|
||||
.show_alert()?;
|
||||
bail!("File `{}` does not exist !", img_path.display());
|
||||
}
|
||||
|
||||
info!("Run server");
|
||||
let port = portpicker::pick_unused_port().ok_or_else(|| anyhow!("Cannot find ununsed port"))?;
|
||||
let user_agent = rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 32);
|
||||
run_server(img_path.to_path_buf(), port, user_agent.clone());
|
||||
|
||||
info!("Create webview");
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Simple panorama viewer")
|
||||
.with_maximized(true)
|
||||
.build(&event_loop)?;
|
||||
let data_dir = directories::ProjectDirs::from("fr", "dalan", "SimplePanoramaViwer").unwrap();
|
||||
let _webview = WebViewBuilder::new(window)?
|
||||
.with_url(&format!("http://127.0.0.1:{}/index.html", port))?
|
||||
.with_user_agent(&user_agent)
|
||||
.with_web_context(&mut WebContext::new(Some(
|
||||
data_dir.cache_dir().to_path_buf(),
|
||||
)))
|
||||
.build()?;
|
||||
|
||||
info!("Event loop");
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::NewEvents(StartCause::Init) => info!("Panorama open"),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
info!("Close");
|
||||
*control_flow = ControlFlow::Exit
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn run_server(img_path: PathBuf, port: u16, user_agent: String) -> JoinHandle<()> {
|
||||
std::thread::spawn(move || {
|
||||
info!("Create runtime");
|
||||
let async_runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
info!("Create response");
|
||||
let index = warp::path("index.html").map(|| {
|
||||
info!("Request `index.html`");
|
||||
let datas = get_file_data("index.html");
|
||||
warp::reply::html(datas)
|
||||
});
|
||||
let css = warp::path!("css" / String).map(move |val: String| {
|
||||
info!("Request css `{}`", &val);
|
||||
let datas = get_file_data(&format!("css/{}", val));
|
||||
warp::http::Response::builder().body(datas)
|
||||
});
|
||||
let js = warp::path!("js" / String).map(move |val: String| {
|
||||
info!("Request js `{}`", &val);
|
||||
let datas = get_file_data(&format!("js/{}", val));
|
||||
warp::http::Response::builder().body(datas)
|
||||
});
|
||||
let img = warp::path!("img")
|
||||
.and(warp::header("user-agent"))
|
||||
.map(move |agent: String| {
|
||||
if agent == user_agent {
|
||||
std::fs::read(&img_path).unwrap()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
});
|
||||
info!("Launch webserver");
|
||||
async_runtime
|
||||
.block_on(warp::serve(img.or(index).or(css).or(js)).run(([127, 0, 0, 1], port)));
|
||||
})
|
||||
}
|
||||
|
||||
fn get_file_data(filename: &str) -> String {
|
||||
if let Some(file) = Embed::get(filename) {
|
||||
std::str::from_utf8(file.data.as_ref())
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
101
utility.sh
101
utility.sh
|
@ -1,101 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
print_help()
|
||||
{
|
||||
echo "SimplePanoramaViewer utility script"
|
||||
echo
|
||||
echo "Use:"
|
||||
echo " bash utility.sh OPTION [PATH_TO_INSTALL_DIR]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -i, --install Install SimplePanoramaViewer in PATH_TO_INSTALL_DIR"
|
||||
echo " -u, --uninstall Uninstall SimplePanoramaViewer from PATH_TO_INSTALL_DIR"
|
||||
echo " -p, --package Generate a package for SimplePanoramaViewer"
|
||||
}
|
||||
|
||||
check_dir()
|
||||
{
|
||||
if [ -z "$1" ]; then
|
||||
echo "You need to pass a directory"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
install()
|
||||
{
|
||||
DESTDIR=$1
|
||||
check_dir "$DESTDIR"
|
||||
if [ -z "$2" ]; then
|
||||
PREFIX=$DESTDIR
|
||||
else
|
||||
PREFIX="$2"
|
||||
DESTDIR=$DESTDIR$PREFIX
|
||||
echo $DESTDIR
|
||||
fi
|
||||
|
||||
mkdir -p "$DESTDIR/share/SimplePanoramaViewer/"
|
||||
mkdir -p "$DESTDIR/bin/"
|
||||
mkdir -p "$DESTDIR/share/applications"
|
||||
mkdir -p "$DESTDIR/share/mime/image"
|
||||
cp SimplePanoramaViewer "$DESTDIR/share/SimplePanoramaViewer"
|
||||
cp -r css "$DESTDIR/share/SimplePanoramaViewer"
|
||||
cp -r html "$DESTDIR/share/SimplePanoramaViewer"
|
||||
cp -r js "$DESTDIR/share/SimplePanoramaViewer"
|
||||
cp install/SimplePanoramaViewer.desktop "$DESTDIR/share/applications"
|
||||
cp install/pjpg.xml "$DESTDIR/share/mime/image"
|
||||
echo "#!/bin/bash
|
||||
$PREFIX/share/SimplePanoramaViewer/SimplePanoramaViewer \"\$@\"" > "$DESTDIR/bin/SimplePanoramaViewer"
|
||||
chmod a+x "$DESTDIR/bin/SimplePanoramaViewer"
|
||||
chmod -R a+r "$DESTDIR/share/SimplePanoramaViewer"
|
||||
}
|
||||
|
||||
uninstall()
|
||||
{
|
||||
DIR=$1
|
||||
check_dir "$DIR"
|
||||
|
||||
rm "$DIR/bin/SimplePanoramaViewer"
|
||||
rm -r "$DIR/share/SimplePanoramaViewer/"
|
||||
rm "$DIR/share/applications/SimplePanoramaViewer.desktop"
|
||||
rm "$DIR/share/mime/image/pjpg.xml"
|
||||
}
|
||||
|
||||
package()
|
||||
{
|
||||
DIR="/tmp/SimplePanoramaViewer"
|
||||
|
||||
mkdir "$DIR"
|
||||
cp SimplePanoramaViewer "$DIR"
|
||||
cp -r css "$DIR"
|
||||
cp -r html "$DIR"
|
||||
cp -r js "$DIR"
|
||||
chmod a+x "$DIR/SimplePanoramaViewer"
|
||||
|
||||
mkdir -p dist
|
||||
cd /tmp || exit
|
||||
tar Jcvf "SimplePanoramaViewer.tar.xz" "SimplePanoramaViewer"
|
||||
|
||||
cd - || exit
|
||||
cp -f "$DIR.tar.xz" dist
|
||||
|
||||
rm -r "$DIR"
|
||||
rm "$DIR.tar.xz"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"-h" | "--help")
|
||||
print_help
|
||||
;;
|
||||
"-i" | "--install")
|
||||
install "$2" "$3"
|
||||
;;
|
||||
"-u" | "--uninstall")
|
||||
uninstall "$2"
|
||||
;;
|
||||
"-p" | "--package")
|
||||
package
|
||||
;;
|
||||
*)
|
||||
print_help
|
||||
;;
|
||||
esac
|
|
@ -2,10 +2,10 @@
|
|||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "SimplePanoramaViewer"
|
||||
#define MyAppVersion "1.1"
|
||||
#define MyAppVersion "2.0.0"
|
||||
#define MyAppPublisher "Dalan"
|
||||
#define MyAppURL "https://www.dalan.fr"
|
||||
#define MyAppExeName "SimplePanoramaViewer.exe"
|
||||
#define MyAppExeName "simple_panorama_viewer.exe"
|
||||
|
||||
[Setup]
|
||||
; NOTE: The value of AppId uniquely identifies this application.
|
||||
|
@ -19,12 +19,12 @@ AppPublisher={#MyAppPublisher}
|
|||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={pf}\{#MyAppName}
|
||||
DefaultDirName={commonpf}\{#MyAppName}
|
||||
DisableProgramGroupPage=yes
|
||||
LicenseFile=LICENSE.rtf
|
||||
OutputBaseFilename={#MyAppName}-{#MyAppVersion}-win64-setup
|
||||
OutputBaseFilename={#MyAppName}-{#MyAppVersion}-x86_64-pc-windows-msvc-setup
|
||||
OutputDir=dist
|
||||
Compression=lzma
|
||||
Compression=lzma2
|
||||
SolidCompression=yes
|
||||
ArchitecturesAllowed=x64
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
|
@ -37,7 +37,7 @@ Name: "french"; MessagesFile: "compiler:Languages\French.isl"
|
|||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
|
||||
[Files]
|
||||
Source: "dist\SimplePanoramaViewer\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||
Source: "target\release\simple_panorama_viewer.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
[Icons]
|
||||
Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue