use crate::avatar::shape::Shape;
const MARGIN: f32 = 0.86;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Eyes {
pub rx: f32,
pub ratio: f32,
pub gap: f32,
pub rise: f32,
pub lean: f32,
pub n: f32,
}
impl Default for Eyes {
fn default() -> Self {
Self::CALM
}
}
impl Eyes {
pub const CALM: Self = Self {
rx: 0.15,
ratio: 1.9,
gap: 0.34,
rise: 0.04,
lean: 0.0,
n: 4.0,
};
pub const WIDE: Self = Self {
rx: 0.2,
ratio: 1.15,
gap: 0.4,
..Self::CALM
};
pub const KEEN: Self = Self {
rx: 0.12,
ratio: 2.8,
lean: 12.0,
..Self::CALM
};
pub const SLEEPY: Self = Self {
rx: 0.19,
ratio: 0.42,
rise: 0.0,
n: 5.0,
..Self::CALM
};
pub const TALL: Self = Self {
rx: 0.11,
ratio: 3.2,
gap: 0.32,
..Self::CALM
};
pub const DOTS: Self = Self {
rx: 0.11,
ratio: 1.0,
gap: 0.3,
n: 2.2,
..Self::CALM
};
pub const SLY: Self = Self {
rx: 0.12,
ratio: 2.5,
lean: 16.0,
..Self::CALM
};
pub const CLOSE: Self = Self {
gap: 0.22,
..Self::CALM
};
pub const FAR: Self = Self {
gap: 0.5,
..Self::CALM
};
pub const LOW: Self = Self {
rise: -0.16,
..Self::CALM
};
pub const PRESETS: [(&'static str, Self); 10] = [
("calm", Self::CALM),
("wide", Self::WIDE),
("keen", Self::KEEN),
("tall", Self::TALL),
("dots", Self::DOTS),
("sly", Self::SLY),
("sleepy", Self::SLEEPY),
("close", Self::CLOSE),
("far", Self::FAR),
("low", Self::LOW),
];
}
impl From<u64> for Eyes {
fn from(seed: u64) -> Self {
let mut h = seed ^ 0x5bf0_3635_ca62_9d7f;
let mut unit = || {
h = (h ^ (h >> 33)).wrapping_mul(0xff51_afd7_ed55_8ccd);
(h >> 40) as f32 / 16_777_216.0
};
let range = |u: f32, lo: f32, hi: f32| lo + u * (hi - lo);
Self {
rx: range(unit(), 0.12, 0.19),
ratio: range(unit(), 1.1, 2.6),
gap: range(unit(), 0.28, 0.42),
rise: range(unit(), -0.02, 0.12),
lean: range(unit(), -12.0, 12.0),
n: range(unit(), 2.6, 5.5),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Eye {
pub cx: f32,
pub cy: f32,
pub rx: f32,
pub ry: f32,
pub rot: f32,
pub n: f32,
}
impl Eyes {
pub fn place(&self, shape: &Shape, drift: f32) -> [Eye; 2] {
let cy = -self.rise;
let reach = self.rx.hypot(self.rx * self.ratio);
let wander = drift * std::f32::consts::SQRT_2;
let fit = [-self.gap, self.gap]
.iter()
.map(|&cx| {
let room = [
(-drift, -drift),
(drift, -drift),
(-drift, drift),
(drift, drift),
]
.iter()
.map(|(dx, dy)| shape.reach(cx + dx, cy + dy))
.fold(f32::INFINITY, f32::min)
* MARGIN;
let want = cx.hypot(cy) + reach;
if want + wander > room {
(room - wander) / want
} else {
1.0
}
})
.fold(1.0f32, f32::min);
[-1.0f32, 1.0].map(|side| Eye {
cx: side * self.gap * fit,
cy: cy * fit,
rx: self.rx * fit,
ry: self.rx * self.ratio * fit,
rot: side * self.lean,
n: self.n,
})
}
}