A character's mouth as scene nodes. The shapes are pixel-mouth's: a character here is only a [MouthSpec] (where its mouth sits and how it is drawn), and this turns the rectangles of a level into nodes. A mouth that is told its shape draws that shape; one that is not shows each shape in turn on the beat (Motion::Flap), so the drawn characters and the pack's are one mouth.

6use pixel_mouth::{Level, MouthSpec, rects};
8use crate::motion::Motion;
9use crate::node::{Color, Node, moving, ra};
10use crate::scene::Mouth;

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}

The mouth shut.

18pub fn closed(spec: &MouthSpec) -> Vec<Node> {
19    level(spec, Level::Closed)
20}

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}
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}