Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
4f959d217d | |||
31035b9c92 | |||
91617b81f6 | |||
9c6c423e16 | |||
be66e2cad3 | |||
2b90b1f893 | |||
6e75f1b022 |
17 changed files with 473 additions and 190 deletions
172
Cargo.lock
generated
172
Cargo.lock
generated
|
@ -1,18 +1,18 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ELApse"
|
||||
version = "0.1.0"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"ftdi-embedded-hal",
|
||||
"image",
|
||||
"piston",
|
||||
"piston2d-graphics",
|
||||
"piston_window",
|
||||
"rand",
|
||||
"rgb",
|
||||
"unreal_asset",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -540,6 +540,32 @@ version = "1.13.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||
dependencies = [
|
||||
"nb 0.1.3",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal-nb"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
|
||||
dependencies = [
|
||||
"embedded-hal 1.0.0",
|
||||
"nb 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum_dispatch"
|
||||
version = "0.3.13"
|
||||
|
@ -623,6 +649,38 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ftdi-embedded-hal"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e328c2738c104e2649586bda4baabf376825db50014eb0d41bee74fb6064deb"
|
||||
dependencies = [
|
||||
"embedded-hal 0.2.7",
|
||||
"embedded-hal 1.0.0",
|
||||
"embedded-hal-nb",
|
||||
"ftdi-mpsse",
|
||||
"libftd2xx",
|
||||
"nb 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ftdi-mpsse"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7cfcda69930a8d2fdcdd7ffb9234fe4c79a8c73934ed4904327d77bfb5078a"
|
||||
dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
|
@ -809,6 +867,16 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.2"
|
||||
|
@ -956,6 +1024,28 @@ version = "0.2.158"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
|
||||
[[package]]
|
||||
name = "libftd2xx"
|
||||
version = "0.32.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f20d68b3138aaabb97edc3db8ed8f418d60f8abd2a7d8220d562906fb38ff08e"
|
||||
dependencies = [
|
||||
"ftdi-mpsse",
|
||||
"libftd2xx-ffi",
|
||||
"log",
|
||||
"paste",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libftd2xx-ffi"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40bed7f53ea45282e0e4f1361d1a8094e62abe0ccfd9a6dbf7e3db932b2789ce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.7"
|
||||
|
@ -1099,6 +1189,21 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fce7b49e1e6d8aa67232ef1c4c936c0af58756eb2db6f65c40bacb39035e7f42"
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||
dependencies = [
|
||||
"nb 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.7.0"
|
||||
|
@ -1753,9 +1858,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
|||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.50"
|
||||
version = "0.8.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
|
||||
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
@ -1921,6 +2026,12 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strict-num"
|
||||
version = "0.1.1"
|
||||
|
@ -2036,6 +2147,21 @@ dependencies = [
|
|||
"strict-num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
|
@ -2093,12 +2219,27 @@ version = "0.24.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unreal_asset"
|
||||
version = "0.1.16"
|
||||
|
@ -2146,6 +2287,17 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
|
@ -2190,6 +2342,12 @@ version = "0.9.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ELApse"
|
||||
version = "0.1.0"
|
||||
version = "0.1.5"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/BlackDragon-B/ELApse"
|
||||
authors = ["BlackDragon-B <kyan@blackdrgn.nl>"]
|
||||
|
@ -9,12 +9,15 @@ readme = "README.md"
|
|||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.17", features = ["derive"] }
|
||||
ftdi-embedded-hal = { version = "0.22.0", features = ["libftd2xx"] }
|
||||
image = "0.25.2"
|
||||
piston = "1.0.0"
|
||||
piston2d-graphics = "0.44.0"
|
||||
piston_window = "0.132.0"
|
||||
rand = "0.8.5"
|
||||
rgb = "0.8.52"
|
||||
unreal_asset = "0.1.16"
|
||||
url = "2.5.2"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 3
|
||||
|
||||
# The profile that 'cargo dist' will build with
|
||||
[profile.dist]
|
||||
|
@ -30,7 +33,7 @@ ci = "github"
|
|||
# The installers to generate for each app
|
||||
installers = []
|
||||
# Target platforms to build apps for (Rust target-triple syntax)
|
||||
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
|
||||
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
||||
|
||||
#[workspace.metadata.dist.github-custom-runners]
|
||||
#aarch64-unknown-linux-gnu = "buildjet-4vcpu-ubuntu-2204-arm"
|
||||
|
|
34
README.md
34
README.md
|
@ -1 +1,35 @@
|
|||
# ELApse
|
||||
|
||||
ELApse is a tool used for processing and displaying the .ELA format from a dead rhythm game
|
||||
|
||||
## Downloading
|
||||
|
||||
You can either download the latest release from the [releases](https://github.com/BlackDragon-B/ELApse/releases) or build it yourself using cargo
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
ELApse --input <INPUT> --output <OUTPUT>
|
||||
|
||||
Options:
|
||||
-i, --input <INPUT> Input to use
|
||||
-o, --output <OUTPUT> Output to use
|
||||
-h, --help Print help
|
||||
-V, --version Print version
|
||||
```
|
||||
## Features
|
||||
### Input
|
||||
- [WARLS](https://github.com/Aircoookie/WLED/wiki/UDP-Realtime-Control) (--input udp://0.0.0.0:21324)
|
||||
- Images (--input example.png)
|
||||
- .uasset/.uexp (--input ELA_BP_Title_in_00.uasset)
|
||||
- File containing raw rgb8 (--input file)
|
||||
- Raw rgb8 from stdin (--input stdin)
|
||||
### Output
|
||||
- [WARLS](https://github.com/Aircoookie/WLED/wiki/UDP-Realtime-Control) (--output udp://0.0.0.0:21324)
|
||||
- Built-in renderer built with [Piston](https://www.piston.rs/) (--output piston)
|
||||
## Contributing
|
||||
|
||||
Feel free to open an issue if you think this tool is missing something critical. Pull requests are always welcome.
|
||||
|
||||
## License
|
||||
|
||||
ELApse is licensed under the [GPLv3](https://choosealicense.com/licenses/gpl-3.0/) license.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{ffi::OsStr, fs::{self, read, File}, os::raw, path::Path};
|
||||
use image::{ImageFormat, ImageReader};
|
||||
use unreal_asset::{asset::AssetTrait, cast, engine_version::EngineVersion, exports::{self, normal_export::NormalExport, Export, ExportBaseTrait}, properties::{array_property::ArrayProperty, color_property::ColorProperty, struct_property::StructProperty, Property}, types::{fname::FNameContainer, PackageIndex}, Asset};
|
||||
use std::{ffi::OsStr, fs::{self, File}, path::Path};
|
||||
use image::ImageFormat;
|
||||
use unreal_asset::{cast, engine_version::EngineVersion, exports::Export, properties::Property, Asset};
|
||||
|
||||
fn rawrgb(path: &str) -> Result<Vec<u8>, String> {
|
||||
match fs::read(path) {
|
||||
|
|
67
src/input.rs
67
src/input.rs
|
@ -1,67 +0,0 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
use std::{io::Read, net::UdpSocket, sync::mpsc::{self, Receiver, Sender}, thread, time::{Duration, Instant}};
|
||||
use crate::{ parser, fileparser };
|
||||
|
||||
pub fn stdin(tx: Sender<Vec<[f32; 4]>>) {
|
||||
let (stdintx, stdinrx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = mpsc::channel();
|
||||
thread::spawn( move || {
|
||||
let mut buf = [0; 1500];
|
||||
let mut e = std::io::stdin().lock();
|
||||
loop {
|
||||
let amt = e.read(&mut buf).unwrap();
|
||||
if buf[..amt].to_vec().len() > 0 {
|
||||
let _ = stdintx.send(buf[..amt].to_vec());
|
||||
}
|
||||
}
|
||||
});
|
||||
thread::spawn( move || {
|
||||
loop {
|
||||
// let amt = e.read(&mut buf).unwrap();
|
||||
match stdinrx.recv_timeout(Duration::from_millis(500)) {
|
||||
Ok(d) => {
|
||||
let r: Vec<[f32; 4]> = parser::parseleds(d, parser::ParserOptions::default().mirror(true).double(true).offset(15));
|
||||
let _ = tx.send(r);
|
||||
},
|
||||
Err(_) => {
|
||||
std::process::exit(0);
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn realtimeudp(tx: Sender<Vec<[f32; 4]>>) {
|
||||
thread::spawn( move || {
|
||||
let socket = UdpSocket::bind("127.0.0.1:21324").unwrap();
|
||||
let mut buf = [0; 1500];
|
||||
loop {
|
||||
let (amt, _src) = socket.recv_from(&mut buf).unwrap();
|
||||
let r = parser::parseleds(buf[..amt].to_vec(), parser::ParserOptions::default().stripheader(2));
|
||||
let _ = tx.send(r);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn file(tx: Sender<Vec<[f32; 4]>>, path: String) {
|
||||
thread::spawn( move || {
|
||||
let Ok(rawbytes) = fileparser::parse(&path) else {
|
||||
std::process::exit(1)
|
||||
};
|
||||
for frame in rawbytes.chunks(720) {
|
||||
let st = Instant::now();
|
||||
let r = parser::parseleds(frame.to_vec(), parser::ParserOptions::default().mirror(true).double(true).offset(15));
|
||||
let _ = tx.send(r);
|
||||
if Duration::from_secs_f32(1.0/60.0) > st.elapsed() {
|
||||
thread::sleep(Duration::from_secs_f32(1.0/60.0)-st.elapsed());
|
||||
};
|
||||
}
|
||||
std::process::exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
||||
pub enum Input {
|
||||
stdin,
|
||||
realtimeudp,
|
||||
file,
|
||||
}
|
29
src/input/file.rs
Normal file
29
src/input/file.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
use std::{sync::mpsc::Sender, thread, time::{Duration, Instant}};
|
||||
use crate::{fileparser, input::Input, parser};
|
||||
|
||||
impl Input {
|
||||
pub fn file(tx: Sender<Vec<[f32; 4]>>, path: String) {
|
||||
thread::spawn(move || {
|
||||
let Ok(rawbytes) = fileparser::parse(&path) else {
|
||||
println!("File not found");
|
||||
std::process::exit(1)
|
||||
};
|
||||
for frame in rawbytes.chunks(720) {
|
||||
let st = Instant::now();
|
||||
let r = parser::parseleds(
|
||||
frame.to_vec(),
|
||||
parser::ParserOptions::default()
|
||||
.mirror(true)
|
||||
.double(true)
|
||||
.offset(15),
|
||||
);
|
||||
let _ = tx.send(r);
|
||||
if Duration::from_secs_f32(1.0 / 60.0) > st.elapsed() {
|
||||
thread::sleep(Duration::from_secs_f32(1.0 / 60.0) - st.elapsed());
|
||||
};
|
||||
}
|
||||
std::process::exit(0);
|
||||
});
|
||||
}
|
||||
}
|
5
src/input/mod.rs
Normal file
5
src/input/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod file;
|
||||
mod realtimeudp;
|
||||
mod stdin;
|
||||
|
||||
pub struct Input;
|
20
src/input/realtimeudp.rs
Normal file
20
src/input/realtimeudp.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
use std::{net::UdpSocket, sync::mpsc::Sender, thread};
|
||||
use crate::{input::Input, parser};
|
||||
|
||||
impl Input {
|
||||
pub fn realtimeudp(tx: Sender<Vec<[f32; 4]>>, bindaddr: String) {
|
||||
thread::spawn(move || {
|
||||
let socket = UdpSocket::bind(bindaddr).unwrap();
|
||||
let mut buf = [0; 1500];
|
||||
loop {
|
||||
let (amt, _src) = socket.recv_from(&mut buf).unwrap();
|
||||
let r = parser::parseleds(
|
||||
buf[..amt].to_vec(),
|
||||
parser::ParserOptions::default().stripheader(2),
|
||||
);
|
||||
let _ = tx.send(r);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
39
src/input/stdin.rs
Normal file
39
src/input/stdin.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
use std::{io::Read, sync::mpsc::{self, Receiver, Sender}, thread, time::Duration};
|
||||
use crate::{input::Input, parser};
|
||||
|
||||
impl Input {
|
||||
pub fn stdin(tx: Sender<Vec<[f32; 4]>>) {
|
||||
let (stdintx, stdinrx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = mpsc::channel();
|
||||
thread::spawn(move || {
|
||||
let mut buf = [0; 1500];
|
||||
let mut e = std::io::stdin().lock();
|
||||
loop {
|
||||
let amt = e.read(&mut buf).unwrap();
|
||||
if buf[..amt].to_vec().len() > 0 {
|
||||
let _ = stdintx.send(buf[..amt].to_vec());
|
||||
}
|
||||
}
|
||||
});
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
// let amt = e.read(&mut buf).unwrap();
|
||||
match stdinrx.recv_timeout(Duration::from_millis(500)) {
|
||||
Ok(d) => {
|
||||
let r: Vec<[f32; 4]> = parser::parseleds(
|
||||
d,
|
||||
parser::ParserOptions::default()
|
||||
.mirror(true)
|
||||
.double(true)
|
||||
.offset(15),
|
||||
);
|
||||
let _ = tx.send(r);
|
||||
}
|
||||
Err(_) => {
|
||||
std::process::exit(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
90
src/main.rs
90
src/main.rs
|
@ -1,4 +1,3 @@
|
|||
#![feature(array_chunks)]
|
||||
mod input;
|
||||
mod output;
|
||||
mod parser;
|
||||
|
@ -6,58 +5,67 @@ mod utils;
|
|||
mod fileparser;
|
||||
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
use clap::{command, error::{self, ContextKind, ContextValue, ErrorKind}, Parser};
|
||||
use clap::Parser;
|
||||
use url::Url;
|
||||
use crate::output::Output;
|
||||
use crate::input::Input;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Name of the person to greet
|
||||
/// Input to use
|
||||
#[arg(short, long)]
|
||||
input: input::Input,
|
||||
|
||||
#[arg(required(false), short, long, requires = "input")]
|
||||
file: Option<String>,
|
||||
input: String,
|
||||
|
||||
/// Output to use
|
||||
#[arg(short, long)]
|
||||
output: output::Output,
|
||||
|
||||
#[arg(required(false), short, long, requires = "output")]
|
||||
destip: Option<String>,
|
||||
output: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
// let mut arg = clap::command!()
|
||||
// .arg(clap::arg!(-o --output <file> "The output file to write to. Use - for stdout."))
|
||||
// .arg_required_else_help(true);
|
||||
let x = true;
|
||||
let (tx, rx): (Sender<Vec<[f32; 4]>>, Receiver<Vec<[f32; 4]>>) = mpsc::channel();
|
||||
// if x {
|
||||
// input::stdin(tx);
|
||||
// } else {
|
||||
// input::realtimeudp(tx)
|
||||
// }
|
||||
match args.input {
|
||||
input::Input::stdin => input::stdin(tx),
|
||||
input::Input::realtimeudp => input::realtimeudp(tx),
|
||||
input::Input::file => {
|
||||
if args.file.is_some() {
|
||||
input::file(tx, args.file.unwrap());
|
||||
} else {
|
||||
utils::err_missing_args(vec!["--file".to_owned()])
|
||||
}
|
||||
},
|
||||
match args.input.as_str() {
|
||||
"-" | "stdin" => Input::stdin(tx),
|
||||
_ => {
|
||||
match Url::parse(&args.input) {
|
||||
Ok(o) => {
|
||||
match o.scheme() {
|
||||
"udp" => {
|
||||
Input::realtimeudp(tx, utils::get_host(o));
|
||||
},
|
||||
"file" => Input::file(tx, o.path().to_owned()),
|
||||
_ => {
|
||||
println!("Unrecognized uri scheme");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
Input::file(tx, args.input);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
// input::file(tx);
|
||||
// output::piston(rx) 21324
|
||||
match args.output {
|
||||
output::Output::piston => output::piston(rx),
|
||||
output::Output::realtimeudp => {
|
||||
if args.destip.is_some() {
|
||||
output::realtimeudp(rx, args.destip.unwrap())
|
||||
} else {
|
||||
utils::err_missing_args(vec!["--destip".to_owned()])
|
||||
}
|
||||
},
|
||||
match args.output.as_str() {
|
||||
"piston" => Output::piston(rx),
|
||||
"ftd2xx" => Output::ft2dxx(rx),
|
||||
_ => {
|
||||
match Url::parse(&args.output) {
|
||||
Ok(o) => {
|
||||
match o.scheme() {
|
||||
"udp" => Output::realtimeudp(rx,utils::get_host(o)),
|
||||
_ => {
|
||||
println!("Unrecognized uri scheme");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
println!("Output out of scope");
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
extern crate piston_window;
|
||||
|
||||
use graphics;
|
||||
use piston_window::*;
|
||||
use graphics::math::Scalar;
|
||||
use std::{net::{SocketAddr, UdpSocket}, sync::mpsc::Receiver, time::Duration};
|
||||
|
||||
use crate::utils::{compare, offset};
|
||||
|
||||
pub fn piston(rx: Receiver<Vec<[f32; 4]>>) {
|
||||
let mut window: PistonWindow =
|
||||
WindowSettings::new("ELApse", [800, 800])
|
||||
.transparent(true)
|
||||
.exit_on_esc(true).build().unwrap();
|
||||
while let Some(e) = window.next() {
|
||||
let r = match rx.recv_timeout(Duration::from_millis(66)) {
|
||||
Ok(e) => e,
|
||||
Err(_) => vec![[0.02, 0.02, 0.02, 1.0]; 480],
|
||||
};
|
||||
|
||||
let size = window.size();
|
||||
let wh = compare(size.height, size.width)*0.75;
|
||||
window.draw_2d(&e, |c, g, _device| {
|
||||
for e in 1..9 {
|
||||
for i in 0..60 {
|
||||
let rwh = wh*(1.0-(e as f64*0.06));
|
||||
circle_arc(r[i*8+(e-1)], rwh/32.0, Scalar::deg_to_rad(offset((((360/60)*i)) as i16, 360, 90) as f64), Scalar::deg_to_rad(offset((((360/60)*i)+6) as i16, 360, 90) as f64), [(size.width/2.0)-(rwh/2.0), (size.height/2.0)-(rwh/2.0), rwh, rwh], c.transform, g);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn realtimeudp(rx: Receiver<Vec<[f32; 4]>>, addr: String) {
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
loop {
|
||||
let leds = rx.recv().unwrap();
|
||||
let mut packet: Vec<u8> = vec![2, 2];
|
||||
for led in leds {
|
||||
packet.push((led[0]*255.0) as u8);
|
||||
packet.push((led[1]*255.0) as u8);
|
||||
packet.push((led[2]*255.0) as u8);
|
||||
}
|
||||
match socket.send_to(&packet, &addr) {
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
println!("Error while sending trying to send data: {}", err);
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
||||
pub enum Output {
|
||||
piston,
|
||||
realtimeudp,
|
||||
}
|
40
src/output/ft2dxx.rs
Normal file
40
src/output/ft2dxx.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
|
||||
use std::sync::mpsc::Receiver;
|
||||
|
||||
use ftdi_embedded_hal::eh0::prelude::_embedded_hal_blocking_spi_Write;
|
||||
use ftdi_embedded_hal::{self as hal, libftd2xx};
|
||||
|
||||
use crate::output::Output;
|
||||
|
||||
impl Output {
|
||||
pub fn ft2dxx(rx: Receiver<Vec<[f32; 4]>>) {
|
||||
let device = libftd2xx::Ft232h::with_description("Single RS232-HS").unwrap();
|
||||
println!("Connected to FT232H");
|
||||
|
||||
let hal = hal::FtHal::init_freq(device, 3_000_000).unwrap();
|
||||
let mut spi = hal.spi().unwrap();
|
||||
|
||||
// led stuff
|
||||
loop {
|
||||
let leds = rx.recv().unwrap();
|
||||
let mut fdata: Vec<u8> = Vec::new();
|
||||
for led in leds {
|
||||
let sled = [led[1], led[0], led[2]]; //Shift colors (be gone when I make a color struct)
|
||||
for i in 0..3 {
|
||||
let mut data = (sled[i].clone() * 255.0) as u8;
|
||||
let patterns = [0b1000_1000, 0b1000_1110, 0b11101000, 0b11101110];
|
||||
for _ in 0..4 {
|
||||
let bits = (data & 0b1100_0000) >> 6;
|
||||
fdata.push(patterns[bits as usize]);
|
||||
data <<= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = spi.write(&fdata);
|
||||
// for _ in 0..140 {
|
||||
// spi.write(std::array::from_ref(&0)).unwrap();
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
5
src/output/mod.rs
Normal file
5
src/output/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod ft2dxx;
|
||||
mod piston;
|
||||
mod realtimeudp;
|
||||
|
||||
pub struct Output;
|
34
src/output/piston.rs
Normal file
34
src/output/piston.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
|
||||
use piston_window::*;
|
||||
use piston_window::graphics::math::Scalar;
|
||||
use std::{sync::mpsc::Receiver, time::Duration};
|
||||
|
||||
use crate::output::Output;
|
||||
use crate::utils::{compare, offset};
|
||||
|
||||
impl Output {
|
||||
pub fn piston(rx: Receiver<Vec<[f32; 4]>>) {
|
||||
let mut window: PistonWindow =
|
||||
WindowSettings::new("ELApse", [800, 800])
|
||||
.transparent(true)
|
||||
.exit_on_esc(true).build().unwrap();
|
||||
while let Some(e) = window.next() {
|
||||
let r = match rx.recv_timeout(Duration::from_millis(66)) {
|
||||
Ok(e) => e,
|
||||
Err(_) => vec![[0.02, 0.02, 0.02, 1.0]; 480],
|
||||
};
|
||||
|
||||
let size = window.size();
|
||||
let wh = compare(size.height, size.width)*0.75;
|
||||
window.draw_2d(&e, |c, g, _device| {
|
||||
for e in 1..9 {
|
||||
for i in 0..60 {
|
||||
let rwh = wh*(1.0-(e as f64*0.06));
|
||||
circle_arc(r[i*8+(e-1)], rwh/32.0, Scalar::deg_to_rad(offset((((360/60)*i)) as i16, 360, 90) as f64), Scalar::deg_to_rad(offset((((360/60)*i)+6) as i16, 360, 90) as f64), [(size.width/2.0)-(rwh/2.0), (size.height/2.0)-(rwh/2.0), rwh, rwh], c.transform, g);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
26
src/output/realtimeudp.rs
Normal file
26
src/output/realtimeudp.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
use std::{net::UdpSocket, sync::mpsc::Receiver};
|
||||
|
||||
use crate::output::Output;
|
||||
|
||||
impl Output {
|
||||
pub fn realtimeudp(rx: Receiver<Vec<[f32; 4]>>, addr: String) {
|
||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
loop {
|
||||
let leds = rx.recv().unwrap();
|
||||
let mut packet: Vec<u8> = vec![2, 2];
|
||||
for led in leds {
|
||||
packet.push((led[0] * 255.0) as u8);
|
||||
packet.push((led[1] * 255.0) as u8);
|
||||
packet.push((led[2] * 255.0) as u8);
|
||||
}
|
||||
match socket.send_to(&packet, &addr) {
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
println!("Error while sending trying to send data: {}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ pub fn parseleds(mut data: Vec<u8>, options: ParserOptions) -> Vec<[f32; 4]> {
|
|||
if data.len() < expectedlen {
|
||||
data.append(&mut vec![5; expectedlen-out.len()]);
|
||||
}
|
||||
for x in data.array_chunks::<3>() {
|
||||
for x in data.as_chunks::<3>().0.iter() {
|
||||
out.push([utils::scaleu8tof32(x[0]), utils::scaleu8tof32(x[1]), utils::scaleu8tof32(x[2]), 1.0]);
|
||||
if options.double && !options.mirror {
|
||||
out.push(out.last().unwrap().clone());
|
||||
|
|
22
src/utils.rs
22
src/utils.rs
|
@ -1,5 +1,3 @@
|
|||
use clap::error::{ContextKind, ContextValue, ErrorKind};
|
||||
|
||||
pub fn compare(a: f64, b: f64) -> f64 {
|
||||
if a > b {
|
||||
b
|
||||
|
@ -20,9 +18,19 @@ pub fn scaleu8tof32(old_value: u8) -> f32 { // Convert a u8 between 0 and 255 to
|
|||
(((old_value as f32 - 0.0) * (0.0 - 1.0)) / (0.0 - 255.0)) + 0.0
|
||||
}
|
||||
|
||||
pub fn err_missing_args(args: Vec<String>) {
|
||||
let mut err = clap::Error::new(ErrorKind::MissingRequiredArgument);
|
||||
err.insert(ContextKind::InvalidArg, ContextValue::Strings(args));
|
||||
let _ = err.print();
|
||||
std::process::exit(1);
|
||||
pub fn get_host(url: url::Url) -> String {
|
||||
match url.host_str() {
|
||||
Some(addr) => {
|
||||
let port = match url.port() {
|
||||
Some(port) => port,
|
||||
None => 21324,
|
||||
};
|
||||
return format!("{}:{}",addr,port);
|
||||
},
|
||||
None => {
|
||||
println!("Invalid host");
|
||||
std::process::exit(1);
|
||||
},
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue