Compare commits

...

15 Commits

Author SHA1 Message Date
Tobias Ollive
66c431f20c improve time management 2022-03-18 14:41:16 +01:00
Tobias Ollive
b332d75c7f refactor and fix starrandom error 2022-03-16 18:44:14 +01:00
Tobias Ollive
6deb31836f add random stars pattern
fix wrong multi devices selection
add color section to simplify devices section
2022-03-15 16:18:29 +01:00
Tobias Ollive
13326a49d9 use macro rule for default implementation of pattern 2022-03-13 10:16:40 +01:00
Tobias Ollive
20b8f6d4db add ring and ringscanner pattern 2022-03-12 21:20:55 +01:00
Tobias Ollive
4dd5587b49 add fill_unstable and use rust fmt 2022-03-12 20:52:37 +01:00
Tobias Ollive
ae215ce252 add fade_random pattern 2022-03-12 20:45:27 +01:00
Tobias Ollive
a9abc2e05b add rain pattern 2022-03-11 18:43:48 +01:00
Tobias Ollive
ee133379f2 add background color for color wipe and my first unit test ! 2022-03-11 17:10:33 +01:00
Tobias Ollive
b718334392 refactor patterns 2022-03-11 12:40:12 +01:00
Tobias Ollive
c2b2517661 add blink pattern 2022-03-10 09:54:02 +01:00
Tobias Ollive
4451917154 add fillUnstable pattern 2022-03-09 18:09:26 +01:00
Tobias Ollive
086e194a1d add first audio support 2022-03-09 17:58:31 +01:00
Tobias Ollive
4c788f7313 add ability to play separate spectacles. 2022-03-09 15:53:42 +01:00
Tobias Ollive
3d8adc0d52 add fillRandom pattern 2022-03-02 18:47:03 +01:00
25 changed files with 2184 additions and 356 deletions

787
Cargo.lock generated
View File

@@ -2,12 +2,64 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "alsa"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b"
dependencies = [
"alsa-sys",
"bitflags",
"libc",
"nix",
]
[[package]]
name = "alsa-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
dependencies = [
"libc",
"pkg-config",
]
[[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.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bindgen"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -42,6 +94,18 @@ dependencies = [
"uuid",
]
[[package]]
name = "bumpalo"
version = "3.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.1.0"
@@ -53,6 +117,24 @@ name = "cc"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
dependencies = [
"jobserver",
]
[[package]]
name = "cesu8"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
@@ -60,6 +142,113 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "3.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_derive"
version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16"
dependencies = [
"heck 0.4.0",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "claxon"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688"
[[package]]
name = "combine"
version = "4.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "coreaudio-rs"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88"
dependencies = [
"bitflags",
"coreaudio-sys",
]
[[package]]
name = "coreaudio-sys"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca4679a59dbd8c15f064c012dfe8c1163b9453224238b59bb9328c142b8b248b"
dependencies = [
"bindgen",
]
[[package]]
name = "cpal"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116"
dependencies = [
"alsa",
"core-foundation-sys",
"coreaudio-rs",
"jni",
"js-sys",
"lazy_static",
"libc",
"mach",
"ndk",
"ndk-glue",
"nix",
"oboe",
"parking_lot",
"stdweb",
"thiserror",
"web-sys",
"winapi",
]
[[package]]
name = "custom_debug"
version = "0.5.0"
@@ -80,6 +269,41 @@ dependencies = [
"synstructure",
]
[[package]]
name = "darling"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "dbus"
version = "0.9.5"
@@ -124,6 +348,12 @@ dependencies = [
"syn",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "futures"
version = "0.3.19"
@@ -224,6 +454,12 @@ dependencies = [
"wasi",
]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hashbrown"
version = "0.11.2"
@@ -239,6 +475,12 @@ dependencies = [
"unicode-segmentation",
]
[[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"
@@ -254,6 +496,18 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hound"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "1.8.0"
@@ -279,12 +533,67 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "jni"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
dependencies = [
"cesu8",
"combine",
"jni-sys",
"log",
"thiserror",
"walkdir",
]
[[package]]
name = "jni-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
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 = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lewton"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030"
dependencies = [
"byteorder",
"ogg",
"tinyvec",
]
[[package]]
name = "libc"
version = "0.2.113"
@@ -300,6 +609,16 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "libloading"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "linked-hash-map"
version = "0.5.4"
@@ -324,6 +643,15 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.4.1"
@@ -339,6 +667,26 @@ dependencies = [
"autocfg",
]
[[package]]
name = "minimp3"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985438f75febf74c392071a975a29641b420dd84431135a6e6db721de4b74372"
dependencies = [
"minimp3-sys",
"slice-deque",
"thiserror",
]
[[package]]
name = "minimp3-sys"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e21c73734c69dc95696c9ed8926a2b393171d98b3f5f5935686a26a487ab9b90"
dependencies = [
"cc",
]
[[package]]
name = "mio"
version = "0.7.14"
@@ -361,6 +709,62 @@ dependencies = [
"winapi",
]
[[package]]
name = "ndk"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4"
dependencies = [
"bitflags",
"jni-sys",
"ndk-sys",
"num_enum",
"thiserror",
]
[[package]]
name = "ndk-context"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e3c5cc68637e21fe8f077f6a1c9e0b9ca495bb74895226b476310f613325884"
[[package]]
name = "ndk-glue"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9ffb7443daba48349d545028777ca98853b018b4c16624aa01223bc29e078da"
dependencies = [
"lazy_static",
"libc",
"log",
"ndk",
"ndk-context",
"ndk-macro",
"ndk-sys",
]
[[package]]
name = "ndk-macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
dependencies = [
"darling",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "ndk-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97"
dependencies = [
"jni-sys",
]
[[package]]
name = "nix"
version = "0.23.1"
@@ -374,6 +778,16 @@ dependencies = [
"memoffset",
]
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"memchr",
"version_check",
]
[[package]]
name = "ntapi"
version = "0.3.6"
@@ -413,12 +827,74 @@ dependencies = [
"libc",
]
[[package]]
name = "num_enum"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "oboe"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2463c8f2e19b4e0d0710a21f8e4011501ff28db1c95d7a5482a553b2100502d2"
dependencies = [
"jni",
"ndk",
"ndk-glue",
"num-derive",
"num-traits",
"oboe-sys",
]
[[package]]
name = "oboe-sys"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd"
dependencies = [
"cc",
]
[[package]]
name = "ogg"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e"
dependencies = [
"byteorder",
]
[[package]]
name = "once_cell"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
@@ -444,6 +920,20 @@ dependencies = [
"winapi",
]
[[package]]
name = "pattern_derive"
version = "0.1.0"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pin-project"
version = "1.0.10"
@@ -482,6 +972,46 @@ version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-crate"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
dependencies = [
"thiserror",
"toml",
]
[[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.36"
@@ -500,6 +1030,36 @@ 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 = "redox_syscall"
version = "0.2.10"
@@ -509,24 +1069,71 @@ dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rodio"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0939e9f626e6c6f1989adb6226a039c855ca483053f0ee7c98b90e41cf731e"
dependencies = [
"claxon",
"cpal",
"hound",
"lewton",
"minimp3",
]
[[package]]
name = "rust_lum"
version = "0.1.0"
dependencies = [
"bluer",
"clap",
"futures",
"pattern_derive",
"rand",
"rodio",
"serde",
"serde_derive",
"serde_yaml",
"tokio",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@@ -576,6 +1183,12 @@ dependencies = [
"yaml-rust",
]
[[package]]
name = "shlex"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@@ -591,12 +1204,35 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
[[package]]
name = "slice-deque"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31ef6ee280cdefba6d2d0b4b78a84a1c1a3f3a4cec98c2d4231c8bc225de0f25"
dependencies = [
"libc",
"mach",
"winapi",
]
[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "stdweb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.22.0"
@@ -612,7 +1248,7 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb"
dependencies = [
"heck",
"heck 0.3.3",
"proc-macro2",
"quote",
"syn",
@@ -641,6 +1277,56 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[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.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.15.0"
@@ -682,6 +1368,15 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
@@ -703,12 +1398,93 @@ dependencies = [
"getrandom",
]
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
[[package]]
name = "web-sys"
version = "0.3.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
@@ -725,6 +1501,15 @@ 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"

View File

@@ -9,7 +9,11 @@ edition = "2018"
bluer = "0.13.1"
tokio = {version = "1.15.0", features = ["full"]}
futures = "0.3.19"
rand = "0.8.5"
rodio = "0.15.0"
clap = { version = "3.1.6", features = ["derive"] }
pattern_derive = { path = "pattern_derive" }
serde_derive = "1.0.136"
serde = "1.0.136"
serde_yaml = "0.8"
serde_yaml = "0.8"

View File

@@ -1,36 +1,81 @@
- pattern:
type: Fade
current_iteration: 0
nbr_iterations: 20
begin_color:
red: 255
green: 0
blue: 0
end_color:
red: 0
green: 0
blue: 255
period:
secs: 0
nanos: 200000000
- pattern:
type: ColorWipe
current_iteration: 0
color:
red: 0
green: 255
blue: 0
infinite: false
period:
secs: 0
nanos: 100000000
- pattern:
type: Scanner
current_iteration: 0
color:
red: 0
green: 0
blue: 255
period:
secs: 0
nanos: 100000000
colors:
- &red
red: 255
green: 0
blue: 0
devices:
- name : toto
mac_addresses :
- C9:81:9C:BA:53:BC
sequence:
- pattern:
type: Rain
color: &blue
red: 0
green: 0
blue: 255
background_color: &yellow
red: 0
green: 255
blue: 255
stability: 10
lines: 5
period: 200
- pattern:
type: Blink
fade:
current_iteration: 10
steps: 20
begin_color: &black
red: 0
green: 0
blue: 0
end_color: &purple
red: 255
green: 0
blue: 255
max_iteration: 10
period: 50
- pattern:
type: Fade
steps: 20
begin_color:
red: 255
green: 0
blue: 0
end_color: *blue
period: 200
- pattern:
type: ColorWipe
max_iteration: 25
color: &green
red: 0
green: 255
blue: 0
period: 100
- pattern:
type: FillRandom
color: *blue
stability: 40
period: 80
- name: titi
sequence:
- pattern:
type: StarsRandom
color: *green
ratio: 8
steps: 20
background_color:
red: 0
green: 255
blue: 100
period: 50
- pattern:
type: Rainbow
max_iteration: 200
period: 10
- pattern:
type: Scanner
color: *blue
period: 100

13
src/args.rs Normal file
View File

@@ -0,0 +1,13 @@
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
pub struct Cli {
/// file for light sequences
#[clap(short, long)]
pub spectacle_file: String,
/// musique file that will be played
#[clap(short, long)]
pub musique_file: String,
}

18
src/audio.rs Normal file
View File

@@ -0,0 +1,18 @@
use rodio::{Decoder, OutputStream, Sink};
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
pub fn play_sound(path: &Path) {
println!("starting musique");
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
let sink = Sink::try_new(&stream_handle).unwrap();
let file = BufReader::new(File::open(path).unwrap());
let source = Some(Decoder::new(file).unwrap()).unwrap();
sink.append(source);
// The sound plays in a separate thread. This call will block the current thread until the sink
// has finished playing all its queued sounds.
sink.sleep_until_end();
}

View File

@@ -39,7 +39,6 @@ pub(crate) async fn bluetooth_scan(
while let Some(evt) = discover.next().await {
match evt {
AdapterEvent::DeviceAdded(addr) => {
println!("new device {}", addr);
if already_scanned.contains(&addr) {
continue;
}
@@ -49,7 +48,6 @@ pub(crate) async fn bluetooth_scan(
Ok(service_found) => {
if service_found {
tx.send(device).await.unwrap();
println!("found service in device {}", addr);
}
}
Err(_) => continue,

24
src/config.rs Normal file
View File

@@ -0,0 +1,24 @@
use crate::pixel_color::PixelColor;
use crate::runner::DeviceSequence;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fs;
#[derive(Serialize, Deserialize)]
pub struct Config<const N: usize> {
devices: Vec<Device<N>>,
colors: Vec<PixelColor>,
}
#[derive(Deserialize, Serialize)]
pub struct Device<const N: usize> {
pub mac_addresses: Option<HashSet<String>>,
name: Option<String>,
pub sequence: DeviceSequence<N>,
}
pub fn load_from_file<const N: usize>(file: &std::path::Path) -> Vec<Device<N>> {
let file = fs::read_to_string(file).unwrap();
let config: Config<N> = serde_yaml::from_str(&*file).unwrap();
config.devices
}

View File

@@ -1,14 +1,22 @@
mod args;
mod audio;
mod bluetooth;
mod config;
mod patterns;
mod pixel_color;
mod runner;
mod spectacle;
use crate::patterns::{ColorWipe, Fade, Patterns, PixelColor};
use crate::runner::Runner;
use crate::args::Cli;
use crate::audio::play_sound;
use crate::config::load_from_file;
use crate::runner::Spectacle;
use bluer::gatt::remote::Characteristic;
use bluer::{Device, Error};
use clap::Parser;
use patterns::Strip;
use std::path::Path;
use std::time::Duration;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
use tokio::sync::watch;
@@ -19,48 +27,15 @@ const BASE_STRIP_DATA: [u8; 3] = [0x00, 0x00, 0x01];
#[tokio::main(flavor = "current_thread")]
async fn main() -> bluer::Result<()> {
let pixel = PixelColor {
red: 231,
green: 124,
blue: 0,
};
let command_line = Cli::parse();
let fade = Fade::<25> {
current_iteration: 0,
nbr_iterations: 0,
begin_color: pixel,
end_color: Default::default(),
};
let context = runner::Context {
pattern: Patterns::Fade(fade),
period: Duration::from_millis(300),
};
let mut runner = runner::Runner::<25>::new();
runner.push(context);
let context = runner::Context {
pattern: Patterns::ColorWipe(ColorWipe {
current_iteration: 0,
color: Default::default(),
infinite: false,
}),
period: Default::default(),
};
runner.push(context);
let yaml = serde_yaml::to_string(&runner).unwrap();
println!("{}", yaml);
let file = Path::new("spectacle.yml");
let config: Runner<25> = Runner::load_from_file(file);
println!("{:?}", config);
let file = Path::new(&command_line.spectacle_file);
let config = load_from_file(file);
let spectacle = Spectacle::from(config);
let spectacle = Arc::new(Mutex::new(spectacle));
let adapter = bluetooth::create_session().await?;
let (tx_scan, mut rx_scan) = mpsc::channel(3);
let (tx_scan, rx_scan) = mpsc::channel(3);
tokio::spawn(bluetooth::bluetooth_scan(
tx_scan,
@@ -68,35 +43,55 @@ async fn main() -> bluer::Result<()> {
SERVICE_UUID,
));
let (tx, rx) = watch::channel(Strip::<STRIP_SIZE>::default());
tokio::spawn(connect_device(spectacle.clone(), rx_scan));
tokio::spawn(async move {
while let Some(device) = rx_scan.recv().await {
bluetooth::connect(&device, 3).await?;
let char = bluetooth::get_char(&device, SERVICE_UUID, PIXEL_DATA_UUID).await?;
if let Some(char) = char {
let mut rx_device = rx.clone();
println!("starting");
let musique_file = command_line.musique_file;
let musique = tokio::task::spawn_blocking(move || play_sound(Path::new(&musique_file)));
let futures;
{
let mut spectacle = spectacle.lock().unwrap();
futures = spectacle.run();
}
let mut joinhandles = vec![];
for future in futures {
joinhandles.push(tokio::spawn(future));
}
for joinhandle in joinhandles {
joinhandle.await.unwrap();
}
musique.await.unwrap();
Ok(())
}
async fn connect_device(
spectacle: Arc<Mutex<Spectacle<STRIP_SIZE>>>,
mut rx_scan: mpsc::Receiver<Device>,
) -> Result<(), Error> {
while let Some(device) = rx_scan.recv().await {
bluetooth::connect(&device, 3).await?;
let char = bluetooth::get_char(&device, SERVICE_UUID, PIXEL_DATA_UUID).await?;
if let Some(char) = char {
let rx: Option<watch::Receiver<Strip<STRIP_SIZE>>>;
{
let spectacle = spectacle.lock().unwrap();
rx = spectacle.get_channel(&device.address());
}
if let Some(mut rx) = rx {
tokio::spawn(async move {
println!("device connected : {}", &device.address());
while rx_device.changed().await.is_ok() {
let strip = *rx_device.borrow();
while rx.changed().await.is_ok() {
let strip = *rx.borrow();
if write_strip(&strip, &char).await.is_err() {
break;
};
}
println!("device {} disconnected", &device.address());
// drop(rx_device);
});
}
}
bluer::Result::Ok(())
});
println!("starting");
config.runner_loop(tx).await;
tokio::time::sleep(Duration::from_secs(5)).await;
println!("error sending value");
Ok(())
}
bluer::Result::Ok(())
}
pub async fn write_strip<const N: usize>(

View File

@@ -1,16 +1,95 @@
use serde_derive::{Deserialize, Serialize};
use std::cmp::{max, min, Ordering};
use std::convert::Infallible;
use std::fmt;
use std::ops::Div;
mod blink;
mod color_wipe;
mod fade;
mod fade_random;
mod fade_unstable;
mod fill_random;
mod fill_unstable;
mod rain;
mod rainbow;
mod ring;
mod ring_scanner;
mod scanner;
mod stars_random;
#[derive(Serialize, Deserialize, Debug)]
use crate::patterns::blink::Blink;
use crate::patterns::color_wipe::ColorWipe;
use crate::patterns::fade::Fade;
use crate::patterns::fade_random::FadeRandom;
use crate::patterns::fade_unstable::FadeUnstable;
use crate::patterns::fill_random::FillRandom;
use crate::patterns::fill_unstable::FillUnstable;
use crate::patterns::rain::Rain;
use crate::patterns::rainbow::Rainbow;
use crate::patterns::ring::Ring;
use crate::patterns::ring_scanner::RingScanner;
use crate::patterns::scanner::Scanner;
use crate::patterns::stars_random::StarsRandom;
use crate::pixel_color::PixelColor;
use serde_derive::{Deserialize, Serialize};
use std::ops::{Index, IndexMut};
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
#[serde(tag = "type")]
pub(crate) enum Patterns<const N: usize> {
Rainbow(Rainbow<N>),
Fade(Fade<N>),
ColorWipe(ColorWipe<N>),
Scanner(Scanner<N>),
FillRandom(FillRandom<N>),
FillUnstable(FillUnstable<N>),
Blink(Blink<N>),
Rain(Rain<N>),
FadeRandom(FadeRandom<N>),
FadeUnstable(FadeUnstable<N>),
Ring(Ring<N>),
RingScanner(RingScanner<N>),
StarsRandom(StarsRandom<N>),
}
/// Pattern that have to be implemented
///
///
///
///
pub trait Pattern: Iterator {
type Strip;
fn init(&mut self) -> Option<Self::Strip> {
None
}
fn is_last_iteration(&self) -> bool;
}
#[macro_export]
macro_rules! impl_pattern_init_background {
($generic: tt) => {
fn init(&mut self) -> Option<Self::Strip> {
if let Some(color) = self.background_color {
Some(Strip::<N>::new(color))
} else {
None
}
}
};
}
#[macro_export]
macro_rules! impl_pattern_last_iteration {
($name:ty) => {
fn is_last_iteration(&self) -> bool {
match self.max_iteration {
None => false,
Some(max_iter) => {
if self.current_iteration >= max_iter {
true
} else {
false
}
}
}
}
};
}
impl<const N: usize> Iterator for Patterns<N> {
@@ -22,44 +101,83 @@ impl<const N: usize> Iterator for Patterns<N> {
Patterns::Fade(p) => p.next(),
Patterns::ColorWipe(p) => p.next(),
Patterns::Scanner(p) => p.next(),
Patterns::FillRandom(p) => p.next(),
Patterns::FillUnstable(p) => p.next(),
Patterns::Blink(p) => p.next(),
Patterns::Rain(p) => p.next(),
Patterns::FadeRandom(p) => p.next(),
Patterns::FadeUnstable(p) => p.next(),
Patterns::Ring(p) => p.next(),
Patterns::RingScanner(p) => p.next(),
Patterns::StarsRandom(p) => p.next(),
}
}
}
#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
pub struct PixelColor {
pub(crate) red: u8,
pub(crate) green: u8,
pub(crate) blue: u8,
}
impl<const N: usize> Pattern for Patterns<N> {
type Strip = Strip<N>;
impl PixelColor {
fn new() -> Self {
Default::default()
fn init(&mut self) -> Option<Self::Strip> {
match self {
Patterns::Rainbow(p) => p.init(),
Patterns::Fade(p) => p.init(),
Patterns::ColorWipe(p) => p.init(),
Patterns::Scanner(p) => p.init(),
Patterns::FillRandom(p) => p.init(),
Patterns::FillUnstable(p) => p.init(),
Patterns::Blink(p) => p.init(),
Patterns::Rain(p) => p.init(),
Patterns::FadeRandom(p) => p.init(),
Patterns::FadeUnstable(p) => p.init(),
Patterns::Ring(p) => p.init(),
Patterns::RingScanner(p) => p.init(),
Patterns::StarsRandom(p) => p.init(),
}
}
}
impl fmt::Display for PixelColor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{},{},{}]", self.red, self.green, self.blue)
}
}
impl Div<u8> for PixelColor {
type Output = PixelColor;
fn div(self, rhs: u8) -> Self::Output {
PixelColor {
red: self.red / rhs,
green: self.green / rhs,
blue: self.blue / rhs,
fn is_last_iteration(&self) -> bool {
match self {
Patterns::Rainbow(p) => p.is_last_iteration(),
Patterns::Fade(p) => p.is_last_iteration(),
Patterns::ColorWipe(p) => p.is_last_iteration(),
Patterns::Scanner(p) => p.is_last_iteration(),
Patterns::FillRandom(p) => p.is_last_iteration(),
Patterns::FillUnstable(p) => p.is_last_iteration(),
Patterns::Blink(p) => p.is_last_iteration(),
Patterns::Rain(p) => p.is_last_iteration(),
Patterns::FadeRandom(p) => p.is_last_iteration(),
Patterns::FadeUnstable(p) => p.is_last_iteration(),
Patterns::Ring(p) => p.is_last_iteration(),
Patterns::RingScanner(p) => p.is_last_iteration(),
Patterns::StarsRandom(p) => p.is_last_iteration(),
}
}
}
#[derive(Copy, Clone)]
pub struct Strip<const N: usize> {
pub strip: [PixelColor; N],
///
/// a basic newtype based on a fix array size.
///
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct Strip<const N: usize>([Option<PixelColor>; N]);
impl<const N: usize> Default for Strip<N> {
fn default() -> Self {
Strip([None; N])
}
}
impl<const N: usize> Index<usize> for Strip<N> {
type Output = Option<PixelColor>;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl<const N: usize> IndexMut<usize> for Strip<N> {
fn index_mut(&mut self, index: usize) -> &mut Option<PixelColor> {
&mut self.0[index]
}
}
impl<const N: usize> Strip<N> {
@@ -67,204 +185,19 @@ impl<const N: usize> Strip<N> {
let mut data: Vec<u8> = vec![];
for i in 0..N {
data.append(&mut vec![
self.strip[i].green,
self.strip[i].red,
self.strip[i].blue,
]);
let color = self[i].unwrap_or(PixelColor::default());
data.append(&mut vec![color.green, color.red, color.blue]);
}
data
}
pub fn fill(&mut self, color: PixelColor) {
self.strip = [color; N];
}
}
impl<const N: usize> Default for Strip<N> {
fn default() -> Self {
pub fn new(color: PixelColor) -> Strip<N> {
Strip {
strip: [PixelColor::new(); N],
0: [Some(color); N],
}
}
}
/// Rainbow pattern /////////////////////////////////////////////////
#[derive(Serialize, Deserialize, Debug)]
pub struct Rainbow<const N: usize> {
pub(crate) current_iteration: usize,
pub(crate) max_iteration: Option<usize>,
pub(crate) step: Option<usize>,
}
impl<const N: usize> Iterator for Rainbow<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(nbr_iteration) = self.max_iteration {
if nbr_iteration == self.current_iteration {
return None;
}
}
let mut strip = Strip::default();
let step = self.step.unwrap_or(255 / N);
for i in 0..N {
let pos = (i * step + self.current_iteration) as u8;
strip.strip[i] = wheel(pos)
}
self.current_iteration += 1;
Some(strip)
}
}
fn wheel(index: u8) -> PixelColor {
let pos = 255 - index;
match pos {
0..=85 => PixelColor {
red: 255 - (pos * 3),
green: 0,
blue: pos * 3,
},
86..=170 => {
let pos = pos - 85;
PixelColor {
red: 0,
green: pos * 3,
blue: 255 - (pos * 3),
}
}
_ => {
let pos = pos - 170;
PixelColor {
red: pos * 3,
green: 255 - (pos * 3),
blue: 0,
}
}
}
}
//////////////////////////////////////////////////////////////////////////
/// Colorwipe pattern ////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#[derive(Serialize, Deserialize, Debug)]
pub struct ColorWipe<const N: usize> {
pub(crate) current_iteration: usize,
pub(crate) color: PixelColor,
pub(crate) infinite: bool,
}
impl<const N: usize> Iterator for ColorWipe<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
let mut strip = Strip::default();
if self.infinite {
self.current_iteration %= N;
} else if self.current_iteration >= N {
return None;
}
for i in 0..self.current_iteration {
strip.strip[i] = self.color;
}
self.current_iteration += 1;
Some(strip)
}
}
//////////////////////////////////////////////////////////////////////////
/// fade pattern ////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#[derive(Serialize, Deserialize, Debug)]
pub struct Fade<const N: usize> {
pub(crate) current_iteration: usize,
pub(crate) nbr_iterations: usize,
pub(crate) begin_color: PixelColor,
pub(crate) end_color: PixelColor,
}
fn fade_value(value_start: u8, value_end: u8, index: usize, nbr_iter: usize) -> u8 {
((value_start as usize * (nbr_iter - index) + value_end as usize * index) / nbr_iter) as u8
}
impl<const N: usize> Iterator for Fade<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
let mut strip = Strip::default();
if self.current_iteration >= self.nbr_iterations {
return None;
}
let red = fade_value(
self.begin_color.red,
self.end_color.red,
self.current_iteration,
self.nbr_iterations,
);
let green = fade_value(
self.begin_color.green,
self.end_color.green,
self.current_iteration,
self.nbr_iterations,
);
let blue = fade_value(
self.begin_color.blue,
self.end_color.blue,
self.current_iteration,
self.nbr_iterations,
);
let current_color = PixelColor { red, green, blue };
strip.fill(current_color);
self.current_iteration += 1;
Some(strip)
}
}
//////////////////////////////////////////////////////////////////////////
/// scanner pattern ////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#[derive(Serialize, Deserialize, Debug)]
pub struct Scanner<const N: usize> {
pub(crate) current_iteration: usize,
pub(crate) color: PixelColor,
pub(crate) background_color: Option<PixelColor>,
}
impl<const N: usize> Iterator for Scanner<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
let mut strip = Strip::<N>::default();
if let Some(c) = self.background_color {
strip.fill(c);
} else {
strip.fill(PixelColor::default());
}
let current_led = self.current_iteration % N;
let min_led;
if current_led < 8 {
min_led = 0;
} else {
min_led = current_led - 8;
}
let mut c = self.color;
for i in min_led..current_led {
strip.strip[i] = c;
c = c / 2;
println!("{}", c);
}
self.current_iteration += 1;
Some(strip)
pub fn fill(&mut self, color: PixelColor) {
self.0.fill(Some(color));
}
}

40
src/patterns/blink.rs Normal file
View File

@@ -0,0 +1,40 @@
use crate::patterns::fade::Fade;
use crate::patterns::Pattern;
use crate::{impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # Blink
///
/// fade from one color to an other then go back several times
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct Blink<const N: usize> {
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) max_iteration: Option<usize>,
fade: Fade<N>,
}
impl<const N: usize> Pattern for Blink<N> {
type Strip = Strip<N>;
impl_pattern_last_iteration!(ColorWipe);
}
impl<const N: usize> Iterator for Blink<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
let value = self.fade.next();
match value {
None => {
if self.is_last_iteration() {
return None;
}
self.current_iteration += 1;
self.fade.current_iteration = 0;
std::mem::swap(&mut self.fade.begin_color, &mut self.fade.end_color);
self.fade.next()
}
Some(value) => Some(value),
}
}
}

View File

@@ -0,0 +1,50 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # Colorwipe pattern
/// every pattern implement the iterator trait
///
/// This pattern fill the strip with a specific color, one by one.
///
/// ### Note
///
/// setting max_iteration to None lead the pattern to loop infinitely
///
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct ColorWipe<const N: usize> {
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) max_iteration: Option<usize>,
pub(crate) color: PixelColor,
pub(crate) background_color: Option<PixelColor>,
}
impl<const N: usize> Pattern for ColorWipe<N> {
type Strip = Strip<N>;
impl_pattern_init_background!(N);
impl_pattern_last_iteration!(ColorWipe);
}
impl<const N: usize> Iterator for ColorWipe<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_last_iteration() {
return None;
}
let mut strip = Strip::default();
if let Some(c) = self.background_color {
strip.fill(c);
}
let iteration = self.current_iteration % N;
for i in 0..iteration {
strip[i] = Some(self.color);
}
self.current_iteration += 1;
Some(strip)
}
}

65
src/patterns/fade.rs Normal file
View File

@@ -0,0 +1,65 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::Strip;
use serde::{Deserialize, Serialize};
/// # fade pattern
/// fade from one color to an other
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct Fade<const N: usize> {
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) steps: usize,
pub(crate) begin_color: PixelColor,
pub(crate) end_color: PixelColor,
}
fn fade_value(value_start: u8, value_end: u8, index: usize, nbr_iter: usize) -> u8 {
((value_start as usize * (nbr_iter - index) + value_end as usize * index) / nbr_iter) as u8
}
impl<const N: usize> Pattern for Fade<N> {
type Strip = Strip<N>;
fn is_last_iteration(&self) -> bool {
self.current_iteration >= self.steps
}
}
impl<const N: usize> Iterator for Fade<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
let mut strip = Strip::default();
if self.is_last_iteration() {
return None;
}
let red = fade_value(
self.begin_color.red,
self.end_color.red,
self.current_iteration,
self.steps,
);
let green = fade_value(
self.begin_color.green,
self.end_color.green,
self.current_iteration,
self.steps,
);
let blue = fade_value(
self.begin_color.blue,
self.end_color.blue,
self.current_iteration,
self.steps,
);
let current_color = PixelColor { red, green, blue };
strip.fill(current_color);
self.current_iteration += 1;
Some(strip)
}
}

View File

@@ -0,0 +1,44 @@
use crate::patterns::fade::Fade;
use crate::patterns::Pattern;
use crate::{impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # FadeRandom
///
/// fade from one color to an other with random variations applied to each pixel
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct FadeRandom<const N: usize> {
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) max_iteration: Option<usize>,
pub(crate) stability: usize,
fade: Fade<N>,
}
impl<const N: usize> Pattern for FadeRandom<N> {
type Strip = Strip<N>;
impl_pattern_last_iteration!(ColorWipe);
}
impl<const N: usize> Iterator for FadeRandom<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
let strip = self.fade.next();
match strip {
None => None,
Some(mut fade) => {
let rng = rand::thread_rng();
for i in 0..N {
match fade[i] {
None => {}
Some(pixel) => {
fade[i] = Some(pixel.random_delta(self.stability, &mut rng.clone()))
}
}
}
Some(fade)
}
}
}
}

View File

@@ -0,0 +1,38 @@
use crate::patterns::fade::Fade;
use crate::patterns::Pattern;
use crate::{impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # FadeUnstable
///
/// fade from one color to an other with random variations applied to the entire strip
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct FadeUnstable<const N: usize> {
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) max_iteration: Option<usize>,
pub(crate) stability: usize,
fade: Fade<N>,
}
impl<const N: usize> Pattern for FadeUnstable<N> {
type Strip = Strip<N>;
impl_pattern_last_iteration!(ColorWipe);
}
impl<const N: usize> Iterator for FadeUnstable<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
let strip = self.fade.next();
match strip {
None => None,
Some(fade) => {
let mut rng = rand::thread_rng();
let pixel = fade[0].unwrap().random_delta(self.stability, &mut rng);
Some(Strip::new(pixel))
}
}
}
}

View File

@@ -0,0 +1,40 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::{impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # FillRandom
///
/// fill strip with color then apply random variation to each pixel
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct FillRandom<const N: usize> {
pub(crate) color: PixelColor,
pub(crate) stability: usize,
pub(crate) max_iteration: Option<usize>,
#[serde(default)]
pub(crate) current_iteration: usize,
}
impl<const N: usize> Pattern for FillRandom<N> {
type Strip = Strip<N>;
impl_pattern_last_iteration!(ColorWipe);
}
impl<const N: usize> Iterator for FillRandom<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_last_iteration() {
return None;
}
let mut strip = Strip::<N>::default();
let rng = rand::thread_rng();
for i in 0..N {
let c = self.color.random_delta(self.stability, &mut rng.clone());
strip[i] = Some(c);
}
self.current_iteration += 1;
Some(strip)
}
}

View File

@@ -0,0 +1,36 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::{impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # FillUnstable
///
/// fill strip with color then apply same random variation to all pixel
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct FillUnstable<const N: usize> {
pub(crate) color: PixelColor,
pub(crate) stability: usize,
pub(crate) max_iteration: Option<usize>,
#[serde(default)]
pub(crate) current_iteration: usize,
}
impl<const N: usize> Pattern for FillUnstable<N> {
type Strip = Strip<N>;
impl_pattern_last_iteration!(FillUnstable);
}
impl<const N: usize> Iterator for FillUnstable<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_last_iteration() {
return None;
}
let mut rng = rand::thread_rng();
let unstable_color = self.color.random_delta(self.stability, &mut rng);
self.current_iteration += 1;
Some(Strip::<N>::new(unstable_color))
}
}

70
src/patterns/rain.rs Normal file
View File

@@ -0,0 +1,70 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::{impl_pattern_last_iteration, Strip};
use rand::Rng;
use serde::{Deserialize, Serialize};
/// # rain pattern
/// randomly fall colors from top or bottom.
///
///
/// # note
///
/// `stability` is the probability (between 0 an 1) that any led will light on.
/// The probability of at least one led light on is `stability*number of lines`
///
/// this pattern works only for 5x5 square
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct Rain<const N: usize> {
pub(crate) color: PixelColor,
pub(crate) background_color: Option<PixelColor>,
pub(crate) max_iteration: Option<usize>,
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) stability: usize,
pub(crate) lines: usize,
#[serde(skip)]
drops: Strip<N>,
}
impl<const N: usize> Pattern for Rain<N> {
type Strip = Strip<N>;
fn init(&mut self) -> Option<Self::Strip> {
if let Some(color) = self.background_color {
self.drops = Strip::<N>::new(color);
Some(self.drops)
} else {
None
}
}
impl_pattern_last_iteration!(ColorRain);
}
impl<const N: usize> Iterator for Rain<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
let mut strip = Strip::<N>::default();
for i in 0..(N - self.lines) {
strip[i + self.lines] = self.drops[i];
}
let mut rng = rand::thread_rng();
for i in 0..self.lines {
if rng.gen_bool(self.stability as f64 / 100 as f64) {
strip[i] = Some(self.color);
} else {
let c = strip[i + self.lines];
if self.background_color != c && c != Some(PixelColor::default()) {
strip[i] = Some(c.unwrap() / 2);
} else {
strip[i] = self.background_color;
}
}
}
self.drops = strip.clone();
self.current_iteration += 1;
Some(strip)
}
}

80
src/patterns/rainbow.rs Normal file
View File

@@ -0,0 +1,80 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::{impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # Rainbow pattern
///
/// every pattern implement the iterator trait
///
/// This pattern display a moving rainbow over the strip
///
/// ### Note
///
/// If max iteration is let to None, it's an infinite pattern.
///
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct Rainbow<const N: usize> {
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) max_iteration: Option<usize>,
pub(crate) step: Option<usize>,
}
impl<const N: usize> Pattern for Rainbow<N> {
type Strip = Strip<N>;
impl_pattern_last_iteration!(Rainbow);
}
impl<const N: usize> Iterator for Rainbow<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_last_iteration() {
return None;
}
let mut strip = Strip::default();
let step = self.step.unwrap_or(255 / N);
for i in 0..N {
let pos = (i * step + self.current_iteration) as u8;
strip[i] = Some(wheel(pos))
}
self.current_iteration += 1;
Some(strip)
}
}
/// compute **rgb** pixel color according to the **hsv** wheel
///
/// # Arguments
///
/// * `index`: position in the hsv wheel
///
/// returns: PixelColor
///
fn wheel(index: u8) -> PixelColor {
let pos = 255 - index;
match pos {
0..=85 => PixelColor {
red: 255 - (pos * 3),
green: 0,
blue: pos * 3,
},
86..=170 => {
let pos = pos - 85;
PixelColor {
red: 0,
green: pos * 3,
blue: 255 - (pos * 3),
}
}
_ => {
let pos = pos - 170;
PixelColor {
red: pos * 3,
green: 255 - (pos * 3),
blue: 0,
}
}
}
}

52
src/patterns/ring.rs Normal file
View File

@@ -0,0 +1,52 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # Ring
///
/// display a ring on the strip
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct Ring<const N: usize> {
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) max_iteration: Option<usize>,
pub(crate) color: PixelColor,
pub(crate) background_color: Option<PixelColor>,
pub(crate) ring_size: usize,
}
impl<const N: usize> Pattern for Ring<N> {
type Strip = Strip<N>;
impl_pattern_init_background!(N);
impl_pattern_last_iteration!(Ring);
}
impl<const N: usize> Iterator for Ring<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_last_iteration() {
return None;
}
let mut strip: Strip<N>;
if let Some(color) = self.background_color {
strip = Strip::new(color);
} else {
strip = Strip::default();
}
let nbr_iter = N / self.ring_size;
let iter = self.current_iteration % nbr_iter;
let first_index = iter * self.ring_size;
let last_index = first_index + self.ring_size;
for i in first_index..last_index {
strip[i] = Some(self.color);
}
self.current_iteration += 1;
Some(strip)
}
}

View File

@@ -0,0 +1,56 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # RingScanner
///
/// display a ring on the strip fading for the previous ones
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct RingScanner<const N: usize> {
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) max_iteration: Option<usize>,
pub(crate) color: PixelColor,
pub(crate) background_color: Option<PixelColor>,
pub(crate) ring_size: usize,
}
impl<const N: usize> Pattern for RingScanner<N> {
type Strip = Strip<N>;
impl_pattern_init_background!(N);
impl_pattern_last_iteration!(ColorWipe);
}
impl<const N: usize> Iterator for RingScanner<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_last_iteration() {
return None;
}
let mut strip: Strip<N>;
if let Some(color) = self.background_color {
strip = Strip::new(color);
} else {
strip = Strip::default();
}
let nbr_iter = N / self.ring_size;
let iter = self.current_iteration % nbr_iter;
for j in 0..iter {
let first_index = j * self.ring_size;
let last_index = first_index + self.ring_size;
let fade_gain = 2 * (iter - j);
for i in first_index..last_index {
strip[i] = Some(self.color / fade_gain as u8);
}
}
self.current_iteration += 1;
Some(strip)
}
}

59
src/patterns/scanner.rs Normal file
View File

@@ -0,0 +1,59 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::{impl_pattern_init_background, impl_pattern_last_iteration, Strip};
use serde::{Deserialize, Serialize};
/// # scanner pattern
/// color one pixel with a color and leave a train of this color fading to black
///
/// # note
///
/// background_color work only for color not set by scanner pattern
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct Scanner<const N: usize> {
#[serde(default)]
pub(crate) current_iteration: usize,
pub(crate) color: PixelColor,
pub(crate) background_color: Option<PixelColor>,
#[serde(default)]
pub(crate) max_iteration: Option<usize>,
}
impl<const N: usize> Pattern for Scanner<N> {
type Strip = Strip<N>;
impl_pattern_init_background!(N);
impl_pattern_last_iteration!(ColorWipe);
}
impl<const N: usize> Iterator for Scanner<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_last_iteration() {
return None;
}
let mut strip: Strip<N>;
if let Some(color) = self.background_color {
strip = Strip::new(color);
} else {
strip = Strip::default();
}
let current_led = self.current_iteration % N;
let min_led;
if current_led < 8 {
min_led = 0;
} else {
min_led = current_led - 8;
}
let mut c = self.color;
for i in min_led..current_led {
strip[i] = Some(c);
c = c / 2;
}
self.current_iteration += 1;
Some(strip)
}
}

View File

@@ -0,0 +1,83 @@
use crate::patterns::Pattern;
use crate::pixel_color::PixelColor;
use crate::{impl_pattern_last_iteration, Strip};
use rand::Rng;
use serde::{Deserialize, Serialize};
/// # StarsRandom
///
/// fill strip with background color then color random pixel with other color fading to background color
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct StarsRandom<const N: usize> {
pub(crate) color: PixelColor,
pub(crate) ratio: usize,
pub(crate) max_iteration: Option<usize>,
pub(crate) background_color: Option<PixelColor>,
pub(crate) steps: usize,
#[serde(default)]
pub(crate) current_iteration: usize,
#[serde(skip)]
strip: Strip<N>,
#[serde(skip)]
step: (i8, i8, i8),
}
impl<const N: usize> Pattern for StarsRandom<N> {
type Strip = Strip<N>;
fn init(&mut self) -> Option<Self::Strip> {
if let Some(color) = self.background_color {
self.strip = Strip::<N>::new(color);
let red = self.color.red as i16 - color.red as i16;
let green = color.green as i16 - self.color.green as i16;
let blue = self.color.blue as i16 - color.blue as i16;
self.step = (
-(red / self.steps as i16) as i8,
-(green / self.steps as i16) as i8,
-(blue / self.steps as i16) as i8,
);
Some(self.strip)
} else {
self.step = (
-((self.color.red / self.steps as u8) as i8),
-((self.color.green / self.steps as u8) as i8),
-((self.color.blue / self.steps as u8) as i8),
);
None
}
}
impl_pattern_last_iteration!(StarsRandom);
}
impl<const N: usize> Iterator for StarsRandom<N> {
type Item = Strip<N>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_last_iteration() {
return None;
}
let mut rng = rand::thread_rng();
for i in 0..N {
let current_color = self.strip[i];
if rng.gen_bool(1.0 / self.ratio as f64) {
self.strip[i] = Some(self.color);
} else {
let color = current_color.unwrap();
let background = self.background_color.unwrap_or(PixelColor::default());
let distance = PixelColor {
red: self.step.0.abs() as u8,
green: self.step.1.abs() as u8,
blue: self.step.2.abs() as u8,
};
if color.is_closer(background, distance) {
self.strip[i] = self.background_color;
} else {
self.strip[i] = Some(color.delta(self.step));
}
}
}
self.current_iteration += 1;
Some(self.strip)
}
}

122
src/pixel_color.rs Normal file
View File

@@ -0,0 +1,122 @@
use rand::prelude::ThreadRng;
use rand::Rng;
use serde_derive::{Deserialize, Serialize};
use std::fmt;
use std::ops::{Add, AddAssign, Div, Sub, SubAssign};
///
/// a struct for an RGB color
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Default, Eq, Hash, PartialEq)]
pub struct PixelColor {
pub(crate) red: u8,
pub(crate) green: u8,
pub(crate) blue: u8,
}
impl fmt::Display for PixelColor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{},{},{}]", self.red, self.green, self.blue)
}
}
impl Div<u8> for PixelColor {
type Output = PixelColor;
fn div(self, rhs: u8) -> Self::Output {
PixelColor {
red: self.red / rhs,
green: self.green / rhs,
blue: self.blue / rhs,
}
}
}
impl Add for PixelColor {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
PixelColor {
red: self.red.saturating_add(rhs.red),
green: self.green.saturating_add(rhs.green),
blue: self.blue.saturating_add(rhs.blue),
}
}
}
impl Sub<u8> for PixelColor {
type Output = Self;
fn sub(self, rhs: u8) -> Self::Output {
PixelColor {
red: self.red.saturating_sub(rhs),
green: self.green.saturating_sub(rhs),
blue: self.blue.saturating_sub(rhs),
}
}
}
impl SubAssign for PixelColor {
fn sub_assign(&mut self, rhs: Self) {
self.red = self.red.saturating_sub(rhs.red);
self.green = self.red.saturating_sub(rhs.green);
self.blue = self.red.saturating_sub(rhs.blue);
}
}
impl AddAssign for PixelColor {
fn add_assign(&mut self, rhs: Self) {
self.red = self.red.saturating_add(rhs.red);
self.green = self.red.saturating_add(rhs.green);
self.blue = self.red.saturating_add(rhs.blue);
}
}
impl PixelColor {
/// apply random delta to pixel and return computed value
///
/// # Arguments
///
/// * `pixel`: pixel to apply delta
/// * `stability`: amplitude of delta between 0 and `stability`
///
/// returns: PixelColor
///
pub fn random_delta(&self, stability: usize, rng: &mut ThreadRng) -> PixelColor {
let minus_interval = -(stability as i8);
let max_interval = stability as i8;
let red_delta = rng.gen_range(minus_interval..max_interval) as i8;
let green_delta = rng.gen_range(minus_interval..max_interval) as i8;
let blue_delta = rng.gen_range(minus_interval..max_interval) as i8;
self.delta((red_delta, green_delta, blue_delta))
}
pub fn delta(&self, delta: (i8, i8, i8)) -> PixelColor {
let (red, green, blue) = delta;
let red = self.red as i16 + red as i16;
let green = self.green as i16 + green as i16;
let blue = self.blue as i16 + blue as i16;
let bound = |color: i16| {
if color < 0 {
0
} else if color > 255 {
255
} else {
color as u8
}
};
PixelColor {
red: bound(red),
green: bound(green),
blue: bound(blue),
}
}
pub fn is_closer(&self, pixel: PixelColor, distance: PixelColor) -> bool {
(self.red as i16 - pixel.red as i16).abs() <= distance.red as i16
&& (self.green as i16 - pixel.green as i16).abs() <= distance.green as i16
&& (self.blue as i16 - pixel.blue as i16).abs() <= distance.blue as i16
}
}

View File

@@ -1,57 +1,234 @@
use crate::patterns::Patterns;
use crate::config::Device;
use crate::patterns::{Pattern, Patterns};
use crate::pixel_color::PixelColor;
use crate::Strip;
use serde_derive::{Deserialize, Serialize};
use std::fs;
use bluer::Address;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::future::Future;
use std::str::FromStr;
use std::time::Duration;
use tokio::sync::watch::Sender;
use tokio::sync::watch::{Receiver, Sender};
use tokio::time::Instant;
#[derive(Serialize, Deserialize, Debug)]
pub(crate) mod period {
use serde::{de::Error as _, Deserialize, Deserializer};
use std::time::Duration;
/// Custom deserializer for period from millisecond value
///
/// # Arguments
///
/// * `deserializer`:
///
/// returns: Result<Duration, <D as Deserializer>::Error>
///
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)?
.parse::<u64>()
.and_then(|d| Ok(Duration::from_millis(d)))
.map_err(|e| D::Error::custom(format!("{}", e)))
}
}
#[derive(Serialize, Deserialize, Debug, Eq, Hash, PartialEq, Copy, Clone)]
pub struct Context<const N: usize> {
pub(crate) pattern: Patterns<N>,
#[serde(deserialize_with = "period::deserialize")]
pub period: Duration,
}
impl<const N: usize> IntoIterator for Runner<N> {
type Item = Context<N>;
type IntoIter = <Vec<Context<N>> as IntoIterator>::IntoIter; // so that you don't have to write std::vec::IntoIter, which nobody remembers anyway
pub(crate) type DeviceSequence<const N: usize> = Vec<Context<N>>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
/// iterate over `runner` and stream strip data to `tx` channel
///
/// # Arguments
///
/// * `runner`: Sequence to stream
/// * `tx`: channel to stream data to
///
/// returns: ()
///
async fn runner_loop<const N: usize>(runner: DeviceSequence<N>, tx: Sender<Strip<N>>) {
let mut background = Strip::<N>::default();
for mut context in runner {
let mut strip = context.pattern.init().unwrap_or(background);
let delay = context.period;
let mut next_instant = Instant::now() + delay;
while tx.send(strip).is_ok() {
if let Some(value) = context.pattern.next() {
strip = apply_mask(background, value);
} else {
break;
}
tokio::time::sleep_until(next_instant).await;
next_instant += delay;
}
background = strip;
}
}
#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct Runner<const N: usize>(Vec<Context<N>>);
fn apply_mask<const N: usize>(previous_strip: Strip<N>, strip: Strip<{ N }>) -> Strip<N> {
let mut new_strip = Strip::default();
for i in 0..N {
if let Some(color) = strip[i] {
new_strip[i] = Some(color);
} else {
new_strip[i] = previous_strip[i];
}
}
new_strip
}
impl<const N: usize> Runner<N> {
pub fn new() -> Runner<N> {
let runner = Vec::<Context<N>>::new();
Runner(runner)
#[derive(Default)]
pub struct Spectacle<const N: usize> {
runners: HashMap<DeviceSequence<N>, (Sender<Strip<N>>, Receiver<Strip<N>>)>,
channels: HashMap<Address, Receiver<Strip<N>>>,
default_runner: Option<(DeviceSequence<N>, Sender<Strip<N>>)>,
default_receiver: Option<Receiver<Strip<N>>>,
}
impl<const N: usize> Spectacle<N> {
pub fn add(&mut self, mac_address: Address, runner: &DeviceSequence<N>) {
if let Some((_, rx)) = self.runners.get(runner as &DeviceSequence<N>) {
self.channels.insert(mac_address, rx.clone());
} else {
let (tx, rx) = tokio::sync::watch::channel(Strip::<N>::new(PixelColor {
red: 0,
green: 255,
blue: 0,
}));
self.runners.insert(runner.clone(), (tx, rx.clone()));
self.channels.insert(mac_address, rx.clone());
}
}
pub fn push(&mut self, value: Context<N>) {
self.0.push(value)
/// add default sequence for any device implementing **adafruit neopixel BLE**
/// not specified in an other sequence.
///
/// # Arguments
///
/// * `runner`: sequence to stream
///
/// returns: ()
///
pub fn add_default(&mut self, runner: DeviceSequence<N>) {
let (tx, rx) = tokio::sync::watch::channel(Strip::<N>::new(PixelColor {
red: 0,
green: 0,
blue: 255,
}));
self.default_runner = Some((runner, tx));
self.default_receiver = Some(rx);
}
pub async fn runner_loop(self, tx: Sender<Strip<N>>) {
for mut context in self {
let mut strip = context.pattern.next().unwrap();
let delay = context.period;
println!("{:?}", delay);
while tx.send(strip).is_ok() {
if let Some(value) = context.pattern.next() {
strip = value;
} else {
break;
/// Get channel associated with the mac address passed in parameters. If no channel found,
/// give back the default channel if existing.
///
/// # Arguments
///
/// * `mac_address`: mac address to look for
///
/// returns: Option<Receiver<Strip<{ N }>>>
///
pub fn get_channel(&self, mac_address: &Address) -> Option<Receiver<Strip<N>>> {
let channel = self.channels.get(mac_address);
match channel {
None => {
let d = &self.default_receiver;
match d {
None => None,
Some(runner) => {
println!("default rx");
Some(runner.clone())
}
}
tokio::time::sleep(delay).await;
}
Some(channel) => {
println!("rx found ");
Some(channel.clone())
}
}
}
pub fn load_from_file(file: &std::path::Path) -> Runner<N> {
let file = fs::read_to_string(file).unwrap();
let spectacle: Runner<N> = serde_yaml::from_str(&*file).unwrap();
/// start to stream configured sequences. Returns the joinhandle of task to wait for
///
///
///
/// returns: Vec<impl Future<Output = ()>>
///
pub fn run(&mut self) -> Vec<impl Future<Output = ()>> {
let mut joinhandles = vec![];
if self.default_runner.is_some() {
let d = std::mem::take(&mut self.default_runner).unwrap();
joinhandles.push(runner_loop(d.0.clone(), d.1));
println!("add default runner loop");
}
for (runner, (tx, _)) in self.runners.drain().into_iter() {
joinhandles.push(runner_loop(runner.clone(), tx));
println!("add custom runner loop");
}
joinhandles
}
}
impl<const N: usize> From<Vec<Device<N>>> for Spectacle<N> {
fn from(config: Vec<Device<N>>) -> Self {
let mut spectacle: Spectacle<N> = Spectacle::default();
for device in config {
if let Some(mac_addresses) = device.mac_addresses {
for mac_address in mac_addresses {
let mac_address = Address::from_str(&*mac_address);
match mac_address {
Ok(address) => {
spectacle.add(address, &device.sequence);
}
Err(_) => {
println!("error reading mac address");
}
}
}
} else {
spectacle.add_default(device.sequence);
}
}
spectacle
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_mask() {
let pixel_entry = PixelColor {
red: 10,
green: 10,
blue: 10,
};
let pixel_mask = PixelColor {
red: 20,
green: 20,
blue: 20,
};
let mut entry = Strip::<5>::new(pixel_entry);
entry[0] = None;
entry[1] = None;
let mut mask = Strip::<5>::new(pixel_mask);
mask[0] = None;
let mut expected = entry.clone();
expected[1] = Some(pixel_mask);
assert_eq!(expected, super::apply_mask(mask, entry));
}
}

View File

@@ -0,0 +1 @@