diff --git a/showbits-thermal-printer/src/documents.rs b/showbits-thermal-printer/src/documents.rs index 235025e..974fe37 100644 --- a/showbits-thermal-printer/src/documents.rs +++ b/showbits-thermal-printer/src/documents.rs @@ -1,5 +1,6 @@ use showbits_typst::Typst; +pub mod egg; pub mod image; pub mod text; diff --git a/showbits-thermal-printer/src/documents/egg/data.json b/showbits-thermal-printer/src/documents/egg/data.json new file mode 100644 index 0000000..a143d7c --- /dev/null +++ b/showbits-thermal-printer/src/documents/egg/data.json @@ -0,0 +1,9 @@ +{ + "covers": 7, + "patterns": 40, + "bad_covers": 1, + "bad_patterns": 7, + "seed": 1, + "mode": null, + "feed": true +} diff --git a/showbits-thermal-printer/src/documents/egg/egg b/showbits-thermal-printer/src/documents/egg/egg new file mode 120000 index 0000000..f2fdb78 --- /dev/null +++ b/showbits-thermal-printer/src/documents/egg/egg @@ -0,0 +1 @@ +../../../../showbits-assets/data/egg \ No newline at end of file diff --git a/showbits-thermal-printer/src/documents/egg/egg_bad b/showbits-thermal-printer/src/documents/egg/egg_bad new file mode 120000 index 0000000..f12189f --- /dev/null +++ b/showbits-thermal-printer/src/documents/egg/egg_bad @@ -0,0 +1 @@ +../../../../showbits-assets/data/egg_bad \ No newline at end of file diff --git a/showbits-thermal-printer/src/documents/egg/lib.typ b/showbits-thermal-printer/src/documents/egg/lib.typ new file mode 120000 index 0000000..ea61142 --- /dev/null +++ b/showbits-thermal-printer/src/documents/egg/lib.typ @@ -0,0 +1 @@ +../lib.typ \ No newline at end of file diff --git a/showbits-thermal-printer/src/documents/egg/main.typ b/showbits-thermal-printer/src/documents/egg/main.typ new file mode 100644 index 0000000..9a1a6f5 --- /dev/null +++ b/showbits-thermal-printer/src/documents/egg/main.typ @@ -0,0 +1,55 @@ +#import "@preview/oxifmt:0.2.1": strfmt +#import "@preview/suiji:0.3.0": * +#import "lib.typ"; +#show: it => lib.init(it) + +#let data = json("data.json") +#let rng = gen-rng(data.seed) + +#let file_series(n, fmt) = array.range(n).map(n => strfmt(fmt, n)) + +#let good_covers = file_series(data.covers, "egg/cover_{:02}.png") +#let good_patterns = file_series(data.patterns, "egg/pattern_{:02}.png") +#let bad_covers = file_series(data.bad_covers, "egg_bad/cover_{:02}.png") +#let bad_patterns = file_series(data.bad_patterns, "egg_bad/pattern_{:02}.png") + +// Always generate random value to so that egg looks the same whether we chose +// the mode directly or randomly. +#let (rng, val) = random(rng) + +#let (covers, patterns) = if data.mode == "good" { + (good_covers, good_patterns) +} else if data.mode == "bad" { + (bad_covers, bad_patterns) +} else if val < 1 / 8 { + (bad_covers, bad_patterns) +} else { + (good_covers, good_patterns) +} + +#context { + let (rng, cover) = choice(rng, covers) + let cover = image(cover, width: lib.width) + let cover_size = measure(cover) + + let pattern_stack_height = 0pt + let pattern_stack = while pattern_stack_height < cover_size.height { + let pattern = () + (rng, pattern) = choice(rng, patterns) + let pattern = image(pattern, width: cover_size.width) + pattern_stack_height += measure(pattern).height + (pattern,) + } + + box( + height: cover_size.height, + clip: true, + stack(dir: ttb, ..pattern_stack), + ) + + place(top + left, cover) +} + +#if data.feed { + lib.feed +} diff --git a/showbits-thermal-printer/src/documents/egg/mod.rs b/showbits-thermal-printer/src/documents/egg/mod.rs new file mode 100644 index 0000000..5e6b564 --- /dev/null +++ b/showbits-thermal-printer/src/documents/egg/mod.rs @@ -0,0 +1,64 @@ +use axum::{Form, extract::State}; +use serde::{Deserialize, Serialize}; + +use crate::{ + drawer::{Command, NewTypstDrawing}, + server::Server, +}; + +#[derive(Serialize)] +struct Data { + covers: usize, + patterns: usize, + bad_covers: usize, + bad_patterns: usize, + seed: i64, + mode: Option, + feed: bool, +} + +#[derive(Deserialize)] +pub struct FormData { + pub seed: Option, + pub mode: Option, + pub feed: Option, +} + +pub async fn post(server: State, Form(form): Form) { + let seed = form.seed.unwrap_or_else(rand::random); + + let data = Data { + covers: showbits_assets::EGG_COVERS.len(), + patterns: showbits_assets::EGG_PATTERNS.len(), + bad_covers: showbits_assets::EGG_BAD_COVERS.len(), + bad_patterns: showbits_assets::EGG_BAD_PATTERNS.len(), + seed, + mode: form.mode, + feed: form.feed.unwrap_or(true), + }; + + let mut typst = super::typst_with_lib() + .with_json("/data.json", &data) + .with_main_file(include_str!("main.typ")); + + for (i, cover) in showbits_assets::EGG_COVERS.iter().enumerate() { + typst.add_file(format!("/egg/cover_{i:02}.png"), *cover); + } + + for (i, pattern) in showbits_assets::EGG_PATTERNS.iter().enumerate() { + typst.add_file(format!("/egg/pattern_{i:02}.png"), *pattern); + } + + for (i, cover) in showbits_assets::EGG_BAD_COVERS.iter().enumerate() { + typst.add_file(format!("/egg_bad/cover_{i:02}.png"), *cover); + } + + for (i, pattern) in showbits_assets::EGG_BAD_PATTERNS.iter().enumerate() { + typst.add_file(format!("/egg_bad/pattern_{i:02}.png"), *pattern); + } + + let _ = server + .tx + .send(Command::draw(NewTypstDrawing::new(typst))) + .await; +} diff --git a/showbits-thermal-printer/src/documents/egg/plugin.wasm b/showbits-thermal-printer/src/documents/egg/plugin.wasm new file mode 120000 index 0000000..04468b6 --- /dev/null +++ b/showbits-thermal-printer/src/documents/egg/plugin.wasm @@ -0,0 +1 @@ +../plugin.wasm \ No newline at end of file diff --git a/showbits-thermal-printer/src/documents/lib.typ b/showbits-thermal-printer/src/documents/lib.typ index 710398f..edb1dbf 100644 --- a/showbits-thermal-printer/src/documents/lib.typ +++ b/showbits-thermal-printer/src/documents/lib.typ @@ -1,6 +1,8 @@ +#let width = 384pt + #let init(it) = { set page( - width: 384pt, + width: width, height: auto, margin: (x: 0pt, y: 4pt), ) diff --git a/showbits-thermal-printer/src/drawer.rs b/showbits-thermal-printer/src/drawer.rs index e89e5d3..030f1ad 100644 --- a/showbits-thermal-printer/src/drawer.rs +++ b/showbits-thermal-printer/src/drawer.rs @@ -2,7 +2,6 @@ mod backlog; mod calendar; mod cells; mod chat_message; -mod egg; mod new_typst; mod photo; mod tictactoe; @@ -15,8 +14,8 @@ use crate::persistent_printer::PersistentPrinter; pub use self::{ backlog::BacklogDrawing, calendar::CalendarDrawing, cells::CellsDrawing, - chat_message::ChatMessageDrawing, egg::EggDrawing, new_typst::NewTypstDrawing, - photo::PhotoDrawing, tictactoe::TicTacToeDrawing, typst::TypstDrawing, + chat_message::ChatMessageDrawing, new_typst::NewTypstDrawing, photo::PhotoDrawing, + tictactoe::TicTacToeDrawing, typst::TypstDrawing, }; pub const FEED: f32 = 96.0; diff --git a/showbits-thermal-printer/src/drawer/egg.rs b/showbits-thermal-printer/src/drawer/egg.rs deleted file mode 100644 index 0826b19..0000000 --- a/showbits-thermal-printer/src/drawer/egg.rs +++ /dev/null @@ -1,98 +0,0 @@ -use image::{RgbaImage, imageops}; -use rand::{Rng, seq::IndexedRandom}; -use showbits_assets::{EGG_BAD_COVERS, EGG_BAD_PATTERNS, EGG_COVERS, EGG_PATTERNS}; -use showbits_common::{ - Node, Tree, WidgetExt, - color::{self, WHITE}, - widgets::{Image, Text}, -}; -use taffy::{AlignItems, Display, FlexDirection, prelude::length, style_helpers::percent}; - -use crate::persistent_printer::PersistentPrinter; - -use super::{Context, Drawing, FEED}; - -pub struct EggDrawing; - -fn load_image(bytes: &[u8]) -> RgbaImage { - image::load_from_memory(bytes) - .expect("malformed image data") - .into_rgba8() -} - -impl Drawing for EggDrawing { - fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> { - let mut rng = rand::rng(); - - // Choose which set of egg images to use - let bad_egg = rng.random_range(0..8) == 0; - let (covers, patterns) = if bad_egg { - (EGG_BAD_COVERS, EGG_BAD_PATTERNS) - } else { - (EGG_COVERS, EGG_PATTERNS) - }; - - // Load images from memory - let covers = covers.iter().map(|img| load_image(img)).collect::>(); - let patterns = patterns - .iter() - .map(|img| load_image(img)) - .collect::>(); - - // Choose a random cover - let cover = covers.choose(&mut rng).expect("too few covers"); - - // Prepare image of appropriate size - let mut image = - RgbaImage::from_pixel(cover.width(), cover.height(), color::to_image_color(WHITE)); - - // Draw patterns onto egg - let mut last_idx = None; - let mut y = rng.random_range(-100_i64..0); - let height: i64 = image.height().into(); - while y < height { - let idx = loop { - let idx = rng.random_range(0..patterns.len()); - if Some(idx) != last_idx { - break idx; - } - }; - - let paint = &patterns[idx]; - imageops::overlay(&mut image, paint, 0, y); - y += <_ as Into>::into(paint.height()); - last_idx = Some(idx); - } - - // Finally, draw the cover - imageops::overlay(&mut image, cover, 0, 0); - - let mut tree = Tree::::new(WHITE); - - let image = Image::new(image) - .with_grow(false) - .with_shrink(false) - .node() - .register(&mut tree)?; - - let text = Text::new() - .with_metrics(Text::default_metrics().scale(2.0)) - .and_plain("Frohe Ostern!") - .widget(&mut ctx.font_stuff) - .node() - .register(&mut tree)?; - - let root = Node::empty() - .with_size_width(percent(1.0)) - .with_padding_bottom(length(FEED)) - .with_display(Display::Flex) - .with_flex_direction(FlexDirection::Column) - .with_align_items(Some(AlignItems::Center)) - .and_child(image) - .and_child(text) - .register(&mut tree)?; - - printer.print_tree(&mut tree, ctx, root)?; - Ok(()) - } -} diff --git a/showbits-thermal-printer/src/server.rs b/showbits-thermal-printer/src/server.rs index 6837026..d9b0167 100644 --- a/showbits-thermal-printer/src/server.rs +++ b/showbits-thermal-printer/src/server.rs @@ -15,8 +15,8 @@ use tokio::{net::TcpListener, sync::mpsc}; use crate::{ documents, drawer::{ - CalendarDrawing, CellsDrawing, ChatMessageDrawing, Command, EggDrawing, PhotoDrawing, - TicTacToeDrawing, TypstDrawing, + CalendarDrawing, CellsDrawing, ChatMessageDrawing, Command, PhotoDrawing, TicTacToeDrawing, + TypstDrawing, }, }; @@ -32,7 +32,7 @@ pub async fn run(tx: mpsc::Sender, addr: String) -> anyhow::Result<()> .route("/calendar", post(post_calendar)) .route("/cells", post(post_cells)) .route("/chat_message", post(post_chat_message)) - .route("/egg", post(post_egg).fallback(get_static_file)) + .route("/egg", post(documents::egg::post).fallback(get_static_file)) .route( "/image", post(documents::image::post).fallback(get_static_file), @@ -109,13 +109,6 @@ async fn post_chat_message(server: State, request: Form) -> impl IntoResponse { - let _ = server.tx.send(Command::draw(EggDrawing)).await; - Redirect::to("egg") -} - // /photo async fn post_photo(server: State, mut multipart: Multipart) -> somehow::Result {