1//! The ginger cat, ported from the Android app's drawing. 2//! 3//! Differences from the app, all in how motion is expressed, none in the poses: the tail 4//! wag slides the tail's upright instead of stretching it, and the blink scales the eyes 5//! about their middle. 6 7use whiskers_art::decor::{INK, spiral}; 8use crate::mood::Mood; 9use crate::motion::Motion; 10use crate::node::{Color, Look, Node, eyes, moving, r, ra}; 11 12const FUR: Color = Color::hex(0xDE886D); 13const INNER_EAR: Color = Color::hex(0xF2A7B0); 14const WHISKER: Color = Color::hex(0xF5EDE6); 15 16pub fn figure(mood: Mood) -> Vec<Node> { 17 if mood == Mood::Sleeping { 18 return sleeping(); 19 } 20 standing(mood) 21} 22 23fn standing(mood: Mood) -> Vec<Node> { 24 let mut out = Vec::new(); 25 // Legs. 26 for x in [3.0, 5.0, 9.0, 11.0] { 27 out.push(r(x, 13.0, 1.0, 2.0, FUR)); 28 } 29 // Tail: the elbow stays, the upright wags. 30 out.push(r(13.0, 12.0, 2.0, 1.0, FUR)); 31 out.push(moving(Motion::TailWag, (0.0, 0.0), vec![r(14.0, 10.5, 1.0, 1.5, FUR)])); 32 // Ears: bottoms fixed on the head, tops perk up when listening. 33 let lift = if mood == Mood::Listening { -1.0 } else { 0.0 }; 34 for (x, top) in [(2.0, 4.0), (3.0, 3.0), (11.0, 3.0), (12.0, 4.0)] { 35 out.push(r(x, top + lift, 1.0, 6.0 - (top + lift), FUR)); 36 } 37 out.push(r(3.0, 4.0 + lift, 1.0, 2.0 - lift, INNER_EAR)); 38 out.push(r(11.0, 4.0 + lift, 1.0, 2.0 - lift, INNER_EAR)); 39 // Arms, then the torso over them. 40 out.push(r(0.0, 9.0, 2.0, 2.0, FUR)); 41 out.push(r(13.0, 9.0, 2.0, 2.0, FUR)); 42 out.push(r(2.0, 6.0, 11.0, 7.0, FUR)); 43 // Nose and whiskers. 44 out.push(r(7.0, 9.5, 1.0, 1.0, INNER_EAR)); 45 for y in [8.0, 11.3] { 46 out.push(r(-1.0, y, 4.0, 0.35, WHISKER)); 47 out.push(r(12.0, y, 4.0, 0.35, WHISKER)); 48 } 49 // Eyes. 50 match mood { 51 Mood::Dizzy => { 52 out.extend(spiral(4.5, 9.0, INK)); 53 out.extend(spiral(10.5, 9.0, INK)); 54 } 55 _ => { 56 let (height, top, look) = match mood { 57 Mood::Listening => (3.0, 7.5, Look::Follow), 58 Mood::Thinking => (2.0, 8.0, Look::Fixed(0.8, -0.8)), 59 _ => (2.0, 8.0, Look::Follow), 60 }; 61 let centre = top + height / 2.0; 62 let pupil = |x| moving(Motion::Blink, (x, centre), vec![r(x, top, 1.0, height, INK)]); 63 out.push(eyes(look, vec![pupil(4.0), pupil(10.0)])); 64 if mood == Mood::Speaking { 65 out.push(moving(Motion::MouthFlap, (7.5, 10.8), vec![ra(6.6, 10.8, 1.8, 1.0, INK, 0.85)])); 66 } 67 } 68 } 69 out 70} 71 72fn sleeping() -> Vec<Node> { 73 let mut out = Vec::new(); 74 for x in [3.0, 5.0, 9.0, 11.0] { 75 out.push(r(x, 9.0, 1.0, 1.0, FUR)); 76 } 77 out.push(r(14.0, 12.5, 2.0, 1.0, FUR)); // tail 78 out.extend([r(2.0, 9.0, 1.0, 1.0, FUR), r(3.0, 8.0, 1.0, 2.0, FUR), r(11.0, 8.0, 1.0, 2.0, FUR), r(12.0, 9.0, 1.0, 1.0, FUR)]); 79 out.extend([r(3.0, 9.0, 1.0, 1.0, INNER_EAR), r(11.0, 9.0, 1.0, 1.0, INNER_EAR)]); 80 out.push(r(1.0, 10.0, 13.0, 5.0, FUR)); 81 out.push(r(-1.0, 13.0, 2.0, 2.0, FUR)); 82 out.push(r(14.0, 13.0, 2.0, 2.0, FUR)); 83 out.push(r(7.0, 12.6, 1.0, 1.0, INNER_EAR)); 84 out.push(r(3.5, 12.0, 2.0, 0.4, INK)); 85 out.push(r(9.5, 12.0, 2.0, 0.4, INK)); 86 for y in [11.5, 14.0] { 87 out.push(r(-1.0, y, 3.0, 0.35, WHISKER)); 88 out.push(r(13.0, y, 4.0, 0.35, WHISKER)); 89 } 90 out 91}