diff --git a/Cargo.lock b/Cargo.lock index c087558..cc4dfc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,11 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19eabfa36efffae27d62d530e30eb951e3d9a999d23c0d58cb010a27e3916744" - [[package]] name = "adler32" version = "1.0.4" @@ -150,7 +144,7 @@ version = "0.1.0" dependencies = [ "image", "imageproc", - "rusttype 0.9.0", + "rusttype", ] [[package]] @@ -215,7 +209,7 @@ dependencies = [ "rand_distr", "rayon", "rulinalg", - "rusttype 0.8.3", + "rusttype", ] [[package]] @@ -258,15 +252,6 @@ version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" -[[package]] -name = "log" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -dependencies = [ - "cfg-if", -] - [[package]] name = "lzw" version = "0.10.0" @@ -532,16 +517,6 @@ dependencies = [ "stb_truetype", ] -[[package]] -name = "rusttype" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40cbe3f2bab536ff4784f14f2a73f1e37550ceb36a074c47a8f4150f57182049" -dependencies = [ - "ab_glyph_rasterizer", - "ttf-parser", -] - [[package]] name = "scoped_threadpool" version = "0.1.9" @@ -574,15 +549,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "ttf-parser" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa7e68b11bcf63b8a990bd7fe894dece47a629dbbba0fce4dbecb4dbac3ef8d" -dependencies = [ - "log", -] - [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 116d809..7fed6b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -image = "0.23.4" +image = "0.23.0" imageproc = "0.20.0" -rusttype = "0.9.0" +rusttype = "0.8.2" diff --git a/out.png b/out.png new file mode 100644 index 0000000..77bc7f1 Binary files /dev/null and b/out.png differ diff --git a/src/DOSVGA.ttf b/src/DOSVGA.ttf new file mode 100644 index 0000000..bf70112 Binary files /dev/null and b/src/DOSVGA.ttf differ diff --git a/src/DejaVuSans.ttf b/src/DejaVuSans.ttf new file mode 100644 index 0000000..9d40c32 Binary files /dev/null and b/src/DejaVuSans.ttf differ diff --git a/src/coders_crux.ttf b/src/coders_crux.ttf new file mode 100644 index 0000000..01b5ea3 Binary files /dev/null and b/src/coders_crux.ttf differ diff --git a/src/fixedsys.ttf b/src/fixedsys.ttf new file mode 100644 index 0000000..7d6946d Binary files /dev/null and b/src/fixedsys.ttf differ diff --git a/src/lilliput steps.ttf b/src/lilliput steps.ttf new file mode 100644 index 0000000..7c2317d Binary files /dev/null and b/src/lilliput steps.ttf differ diff --git a/src/main.rs b/src/main.rs index 370d1b2..1e7625b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,198 +1,301 @@ use std::default::Default; +use rusttype::{Scale, FontCollection, Font}; +use imageproc::drawing; +use image::{GrayImage, RgbImage, Rgb, Luma}; +use std::path::Path; +use imageproc::rect::Rect; -mod bgm { - use std::fmt; - use std::default::Default; - use std::fmt::Display; +use std::fmt; +use std::fmt::Display; - use image::{Rgb, RgbImage}; - use imageproc::drawing::draw_text_mut; - use rusttype::{FontCollection, Scale}; +#[derive(Debug,Clone,Copy,Eq,PartialEq)] +pub enum PlayerSide { + Black = 0, + White = 1, +} + +#[derive(Debug,Clone,Copy)] +pub enum Phase { + PickSides, + Turn(PlayerSide), +} + +#[derive(Debug,Clone,Default)] +pub struct Player { + pub born_off: u32, + pub on_bar: u32, +} - #[derive(Debug,Clone,Copy,Eq,PartialEq)] - pub enum Color { - Black = 0, - White = 1, +#[derive(Debug,Clone,Copy)] +pub enum Bin { + Free, + Black(u32), + White(u32), +} + +#[derive(Debug)] +pub struct Game { + pub phase : Phase, + pub multiplier : u32, + pub roll : [u8; 2], + pub players : [Player; 2], + pub bins : [Bin; 24], +} + +impl Default for Game { + fn default() -> Self { + Game { + phase: Phase::PickSides, + multiplier: 1, + roll: [0; 2], + players: [ + Player::default(), + Player::default(), + ], + bins: [Bin::Free; 24], + } } - - #[derive(Debug,Clone,Copy)] - pub enum Phase { - PickSides, - Turn(Color), +} + +impl Display for Game { + fn fmt(&self, f : &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, " ")?; + for n in (13..=24).rev() { write!(f, "{:2}. ", n)?; } + write!(f, "\n╭ ")?; + + for b in &self.bins[12..=23] { + match *b { + Bin::Free => write!(f, "┈┈┈ ")?, + Bin::Black(n) => write!(f, "{:2}█ ", n)?, + Bin::White(n) => write!(f, "{:2}░ ", n)? + } + } + write!(f, "╴\n╰ ")?; + + for b in (&self.bins[0..=11]).iter().rev() { + match *b { + Bin::Free => write!(f, "┈┈┈ ")?, + Bin::Black(n) => write!(f, "{:2}█ ", n)?, + Bin::White(n) => write!(f, "{:2}░ ", n)? + } + } + + write!(f, "╴\n ")?; + for n in (1..=12).rev() { write!(f, "{:2}. ", n)?; } + write!(f, "\n")?; + + Ok(()) } +} + +fn main() { + let mut board = Game::default(); - #[derive(Debug,Clone,Default)] - pub struct Player { - pub born_off: u32, - pub on_bar: u32, - } + board.bins[4] = Bin::Black(5); + board.bins[5] = Bin::Black(2); + board.bins[6] = Bin::Black(7); - #[derive(Debug,Clone,Copy)] - pub enum Bin { - Free, - Black(u32), - White(u32), + board.bins[14] = Bin::White(9); + board.bins[15] = Bin::White(10); + board.bins[16] = Bin::White(11); + board.bins[2] = Bin::White(2); + + + // // let mut image = RgbImage::new(200, 200); + // let mut image : RgbImage = RgbImage::new(200, 200); + // + // let font = Vec::from(include_bytes!("DejaVuSans.ttf") as &[u8]); + // let font = FontCollection::from_bytes(font) + // .unwrap() + // .into_font() + // .unwrap(); + // + // let height = 12.0; + // let scale = Scale { + // x: height, + // y: height, + // }; + // + // let white = Rgb([255u8, 255u8, 255u8]); + // + // draw_text_mut( + // &mut image, + // white, + // 0, + // 0, + // scale, + // &font, + // "Hello, world!", + // ); + // + // imageproc::drawing::draw_line_segment_mut(&mut image, (0.0, 0.0), (200.0,200.0), white); + // + // let _ = image.save("out.gif").unwrap(); + + let mut image = CrispImage::new(250, 60); + + image.fill(BG); + + fn draw_disc(image : &mut CrispImage, pos: (i32, i32), bin : Bin) { + match bin { + Bin::Free => { + image.circle_filled(GRAY, pos, 7); + }, + Bin::Black(n) => { + let s = n.to_string(); + // image.circle_filled(BLACK, pos, 7); + image.circle(WHITE, pos, 7); + image.text(WHITE, (pos.0 - (s.len() as i32 * 5)/2, pos.1 - 3), &s); + }, + Bin::White(n) => { + let s = n.to_string(); + image.circle_filled(WHITE, pos, 7); + image.circle(WHITE, pos, 7); + image.text(BLACK, (pos.0 - (s.len() as i32 * 5)/2, pos.1 - 3), &s); + }, + } } - - #[derive(Debug)] - pub struct Game { - pub phase : Phase, - pub multiplier : u32, - pub roll : [u8; 2], - pub players : [Player; 2], - pub bins : [Bin; 24], + + for (i, n) in (13..=24).enumerate() { + let s = n.to_string(); + image.text(YELLOW, (10 + 20 * i as i32 - (s.len() as i32 * 5)/2, 2), &s); } - - impl Default for Game { - fn default() -> Self { - Game { - phase: Phase::PickSides, - multiplier: 1, - roll: [0; 2], - players: [ - Player::default(), - Player::default(), - ], - bins: [Bin::Free; 24], - } - } + + for (i, n) in (1..=12).rev().enumerate() { + let s = n.to_string(); + image.text(YELLOW, (10 + 20 * i as i32 - (s.len() as i32 * 5)/2, 51), &s); } - - impl Display for Game { - fn fmt(&self, f : &mut fmt::Formatter<'_>) -> fmt::Result { - /* - - const BLACK_DIGITS : [char; 20] = [ - '❶', '❷', '❸', '❹', '❺', '❻', '❼', '❽', '❾', '❿', - '⓫', '⓬', '⓭', '⓮', '⓯', '⓰', '⓱', '⓲', '⓳', '⓴', - ]; - - const WHITE_DIGITS : [char; 20] = [ - '➀', '➁', '➂', '➃', '➄', '➅', '➆', '➇', '➈', '➉', - '⑪', '⑫', '⑬', '⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳', - ]; - - write!(f, "13 14 15 16 17 18 19 20 21 22 23 24\n")?; - for b in &self.bins[12..=23] { - match *b { - Bin::Free => write!(f, "-- ")?, - Bin::Black(n) => { - if n > 0 && n <= BLACK_DIGITS.len() as u32 { - write!(f, "{} ", BLACK_DIGITS[(n-1) as usize])?; - } else { - write!(f, "{} ", n)?; - } - } - Bin::White(n) => { - if n > 0 && n <= WHITE_DIGITS.len() as u32 { - write!(f, "{} ", WHITE_DIGITS[(n-1) as usize])?; - } else { - write!(f, "{} ", n)?; - } - } - } - - } - write!(f, "\n")?; - - for b in (&self.bins[0..=11]).iter().rev() { - match *b { - Bin::Free => write!(f, "-- ")?, - Bin::Black(n) => { - if n > 0 && n <= BLACK_DIGITS.len() as u32 { - write!(f, "{} ", BLACK_DIGITS[(n-1) as usize])?; - } else { - write!(f, "{} ", n)?; - } - } - Bin::White(n) => { - if n > 0 && n <= WHITE_DIGITS.len() as u32 { - write!(f, "{} ", WHITE_DIGITS[(n-1) as usize])?; - } else { - write!(f, "{} ", n)?; - } - } - } - } - - write!(f, "\n")?; - write!(f, "12 11 10 9 8 7 6 5 4 3 2 1\n")?; - - */ - - //* - write!(f, " ")?; - for n in (13..=24).rev() { write!(f, "{:2}. ", n)?; } - write!(f, "\n╭ ")?; - - for b in &self.bins[12..=23] { - match *b { - Bin::Free => write!(f, "┈┈┈ ")?, - Bin::Black(n) => write!(f, "{:2}█ ", n)?, - Bin::White(n) => write!(f, "{:2}░ ", n)? - } - } - write!(f, "╴\n╰ ")?; - - for b in (&self.bins[0..=11]).iter().rev() { - match *b { - Bin::Free => write!(f, "┈┈┈ ")?, - Bin::Black(n) => write!(f, "{:2}█ ", n)?, - Bin::White(n) => write!(f, "{:2}░ ", n)? - } - } - write!(f, "╴\n ")?; - for n in (1..=12).rev() { write!(f, "{:2}. ", n)?; } - write!(f, "\n")?; - //*/ + // + // let s = "1"; + // image.text(YELLOW, (10 - (s.len() as i32 * 5)/2, 2), &s); + // + // let s = "13"; + // image.text(YELLOW, (30 - (s.len() as i32 * 5)/2, 2), &s); + + draw_disc(&mut image, (10, 20), Bin::Black(7)); + draw_disc(&mut image, (30, 20), Bin::Black(13)); + draw_disc(&mut image, (50, 20), Bin::Free); + draw_disc(&mut image, (70, 20), Bin::White(69)); + draw_disc(&mut image, (90, 20), Bin::White(9)); + for n in 1..=7 { + draw_disc(&mut image, (90+n*20, 20), Bin::Free); + } - /* - const DIGITS : &[u8; 36] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for n in 0..=11 { + draw_disc(&mut image, (10 + n*20, 40), Bin::Free); + } - write!(f, " 14 16 18 20 22 24\n")?; - write!(f, " 13╲╱15╲╱17╲╱19╲╱21╲╱23╲╱\n")?; - write!(f, "╭")?; + image.render("out.png"); - for b in &self.bins[12..=23] { - match *b { - Bin::Free => write!(f, "──")?, - Bin::Black(n) => write!(f, "{}█", DIGITS[n as usize] as char)?, - Bin::White(n) => write!(f, "{}░", DIGITS[n as usize] as char)? - } - } - write!(f, "─┘\n╰")?; - - for b in (&self.bins[0..=11]).iter().rev() { - match *b { - Bin::Free => write!(f, "──")?, - Bin::Black(n) => write!(f, "{}█", DIGITS[n as usize] as char)?, - Bin::White(n) => write!(f, "{}░", DIGITS[n as usize] as char)? - } - } + println!("{}", board); +} - write!(f, "─┐\n")?; - write!(f, " 12╱╲10╱╲8.╱╲6.╱╲4.╱╲2.╱╲\n")?; - write!(f, " 11 9. 7. 5. 3. 1.\n")?; - */ +type Color = Rgb; - Ok(()) +struct CrispImage<'a> { + image : RgbImage, + font: Font<'a>, + width: u32, + height: u32, + font_scale: Scale, +} + +const BG : Color = Rgb([18, 26, 36]); + +const BLACK : Color = Rgb([0, 0, 0]); +const GRAY : Color = Rgb([67, 83, 104]); +const WHITE : Color = Rgb([255, 255, 255]); +const RED : Color = Rgb([255, 0, 0]); +const YELLOW : Color = Rgb([255, 255, 0]); +const GREEN : Color = Rgb([0, 255, 0]); +const BLUE : Color = Rgb([0, 0, 255]); +const CYAN : Color = Rgb([0, 255, 255]); +const MAGENTA : Color = Rgb([255, 0, 255]); + +impl<'a> CrispImage<'a> { + fn new(width: u32, height: u32) -> Self { + let mut image : RgbImage = RgbImage::new(width, height); + + // let font_bytes = include_bytes!("DejaVuSans.ttf"); + // let font_bytes = include_bytes!("lilliput steps.ttf"); + // let font_bytes = include_bytes!("fixedsys.ttf"); // 16.2 + // let font_bytes = include_bytes!("DOSVGA.ttf"); // 16 + let font_bytes = include_bytes!("coders_crux.ttf"); // 8.86 + + let font = Vec::from(font_bytes as &[u8]); + + let font = FontCollection::from_bytes(font) + .unwrap() + .into_font() + .unwrap(); + + Self { + image, + font, + width, + height, + font_scale: Scale { + /* x: 16., + y: 16.,*/ + x: 8.86, + y: 8.86, + }, } } -} -fn main() { - let mut board = bgm::Game::default(); - - board.bins[4] = bgm::Bin::Black(5); - board.bins[5] = bgm::Bin::Black(2); - board.bins[6] = bgm::Bin::Black(7); - - board.bins[14] = bgm::Bin::White(9); - board.bins[15] = bgm::Bin::White(10); - board.bins[16] = bgm::Bin::White(11); - board.bins[2] = bgm::Bin::White(2); + fn fill(&mut self, color: Color) { + self.rect_filled(color, (0,0), (self.width, self.height)); + } + fn render(&self, path : impl AsRef) { + let _ = self.image.save(path).unwrap(); + } - println!("{}", board); + fn font_scale(&mut self, f : f32) { + self.font_scale.x = f; + self.font_scale.y = f; + } + + fn text(&mut self, color: Color, pos : (i32, i32), text: &str) { + assert!(pos.0 >= 0); + assert!(pos.1 >= 0); + + drawing::draw_text_mut( + &mut self.image, + color, + pos.0 as u32, + pos.1 as u32, + self.font_scale, + &self.font, + text, + ); + } + + fn circle(&mut self, color: Color, center: (i32, i32), radius: i32) { + drawing::draw_hollow_circle_mut(&mut self.image, center, radius, color); + } + + fn circle_filled(&mut self, color: Color, center: (i32, i32), radius: i32) { + drawing::draw_filled_circle_mut(&mut self.image, center, radius, color); + } + + fn line(&mut self, color: Color, from: (i32, i32), to: (i32, i32)) { + drawing::draw_line_segment_mut(&mut self.image, + (from.0 as f32, from.1 as f32), + (to.0 as f32, to.1 as f32), + color); + } + + fn rect(&mut self, color: Color, tl: (i32, i32), size: (u32, u32)) { + drawing::draw_hollow_rect_mut(&mut self.image, + Rect::at(tl.0, tl.1).of_size(size.0, size.1), + color); + } + + fn rect_filled(&mut self, color: Color, tl: (i32, i32), size: (u32, u32)) { + drawing::draw_filled_rect_mut(&mut self.image, + Rect::at(tl.0, tl.1).of_size(size.0, size.1), + color); + } }