use super::signals::SubscribedSnapshot;
use super::*;
pub(crate) fn affine_near(left: vello::kurbo::Affine, right: vello::kurbo::Affine) -> bool {
left.as_coeffs()
.iter()
.zip(right.as_coeffs())
.all(|(left, right)| (*left - right).abs() <= 0.001)
}
pub(crate) fn rect_near(left: vello::kurbo::Rect, right: vello::kurbo::Rect) -> bool {
(left.x0 - right.x0).abs() <= 0.001
&& (left.y0 - right.y0).abs() <= 0.001
&& (left.x1 - right.x1).abs() <= 0.001
&& (left.y1 - right.y1).abs() <= 0.001
}
impl HydrolysisRenderer {
#[cfg(test)]
pub(crate) fn scene_is_empty(&self) -> bool {
!scene_has_content(&self.scene)
}
pub(super) fn resolve_animated_scalar_with_discriminator<S>(
&mut self,
signal: &S,
discriminator: usize,
) -> f32
where
S: Signal<Output = f32> + Clone + 'static,
{
let Some(identity) = signal.identity() else {
return self.read_signal(signal);
};
let now = self.frame_instant;
let key = AnimationKey::scalar_with_discriminator(identity, discriminator);
let (subscription, observed_value) = SubscribedSnapshot::new(signal);
let handle = self
.animation_controller
.bind_scalar(key, observed_value, now);
let watcher_handle = handle.clone();
let signals = self.signals.clone();
let guard = subscription.activate(move |update| {
watcher_handle.apply_update_from_context(update, signals.frame_clock());
signals.request_redraw();
});
self.lifecycle.current_frame_retain.push(Retain::new(guard));
handle.sample(now)
}
pub(crate) fn sample_morph_progress(
&mut self,
animation: waterui_shape::MorphAnimation,
node_id: usize,
) -> f32 {
if animation.duration.is_zero() {
return 1.0;
}
let key = AnimationKey::renderer_local_repeating(node_id);
let elapsed = self.animation_controller.bind_timeline_phase(
key,
animation.duration,
animation.repeat,
self.frame_instant,
);
let raw = elapsed.as_secs_f32() / animation.duration.as_secs_f32();
let cycle = if animation.repeat {
let base = raw.fract();
assert!(
raw.is_finite() && raw >= 0.0,
"morph animation cycle index must be finite and non-negative"
);
let index = raw.floor() as u64;
if animation.autoreverse && index % 2 == 1 {
1.0 - base
} else {
base
}
} else {
raw.clamp(0.0, 1.0)
};
animation.easing.ease(cycle).clamp(0.0, 1.0)
}
}