1use crate::avatar::shape::Shape;
5
6const MARGIN: f32 = 0.86;
9
10#[derive(Clone, Copy, Debug, PartialEq)]
11pub struct Eyes {
12 pub rx: f32,
14 pub ratio: f32,
16 pub gap: f32,
18 pub rise: f32,
20 pub lean: f32,
22 pub n: f32,
24}
25
26impl Default for Eyes {
27 fn default() -> Self {
28 Self::CALM
29 }
30}
31
32impl Eyes {
33 pub const CALM: Self = Self {
34 rx: 0.15,
35 ratio: 1.9,
36 gap: 0.34,
37 rise: 0.04,
38 lean: 0.0,
39 n: 4.0,
40 };
41 pub const WIDE: Self = Self {
42 rx: 0.2,
43 ratio: 1.15,
44 gap: 0.4,
45 ..Self::CALM
46 };
47 pub const KEEN: Self = Self {
48 rx: 0.12,
49 ratio: 2.8,
50 lean: 12.0,
51 ..Self::CALM
52 };
53 pub const SLEEPY: Self = Self {
54 rx: 0.19,
55 ratio: 0.42,
56 rise: 0.0,
57 n: 5.0,
58 ..Self::CALM
59 };
60 pub const TALL: Self = Self {
61 rx: 0.11,
62 ratio: 3.2,
63 gap: 0.32,
64 ..Self::CALM
65 };
66 pub const DOTS: Self = Self {
68 rx: 0.11,
69 ratio: 1.0,
70 gap: 0.3,
71 n: 2.2,
72 ..Self::CALM
73 };
74 pub const SLY: Self = Self {
77 rx: 0.12,
78 ratio: 2.5,
79 lean: 16.0,
80 ..Self::CALM
81 };
82 pub const CLOSE: Self = Self {
83 gap: 0.22,
84 ..Self::CALM
85 };
86 pub const FAR: Self = Self {
87 gap: 0.5,
88 ..Self::CALM
89 };
90 pub const LOW: Self = Self {
91 rise: -0.16,
92 ..Self::CALM
93 };
94
95 pub const PRESETS: [(&'static str, Self); 10] = [
96 ("calm", Self::CALM),
97 ("wide", Self::WIDE),
98 ("keen", Self::KEEN),
99 ("tall", Self::TALL),
100 ("dots", Self::DOTS),
101 ("sly", Self::SLY),
102 ("sleepy", Self::SLEEPY),
103 ("close", Self::CLOSE),
104 ("far", Self::FAR),
105 ("low", Self::LOW),
106 ];
107}
108
109impl From<u64> for Eyes {
110 fn from(seed: u64) -> Self {
111 let mut h = seed ^ 0x5bf0_3635_ca62_9d7f;
113 let mut unit = || {
114 h = (h ^ (h >> 33)).wrapping_mul(0xff51_afd7_ed55_8ccd);
115 (h >> 40) as f32 / 16_777_216.0
116 };
117 let range = |u: f32, lo: f32, hi: f32| lo + u * (hi - lo);
118 Self {
119 rx: range(unit(), 0.12, 0.19),
120 ratio: range(unit(), 1.1, 2.6),
121 gap: range(unit(), 0.28, 0.42),
122 rise: range(unit(), -0.02, 0.12),
123 lean: range(unit(), -12.0, 12.0),
124 n: range(unit(), 2.6, 5.5),
125 }
126 }
127}
128
129#[derive(Clone, Copy, Debug, PartialEq)]
131pub struct Eye {
132 pub cx: f32,
133 pub cy: f32,
134 pub rx: f32,
135 pub ry: f32,
136 pub rot: f32,
137 pub n: f32,
138}
139
140impl Eyes {
141 pub fn place(&self, shape: &Shape, drift: f32) -> [Eye; 2] {
149 let cy = -self.rise;
150 let reach = self.rx.hypot(self.rx * self.ratio);
151 let wander = drift * std::f32::consts::SQRT_2;
152 let fit = [-self.gap, self.gap]
153 .iter()
154 .map(|&cx| {
155 let room = [
156 (-drift, -drift),
157 (drift, -drift),
158 (-drift, drift),
159 (drift, drift),
160 ]
161 .iter()
162 .map(|(dx, dy)| shape.reach(cx + dx, cy + dy))
163 .fold(f32::INFINITY, f32::min)
164 * MARGIN;
165 let want = cx.hypot(cy) + reach;
166 if want + wander > room {
167 (room - wander) / want
168 } else {
169 1.0
170 }
171 })
172 .fold(1.0f32, f32::min);
173
174 [-1.0f32, 1.0].map(|side| Eye {
175 cx: side * self.gap * fit,
176 cy: cy * fit,
177 rx: self.rx * fit,
178 ry: self.rx * self.ratio * fit,
179 rot: side * self.lean,
180 n: self.n,
181 })
182 }
183}