source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
[[package]]
name = "cellseq"
version = "0.1.0"
dependencies = [
- "crossbeam",
"crossterm",
"eyre",
"ndarray",
"rand",
+ "tokio",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-[[package]]
-name = "crossbeam"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
-dependencies = [
- "cfg-if",
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-epoch",
- "crossbeam-queue",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-channel"
-version = "0.5.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-deque"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
-dependencies = [
- "cfg-if",
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
-dependencies = [
- "autocfg",
- "cfg-if",
- "crossbeam-utils",
- "memoffset",
- "scopeguard",
-]
-
-[[package]]
-name = "crossbeam-queue"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
-dependencies = [
- "cfg-if",
-]
-
[[package]]
name = "crossterm"
version = "0.26.1"
[[package]]
name = "getrandom"
-version = "0.2.9"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "indenter"
version = "0.3.3"
[[package]]
name = "libc"
-version = "0.2.144"
+version = "0.2.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
+checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
[[package]]
name = "lock_api"
-version = "0.4.9"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
[[package]]
name = "log"
-version = "0.4.18"
+version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
+checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "matrixmultiply"
"rawpointer",
]
-[[package]]
-name = "memoffset"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
-dependencies = [
- "autocfg",
-]
-
[[package]]
name = "mio"
version = "0.8.8"
"libc",
"log",
"wasi",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
"autocfg",
]
+[[package]]
+name = "num_cpus"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
[[package]]
name = "once_cell"
-version = "1.17.2"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "parking_lot"
[[package]]
name = "parking_lot_core"
-version = "0.9.7"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
+checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
- "windows-sys 0.45.0",
+ "windows-targets",
]
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+[[package]]
+name = "proc-macro2"
+version = "1.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+dependencies = [
+ "proc-macro2",
+]
+
[[package]]
name = "rand"
version = "0.8.5"
[[package]]
name = "redox_syscall"
-version = "0.2.16"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+[[package]]
+name = "socket2"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tokio"
+version = "1.28.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2"
+dependencies = [
+ "autocfg",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-[[package]]
-name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
-]
-
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets 0.48.0",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
+ "windows-targets",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
]
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
-[[package]]
-name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
-[[package]]
-name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
-
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
rand = "0.8"
crossterm = "0.26"
eyre = "0.6"
-crossbeam = "0.8.2"
+tokio = { version = "1", features = ["full"] }
-use crate::{Map, Point};
-use crossterm::style::{
- Attribute, Attributes,
- Color::{Black, Green, Grey},
- Colors,
+use crossterm::{
+ cursor::MoveTo,
+ queue,
+ style::{
+ Attribute::{Bold, Reset},
+ Color::{Black, Green, Grey},
+ ContentStyle, Print, SetStyle,
+ },
};
use ndarray::Array2;
use rand::{thread_rng, Rng};
-use std::{cell::Cell, ops::Deref};
+use std::{cell::Cell, io::stdout, ops::Deref};
use super::*;
#[derive(Debug, Clone)]
pub struct World {
pub map: Array2<Cell<State>>,
+ pub area: Area,
}
impl World {
pub fn new(area: Area) -> Self {
let map = Array2::from_elem((area.width(), area.height()), Cell::new(State::Dead));
- Self { map }
+ Self { map, area }
}
pub fn randomize(&mut self, val: f64) {
};
}
- wrap!(self.column(0), self.column(self.x_size() - 2));
- wrap!(self.column(self.x_size() - 1), self.column(1));
- wrap!(self.row(0), self.row(self.y_size() - 2));
- wrap!(self.row(self.y_size() - 1), self.row(1));
+ wrap!(self.column(0), self.column(self.area.width() - 2));
+ wrap!(self.column(self.area.width() - 1), self.column(1));
+ wrap!(self.row(0), self.row(self.area.height() - 2));
+ wrap!(self.row(self.area.height() - 1), self.row(1));
}
-}
-impl Map<Cell<State>> for World {
- fn update(&mut self) {
+ pub fn update(&mut self) {
let mut previous = self.clone();
previous.wrap_walls();
for (next, prev) in self
}
}
- fn x_size(&self) -> usize {
- self.ncols()
- }
-
- fn y_size(&self) -> usize {
- self.nrows()
- }
-
- fn characters(&self) -> (char, char) {
- ('●', '◌')
- }
-
- fn colors(&self) -> (Colors, Colors) {
- (Colors::new(Green, Black), Colors::new(Grey, Black))
- }
-
- fn styles(&self) -> (Attributes, Attributes) {
- (Attribute::Bold.into(), Attribute::Reset.into())
- }
-
- fn draw_point(&self, point: Point) -> Option<char> {
- if let Some(cell) = self.get((point.y, point.x)) {
+ fn draw_point(&self, point: Point, offset: Point) -> Result<()> {
+ let mut style = ContentStyle {
+ foreground_color: None,
+ background_color: Some(Black),
+ underline_color: None,
+ attributes: Reset.into(),
+ };
+ let char = if let Some(cell) = self.get::<(usize, usize)>(point.into()) {
if cell.get() == State::Alive {
- return Some('●');
+ style.foreground_color = Some(Green);
+ style.attributes = Bold.into();
+ '●'
} else {
- return Some('◌');
+ style.background_color = Some(Grey);
+ '◌'
}
} else {
- return None;
- }
+ ' '
+ };
+
+ queue!(
+ stdout(),
+ MoveTo(offset.x, offset.y),
+ SetStyle(style),
+ Print(char)
+ )?;
+ Ok(())
}
}
-mod actions;
-mod area;
-mod keys;
-mod layout;
-mod map;
-mod point;
-mod render;
-mod selector;
-
-pub use actions::*;
-pub use area::*;
-pub use keys::*;
-pub use layout::*;
-pub use map::*;
-pub use point::*;
-pub use render::*;
-pub use selector::*;
-
use super::*;
-use crossterm::{
- cursor::MoveTo,
- event::{read, Event},
- execute, queue,
- style::{Print, SetAttributes, SetColors},
- terminal,
-};
-use eyre::Result;
-use std::{
- io::{stdout, Write},
- sync::{Arc, Mutex},
- thread,
- time::Duration,
-};
-
-pub fn draw_map<T>(map: &impl Map<T>, area: &Area) -> Result<()> {
- let ((x_zero, y_zero), (x_max, y_max)) = area.to_u16()?;
-
- let origin = Point::new(x_zero.into(), y_zero.into());
-
- let (char_on, char_off) = map.characters();
- let (on_colors, off_colors) = map.colors();
- let (style_on, style_off) = map.styles();
-
- for x in 0..=(map.x_size()) {
- for y in 0..=(map.y_size()) {
- let point = Point::new(x, y);
- let char = map.draw_point(point).unwrap_or(' ');
- let (x_off, y_off) = origin.u16_offset(point)?;
-
- if x_off <= x_zero || x_off >= x_max || y_off <= y_zero || y_off >= y_max - 1 {
- continue;
- }
-
- queue!(
- stdout(),
- MoveTo(x_off, y_off),
- SetAttributes(style_on),
- SetColors(on_colors),
- Print(char)
- )?;
+use std::ops::{Add, Sub};
+
+#[derive(Clone, Debug, Copy, PartialEq, Eq)]
+pub struct Point {
+ pub x: u16,
+ pub y: u16,
+}
+
+impl Point {
+ pub fn new(x: u16, y: u16) -> Self {
+ Self { x, y }
+ }
+}
+
+impl Add for Point {
+ type Output = Self;
+ fn add(self, rhs: Self) -> Self::Output {
+ Self {
+ x: self.x + rhs.x,
+ y: self.y + rhs.y,
+ }
+ }
+}
+
+impl Sub for Point {
+ type Output = Self;
+ fn sub(self, rhs: Self) -> Self::Output {
+ Self {
+ x: if rhs.x < self.x { self.x - rhs.x } else { 0 },
+ y: if rhs.y < self.y { self.y - rhs.y } else { 0 },
}
}
+}
+
+impl From<(usize, usize)> for Point {
+ fn from(value: (usize, usize)) -> Self {
+ Self {
+ x: value.0.try_into().unwrap_or(0),
+ y: value.1.try_into().unwrap_or(0),
+ }
+ }
+}
+
+impl Into<(usize, usize)> for Point {
+ fn into(self) -> (usize, usize) {
+ (
+ self.x.try_into().unwrap_or(0),
+ self.y.try_into().unwrap_or(0),
+ )
+ }
+}
- area.outline_area()?;
+#[derive(Clone, Debug, Copy, PartialEq, Eq)]
+pub struct Area {
+ pub origin: Point,
+ pub max: Point,
+}
- stdout().flush()?;
+impl Area {
+ pub fn new(origin: Point, max: Point) -> Self {
+ Self { origin, max }
+ }
+
+ pub fn contains(&self, point: Point) -> bool {
+ point.x >= self.origin.x
+ && point.y >= self.origin.y
+ && point.x <= self.max.x
+ && point.y <= self.max.y
+ }
+
+ pub fn height(&self) -> usize {
+ (self.max.y - self.origin.y).try_into().unwrap_or(0)
+ }
+
+ pub fn width(&self) -> usize {
+ (self.max.x - self.origin.x).try_into().unwrap_or(0)
+ }
+}
- Ok(())
+impl From<(Point, Point)> for Area {
+ fn from(value: (Point, Point)) -> Self {
+ Self {
+ origin: value.0,
+ max: value.1,
+ }
+ }
}
-pub fn loop_map<T>(
- map: &(impl Map<T> + Clone),
- area: &Area,
- time: Duration,
- steps: usize,
-) -> Result<()> {
- loop {
- let mut tmp = map.clone();
- for _ in 0..steps {
- execute!(stdout(), terminal::Clear(terminal::ClearType::All))?;
- draw_map(&tmp, area)?;
- stdout().flush()?;
- tmp.update();
- thread::sleep(time);
+impl From<(u16, u16, u16, u16)> for Area {
+ fn from(value: (u16, u16, u16, u16)) -> Self {
+ Self {
+ origin: Point::new(value.0, value.1),
+ max: Point::new(value.2, value.3),
}
}
}
+
+impl Into<(Point, Point)> for Area {
+ fn into(self) -> (Point, Point) {
+ (self.origin, self.max)
+ }
+}
+
+impl Into<(u16, u16, u16, u16)> for Area {
+ fn into(self) -> (u16, u16, u16, u16) {
+ (self.origin.x, self.origin.y, self.max.x, self.max.y)
+ }
+}
+++ /dev/null
-use super::*;
-
-#[derive(Debug)]
-pub enum Action {
- None,
- Move(Direction),
- Clock(Clock),
- Channel(usize),
- Select,
- SelectArea,
- SwitchPanel,
- Reload,
- Randomize,
- Exit,
- Help,
- Edit,
-}
-
-#[derive(Debug)]
-pub enum Clock {
- Stop,
- Start,
- Pause,
- Faster(usize),
- Slower(usize),
-}
-
-#[derive(Debug)]
-pub enum Direction {
- Up,
- Down,
- Left,
- Right,
-}
-
-pub fn action(action: Action, state: ArcState) -> Result<()> {
- match action {
- Action::None => Ok(()),
- Action::Exit => exit(),
- Action::Move(direction) => move_cursor(direction, state),
- Action::Clock(clock) => transport_action(clock, state),
- Action::Channel(n) => change_channel(n, state),
- Action::Select => Ok(()),
- Action::SelectArea => Ok(()),
- Action::Reload => Ok(()),
- Action::Randomize => Ok(()),
- Action::Help => Ok(()),
- Action::Edit => Ok(()),
- Action::SwitchPanel => switch_panel(state),
- }
-}
-
-fn transport_action(clock: Clock, state: ArcState) -> Result<()> {
- let mut state = state.lock().unwrap();
- match clock {
- Clock::Stop => state.transport.running = false,
- Clock::Start => state.transport.running = true,
- Clock::Pause => state.transport.running = !state.transport.running,
- Clock::Faster(n) => state.transport.bpm += n,
- Clock::Slower(n) => state.transport.bpm -= n,
- };
- Ok(())
-}
-
-fn move_cursor(direction: Direction, state: ArcState) -> Result<()> {
- let mut state = state.lock().unwrap();
- match direction {
- Direction::Up => {
- if state.cursor.position.y > 1 {
- state.cursor.position.y -= 1
- }
- }
- Direction::Down => {
- if state.cursor.position.y < state.cursor.area.height() {
- state.cursor.position.y += 1
- }
- }
- Direction::Left => {
- if state.cursor.position.x > 1 {
- state.cursor.position.x -= 1
- }
- }
- Direction::Right => {
- if state.cursor.position.x < state.cursor.area.width() {
- state.cursor.position.x += 1
- }
- }
- }
- Ok(())
-}
-
-fn switch_panel(state: ArcState) -> Result<()> {
- let mut mutex = state.lock().unwrap();
- mutex.cursor.area = if mutex.cursor.area == mutex.layout.mask {
- mutex.layout.cells
- } else {
- mutex.layout.mask
- };
- Ok(())
-}
-
-fn change_channel(channel: usize, state: ArcState) -> Result<()> {
- let mut mutex = state.lock().unwrap();
- mutex.channels.0 = channel;
- draw_map(&mutex.mask[channel], &mutex.layout.mask)?;
- Ok(())
-}
+++ /dev/null
-use crossterm::{
- cursor::MoveTo,
- queue,
- style::{
- Color::{Black, White},
- Colors, SetColors,
- },
-};
-use eyre::Result;
-
-use super::*;
-
-#[derive(Clone, Debug, PartialEq, Eq, Copy)]
-pub struct Area {
- pub origin: Point,
- pub max: Point,
-}
-
-impl From<(Point, Point)> for Area {
- fn from(value: (Point, Point)) -> Self {
- Self {
- origin: value.0,
- max: value.1,
- }
- }
-}
-
-impl Area {
- pub fn new(x_zero: usize, y_zero: usize, x_max: usize, y_max: usize) -> Self {
- Self {
- origin: Point::new(x_zero, y_zero),
- max: Point::new(x_max, y_max),
- }
- }
-
- pub fn to_u16(&self) -> Result<((u16, u16), (u16, u16))> {
- Ok((
- (self.origin.x.try_into()?, self.origin.y.try_into()?),
- (self.max.x.try_into()?, self.max.y.try_into()?),
- ))
- }
-
- pub fn width(&self) -> usize {
- self.max.y - self.origin.y
- }
-
- pub fn height(&self) -> usize {
- self.max.x - self.origin.x
- }
-
- pub fn outline_area(&self) -> Result<()> {
- let colors = Colors::new(White, Black);
- let ((x_zero, y_zero), (x_max, y_max)) = self.to_u16()?;
-
- for y in y_zero + 1..y_max - 1 {
- queue!(
- stdout(),
- MoveTo(x_zero, y),
- SetColors(colors),
- Print("┃"),
- MoveTo(x_max, y),
- SetColors(colors),
- Print("┃")
- )?;
- }
-
- for x in x_zero + 1..x_max {
- queue!(
- stdout(),
- MoveTo(x, y_zero),
- SetColors(colors),
- Print("━"),
- MoveTo(x, y_max - 1),
- SetColors(colors),
- Print("━")
- )?;
- }
-
- for ((x, y), c) in [
- (x_zero, y_zero),
- (x_max, y_zero),
- (x_zero, y_max - 1),
- (x_max, y_max - 1),
- ]
- .iter()
- .zip(['┏', '┓', '┗', '┛'].iter())
- {
- queue!(stdout(), MoveTo(*x, *y), SetColors(colors), Print(c))?;
- }
-
- Ok(())
- }
-}
+++ /dev/null
-use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
-
-use super::*;
-use crate::Clock;
-
-pub fn key_event(event: KeyEvent) -> Action {
- match event.modifiers {
- KeyModifiers::NONE => match_normal_key(event.code),
- KeyModifiers::CONTROL => match_ctl_key(event.code),
- KeyModifiers::SHIFT => match_shift_key(event.code),
- _ => Action::None,
- }
-}
-
-pub fn match_normal_key(key: KeyCode) -> Action {
- match key {
- KeyCode::Enter => Action::Clock(Clock::Pause),
- KeyCode::Esc => Action::Exit,
- KeyCode::Tab => Action::SwitchPanel,
- KeyCode::Up => Action::Move(Direction::Up),
- KeyCode::Down => Action::Move(Direction::Down),
- KeyCode::Right => Action::Move(Direction::Right),
- KeyCode::Left => Action::Move(Direction::Left),
- KeyCode::Char(c) => match c {
- '?' => Action::Help,
- ' ' => Action::Select,
- '[' => Action::Clock(Clock::Slower(1)),
- ']' => Action::Clock(Clock::Faster(1)),
- '{' => Action::Clock(Clock::Slower(5)),
- '}' => Action::Clock(Clock::Faster(5)),
- 'q' => Action::Exit,
- 'r' => Action::Reload,
- 'j' => Action::Move(Direction::Down),
- 'k' => Action::Move(Direction::Up),
- 'h' => Action::Move(Direction::Left),
- 'l' => Action::Move(Direction::Right),
- '1' => Action::Channel(1),
- '2' => Action::Channel(2),
- '3' => Action::Channel(3),
- '4' => Action::Channel(4),
- '5' => Action::Channel(5),
- '6' => Action::Channel(6),
- '7' => Action::Channel(7),
- '8' => Action::Channel(8),
- '9' => Action::Channel(9),
- '0' => Action::Channel(10),
- _ => Action::None,
- },
- _ => Action::None,
- }
-}
-
-pub fn match_ctl_key(key: KeyCode) -> Action {
- match key {
- KeyCode::Char('c') => Action::Exit,
- _ => Action::None,
- }
-}
-
-pub fn match_shift_key(key: KeyCode) -> Action {
- match key {
- KeyCode::Enter => Action::Clock(Clock::Stop),
- KeyCode::Char(c) => match c {
- ' ' => Action::SelectArea,
- 'r' => Action::Randomize,
- _ => Action::None,
- },
- _ => Action::None,
- }
-}
+++ /dev/null
-use crossterm::terminal;
-use eyre::Result;
-
-use super::*;
-
-#[derive(Clone, Debug)]
-pub struct Layout {
- pub screen: Area,
- pub cells: Area,
- pub mask: Area,
- pub channels: Area,
- pub transport: Area,
-}
-
-impl Layout {
- pub fn build() -> Result<Self> {
- let (col, row) = terminal::size()?;
-
- let col: usize = col.into();
- let row: usize = row.into();
-
- let screen = Area::new(0, 0, col, row);
- let cells = Area::new(1, 0, col / 2 - 6, row / 2 - 1);
- let mask = Area::new(col / 2 + 6, 0, col - 2, row / 2 - 1);
- let channels = Area::new(1, row / 2 + 1, col - 2, row - 1);
- let transport = Area::new(col / 2 - 5, 4, col / 2 + 5, 9);
-
- Ok(Self {
- screen,
- cells,
- mask,
- channels,
- transport,
- })
- }
-
- pub fn draw_outlines(&self) -> Result<()> {
- self.cells.outline_area()?;
- self.mask.outline_area()?;
- self.channels.outline_area()?;
- self.transport.outline_area()?;
- Ok(())
- }
-}
+++ /dev/null
-use crate::Point;
-use crossterm::style::{
- Attribute, Attributes,
- Color::{Black, White},
- Colors,
-};
-use ndarray::Array2;
-use rand::{thread_rng, Rng};
-use std::ops::{Deref, DerefMut};
-
-use super::*;
-
-pub trait Map<T> {
- fn draw_point(&self, point: Point) -> Option<char>;
- fn x_size(&self) -> usize;
- fn y_size(&self) -> usize;
- fn characters(&self) -> (char, char);
- fn colors(&self) -> (Colors, Colors);
- fn styles(&self) -> (Attributes, Attributes);
- fn update(&mut self);
-}
-
-#[derive(Debug, Clone)]
-pub struct Mask {
- pub mask: Array2<Note>,
- pub colors: (Colors, Colors),
-}
-
-impl Mask {
- pub fn new(area: Area) -> Self {
- let mask = Array2::from_elem((area.width(), area.height()), Note::Off);
- Self {
- mask,
- colors: (Colors::new(White, Black), Colors::new(Black, Black)),
- }
- }
-
- pub fn randomize(&mut self, val: f64, scale: Scale) {
- let mut rng = thread_rng();
- for f in self.iter_mut() {
- if rng.gen::<f64>() > val {
- let notes = scale.to_notes();
- *f = notes[rng.gen::<usize>() % notes.len()];
- }
- }
- }
-}
-
-impl Deref for Mask {
- type Target = Array2<Note>;
- fn deref(&self) -> &Self::Target {
- &self.mask
- }
-}
-
-impl DerefMut for Mask {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.mask
- }
-}
-
-impl Map<Note> for Mask {
- fn x_size(&self) -> usize {
- self.ncols()
- }
-
- fn y_size(&self) -> usize {
- self.nrows()
- }
-
- fn draw_point(&self, point: Point) -> Option<char> {
- self.get((point.y, point.x)).map(|n| n.to_char())
- }
-
- fn characters(&self) -> (char, char) {
- ('■', '□')
- }
-
- fn colors(&self) -> (Colors, Colors) {
- self.colors
- }
-
- fn styles(&self) -> (Attributes, Attributes) {
- let on = Attributes::from(Attribute::Bold);
- let off = Attributes::from(Attribute::Reset);
- (on, off)
- }
-
- fn update(&mut self) {}
-}
+++ /dev/null
-use crossterm::style::{Attribute, Color};
-use eyre::Result;
-use std::ops::{Add, Div, Mul, Sub};
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct Pixel {
- pub location: Point,
- pub value: char,
- pub fg: Color,
- pub bg: Color,
- pub style: Attribute,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
-pub struct Point {
- pub x: usize,
- pub y: usize,
-}
-
-impl Point {
- pub fn new(x: usize, y: usize) -> Self {
- Self { x, y }
- }
-
- pub fn u16_offset(&self, offset: Point) -> Result<(u16, u16)> {
- let output = *self + offset;
- Ok((output.x.try_into()?, output.y.try_into()?))
- }
-}
-
-impl Add for Point {
- type Output = Self;
- fn add(self, rhs: Self) -> Self::Output {
- Self {
- x: self.x + rhs.x,
- y: self.y + rhs.y,
- }
- }
-}
-
-impl Sub for Point {
- type Output = Self;
- fn sub(self, rhs: Self) -> Self::Output {
- Self {
- x: self.x - rhs.x,
- y: self.y - rhs.y,
- }
- }
-}
-
-impl Mul for Point {
- type Output = Self;
- fn mul(self, rhs: Self) -> Self::Output {
- Self {
- x: self.x * rhs.x,
- y: self.y * rhs.y,
- }
- }
-}
-
-impl Div for Point {
- type Output = Self;
- fn div(self, rhs: Self) -> Self::Output {
- Self {
- x: self.x / rhs.x,
- y: self.y / rhs.y,
- }
- }
-}
+++ /dev/null
-use super::*;
-
-use crossbeam::channel::{bounded, Receiver, Sender};
-
-pub type ArcState = Arc<Mutex<GlobalState>>;
-
-pub fn main_loop(state: GlobalState) -> Result<()> {
- execute!(stdout(), terminal::Clear(terminal::ClearType::All))?;
-
- state.layout.draw_outlines()?;
-
- let (clock, sync) = bounded::<()>(0);
- let state = Arc::new(Mutex::new(state));
-
- let timer_state = Arc::clone(&state);
- let world_state = Arc::clone(&state);
- let action_state = Arc::clone(&state);
- // let cursor_state = Arc::clone(&state);
-
- let timer = std::thread::spawn(move || timer_loop(timer_state, clock));
- let world = std::thread::spawn(move || world_loop(world_state, sync));
- let action = std::thread::spawn(move || action_loop(action_state));
- // let cursor = std::thread::spawn(move || cursor_loop(cursor_state));
-
- timer.join().unwrap()?;
- world.join().unwrap()?;
- action.join().unwrap()?;
- // cursor.join().unwrap()?;
-
- Ok(())
-}
-
-fn timer_loop(state: ArcState, clock: Sender<()>) -> Result<()> {
- loop {
- let tick = state.lock().unwrap().tick();
- thread::sleep(tick);
- clock.send(())?;
- }
-}
-
-pub fn world_loop(state: ArcState, sync: Receiver<()>) -> Result<()> {
- loop {
- let mutex = state.lock().unwrap();
- if mutex.transport.running {
- let mut world = mutex.world.clone();
- let mask = mutex.mask[mutex.channels.0].clone();
- let world_layout = mutex.layout.cells;
- let mask_layout = mutex.layout.mask;
- drop(mutex);
-
- world.update();
- draw_map(&world, &world_layout)?;
- draw_map(&mask, &mask_layout)?;
- sync.recv()?;
- state.lock().as_mut().unwrap().world = world;
- } else {
- drop(mutex);
- sync.recv()?;
- }
- }
-}
-
-pub fn action_loop(state: ArcState) -> Result<()> {
- while let Ok(event) = read() {
- match event {
- Event::Key(key) => action(key_event(key), state.clone())?,
- Event::Resize(_, _) => state.lock().unwrap().layout = Layout::build()?,
- _ => (),
- }
- }
- Ok(())
-}
-
-pub fn cursor_loop(state: ArcState) -> Result<()> {
- loop {
- state.lock().unwrap().cursor.render()?;
- std::thread::sleep(Duration::from_millis(100))
- }
-}
-
-// pub fn render_loop(state: &mut GlobalState) -> Result<()> {
-// execute!(stdout(), terminal::Clear(terminal::ClearType::All))?;
-
-// state.layout.draw_outlines()?;
-
-// let world = Arc::new(Mutex::new(state.world.clone()));
-// let state_arc = Arc::new(Mutex::new(state));
-
-// loop {
-// let arc = Arc::clone(&state_arc);
-// let tick = arc.lock().unwrap().tick();
-// let timer = std::thread::spawn(move || thread::sleep(tick));
-
-// let event = std::thread::spawn(move || {
-// if poll(tick).unwrap() {
-// match read().unwrap() {
-// Event::Key(key) => key_event(key),
-// _ => Action::None,
-// }
-// } else {
-// Action::None
-// }
-// });
-
-// let mut maps = std::thread::spawn(|| {});
-// if arc.lock().unwrap().transport.running {
-// let map = world.clone();
-// let mut mask = arc.lock().unwrap().mask.clone();
-// let cell_area = arc.lock().unwrap().layout.cells;
-// let mask_area = arc.lock().unwrap().layout.mask;
-
-// maps = std::thread::spawn(move || {
-// let mut map = map.lock().unwrap();
-// map.update();
-// let tmp = map.clone();
-// draw_map(&tmp, &cell_area).unwrap();
-// mask[0].update();
-// let tmp = mask[0].clone();
-// draw_map(&tmp, &mask_area).unwrap();
-// });
-// }
-
-// let area = arc.lock().unwrap().layout.transport;
-
-// arc.lock().unwrap().transport.render(area)?;
-
-// action(event.join().unwrap(), arc.clone())?;
-// maps.join().unwrap();
-
-// arc.lock().unwrap().cursor.render()?;
-
-// timer.join().unwrap();
-// stdout().flush()?;
-// }
-// }
+++ /dev/null
-use crate::Point;
-use crossterm::{
- cursor, queue,
- style::{self, Attribute, Attributes, Color, Colors},
-};
-use eyre::Result;
-use std::{
- io::stdout,
- ops::{Deref, DerefMut},
-};
-
-use super::*;
-
-#[derive(Debug, Clone, Copy)]
-pub struct Cursor {
- pub position: Point,
- pub color: Colors,
- pub style: Attributes,
- pub char: char,
- pub area: Area,
-}
-
-impl Deref for Cursor {
- type Target = Point;
- fn deref(&self) -> &Self::Target {
- &self.position
- }
-}
-
-impl DerefMut for Cursor {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.position
- }
-}
-
-impl Default for Cursor {
- fn default() -> Self {
- Self::new(Area::new(0, 0, 0, 0))
- }
-}
-
-impl Cursor {
- pub fn new(area: Area) -> Self {
- Self {
- position: Point::new(1, 1),
- color: Colors::new(Color::White, Color::Black),
- char: '█',
- style: Attributes::from(Attribute::Bold),
- area,
- }
- }
-
- pub fn render(&self) -> Result<()> {
- let offset = self.area.origin + self.position;
- queue!(
- stdout(),
- cursor::Hide,
- cursor::MoveTo(offset.x.try_into().unwrap(), offset.y.try_into().unwrap()),
- style::SetAttributes(self.style),
- style::SetColors(self.color),
- cursor::Show,
- style::Print(self.char.to_string())
- )?;
- Ok(())
- }
-}
mod cells;
mod graphics;
-mod music;
-mod state;
pub use cells::*;
pub use graphics::*;
-pub use music::*;
-pub use state::*;
-use eyre::Result;
-use std::time::Duration;
-
-pub fn exit() -> Result<()> {
- crossterm::terminal::disable_raw_mode()?;
- crossterm::execute!(std::io::stdout(), crossterm::cursor::Show)?;
- std::process::exit(0);
-}
+#[cfg(test)]
+mod tests;
-pub fn bpm_to_ms(bpm: usize) -> Duration {
- let ms = 60000 / bpm;
- Duration::from_millis(ms.try_into().unwrap())
-}
+use eyre::Result;
-use cellseq::*;
-use crossterm::{cursor::Hide, execute, terminal};
use eyre::Result;
-use std::io::stdout;
-fn main() -> Result<()> {
- terminal::enable_raw_mode()?;
- execute!(stdout(), Hide)?;
-
- let mut state = GlobalState::build()?;
-
- state.world.randomize(0.75);
- state.mask[0].randomize(0.75, Scale::Aeolian);
-
- match main_loop(state) {
- Ok(_) => exit(),
- Err(e) => {
- eprintln!("{}", e);
- exit()
- }
- }
+#[tokio::main]
+async fn main() -> Result<()> {
+ todo!()
}
+++ /dev/null
-mod scale;
-mod transport;
-
-pub use scale::*;
-pub use transport::*;
-
-use std::{fmt::Display, ops::Deref};
-
-#[derive(Clone, Copy, Debug)]
-pub struct TimeSignature {
- pub top: usize,
- pub bottom: usize,
-}
-
-impl From<(usize, usize)> for TimeSignature {
- fn from(value: (usize, usize)) -> Self {
- Self {
- top: value.0,
- bottom: value.1,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Note {
- Off,
- A(Acc),
- B(Acc),
- C(Acc),
- D(Acc),
- E(Acc),
- F(Acc),
- G(Acc),
-}
-
-impl Display for Note {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let str = match self {
- Note::Off => "".to_string(),
- Note::A(a) => format!("A{a}"),
- Note::B(a) => format!("B{a}"),
- Note::C(a) => format!("C{a}"),
- Note::D(a) => format!("D{a}"),
- Note::E(a) => format!("E{a}"),
- Note::F(a) => format!("F{a}"),
- Note::G(a) => format!("G{a}"),
- };
-
- write!(f, "{str}")
- }
-}
-
-impl Note {
- pub fn to_char(&self) -> char {
- match self {
- Note::Off => ' ',
- Note::A(_) => 'a',
- Note::B(_) => 'b',
- Note::C(_) => 'c',
- Note::D(_) => 'd',
- Note::E(_) => 'e',
- Note::F(_) => 'f',
- Note::G(_) => 'g',
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Acc {
- Flt,
- Nat,
- Shp,
-}
-
-impl Display for Acc {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let str = match self {
- Acc::Flt => "b",
- Acc::Nat => "",
- Acc::Shp => "#",
- };
-
- write!(f, "{str}")
- }
-}
+++ /dev/null
-use super::*;
-
-use super::{
- Acc::{Nat, Shp},
- Note::{A, B, C, D, E, F, G},
-};
-
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
-pub enum Scale {
- Ionian,
- Dorian,
- Phrygian,
- Lydian,
- Mixolydian,
- Aeolian,
- Locrian,
- MajPent,
- SusEgypt,
- BlueMajPent,
- BlueMinPen,
- MinPent,
- WholeTone,
- Chromatic,
-}
-
-impl Scale {
- pub fn to_notes(&self) -> Vec<Note> {
- let octave = [
- C(Nat),
- C(Shp),
- D(Nat),
- D(Shp),
- E(Nat),
- F(Nat),
- F(Shp),
- G(Nat),
- G(Shp),
- A(Nat),
- A(Shp),
- B(Nat),
- ];
-
- let diatonic = [1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1];
- let pentatonic = [1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0];
- let whole_tone = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0];
-
- fn mask_scale(notes: &[Note; 12], mask: [u8; 12]) -> Vec<Note> {
- notes
- .iter()
- .zip(mask.iter())
- .filter_map(|(note, mask)| if *mask == 1 { Some(*note) } else { None })
- .collect()
- }
-
- macro_rules! rotate {
- ($s:expr,r,$n:expr) => {{
- $s.rotate_right($n);
- $s
- }};
- ($s:expr,l,$n:expr) => {{
- $s.rotate_left($n);
- $s
- }};
- }
-
- match self {
- Scale::Ionian => mask_scale(&octave, diatonic),
- Scale::Dorian => rotate!(mask_scale(&octave, diatonic), l, 2),
- Scale::Phrygian => rotate!(mask_scale(&octave, diatonic), l, 4),
- Scale::Lydian => rotate!(mask_scale(&octave, diatonic), l, 5),
- Scale::Mixolydian => rotate!(mask_scale(&octave, diatonic), r, 5),
- Scale::Aeolian => rotate!(mask_scale(&octave, diatonic), r, 3),
- Scale::Locrian => rotate!(mask_scale(&octave, diatonic), r, 1),
- Scale::MajPent => mask_scale(&octave, pentatonic),
- Scale::SusEgypt => rotate!(mask_scale(&octave, pentatonic), l, 2),
- Scale::BlueMinPen => rotate!(mask_scale(&octave, pentatonic), l, 4),
- Scale::BlueMajPent => rotate!(mask_scale(&octave, pentatonic), r, 5),
- Scale::MinPent => rotate!(mask_scale(&octave, pentatonic), r, 3),
- Scale::WholeTone => mask_scale(&octave, whole_tone),
- Scale::Chromatic => octave.to_vec(),
- }
- }
-}
+++ /dev/null
-use super::*;
-
-#[derive(Clone, Copy, Debug)]
-pub struct Song {
- pub key: Option<Note>,
- pub scale: Option<Scale>,
-}
-
-impl Song {
- pub fn new(key: Option<Note>, scale: Option<Scale>) -> Self {
- Self { key, scale }
- }
-}
+++ /dev/null
-mod transport;
-
-pub use transport::*;
-
-use crossbeam::channel::{bounded, Receiver, Sender};
-
-use super::*;
-
-#[derive(Debug, Clone)]
-pub struct GlobalState {
- pub world: World,
- pub layout: Layout,
- pub transport: Transport,
- pub song: Song,
- pub mask: Vec<Mask>,
- pub channels: (usize, Vec<MidiChannel>),
- pub cursor: Cursor,
- pub update_mask: (Sender<usize>, Receiver<usize>),
-}
-
-impl GlobalState {
- pub fn build() -> Result<Self> {
- let layout = Layout::build()?;
-
- let world = World::new(layout.cells);
-
- let channels = Vec::with_capacity(10);
- let mut mask: Vec<Mask> = Vec::with_capacity(10);
-
- for _ in 0..10 {
- mask.push(Mask::new(layout.mask));
- }
-
- let transport = Transport::new(4, 4, 120);
-
- let cursor = Cursor::new(layout.mask);
-
- let song = Song::new(None, None);
-
- let update_mask = bounded::<usize>(0);
-
- Ok(Self {
- world,
- layout,
- transport,
- song,
- mask,
- channels: (0, channels),
- cursor,
- update_mask,
- })
- }
-
- pub fn tick(&self) -> Duration {
- bpm_to_ms(self.transport.bpm)
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct MidiChannel {
- pub num: usize,
- pub poly_num: usize,
-}
+++ /dev/null
-use super::*;
-
-use crossterm::{
- cursor::MoveTo,
- queue,
- style::{Attribute, Color, Colors, Print, SetAttributes, SetColors},
-};
-use std::io::stdout;
-
-#[derive(Debug, Clone, Copy)]
-pub struct Transport {
- pub running: bool,
- pub bpm: usize,
- pub sig: TimeSignature,
- pub repeat: usize,
-}
-
-impl Transport {
- pub fn new(top: usize, bottom: usize, bpm: usize) -> Self {
- Self {
- sig: TimeSignature { top, bottom },
- bpm,
- running: true,
- repeat: 0,
- }
- }
-
- pub fn render(&self, area: Area) -> Result<()> {
- let ((x_zero, y_zero), (_, _)) = area.to_u16()?;
-
- let bpm = format!("bpm: {}", self.bpm);
- let sig = format!("sig: {}/{}", self.sig.top, self.sig.bottom);
- let rep = format!("rep: {}", self.repeat);
-
- queue!(
- stdout(),
- MoveTo(x_zero + 1, y_zero + 1),
- SetAttributes(Attribute::Bold.into()),
- SetColors(Colors::new(Color::White, Color::Black)),
- Print(bpm),
- MoveTo(x_zero + 1, y_zero + 2),
- SetAttributes(Attribute::Bold.into()),
- SetColors(Colors::new(Color::White, Color::Black)),
- Print(sig),
- MoveTo(x_zero + 1, y_zero + 3),
- SetAttributes(Attribute::Bold.into()),
- SetColors(Colors::new(Color::White, Color::Black)),
- Print(rep),
- )?;
-
- Ok(())
- }
-}
--- /dev/null
+use super::*;
+
+#[test]
+fn area_tests() -> Result<()> {
+ let origin = Point::new(5, 5);
+ let max = Point::new(35, 25);
+ let area = Area::new(origin, max);
+
+ assert_eq!(area.height(), 20);
+ assert_eq!(area.width(), 30);
+
+ assert!(area.contains(Point::new(20, 20)));
+ assert!(area.contains(Point::new(5, 10)));
+ assert!(area.contains(Point::new(24, 25)));
+ assert!(area.contains(Point::new(35, 25)));
+ assert!(area.contains(Point::new(5, 5)));
+
+ assert!(!area.contains(Point::new(0, 10)));
+ assert!(!area.contains(Point::new(15, 0)));
+ assert!(!area.contains(Point::new(55, 24)));
+ assert!(!area.contains(Point::new(15, 60)));
+
+ assert_eq!(area, Area::from((Point::new(5, 5), Point::new(35, 25))));
+ assert_eq!(area, Area::from((5, 5, 35, 25)));
+ assert_ne!(area, Area::from((5, 55, 35, 25)));
+ Ok(())
+}
+
+#[test]
+fn point_tests() -> Result<()> {
+ let p1 = Point::new(10, 10);
+ let p2 = Point::new(15, 30);
+
+ assert_eq!(p2 - p1, Point::new(5, 20));
+ assert_eq!(p1 - p2, Point::new(0, 0));
+
+ assert_eq!(Point::new(5, 5), Point::from((5, 5)));
+ Ok(())
+}
+++ /dev/null
-use super::*;
-
-pub struct Transport {
- pub running: bool,
- pub bpm: usize,
- pub sig: TimeSignature,
- pub repeat: usize,
-}
-
-impl Transport {
- pub fn new(top: usize, bottom: usize, bpm: usize) -> Self {
- Self {
- sig: TimeSignature { top, bottom },
- bpm,
- running: true,
- repeat: 0,
- }
- }
-}