Format Rust sources at 100 columns
This commit is contained in:
1 parent
4661f595f8
commit
610478f8c7
336 files changed
+23497
-7892
No files matched your search
@@ -59,7 +59,11 @@ impl VelocityTracker {
|
||||
if divisor.abs() < 1e-12 {
|
||||
let (first, position) = self.samples[0];
|
||||
let dt = last.saturating_sub(first).as_secs_f64();
|
||||
return if dt > 0.0 { (origin - position) / dt } else { 0.0 };
|
||||
return if dt > 0.0 {
|
||||
(origin - position) / dt
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
}
|
||||
for entry in &mut matrix[col][col..=degree + 1] {
|
||||
*entry /= divisor;
|
||||
@@ -70,7 +74,10 @@ impl VelocityTracker {
|
||||
continue;
|
||||
}
|
||||
let factor = values[col];
|
||||
for (entry, pivot) in values[col..=degree + 1].iter_mut().zip(&pivot_row[col..=degree + 1]) {
|
||||
for (entry, pivot) in values[col..=degree + 1]
|
||||
.iter_mut()
|
||||
.zip(&pivot_row[col..=degree + 1])
|
||||
{
|
||||
*entry -= factor * pivot;
|
||||
}
|
||||
}
|
||||
@@ -89,7 +96,12 @@ pub fn project(position: f64, velocity: f64, rate: f64) -> f64 {
|
||||
/// Nonlinear resistance, bounded by `extent`; the derivative transfers the
|
||||
/// velocity of the displayed (resisted) position into the release spring.
|
||||
pub fn rubber_band(distance: f64, extent: f64, resistance: f64) -> (f64, f64) {
|
||||
if !distance.is_finite() || !extent.is_finite() || extent <= 0.0 || !resistance.is_finite() || resistance <= 0.0 {
|
||||
if !distance.is_finite()
|
||||
|| !extent.is_finite()
|
||||
|| extent <= 0.0
|
||||
|| !resistance.is_finite()
|
||||
|| resistance <= 0.0
|
||||
{
|
||||
return (0.0, 0.0);
|
||||
}
|
||||
let denominator = 1.0 + resistance * distance.abs() / extent;
|
||||
@@ -108,7 +120,9 @@ mod tests {
|
||||
let (position, derivative) = rubber_band(x, 64.0, 0.55);
|
||||
assert!((0.0..64.0).contains(&position));
|
||||
assert_eq!(rubber_band(-x, 64.0, 0.55).0, -position);
|
||||
let measured = (rubber_band(x + 0.0001, 64.0, 0.55).0 - rubber_band(x - 0.0001, 64.0, 0.55).0) / 0.0002;
|
||||
let measured = (rubber_band(x + 0.0001, 64.0, 0.55).0
|
||||
- rubber_band(x - 0.0001, 64.0, 0.55).0)
|
||||
/ 0.0002;
|
||||
assert!((measured - derivative).abs() < 1e-6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,12 @@ impl AnimatedValue {
|
||||
self.advance_with_policy(delta, config, config.crossing)
|
||||
}
|
||||
|
||||
pub fn advance_with_policy(&mut self, delta: Duration, config: SpringConfig, policy: CrossingPolicy) -> bool {
|
||||
pub fn advance_with_policy(
|
||||
&mut self,
|
||||
delta: Duration,
|
||||
config: SpringConfig,
|
||||
policy: CrossingPolicy,
|
||||
) -> bool {
|
||||
let config = config.normalized();
|
||||
let settled = |value: &Self| {
|
||||
(value.current - value.target).abs() <= config.position_tolerance
|
||||
@@ -167,7 +172,12 @@ impl AnimatedRect {
|
||||
self.advance_with_policy(delta, config, config.crossing)
|
||||
}
|
||||
|
||||
pub fn advance_with_policy(&mut self, delta: Duration, config: SpringConfig, policy: CrossingPolicy) -> bool {
|
||||
pub fn advance_with_policy(
|
||||
&mut self,
|
||||
delta: Duration,
|
||||
config: SpringConfig,
|
||||
policy: CrossingPolicy,
|
||||
) -> bool {
|
||||
let config = config.normalized();
|
||||
if delta.is_zero() {
|
||||
return !self.is_settled(config);
|
||||
@@ -278,7 +288,8 @@ impl ClientGeometry {
|
||||
|
||||
/// The hard bound for the current uninterrupted client-buffer wait.
|
||||
pub fn commit_deadline(&self) -> Option<Duration> {
|
||||
self.waiting_since.map(|started| started + CLIENT_COMMIT_TIMEOUT)
|
||||
self.waiting_since
|
||||
.map(|started| started + CLIENT_COMMIT_TIMEOUT)
|
||||
}
|
||||
|
||||
pub fn timed_out(&self, now: Duration) -> bool {
|
||||
@@ -354,7 +365,12 @@ impl WindowGeometry {
|
||||
self.client.request_size(size, now).then_some(size)
|
||||
}
|
||||
|
||||
pub fn set_presentation_target(&mut self, rect: Rect, fullscreen: bool, now: Duration) -> Option<ClientSize> {
|
||||
pub fn set_presentation_target(
|
||||
&mut self,
|
||||
rect: Rect,
|
||||
fullscreen: bool,
|
||||
now: Duration,
|
||||
) -> Option<ClientSize> {
|
||||
self.set_presentation_mode(
|
||||
rect,
|
||||
if fullscreen {
|
||||
@@ -366,7 +382,12 @@ impl WindowGeometry {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_presentation_mode(&mut self, rect: Rect, mode: PresentationMode, now: Duration) -> Option<ClientSize> {
|
||||
pub fn set_presentation_mode(
|
||||
&mut self,
|
||||
rect: Rect,
|
||||
mode: PresentationMode,
|
||||
now: Duration,
|
||||
) -> Option<ClientSize> {
|
||||
let rect = normalized_rect(rect);
|
||||
let before = (self.visual.current, self.decorations);
|
||||
if self.mode != mode || (self.zoom.is_some() && self.logical != rect) {
|
||||
@@ -374,7 +395,11 @@ impl WindowGeometry {
|
||||
.zoom
|
||||
.map(|zoom| zoom.decorations)
|
||||
.unwrap_or_else(|| AnimatedValue::new(self.decorations));
|
||||
decorations.set_target(if mode == PresentationMode::Fullscreen { 0.0 } else { 1.0 });
|
||||
decorations.set_target(if mode == PresentationMode::Fullscreen {
|
||||
0.0
|
||||
} else {
|
||||
1.0
|
||||
});
|
||||
self.zoom = Some(ZoomTransition { decorations });
|
||||
}
|
||||
self.mode = mode;
|
||||
@@ -416,14 +441,24 @@ impl WindowGeometry {
|
||||
requested_size
|
||||
}
|
||||
|
||||
pub fn advance(&mut self, delta: Duration, config: SpringConfig, animations_enabled: bool) -> bool {
|
||||
pub fn advance(
|
||||
&mut self,
|
||||
delta: Duration,
|
||||
config: SpringConfig,
|
||||
animations_enabled: bool,
|
||||
) -> bool {
|
||||
let previous = (self.visual.current, self.decorations);
|
||||
let active = self.advance_presentation(delta, config, animations_enabled);
|
||||
self.presentation_changed = previous != (self.visual.current, self.decorations);
|
||||
active
|
||||
}
|
||||
|
||||
fn advance_presentation(&mut self, delta: Duration, config: SpringConfig, animations_enabled: bool) -> bool {
|
||||
fn advance_presentation(
|
||||
&mut self,
|
||||
delta: Duration,
|
||||
config: SpringConfig,
|
||||
animations_enabled: bool,
|
||||
) -> bool {
|
||||
if !animations_enabled {
|
||||
self.visual.snap();
|
||||
self.decorations = if self.is_fullscreen() { 0.0 } else { 1.0 };
|
||||
@@ -483,7 +518,9 @@ fn advance_property(
|
||||
config: SpringConfig,
|
||||
policy: CrossingPolicy,
|
||||
) {
|
||||
if policy == CrossingPolicy::NoCrossing && spring::crosses(*current, *velocity, target, dt, config) {
|
||||
if policy == CrossingPolicy::NoCrossing
|
||||
&& spring::crosses(*current, *velocity, target, dt, config)
|
||||
{
|
||||
*current = target;
|
||||
*velocity = 0.0;
|
||||
} else {
|
||||
@@ -492,7 +529,13 @@ fn advance_property(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn advance_value(current: &mut f64, target: f64, velocity: &mut f64, dt: f64, config: SpringConfig) {
|
||||
fn advance_value(
|
||||
current: &mut f64,
|
||||
target: f64,
|
||||
velocity: &mut f64,
|
||||
dt: f64,
|
||||
config: SpringConfig,
|
||||
) {
|
||||
(*current, *velocity) = spring::solve(*current, *velocity, target, dt, config);
|
||||
}
|
||||
|
||||
@@ -560,16 +603,31 @@ mod tests {
|
||||
));
|
||||
}
|
||||
assert_eq!(client.commit_deadline(), Some(Duration::from_millis(500)));
|
||||
assert!(!client.commit(ClientSize { width: 49, height: 100 }));
|
||||
assert!(!client.commit(ClientSize {
|
||||
width: 49,
|
||||
height: 100
|
||||
}));
|
||||
assert!(client.expire_wait(Duration::from_millis(499)).is_none());
|
||||
assert_eq!(
|
||||
client.expire_wait(Duration::from_millis(500)),
|
||||
Some(ClientSize { width: 50, height: 100 })
|
||||
Some(ClientSize {
|
||||
width: 50,
|
||||
height: 100
|
||||
})
|
||||
);
|
||||
assert_eq!(client.commit_deadline(), None);
|
||||
assert!(client.request_size(ClientSize { width: 51, height: 100 }, Duration::from_millis(600)));
|
||||
assert!(client.request_size(
|
||||
ClientSize {
|
||||
width: 51,
|
||||
height: 100
|
||||
},
|
||||
Duration::from_millis(600)
|
||||
));
|
||||
assert_eq!(client.commit_deadline(), Some(Duration::from_millis(1100)));
|
||||
assert!(client.commit(ClientSize { width: 51, height: 100 }));
|
||||
assert!(client.commit(ClientSize {
|
||||
width: 51,
|
||||
height: 100
|
||||
}));
|
||||
assert_eq!(client.commit_deadline(), None);
|
||||
}
|
||||
|
||||
@@ -634,10 +692,17 @@ mod tests {
|
||||
assert_eq!(geometry.visual.current, rect);
|
||||
assert_eq!(
|
||||
geometry.decorations,
|
||||
if mode == PresentationMode::Fullscreen { 0.0 } else { 1.0 }
|
||||
if mode == PresentationMode::Fullscreen {
|
||||
0.0
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
);
|
||||
assert!(!geometry.is_zooming());
|
||||
assert_eq!(geometry.is_fullscreen(), mode == PresentationMode::Fullscreen);
|
||||
assert_eq!(
|
||||
geometry.is_fullscreen(),
|
||||
mode == PresentationMode::Fullscreen
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,7 +729,9 @@ mod tests {
|
||||
fn zoom_finishes_without_a_visible_final_edge_jump() {
|
||||
let tiled = Rect::new(12.0, 12.0, 942.0, 1056.0);
|
||||
let fullscreen = Rect::new(0.0, 0.0, 1920.0, 1080.0);
|
||||
for (start, target, fullscreen_target) in [(tiled, fullscreen, true), (fullscreen, tiled, false)] {
|
||||
for (start, target, fullscreen_target) in
|
||||
[(tiled, fullscreen, true), (fullscreen, tiled, false)]
|
||||
{
|
||||
let mut geometry = WindowGeometry::new(start, None);
|
||||
if !fullscreen_target {
|
||||
geometry.set_presentation_target(start, true, Duration::ZERO);
|
||||
@@ -674,7 +741,11 @@ mod tests {
|
||||
let mut finished = false;
|
||||
for _ in 0..1000 {
|
||||
let before = geometry.visual.current;
|
||||
if !geometry.advance(Duration::from_secs_f64(1.0 / 144.0), SpringConfig::default(), true) {
|
||||
if !geometry.advance(
|
||||
Duration::from_secs_f64(1.0 / 144.0),
|
||||
SpringConfig::default(),
|
||||
true,
|
||||
) {
|
||||
let right_before = before.x + before.width;
|
||||
let right_after = target.x + target.width;
|
||||
assert!(
|
||||
@@ -701,7 +772,9 @@ mod tests {
|
||||
let progress = 1.0 - geometry.decorations;
|
||||
let visual = geometry.visual.current;
|
||||
assert!((visual.x - (start.x + (end.x - start.x) * progress)).abs() < 1e-9);
|
||||
assert!((visual.width - (start.width + (end.width - start.width) * progress)).abs() < 1e-9);
|
||||
assert!(
|
||||
(visual.width - (start.width + (end.width - start.width) * progress)).abs() < 1e-9
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,7 +785,11 @@ mod tests {
|
||||
let mut geometry = WindowGeometry::new(tiled, None);
|
||||
for enabled in [true, false, true, false] {
|
||||
let before = (geometry.visual.current, geometry.decorations);
|
||||
geometry.set_presentation_target(if enabled { fullscreen } else { tiled }, enabled, Duration::ZERO);
|
||||
geometry.set_presentation_target(
|
||||
if enabled { fullscreen } else { tiled },
|
||||
enabled,
|
||||
Duration::ZERO,
|
||||
);
|
||||
assert_eq!((geometry.visual.current, geometry.decorations), before);
|
||||
geometry.advance(Duration::from_millis(48), SpringConfig::default(), true);
|
||||
}
|
||||
@@ -756,15 +833,24 @@ mod tests {
|
||||
Some(ClientSize::from_rect(middle))
|
||||
);
|
||||
geometry.advance(Duration::from_millis(64), SpringConfig::default(), true);
|
||||
assert_eq!(geometry.presentation_size_request(Duration::from_millis(64)), None);
|
||||
assert_eq!(
|
||||
geometry.set_presentation_mode(end, PresentationMode::Normal, Duration::from_millis(64)),
|
||||
geometry.presentation_size_request(Duration::from_millis(64)),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
geometry.set_presentation_mode(
|
||||
end,
|
||||
PresentationMode::Normal,
|
||||
Duration::from_millis(64)
|
||||
),
|
||||
Some(ClientSize::from_rect(end))
|
||||
);
|
||||
let mut requests = Vec::new();
|
||||
for frame in 1..240 {
|
||||
geometry.advance(Duration::from_millis(8), SpringConfig::default(), true);
|
||||
if let Some(size) = geometry.presentation_size_request(Duration::from_millis(64 + frame * 8)) {
|
||||
if let Some(size) =
|
||||
geometry.presentation_size_request(Duration::from_millis(64 + frame * 8))
|
||||
{
|
||||
assert_eq!(geometry.visual.current.width, end.width);
|
||||
requests.push(size);
|
||||
}
|
||||
@@ -809,8 +895,13 @@ mod tests {
|
||||
true,
|
||||
);
|
||||
let current = geometry.visual.current;
|
||||
let edges = |rect: Rect| [rect.x, rect.y, rect.x + rect.width, rect.y + rect.height];
|
||||
for ((before, after), end) in edges(previous).into_iter().zip(edges(current)).zip(edges(target))
|
||||
let edges = |rect: Rect| {
|
||||
[rect.x, rect.y, rect.x + rect.width, rect.y + rect.height]
|
||||
};
|
||||
for ((before, after), end) in edges(previous)
|
||||
.into_iter()
|
||||
.zip(edges(current))
|
||||
.zip(edges(target))
|
||||
{
|
||||
assert!((after - end).abs() <= (before - end).abs() + 1e-9);
|
||||
}
|
||||
@@ -967,7 +1058,11 @@ mod tests {
|
||||
let mut geometry = WindowGeometry::new(rect, None);
|
||||
|
||||
assert!(geometry.set_logical_target(rect, Duration::ZERO).is_some());
|
||||
assert!(geometry.set_logical_target(rect, Duration::from_millis(16)).is_none());
|
||||
assert!(
|
||||
geometry
|
||||
.set_logical_target(rect, Duration::from_millis(16))
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1026,7 +1121,10 @@ mod tests {
|
||||
assert!(sixty.current.width <= target.width);
|
||||
}
|
||||
for _ in 0..288 {
|
||||
one_forty_four.advance(Duration::from_secs_f64(1.0 / 144.0), SpringConfig::default());
|
||||
one_forty_four.advance(
|
||||
Duration::from_secs_f64(1.0 / 144.0),
|
||||
SpringConfig::default(),
|
||||
);
|
||||
}
|
||||
|
||||
assert!(sixty.is_settled(SpringConfig::default()));
|
||||
|
||||
@@ -117,7 +117,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn speed_validation_rejects_extremes_and_accepts_safe_boundaries() {
|
||||
for speed in [f64::NAN, f64::INFINITY, 0.0, -1.0, 1e-100, 1e100, 0.009, 101.0] {
|
||||
for speed in [
|
||||
f64::NAN,
|
||||
f64::INFINITY,
|
||||
0.0,
|
||||
-1.0,
|
||||
1e-100,
|
||||
1e100,
|
||||
0.009,
|
||||
101.0,
|
||||
] {
|
||||
let settings = MotionSettings {
|
||||
speed,
|
||||
..Default::default()
|
||||
@@ -164,7 +173,10 @@ mod tests {
|
||||
.unwrap();
|
||||
assert!((spring.stiffness - 157.913670417).abs() < 1e-8);
|
||||
assert!((spring.damping / (2.0 * spring.stiffness.sqrt()) - ratio).abs() < 1e-10);
|
||||
assert!((std::f64::consts::TAU * (spring.mass / spring.stiffness).sqrt() - 0.5).abs() < 1e-10);
|
||||
assert!(
|
||||
(std::f64::consts::TAU * (spring.mass / spring.stiffness).sqrt() - 0.5).abs()
|
||||
< 1e-10
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,13 @@ use super::SpringConfig;
|
||||
|
||||
/// Solve m y'' + c y' + k y = 0, with y = current - target.
|
||||
/// No settling tolerance or crossing clamp is applied here.
|
||||
pub(super) fn solve(current: f64, velocity: f64, target: f64, dt: f64, config: SpringConfig) -> (f64, f64) {
|
||||
pub(super) fn solve(
|
||||
current: f64,
|
||||
velocity: f64,
|
||||
target: f64,
|
||||
dt: f64,
|
||||
config: SpringConfig,
|
||||
) -> (f64, f64) {
|
||||
if dt <= 0.0 || !dt.is_finite() {
|
||||
return (current, velocity);
|
||||
}
|
||||
@@ -20,7 +26,8 @@ pub(super) fn solve(current: f64, velocity: f64, target: f64, dt: f64, config: S
|
||||
// rather than replacing a neighborhood with the critical solution.
|
||||
if z2.abs() < 0.01 {
|
||||
let c = 1.0 + z2 * (0.5 + z2 * (1.0 / 24.0 + z2 * (1.0 / 720.0 + z2 / 40320.0)));
|
||||
let s = dt * (1.0 + z2 * (1.0 / 6.0 + z2 * (1.0 / 120.0 + z2 * (1.0 / 5040.0 + z2 / 362880.0))));
|
||||
let s = dt
|
||||
* (1.0 + z2 * (1.0 / 6.0 + z2 * (1.0 / 120.0 + z2 * (1.0 / 5040.0 + z2 / 362880.0))));
|
||||
let e = (-a * dt).exp();
|
||||
return (
|
||||
target + e * (y * c + (velocity + a * y) * s),
|
||||
@@ -46,13 +53,22 @@ pub(super) fn solve(current: f64, velocity: f64, target: f64, dt: f64, config: S
|
||||
let fast_coefficient = (slow * y - velocity) / (2.0 * q);
|
||||
let slow_term = slow_coefficient * (slow * dt).exp();
|
||||
let fast_term = fast_coefficient * (fast * dt).exp();
|
||||
(target + slow_term + fast_term, slow * slow_term + fast * fast_term)
|
||||
(
|
||||
target + slow_term + fast_term,
|
||||
slow * slow_term + fast * fast_term,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// True if the physical trajectory reaches equilibrium anywhere in (0, dt].
|
||||
/// Endpoint-only tests miss an even number of crossings after a long stall.
|
||||
pub(super) fn crosses(current: f64, velocity: f64, target: f64, dt: f64, config: SpringConfig) -> bool {
|
||||
pub(super) fn crosses(
|
||||
current: f64,
|
||||
velocity: f64,
|
||||
target: f64,
|
||||
dt: f64,
|
||||
config: SpringConfig,
|
||||
) -> bool {
|
||||
if dt <= 0.0 {
|
||||
return false;
|
||||
}
|
||||
@@ -70,7 +86,11 @@ pub(super) fn crosses(current: f64, velocity: f64, target: f64, dt: f64, config:
|
||||
let crossing = if d < 0.0 {
|
||||
let w = (-d).sqrt();
|
||||
let phase = (-y * w).atan2(b).rem_euclid(std::f64::consts::PI);
|
||||
let phase = if phase == 0.0 { std::f64::consts::PI } else { phase };
|
||||
let phase = if phase == 0.0 {
|
||||
std::f64::consts::PI
|
||||
} else {
|
||||
phase
|
||||
};
|
||||
phase / w
|
||||
} else if y == 0.0 || b == 0.0 {
|
||||
return false;
|
||||
|
||||
@@ -25,7 +25,12 @@ impl FakeClock {
|
||||
}
|
||||
}
|
||||
|
||||
fn sample(&mut self, time: Duration, value: &mut AnimatedValue, config: SpringConfig) -> Sample {
|
||||
fn sample(
|
||||
&mut self,
|
||||
time: Duration,
|
||||
value: &mut AnimatedValue,
|
||||
config: SpringConfig,
|
||||
) -> Sample {
|
||||
assert!(time >= self.now);
|
||||
self.now = time;
|
||||
let delta = time - self.last;
|
||||
@@ -58,7 +63,8 @@ impl FakeClock {
|
||||
fn legacy_advance(value: &mut AnimatedValue, delta: Duration, config: SpringConfig) {
|
||||
let config = config.normalized();
|
||||
let settled = |v: &AnimatedValue| {
|
||||
(v.current - v.target).abs() <= config.position_tolerance && v.velocity.abs() <= config.velocity_tolerance
|
||||
(v.current - v.target).abs() <= config.position_tolerance
|
||||
&& v.velocity.abs() <= config.velocity_tolerance
|
||||
};
|
||||
if settled(value) {
|
||||
value.snap();
|
||||
@@ -70,7 +76,8 @@ fn legacy_advance(value: &mut AnimatedValue, delta: Duration, config: SpringConf
|
||||
let dt = seconds / steps as f64;
|
||||
for _ in 0..steps {
|
||||
let old = value.current - value.target;
|
||||
value.velocity += (-config.stiffness * old - config.damping * value.velocity) / config.mass * dt;
|
||||
value.velocity +=
|
||||
(-config.stiffness * old - config.damping * value.velocity) / config.mass * dt;
|
||||
value.current += value.velocity * dt;
|
||||
if old != 0.0 && old.signum() != (value.current - value.target).signum() {
|
||||
value.snap();
|
||||
@@ -91,7 +98,11 @@ fn capture_old_golden_trace_and_retarget_policy() {
|
||||
let mut value = AnimatedValue::new(0.0);
|
||||
value.set_target(500.0);
|
||||
for i in 1..=18 {
|
||||
legacy_advance(&mut value, Duration::from_secs_f64(1.0 / 60.0), SpringConfig::default());
|
||||
legacy_advance(
|
||||
&mut value,
|
||||
Duration::from_secs_f64(1.0 / 60.0),
|
||||
SpringConfig::default(),
|
||||
);
|
||||
if [1, 3, 6, 12, 18].contains(&i) {
|
||||
println!("old normal {i}: {}, {}", value.current, value.velocity);
|
||||
}
|
||||
@@ -99,7 +110,11 @@ fn capture_old_golden_trace_and_retarget_policy() {
|
||||
|
||||
let mut value = AnimatedValue::new(0.0);
|
||||
value.set_target(1000.0);
|
||||
legacy_advance(&mut value, Duration::from_millis(40), SpringConfig::default());
|
||||
legacy_advance(
|
||||
&mut value,
|
||||
Duration::from_millis(40),
|
||||
SpringConfig::default(),
|
||||
);
|
||||
println!("old retarget before: {}, {}", value.current, value.velocity);
|
||||
let position = value.current;
|
||||
let velocity = value.velocity;
|
||||
@@ -127,7 +142,11 @@ fn long_stall_uses_the_whole_elapsed_duration() {
|
||||
let sample = clock.sample(Duration::from_millis(416), &mut value, config);
|
||||
let t: f64 = 0.416;
|
||||
let decay = (-10.0 * t).exp();
|
||||
close(sample.current, 500.0 - 500.0 * (1.0 + 10.0 * t) * decay, 1e-8);
|
||||
close(
|
||||
sample.current,
|
||||
500.0 - 500.0 * (1.0 + 10.0 * t) * decay,
|
||||
1e-8,
|
||||
);
|
||||
close(sample.velocity, 50000.0 * t * decay, 1e-8);
|
||||
assert_eq!(sample.time, Duration::from_millis(416));
|
||||
assert_eq!(sample.target, 500.0);
|
||||
@@ -147,13 +166,25 @@ fn fake_clock_pause_excludes_three_hundred_milliseconds() {
|
||||
let mut clock = FakeClock::new();
|
||||
let mut value = AnimatedValue::new(0.0);
|
||||
value.set_target(500.0);
|
||||
clock.sample(Duration::from_millis(40), &mut value, SpringConfig::default());
|
||||
clock.sample(
|
||||
Duration::from_millis(40),
|
||||
&mut value,
|
||||
SpringConfig::default(),
|
||||
);
|
||||
let frozen = value;
|
||||
clock.pause();
|
||||
clock.sample(Duration::from_millis(340), &mut value, SpringConfig::default());
|
||||
clock.sample(
|
||||
Duration::from_millis(340),
|
||||
&mut value,
|
||||
SpringConfig::default(),
|
||||
);
|
||||
assert_eq!(value, frozen);
|
||||
clock.resume(Duration::from_millis(340));
|
||||
clock.sample(Duration::from_millis(356), &mut value, SpringConfig::default());
|
||||
clock.sample(
|
||||
Duration::from_millis(356),
|
||||
&mut value,
|
||||
SpringConfig::default(),
|
||||
);
|
||||
let mut expected = frozen;
|
||||
expected.advance(Duration::from_millis(16), SpringConfig::default());
|
||||
assert_eq!(value, expected);
|
||||
@@ -182,7 +213,10 @@ fn reference(x: f64, v: f64, target: f64, dt: f64, config: SpringConfig) -> (f64
|
||||
let b = (v + a * y) / w;
|
||||
let (s, c) = (w * dt).sin_cos();
|
||||
let e = (-a * dt).exp();
|
||||
(target + e * (y * c + b * s), e * (v * c - (a * v + omega2 * y) / w * s))
|
||||
(
|
||||
target + e * (y * c + b * s),
|
||||
e * (v * c - (a * v + omega2 * y) / w * s),
|
||||
)
|
||||
} else {
|
||||
let q = d.sqrt();
|
||||
let r1 = -a + q;
|
||||
@@ -197,7 +231,15 @@ fn reference(x: f64, v: f64, target: f64, dt: f64, config: SpringConfig) -> (f64
|
||||
|
||||
#[test]
|
||||
fn solver_reference_regimes_initial_states_and_times() {
|
||||
for damping in [0.0, 5.0, 20.0, 20.0 * (1.0 - 1e-9), 20.0 * (1.0 + 1e-9), 53.0, 2000.0] {
|
||||
for damping in [
|
||||
0.0,
|
||||
5.0,
|
||||
20.0,
|
||||
20.0 * (1.0 - 1e-9),
|
||||
20.0 * (1.0 + 1e-9),
|
||||
53.0,
|
||||
2000.0,
|
||||
] {
|
||||
let config = SpringConfig {
|
||||
stiffness: 100.0,
|
||||
damping,
|
||||
@@ -256,7 +298,11 @@ fn normal_trace_records_the_old_numerical_error() {
|
||||
old.set_target(500.0);
|
||||
let mut max_error: f64 = 0.0;
|
||||
for i in 1..=18 {
|
||||
legacy_advance(&mut old, Duration::from_secs_f64(1.0 / 60.0), SpringConfig::default());
|
||||
legacy_advance(
|
||||
&mut old,
|
||||
Duration::from_secs_f64(1.0 / 60.0),
|
||||
SpringConfig::default(),
|
||||
);
|
||||
if let Some((_, x, v)) = gold.iter().find(|(n, _, _)| *n == i) {
|
||||
close(old.current, *x, 1e-9);
|
||||
close(old.velocity, *v, 1e-9);
|
||||
@@ -303,7 +349,11 @@ fn gesture_flick_trace_crosses_once_and_settles_while_clamp_stops_at_crossing()
|
||||
let mut crossings = 0;
|
||||
let mut previous_error: f64 = -100.0;
|
||||
for ms in 1..=1500 {
|
||||
free.advance_with_policy(Duration::from_millis(1), config, CrossingPolicy::AllowOvershoot);
|
||||
free.advance_with_policy(
|
||||
Duration::from_millis(1),
|
||||
config,
|
||||
CrossingPolicy::AllowOvershoot,
|
||||
);
|
||||
clamped.advance_with_policy(Duration::from_millis(1), config, CrossingPolicy::NoCrossing);
|
||||
let error = free.current - free.target;
|
||||
if error != 0.0 && previous_error.signum() != error.signum() {
|
||||
@@ -332,17 +382,29 @@ fn rapid_retarget_trace_preserves_position_and_the_golden_policy() {
|
||||
let mut clock = FakeClock::new();
|
||||
let mut value = AnimatedValue::new(0.0);
|
||||
value.set_target(1000.0);
|
||||
clock.sample(Duration::from_millis(40), &mut value, SpringConfig::default());
|
||||
clock.sample(
|
||||
Duration::from_millis(40),
|
||||
&mut value,
|
||||
SpringConfig::default(),
|
||||
);
|
||||
let before = value;
|
||||
value.retarget_preserving_motion(2000.0);
|
||||
assert_eq!(value.current, before.current);
|
||||
assert_eq!(value.velocity, before.velocity);
|
||||
clock.sample(Duration::from_millis(65), &mut value, SpringConfig::default());
|
||||
clock.sample(
|
||||
Duration::from_millis(65),
|
||||
&mut value,
|
||||
SpringConfig::default(),
|
||||
);
|
||||
let before = value;
|
||||
value.retarget_preserving_motion(-500.0);
|
||||
assert_eq!(value.current, before.current);
|
||||
assert_eq!(value.velocity, before.velocity);
|
||||
clock.sample(Duration::from_millis(81), &mut value, SpringConfig::default());
|
||||
clock.sample(
|
||||
Duration::from_millis(81),
|
||||
&mut value,
|
||||
SpringConfig::default(),
|
||||
);
|
||||
assert_ne!(value.velocity, 0.0);
|
||||
|
||||
// Apply the current policy to the exact old in-flight state too.
|
||||
@@ -366,7 +428,11 @@ fn interrupted_resize_and_zoom_targets_preserve_position_and_velocity() {
|
||||
] {
|
||||
let before = (geometry.visual.current, geometry.decorations);
|
||||
let velocity = geometry.visual.velocity;
|
||||
geometry.set_presentation_mode(Rect::new(200., 100., 700., 500.), mode, Duration::from_millis(40));
|
||||
geometry.set_presentation_mode(
|
||||
Rect::new(200., 100., 700., 500.),
|
||||
mode,
|
||||
Duration::from_millis(40),
|
||||
);
|
||||
assert_eq!((geometry.visual.current, geometry.decorations), before);
|
||||
assert_eq!(geometry.visual.velocity, velocity);
|
||||
geometry.advance(Duration::ZERO, SpringConfig::default(), true);
|
||||
@@ -418,7 +484,10 @@ fn variable_cadence_traces_match_reference_at_every_absolute_timestamp() {
|
||||
reference_settle_us += 100;
|
||||
}
|
||||
let exact_settle = Duration::from_micros(reference_settle_us);
|
||||
assert!(settled_at.abs_diff(exact_settle) <= Duration::from_micros(*schedule.iter().max().unwrap() + 100));
|
||||
assert!(
|
||||
settled_at.abs_diff(exact_settle)
|
||||
<= Duration::from_micros(*schedule.iter().max().unwrap() + 100)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,7 +545,10 @@ fn solver_is_finite_for_realistic_ranges_and_invalid_configuration_falls_back()
|
||||
..SpringConfig::default()
|
||||
};
|
||||
let (x, v) = physical(-10000., 50000., 10000., dt, config);
|
||||
assert!(x.is_finite() && v.is_finite(), "{config:?} dt {dt}: {x} {v}");
|
||||
assert!(
|
||||
x.is_finite() && v.is_finite(),
|
||||
"{config:?} dt {dt}: {x} {v}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -514,7 +586,10 @@ fn solver_is_finite_for_realistic_ranges_and_invalid_configuration_falls_back()
|
||||
#[test]
|
||||
fn physical_zero_and_negative_time_do_not_move_state() {
|
||||
for dt in [0.0, -0.01] {
|
||||
assert_eq!(physical(5.0, 100.0, 10.0, dt, SpringConfig::default()), (5.0, 100.0));
|
||||
assert_eq!(
|
||||
physical(5.0, 100.0, 10.0, dt, SpringConfig::default()),
|
||||
(5.0, 100.0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,7 +631,11 @@ fn creation_clock_excludes_earlier_idle_time_and_records_all_sample_fields() {
|
||||
clock.resume(created);
|
||||
let mut value = AnimatedValue::new(0.0);
|
||||
value.set_target(500.0);
|
||||
let first = clock.sample(created + Duration::from_millis(16), &mut value, SpringConfig::default());
|
||||
let first = clock.sample(
|
||||
created + Duration::from_millis(16),
|
||||
&mut value,
|
||||
SpringConfig::default(),
|
||||
);
|
||||
let exact = reference(0.0, 0.0, 500.0, 0.016, SpringConfig::default());
|
||||
close(first.current, exact.0, 1e-8);
|
||||
close(first.velocity, exact.1, 1e-8);
|
||||
|
||||
Reference in new issue
Block a user