T49: round 1 fix: a change of mood, though the act stays as inside a wink, and a press each ease her from the pose she was in, where a mood that changed mid-hop dropped her 12 points and an old finish cleared mid-glance snapped her eyes back; her brows ease with her
This commit is contained in:
6 files changed
+112
-20
No files matched your search
+96
-12
@@ -167,9 +167,10 @@ fn show(main: WindowHandle<Workspace>, cx: &mut App) -> anyhow::Result<WindowHan
|
||||
let pet = cx.open_window(options, |_, cx| {
|
||||
cx.new(|cx| {
|
||||
let feel = cx.observe(&workspace, move |pet: &mut Pet, workspace, cx| {
|
||||
let now = feeling(workspace.read(cx), cx);
|
||||
if now != pet.mood {
|
||||
pet.feel(now, cx);
|
||||
let mood = feeling(workspace.read(cx), cx);
|
||||
if mood != pet.mood {
|
||||
pet.feel(mood, Instant::now());
|
||||
cx.notify();
|
||||
}
|
||||
});
|
||||
Pet {
|
||||
@@ -244,14 +245,26 @@ fn wayland(_: &Window) -> bool {
|
||||
}
|
||||
|
||||
impl Pet {
|
||||
/// Takes up `mood`, whose act begins now.
|
||||
fn feel(&mut self, mood: Mood, cx: &mut Context<Self>) {
|
||||
/// Takes up `mood` at `now`, its act beginning there, eased from where she is.
|
||||
fn feel(&mut self, mood: Mood, now: Instant) {
|
||||
log::debug!("pet: Ely feels {mood:?}, was {:?}", self.mood);
|
||||
(self.mood, self.since) = (mood, Instant::now());
|
||||
cx.notify();
|
||||
(self.mood, self.since) = (mood, now);
|
||||
self.ease(now);
|
||||
}
|
||||
|
||||
/// Her pose now, eased from her last one while a change of act is fresh.
|
||||
/// A press at `now`, which winks her, eased from where she is.
|
||||
fn press(&mut self, now: Instant) {
|
||||
log::debug!("pet: Ely is pressed");
|
||||
self.petted = Some(now);
|
||||
self.ease(now);
|
||||
}
|
||||
|
||||
/// Eases her from her last pose, from `now`, as a move restarts.
|
||||
fn ease(&mut self, now: Instant) {
|
||||
self.eased = self.last.clone().map(|last| (last, now));
|
||||
}
|
||||
|
||||
/// Her pose now, eased from where she was while a change of act or mood, or a press, is fresh.
|
||||
fn pose(&mut self, now: Instant) -> Pose {
|
||||
let since = |at: Instant| now.saturating_duration_since(at).as_secs_f64();
|
||||
let mut pose = pose(
|
||||
@@ -263,7 +276,7 @@ impl Pet {
|
||||
if pose.act != self.act {
|
||||
log::debug!("pet: Ely goes from {:?} to {:?}", self.act, pose.act);
|
||||
self.act = pose.act;
|
||||
self.eased = self.last.take().map(|last| (last, now));
|
||||
self.ease(now);
|
||||
}
|
||||
if let Some((from, at)) = &self.eased {
|
||||
let into = since(*at) as f32;
|
||||
@@ -315,8 +328,7 @@ impl Render for Pet {
|
||||
div().size_full().child(drawn.size_full()).on_mouse_down(
|
||||
MouseButton::Left,
|
||||
cx.listener(|pet, _, _, cx| {
|
||||
log::debug!("pet: Ely is pressed");
|
||||
pet.petted = Some(Instant::now());
|
||||
pet.press(Instant::now());
|
||||
cx.notify();
|
||||
}),
|
||||
)
|
||||
@@ -325,7 +337,79 @@ impl Render for Pet {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::{look::Mark, *};
|
||||
|
||||
/// Her, `mood` since `since`, come long before.
|
||||
fn ely(mood: Mood, since: Instant) -> Pet {
|
||||
Pet {
|
||||
rig: Rc::new(Rig::new()),
|
||||
mood,
|
||||
since,
|
||||
shown: since - Duration::from_secs(60),
|
||||
petted: None,
|
||||
act: Act::Calm,
|
||||
last: None,
|
||||
eased: None,
|
||||
left: LEFT,
|
||||
glue: None,
|
||||
_watch: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_change_of_mood_or_a_press_eases_her_from_where_she_is_though_the_act_stays() {
|
||||
let start = Instant::now();
|
||||
let at = |ms: u64| start + Duration::from_millis(ms);
|
||||
// A wait hops her, a press winks, and a program starts working inside the wink.
|
||||
let mut pet = ely(Mood::Waiting, start);
|
||||
pet.press(at(2250));
|
||||
let before = pet.pose(at(2750));
|
||||
pet.feel(Mood::Working, at(2750));
|
||||
let after = pet.pose(at(2751));
|
||||
assert_eq!((before.act, after.act), (Act::Wink, Act::Wink));
|
||||
assert!(
|
||||
(after.lift - before.lift).abs() < 1.0,
|
||||
"no drop: {} then {}",
|
||||
before.lift,
|
||||
after.lift
|
||||
);
|
||||
let marks: Vec<Mark> = after.marks.iter().map(|(mark, _)| *mark).collect();
|
||||
assert!(
|
||||
marks.contains(&Mark::Bang),
|
||||
"the wait's mark fades: {marks:?}"
|
||||
);
|
||||
// An old finish clears mid-glance: calm before and after, her eyes turn back at her pace.
|
||||
let mut pet = ely(Mood::Done, start);
|
||||
let before = pet.pose(at(13_600));
|
||||
pet.feel(Mood::Calm, at(13_600));
|
||||
let after = pet.pose(at(13_601));
|
||||
assert_eq!((before.act, after.act), (Act::Calm, Act::Calm));
|
||||
assert!(
|
||||
before.look.0 < -5.0 && (after.look.0 - before.look.0).abs() < 0.5,
|
||||
"no snap: {:?} then {:?}",
|
||||
before.look,
|
||||
after.look
|
||||
);
|
||||
let later = pet.pose(at(14_000));
|
||||
assert!(
|
||||
pet.eased.is_none() && later.look.0.abs() < 0.5,
|
||||
"eased in by then"
|
||||
);
|
||||
// A second press mid-hop restarts the wink without a drop.
|
||||
let mut pet = ely(Mood::Calm, start);
|
||||
pet.press(at(1000));
|
||||
let before = pet.pose(at(1300));
|
||||
pet.press(at(1300));
|
||||
let after = pet.pose(at(1301));
|
||||
assert!(
|
||||
before.lift > 10.0 && (after.lift - before.lift).abs() < 1.0,
|
||||
"no drop: {} then {}",
|
||||
before.lift,
|
||||
after.lift
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn she_holds_the_edge_of_any_pane() {
|
||||
|
||||
+6
-4
@@ -119,7 +119,8 @@ pub struct Pose {
|
||||
pub mouth: Mouth,
|
||||
/// The mouth's size, which pops as it changes.
|
||||
pub pop: f32,
|
||||
pub brows: bool,
|
||||
/// How much her worried brows show.
|
||||
pub brows: f32,
|
||||
pub flush: f32,
|
||||
/// Each hand pressed down, left then right.
|
||||
pub hands: [f32; 2],
|
||||
@@ -148,7 +149,7 @@ pub fn pose(mood: Mood, held: f64, shown: f64, petted: Option<f64>) -> Pose {
|
||||
pose
|
||||
}
|
||||
|
||||
/// `to`, `since` seconds after a change of act, eased from `from`, her last pose before it: her place and her moves blend, the marks `to` lacks fade, and the shapes are `to`'s.
|
||||
/// `to`, `since` seconds after a change of act or mood, or a press, eased from `from`, her pose then: her place, her moves and her brows blend, the marks `to` lacks fade, and the shapes are `to`'s.
|
||||
pub fn blend(from: &Pose, to: Pose, since: f32) -> Pose {
|
||||
let k = smooth((since / EASE).clamp(0.0, 1.0));
|
||||
let mix = |a: f32, b: f32| a + (b - a) * k;
|
||||
@@ -168,6 +169,7 @@ pub fn blend(from: &Pose, to: Pose, since: f32) -> Pose {
|
||||
ahoge: mix(from.ahoge, to.ahoge),
|
||||
look: (mix(from.look.0, to.look.0), mix(from.look.1, to.look.1)),
|
||||
wide: mix(from.wide, to.wide),
|
||||
brows: mix(from.brows, to.brows),
|
||||
flush: mix(from.flush, to.flush),
|
||||
hands: [
|
||||
mix(from.hands[0], to.hands[0]),
|
||||
@@ -196,7 +198,7 @@ fn idle(act: Act, t: f64, period: f32, amp: f32) -> Pose {
|
||||
eyes: Eyes::Open,
|
||||
mouth: Mouth::Smile,
|
||||
pop: popping(early(t)),
|
||||
brows: false,
|
||||
brows: 0.0,
|
||||
flush: 0.0,
|
||||
hands: [0.0; 2],
|
||||
marks: Vec::new(),
|
||||
@@ -290,7 +292,7 @@ fn waiting(t: f64) -> Pose {
|
||||
/// An error: brows up, a frown, a shake, a smaller one every 4.5 s, and a drop that runs down.
|
||||
fn fretting(t: f64) -> Pose {
|
||||
let mut pose = idle(Act::Fret, t, 2.4, 1.5);
|
||||
pose.brows = true;
|
||||
pose.brows = 1.0;
|
||||
pose.mouth = Mouth::Frown;
|
||||
pose.look = (0.0, 3.0);
|
||||
pose.tilt = -3.0;
|
||||
|
||||
@@ -139,7 +139,7 @@ fn a_wait_widens_her_eyes_and_hops_her_twice_every_round() {
|
||||
#[test]
|
||||
fn an_error_frets_her_with_a_shake_and_a_drop() {
|
||||
let fret = |held: f64| at(Mood::Error, held);
|
||||
assert!(fret(1.0).brows);
|
||||
assert_eq!(fret(1.0).brows, 1.0);
|
||||
assert_eq!((fret(1.0).act, fret(1.0).mouth), (Act::Fret, Mouth::Frown));
|
||||
assert!(fret(0.15).shake.abs() > 1.0, "a shake");
|
||||
assert_eq!(fret(1.0).shake, 0.0);
|
||||
@@ -200,4 +200,10 @@ fn a_change_of_act_eases_her_from_her_last_pose() {
|
||||
.1;
|
||||
assert!(bang.alpha > 0.0 && bang.alpha < 1.0);
|
||||
assert_eq!(leaving.eyes, Eyes::Open, "the shapes are the new act's");
|
||||
let fretting = at(Mood::Error, 1.0);
|
||||
let calming = blend(&fretting, at(Mood::Calm, 0.1), 0.1);
|
||||
assert!(
|
||||
calming.brows > 0.0 && calming.brows < 1.0,
|
||||
"her brows ease down"
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -196,7 +196,7 @@ fn moved(
|
||||
Bone::Iris(side) if open(side) => (shift(squash(side, pose.wide), by(pose.look)), 1.0),
|
||||
Bone::Arc(side) if shut(side) => (squash(side, 1.0), 1.0),
|
||||
Bone::Lines if pose.eyes == Eyes::Lines => (head, 1.0),
|
||||
Bone::Brows if pose.brows => (head, 1.0),
|
||||
Bone::Brows if pose.brows > 0.0 => (head, pose.brows),
|
||||
Bone::Blush => (head, 1.0 + 0.6 * pose.flush),
|
||||
Bone::Mouth(mouth) if mouth == pose.mouth => (grow(head, pivot, (pose.pop, pose.pop)), 1.0),
|
||||
Bone::Lock(side) => {
|
||||
|
||||
Reference in new issue
Block a user