1//! A character's mouth as scene nodes. The shapes are `pixel-mouth`'s: a character here is only a 2//! [`MouthSpec`] (where its mouth sits and how it is drawn), and this turns the rectangles of a level into nodes. 3//! A mouth that is told its shape draws that shape; one that is not shows each shape in turn on the beat 4//! (`Motion::Flap`), so the drawn characters and the pack's are one mouth. 5 6use pixel_mouth::{Level, MouthSpec, rects}; 7 8use crate::motion::Motion; 9use crate::node::{Color, Node, moving, ra}; 10use crate::scene::Mouth; 11 12/// The rectangles of `level` as nodes. 13pub fn level(spec: &MouthSpec, level: Level) -> Vec<Node> { 14 rects(spec, level).into_iter().map(|r| ra(r.x, r.y, r.w, r.h, Color::hex(r.rgb), r.alpha)).collect() 15} 16 17/// The mouth shut. 18pub fn closed(spec: &MouthSpec) -> Vec<Node> { 19 level(spec, Level::Closed) 20} 21 22/// The mouth while speaking, in `mouth`'s shape, or on the beat when the shape is not known. 23pub fn speaking(spec: &MouthSpec, mouth: Option<Mouth>) -> Vec<Node> { 24 match mouth { 25 Some(shape) => level(spec, shape), 26 None => Level::ALL.into_iter().map(|l| moving(Motion::Flap(l), (0.0, 0.0), level(spec, l))).collect(), 27 } 28} 29 30#[cfg(test)] 31mod tests { 32 use super::*; 33 use pixel_mouth::{Palette, Style}; 34 35 const SPEC: MouthSpec = MouthSpec { anchor: (7.5, 10.4), size: 2.2, style: Style::Cat, palette: Palette { ink: 0, beak: 0 } }; 36 37 #[test] 38 fn a_told_shape_is_drawn_and_an_untold_one_follows_the_beat() { 39 assert_eq!(speaking(&SPEC, Some(Level::Wide)), level(&SPEC, Level::Wide)); 40 let beat = speaking(&SPEC, None); 41 assert_eq!(beat.len(), 4); 42 assert!(beat.iter().all(|n| matches!(n, Node::Group(g) if matches!(g.motion, Motion::Flap(_))))); 43 } 44}