Initial commit
This commit is contained in:
commit
893fcb8fb1
7 changed files with 1501 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
863
Cargo.lock
generated
Normal file
863
Cargo.lock
generated
Normal file
|
@ -0,0 +1,863 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14cc0e06fb5f67e5d6beadf3a382fec9baca1aa751c6d5368fdeee7e5932c215"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"deflate",
|
||||
"flume",
|
||||
"half",
|
||||
"inflate",
|
||||
"lebe",
|
||||
"smallvec",
|
||||
"threadpool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.10.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ceeb589a3157cac0ab8cc585feb749bd2cea5cb55a6ee802ad72d9fd38303da"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"pin-project",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28edd9d7bc256be2502e325ac0628bde30b7001b9b52e0abe31a1a9dc2701212"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"png",
|
||||
"scoped_threadpool",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inflate"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"deflate",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped_threadpool"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "splink_client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"image",
|
||||
"rand",
|
||||
"termion",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.96"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"numtoa",
|
||||
"redox_syscall",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "threadpool"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
|
||||
dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cfada0986f446a770eca461e8c6566cb879682f7d687c8348aa0c857bd52286"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "splink_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
clap = { version = "3.1.18", features = ["derive", "deprecated"] }
|
||||
image = "0.24.2"
|
||||
rand = "0.8.5"
|
||||
termion = "1.5.6"
|
||||
thiserror = "1.0.31"
|
133
src/lib.rs
Normal file
133
src/lib.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
#![warn(clippy::pedantic)]
|
||||
|
||||
mod strandifier;
|
||||
pub use strandifier::{Strandifier, StrandifierError};
|
||||
|
||||
mod sender;
|
||||
pub use sender::{SenderError, send_strand, send_frame};
|
||||
|
||||
/// The LED wall's layout.
|
||||
///
|
||||
/// The wall consists of a grid of panels (`num_panels_h` by `num_panels_v`), each of which
|
||||
/// contains one strand. Each strand is wound in an S-shape, going sideways for `gang_len`
|
||||
/// pixels before going down and sideways again in the opposite direction. There are `num_gangs`
|
||||
/// of these gangs per strand.
|
||||
///
|
||||
/// The following is a strand with a `gang_len` of 5 and `num_gangs` of 4:
|
||||
///
|
||||
/// ```plain
|
||||
/// 1 5
|
||||
/// x - x - x - x - x
|
||||
/// |
|
||||
/// x - x - x - x - x
|
||||
/// |
|
||||
/// x - x - x - x - x
|
||||
/// |
|
||||
/// x - x - x - x - x
|
||||
/// 20 16
|
||||
/// ```
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Layout {
|
||||
pub gang_len: u32,
|
||||
pub num_gangs: u32,
|
||||
|
||||
pub num_panels_h: u32,
|
||||
pub num_panels_v: u32,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
/// The length of each strand.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use splink_client::Layout;
|
||||
/// let layout = Layout {
|
||||
/// gang_len: 5,
|
||||
/// num_gangs: 4,
|
||||
///
|
||||
/// num_panels_h: 3,
|
||||
/// num_panels_v: 6,
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(layout.strand_len(), 20);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn strand_len(&self) -> u32 {
|
||||
self.gang_len * self.num_gangs
|
||||
}
|
||||
|
||||
/// The number of strands/panels on the wall.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use splink_client::Layout;
|
||||
/// let layout = Layout {
|
||||
/// gang_len: 5,
|
||||
/// num_gangs: 4,
|
||||
///
|
||||
/// num_panels_h: 3,
|
||||
/// num_panels_v: 6,
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(layout.num_strands(), 18);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn num_strands(&self) -> u32 {
|
||||
self.num_panels_h * self.num_panels_v
|
||||
}
|
||||
|
||||
/// The total number of pixels on the wall.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use splink_client::Layout;
|
||||
/// let layout = Layout {
|
||||
/// gang_len: 5,
|
||||
/// num_gangs: 4,
|
||||
///
|
||||
/// num_panels_h: 3,
|
||||
/// num_panels_v: 6,
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(layout.num_pixels(), 360);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn num_pixels(&self) -> u32 {
|
||||
self.strand_len() * self.num_panels_h * self.num_panels_v
|
||||
}
|
||||
|
||||
/// The width of the wall, in pixels.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use splink_client::Layout;
|
||||
/// let layout = Layout {
|
||||
/// gang_len: 5,
|
||||
/// num_gangs: 4,
|
||||
///
|
||||
/// num_panels_h: 3,
|
||||
/// num_panels_v: 6,
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(layout.width_px(), 15);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn width_px(&self) -> u32 {
|
||||
self.gang_len * self.num_panels_h
|
||||
}
|
||||
|
||||
/// The height of the wall, in pixels.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use splink_client::Layout;
|
||||
/// let layout = Layout {
|
||||
/// gang_len: 5,
|
||||
/// num_gangs: 4,
|
||||
///
|
||||
/// num_panels_h: 3,
|
||||
/// num_panels_v: 6,
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(layout.height_px(), 24);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn height_px(&self) -> u32 {
|
||||
self.num_gangs * self.num_panels_v
|
||||
}
|
||||
}
|
124
src/main.rs
Normal file
124
src/main.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
#![warn(clippy::pedantic)]
|
||||
|
||||
use std::{
|
||||
f32::consts::FRAC_PI_2,
|
||||
io::stdout,
|
||||
net::{Ipv4Addr, SocketAddr, UdpSocket},
|
||||
thread::sleep,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use image::{Pixel, Rgb, RgbImage};
|
||||
use rand::Rng;
|
||||
|
||||
use splink_client::{Layout, send_frame};
|
||||
|
||||
/// Blinkenwall v3 prototype client
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Timeout in ms for frame completion responses (0 to disable)
|
||||
#[clap(long, default_value_t = 3)]
|
||||
response_timeout: u64,
|
||||
|
||||
/// Local address and port to bind to
|
||||
#[clap(long = "bind")]
|
||||
bind_addr: Option<SocketAddr>,
|
||||
|
||||
/// Controller's address
|
||||
remote_addr: SocketAddr,
|
||||
}
|
||||
|
||||
|
||||
fn get_frame(layout: Layout, frame: u32) -> RgbImage {
|
||||
#![allow(
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_lossless,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss
|
||||
)]
|
||||
|
||||
let w = layout.width_px();
|
||||
let h = layout.height_px();
|
||||
|
||||
let frame = frame % 60;
|
||||
let frame = frame as f32 / 3.0;
|
||||
|
||||
RgbImage::from_fn(w, h, |x, y| {
|
||||
let dist_from_center = {
|
||||
let x = x as f32;
|
||||
let y = y as f32;
|
||||
|
||||
let center_x = w as f32 / 2.0;
|
||||
let center_y = h as f32 / 2.0;
|
||||
|
||||
((x - center_x).powf(2.0) + (y - center_y).powf(2.0)).sqrt()
|
||||
};
|
||||
|
||||
let radius = frame.powf(1.8);
|
||||
|
||||
let dist_from_radius = dist_from_center - radius;
|
||||
let dist = 0.3 * dist_from_radius;
|
||||
|
||||
Rgb([100, 0, 100]).map(|x| {
|
||||
(if dist < 0.0 {
|
||||
(1.0 - (dist.abs() / 10.0)).max(0.0)
|
||||
} else {
|
||||
dist.min(FRAC_PI_2).cos()
|
||||
} * x as f32) as u8
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn print_image(image: &RgbImage) {
|
||||
let _hide = termion::cursor::HideCursor::from(stdout());
|
||||
|
||||
print!("{}", termion::cursor::Goto(1, 1));
|
||||
for row in image.rows() {
|
||||
for &Rgb([r, g, b]) in row {
|
||||
print!("{} ", termion::color::Rgb(r, g, b).bg_string());
|
||||
}
|
||||
println!("{}", termion::color::Reset.bg_str());
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
let bind_addr = args
|
||||
.bind_addr
|
||||
.unwrap_or_else(|| (Ipv4Addr::UNSPECIFIED, 0).into());
|
||||
let socket = UdpSocket::bind(bind_addr)?;
|
||||
|
||||
socket.connect(args.remote_addr)?;
|
||||
socket.set_read_timeout(match args.response_timeout {
|
||||
0 => None,
|
||||
t => Some(Duration::from_millis(t)),
|
||||
})?;
|
||||
|
||||
let layout = Layout {
|
||||
gang_len: 8,
|
||||
num_gangs: 32,
|
||||
|
||||
num_panels_h: 6,
|
||||
num_panels_v: 1,
|
||||
};
|
||||
|
||||
for frame in 0.. {
|
||||
let image = get_frame(layout, frame);
|
||||
|
||||
print_image(&image);
|
||||
|
||||
let frame_num: u32 = rand::thread_rng().gen();
|
||||
send_frame(&socket, layout, frame_num, &image)?;
|
||||
|
||||
sleep(Duration::from_millis(16));
|
||||
}
|
||||
|
||||
let image = RgbImage::from_fn(96, 64, |_x, y| {
|
||||
[Rgb([0, 3, 1]), Rgb([1, 3, 0]), Rgb([3, 0, 1])][y as usize % 3]
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
96
src/sender.rs
Normal file
96
src/sender.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
use std::{
|
||||
io::{self, ErrorKind},
|
||||
net::UdpSocket,
|
||||
};
|
||||
|
||||
use image::{Rgb, RgbImage};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{Layout, Strandifier, StrandifierError};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub enum SenderError {
|
||||
#[error("IO error")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("Strandifier failed")]
|
||||
Strandifier(#[from] StrandifierError),
|
||||
#[error("Controller failed to confirm frame")]
|
||||
ConfirmationTimeout,
|
||||
#[error("Invalid confirmation packet: {0:?}")]
|
||||
ConfirmationInvalid(Vec<u8>),
|
||||
|
||||
#[error("Confirmation packet for wrong frame number: expected {expected}, got {got}")]
|
||||
ConfirmationMismatched { expected: u32, got: u32 },
|
||||
}
|
||||
|
||||
/// Sends a strand's data to the controller.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if network communication fails.
|
||||
pub fn send_strand<I: IntoIterator<Item = Rgb<u8>>>(
|
||||
socket: &UdpSocket,
|
||||
strand_num: u32,
|
||||
frame_num: u32,
|
||||
pixels: I,
|
||||
) -> Result<(), SenderError> {
|
||||
const MAGIC: u32 = u32::from_be_bytes(*b"PIXL");
|
||||
|
||||
let pixels = pixels.into_iter().map(|c| {
|
||||
let mut bytes = [0; 4];
|
||||
bytes[1..].copy_from_slice(&c.0);
|
||||
|
||||
u32::from_be_bytes(bytes)
|
||||
});
|
||||
|
||||
let buf: Vec<_> = [MAGIC, strand_num, frame_num]
|
||||
.into_iter()
|
||||
.chain(pixels)
|
||||
.flat_map(u32::to_be_bytes)
|
||||
.collect();
|
||||
|
||||
socket.send(&buf)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends an entire image frame to the controller.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the image does not have the correct dimensions, if network communication
|
||||
/// fails or if the controller does not confirm reception of the frame.
|
||||
pub fn send_frame(
|
||||
socket: &UdpSocket,
|
||||
layout: Layout,
|
||||
frame_num: u32,
|
||||
image: &RgbImage,
|
||||
) -> Result<(), SenderError> {
|
||||
for strand_num in 0..layout.num_strands() {
|
||||
let data = Strandifier::make_strand(layout, image, strand_num)?;
|
||||
send_strand(socket, strand_num, frame_num, data)?;
|
||||
}
|
||||
|
||||
let mut buf = vec![0; 100];
|
||||
let len = match socket.recv(&mut buf) {
|
||||
Ok(len) => len,
|
||||
//Err(e) if e.kind() == ErrorKind::TimedOut => return Err(SenderError::ConfirmationTimeout),
|
||||
Err(e) => return Err(SenderError::ConfirmationTimeout),
|
||||
Err(e) => return Err(SenderError::Io(e)),
|
||||
};
|
||||
let buf = &buf[..len];
|
||||
let response_frame_num = u32::from_be_bytes(
|
||||
buf.try_into()
|
||||
.map_err(|_| SenderError::ConfirmationInvalid(buf.to_vec()))?,
|
||||
);
|
||||
|
||||
if response_frame_num != frame_num {
|
||||
return Err(SenderError::ConfirmationMismatched {
|
||||
expected: frame_num,
|
||||
got: response_frame_num,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
270
src/strandifier.rs
Normal file
270
src/strandifier.rs
Normal file
|
@ -0,0 +1,270 @@
|
|||
use std::iter::FusedIterator;
|
||||
|
||||
use image::{Rgb, RgbImage};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::Layout;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Error)]
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub enum StrandifierError {
|
||||
#[error("Wrong image dimensions")]
|
||||
WrongDimensions,
|
||||
#[error("Invalid strand number")]
|
||||
InvalidStrand,
|
||||
}
|
||||
|
||||
/// Iterator over a strand's pixels, extracted from an [`RgbImage`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Strandifier<'a> {
|
||||
layout: Layout,
|
||||
image: &'a RgbImage,
|
||||
|
||||
pixels_remaining: u32,
|
||||
next_x: u32,
|
||||
next_y: u32,
|
||||
}
|
||||
|
||||
impl<'a> Strandifier<'a> {
|
||||
/// Create an iterator over a strand's pixels.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `StrandifierError::WrongDimensions` if the `layout` does not match up with the
|
||||
/// `image`'s dimensions.
|
||||
///
|
||||
/// Returns `StrandifierError::InvalidStrand` if the supplied strand number is higher than the
|
||||
/// number of strands in the [`Layout`].
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn make_strand(
|
||||
layout: Layout,
|
||||
image: &'a RgbImage,
|
||||
strand_num: u32,
|
||||
) -> Result<Self, StrandifierError> {
|
||||
if layout.width_px() != image.width() || layout.height_px() != image.height() {
|
||||
return Err(StrandifierError::WrongDimensions);
|
||||
}
|
||||
|
||||
if strand_num > layout.num_strands() {
|
||||
return Err(StrandifierError::InvalidStrand);
|
||||
}
|
||||
|
||||
let panel_x = strand_num % layout.num_panels_h;
|
||||
let panel_y = strand_num / layout.num_panels_h;
|
||||
|
||||
let first_x = panel_x * layout.gang_len;
|
||||
let first_y = panel_y * layout.num_gangs;
|
||||
|
||||
Ok(Self {
|
||||
layout,
|
||||
image,
|
||||
|
||||
pixels_remaining: layout.strand_len(),
|
||||
next_x: first_x,
|
||||
next_y: first_y,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Strandifier<'a> {
|
||||
type Item = Rgb<u8>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.pixels_remaining == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let x = self.next_x;
|
||||
let y = self.next_y;
|
||||
|
||||
if (y % self.layout.num_gangs) % 2 == 0 {
|
||||
// Right-moving gang
|
||||
if x % self.layout.gang_len == self.layout.gang_len - 1 {
|
||||
// End of gang - move down
|
||||
self.next_y += 1;
|
||||
} else {
|
||||
// move right
|
||||
self.next_x += 1;
|
||||
}
|
||||
} else {
|
||||
// Left-moving gang
|
||||
if x % self.layout.gang_len == 0 {
|
||||
// End of gang - move down
|
||||
self.next_y += 1;
|
||||
} else {
|
||||
// move left
|
||||
self.next_x -= 1;
|
||||
}
|
||||
}
|
||||
self.pixels_remaining -= 1;
|
||||
|
||||
Some(*self.image.get_pixel(x, y))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let remaining = self.pixels_remaining.try_into().unwrap();
|
||||
|
||||
(remaining, Some(remaining))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FusedIterator for Strandifier<'a> {}
|
||||
impl<'a> ExactSizeIterator for Strandifier<'a> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn test_image(layout: Layout) -> RgbImage {
|
||||
let width = layout.width_px();
|
||||
let height = layout.height_px();
|
||||
|
||||
RgbImage::from_fn(width, height, |x, y| {
|
||||
Rgb([x.try_into().unwrap(), y.try_into().unwrap(), 0])
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strandifier_basic() {
|
||||
let layout = Layout {
|
||||
gang_len: 5,
|
||||
num_gangs: 4,
|
||||
|
||||
num_panels_h: 3,
|
||||
num_panels_v: 6,
|
||||
};
|
||||
let image = test_image(layout);
|
||||
|
||||
let strand = Strandifier::make_strand(layout, &image, 0).unwrap();
|
||||
assert_eq!(
|
||||
strand.collect::<Vec<_>>(),
|
||||
[
|
||||
Rgb([0, 0, 0]),
|
||||
Rgb([1, 0, 0]),
|
||||
Rgb([2, 0, 0]),
|
||||
Rgb([3, 0, 0]),
|
||||
Rgb([4, 0, 0]),
|
||||
//
|
||||
Rgb([4, 1, 0]),
|
||||
Rgb([3, 1, 0]),
|
||||
Rgb([2, 1, 0]),
|
||||
Rgb([1, 1, 0]),
|
||||
Rgb([0, 1, 0]),
|
||||
//
|
||||
Rgb([0, 2, 0]),
|
||||
Rgb([1, 2, 0]),
|
||||
Rgb([2, 2, 0]),
|
||||
Rgb([3, 2, 0]),
|
||||
Rgb([4, 2, 0]),
|
||||
//
|
||||
Rgb([4, 3, 0]),
|
||||
Rgb([3, 3, 0]),
|
||||
Rgb([2, 3, 0]),
|
||||
Rgb([1, 3, 0]),
|
||||
Rgb([0, 3, 0]),
|
||||
]
|
||||
);
|
||||
|
||||
let strand = Strandifier::make_strand(layout, &image, 4).unwrap();
|
||||
assert_eq!(
|
||||
strand.collect::<Vec<_>>(),
|
||||
[
|
||||
Rgb([5, 4, 0]),
|
||||
Rgb([6, 4, 0]),
|
||||
Rgb([7, 4, 0]),
|
||||
Rgb([8, 4, 0]),
|
||||
Rgb([9, 4, 0]),
|
||||
//
|
||||
Rgb([9, 5, 0]),
|
||||
Rgb([8, 5, 0]),
|
||||
Rgb([7, 5, 0]),
|
||||
Rgb([6, 5, 0]),
|
||||
Rgb([5, 5, 0]),
|
||||
//
|
||||
Rgb([5, 6, 0]),
|
||||
Rgb([6, 6, 0]),
|
||||
Rgb([7, 6, 0]),
|
||||
Rgb([8, 6, 0]),
|
||||
Rgb([9, 6, 0]),
|
||||
//
|
||||
Rgb([9, 7, 0]),
|
||||
Rgb([8, 7, 0]),
|
||||
Rgb([7, 7, 0]),
|
||||
Rgb([6, 7, 0]),
|
||||
Rgb([5, 7, 0]),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strandifier_thin() {
|
||||
let layout = Layout {
|
||||
gang_len: 1,
|
||||
num_gangs: 6,
|
||||
|
||||
num_panels_h: 3,
|
||||
num_panels_v: 4,
|
||||
};
|
||||
let image = test_image(layout);
|
||||
|
||||
let strand = Strandifier::make_strand(layout, &image, 0).unwrap();
|
||||
assert_eq!(
|
||||
strand.collect::<Vec<_>>(),
|
||||
[
|
||||
Rgb([0, 0, 0]),
|
||||
Rgb([0, 1, 0]),
|
||||
Rgb([0, 2, 0]),
|
||||
Rgb([0, 3, 0]),
|
||||
Rgb([0, 4, 0]),
|
||||
Rgb([0, 5, 0]),
|
||||
]
|
||||
);
|
||||
|
||||
let layout = Layout {
|
||||
gang_len: 6,
|
||||
num_gangs: 1,
|
||||
|
||||
num_panels_h: 3,
|
||||
num_panels_v: 4,
|
||||
};
|
||||
let image = test_image(layout);
|
||||
|
||||
let strand = Strandifier::make_strand(layout, &image, 11).unwrap();
|
||||
assert_eq!(
|
||||
strand.collect::<Vec<_>>(),
|
||||
[
|
||||
Rgb([12, 3, 0]),
|
||||
Rgb([13, 3, 0]),
|
||||
Rgb([14, 3, 0]),
|
||||
Rgb([15, 3, 0]),
|
||||
Rgb([16, 3, 0]),
|
||||
Rgb([17, 3, 0]),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strandifier_errors() {
|
||||
let layout = Layout {
|
||||
gang_len: 5,
|
||||
num_gangs: 4,
|
||||
|
||||
num_panels_h: 3,
|
||||
num_panels_v: 6,
|
||||
};
|
||||
let image = test_image(layout);
|
||||
assert_eq!(
|
||||
Strandifier::make_strand(layout, &image, 20).unwrap_err(),
|
||||
StrandifierError::InvalidStrand
|
||||
);
|
||||
|
||||
let layout2 = Layout {
|
||||
num_gangs: 6,
|
||||
..layout
|
||||
};
|
||||
assert_eq!(
|
||||
Strandifier::make_strand(layout2, &image, 0).unwrap_err(),
|
||||
StrandifierError::WrongDimensions
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue