use crate::bezier::PathEvaluate;
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DrawValues {
pub dash_array: f32,
pub dash_offset: f32,
}
impl DrawValues {
pub fn progress(&self) -> f32 {
if self.dash_array <= f32::EPSILON {
return 1.0;
}
(1.0 - self.dash_offset / self.dash_array).clamp(0.0, 1.0)
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn to_css(&self) -> alloc::string::String {
alloc::format!(
"stroke-dasharray: {:.3}; stroke-dashoffset: {:.3}",
self.dash_array,
self.dash_offset
)
}
}
pub trait DrawSvg {
fn total_length(&self) -> f32;
fn draw_on(&self, progress: f32) -> DrawValues {
let p = progress.clamp(0.0, 1.0);
let length = self.total_length();
DrawValues {
dash_array: length,
dash_offset: length * (1.0 - p),
}
}
fn draw_on_reverse(&self, progress: f32) -> DrawValues {
let p = progress.clamp(0.0, 1.0);
let length = self.total_length();
DrawValues {
dash_array: length,
dash_offset: length * p,
}
}
}
impl<T: PathEvaluate> DrawSvg for T {
#[inline]
fn total_length(&self) -> f32 {
self.arc_length()
}
}
#[cfg(all(test, any(feature = "std", feature = "alloc")))]
mod tests {
use super::*;
use crate::poly::LineSegment;
#[test]
fn draw_on_zero_is_invisible() {
let line = LineSegment::new([0.0, 0.0], [100.0, 0.0]);
let v = line.draw_on(0.0);
assert_eq!(v.dash_array, 100.0);
assert_eq!(v.dash_offset, 100.0);
assert_eq!(v.progress(), 0.0);
}
#[test]
fn draw_on_one_is_fully_visible() {
let line = LineSegment::new([0.0, 0.0], [100.0, 0.0]);
let v = line.draw_on(1.0);
assert_eq!(v.dash_offset, 0.0);
assert_eq!(v.progress(), 1.0);
}
#[test]
fn draw_on_half() {
let line = LineSegment::new([0.0, 0.0], [100.0, 0.0]);
let v = line.draw_on(0.5);
assert!((v.dash_array - 100.0).abs() < 0.001);
assert!((v.dash_offset - 50.0).abs() < 0.001);
assert!((v.progress() - 0.5).abs() < 0.001);
}
#[test]
fn draw_on_reverse_inverts_progress() {
let line = LineSegment::new([0.0, 0.0], [200.0, 0.0]);
let fwd = line.draw_on(0.3);
let rev = line.draw_on_reverse(0.7);
assert!((fwd.dash_offset - rev.dash_offset).abs() < 0.001);
}
#[cfg(any(feature = "std", feature = "alloc"))]
#[test]
fn to_css_formats_correctly() {
let v = DrawValues {
dash_array: 314.159,
dash_offset: 157.080,
};
let css = v.to_css();
assert!(css.contains("stroke-dasharray"));
assert!(css.contains("stroke-dashoffset"));
}
}