1//! Bunny: a sitting rabbit on the app's 15 by 16 grid, drawn for the site. Long upright ears 2//! with a pink inside, a pear-shaped body (a narrow head over a wide lap), round dark eyes with 3//! a highlight, pink cheeks, a small split mouth, big hind feet and a round white tail. Cream on 4//! a dark page. 5//! 6//! Layout, back to front: tail, feet, ears, head and body, belly, cheeks, muzzle, eyes, mouth. 7//! The eyes are `Role::Eyes` groups so the gaze moves them; each blinks about its own middle. 8 9use whiskers_art::decor::{INK, WHITE, spiral}; 10 11use crate::mood::Mood; 12use crate::motion::Motion; 13use crate::node::{Color, Look, Node, eyes, moving, r, ra}; 14 15const FUR: Color = Color::hex(0xF1EBDD); 16const SHADE: Color = Color::hex(0xD3C8B0); 17const INNER: Color = Color::hex(0xF0B5C0); 18const NOSE: Color = Color::hex(0xE48CA0); 19const EYE: Color = Color::hex(0x2E2430); 20 21/// The figure in `mood`: standing up, or lying down when asleep. 22pub fn figure(mood: Mood) -> Vec<Node> { 23 match mood { 24 Mood::Sleeping => sleeping(), 25 Mood::Idle | Mood::Listening | Mood::Thinking | Mood::Speaking | Mood::Dizzy => sitting(mood), 26 } 27} 28 29fn sitting(mood: Mood) -> Vec<Node> { 30 let mut out = vec![ 31 // The tail: a small round puff that wags. 32 moving(Motion::TailWag, (0.0, 0.0), vec![r(13.0, 11.8, 1.0, 2.4, WHITE), r(12.6, 12.2, 1.8, 1.6, WHITE)]), 33 // Feet: big hind feet and small front ones. 34 r(2.0, 14.0, 3.0, 1.0, FUR), 35 r(2.0, 14.7, 3.0, 0.3, SHADE), 36 r(10.0, 14.0, 3.0, 1.0, FUR), 37 r(10.0, 14.7, 3.0, 0.3, SHADE), 38 r(5.5, 14.0, 1.5, 1.0, FUR), 39 r(8.0, 14.0, 1.5, 1.0, FUR), 40 ]; 41 ears(mood, &mut out); 42 // Head over a wider lap, and the pale belly. 43 out.push(r(3.0, 5.5, 9.0, 4.5, FUR)); 44 out.push(r(2.0, 9.5, 11.0, 4.5, FUR)); 45 out.push(r(4.5, 11.0, 6.0, 3.0, WHITE)); 46 // Cheeks, muzzle and nose. 47 out.push(ra(3.6, 8.6, 1.4, 0.8, INNER, 0.6)); 48 out.push(ra(10.0, 8.6, 1.4, 0.8, INNER, 0.6)); 49 out.push(r(6.0, 8.2, 3.0, 2.0, WHITE)); 50 out.push(r(7.0, 8.3, 1.0, 0.7, NOSE)); 51 face(mood, &mut out); 52 out 53} 54 55/// Ears: tall, two wide, a pink strip inside. Perked when listening, one folded when thinking, 56/// flopped out to the sides when dizzy. 57fn ears(mood: Mood, out: &mut Vec<Node>) { 58 match mood { 59 Mood::Dizzy => { 60 for (x, inner) in [(2.0, 2.5), (11.0, 11.5)] { 61 out.push(r(x, 3.5, 2.0, 3.0, FUR)); 62 out.push(r(inner, 4.0, 1.0, 2.0, INNER)); 63 } 64 } 65 _ => { 66 let lift = if mood == Mood::Listening { -1.0 } else { 0.0 }; 67 let top = lift; 68 out.push(r(4.0, top, 2.0, 6.0 - top, FUR)); 69 out.push(r(4.5, top + 1.2, 1.0, 4.3 - top, INNER)); 70 if mood == Mood::Thinking { 71 // The right ear is folded over at the tip. 72 out.push(r(9.0, 1.5, 2.0, 4.5, FUR)); 73 out.push(r(9.5, 2.7, 1.0, 3.0, INNER)); 74 out.push(r(10.0, 1.0, 1.8, 0.9, FUR)); 75 } else { 76 out.push(r(9.0, top, 2.0, 6.0 - top, FUR)); 77 out.push(r(9.5, top + 1.2, 1.0, 4.3 - top, INNER)); 78 } 79 } 80 } 81} 82 83/// One eye: a dark round with a highlight, `x` its left edge, shutting about its middle. 84fn eye(x: f32, top: f32, height: f32) -> Node { 85 moving( 86 Motion::Blink, 87 (x + 0.8, top + height / 2.0), 88 vec![r(x, top, 1.6, height, EYE), r(x + 0.25, top + 0.25, 0.5, 0.5, WHITE)], 89 ) 90} 91 92fn face(mood: Mood, out: &mut Vec<Node>) { 93 // The small split mouth under the nose. 94 let closed = |out: &mut Vec<Node>| { 95 out.push(ra(7.35, 9.0, 0.3, 0.5, EYE, 0.85)); 96 out.push(ra(6.7, 9.5, 0.7, 0.3, EYE, 0.85)); 97 out.push(ra(7.6, 9.5, 0.7, 0.3, EYE, 0.85)); 98 }; 99 match mood { 100 Mood::Dizzy => { 101 out.extend(spiral(5.2, 7.6, INK)); 102 out.extend(spiral(9.8, 7.6, INK)); 103 out.push(ra(7.0, 9.4, 1.0, 0.8, EYE, 0.85)); 104 } 105 Mood::Idle | Mood::Listening | Mood::Thinking | Mood::Speaking => { 106 let (look, top, height) = match mood { 107 Mood::Listening => (Look::Follow, 6.4, 2.2), 108 Mood::Thinking => (Look::Fixed(0.8, -0.8), 6.8, 1.6), 109 _ => (Look::Follow, 6.8, 1.6), 110 }; 111 out.push(eyes(look, vec![eye(4.4, top, height), eye(9.0, top, height)])); 112 if mood == Mood::Speaking { 113 out.push(moving(Motion::MouthFlap, (7.5, 9.4), vec![ra(6.7, 9.4, 1.6, 1.0, EYE, 0.85)])); 114 } else { 115 closed(out); 116 } 117 } 118 Mood::Sleeping => unreachable!("a sleeping rabbit is drawn by `sleeping`"), 119 } 120} 121 122/// Lying in a loaf with the ears laid back along the top and the eyes shut. 123fn sleeping() -> Vec<Node> { 124 vec![ 125 r(13.0, 13.0, 2.0, 2.0, WHITE), 126 r(2.0, 10.0, 11.0, 5.0, FUR), 127 r(3.0, 9.2, 5.0, 1.2, FUR), 128 r(3.4, 9.6, 4.0, 0.5, INNER), 129 r(4.5, 13.0, 6.0, 2.0, WHITE), 130 r(2.0, 14.6, 3.0, 0.4, SHADE), 131 r(7.0, 12.4, 1.0, 0.7, NOSE), 132 r(4.2, 12.0, 1.8, 0.4, EYE), 133 r(9.0, 12.0, 1.8, 0.4, EYE), 134 ra(3.2, 12.6, 1.2, 0.7, INNER, 0.6), 135 ra(10.6, 12.6, 1.2, 0.7, INNER, 0.6), 136 ] 137}