//! Dashed and dotted strokes are evenly spaced along the whole line, in any direction. use notescraft_whiteboard::{StrokeStyle, rough::dash}; fn length(piece: &[[f32; 2]]) -> f32 { piece.windows(2).map(|pair| ((pair[1][0] - pair[0][0]).powi(2) + (pair[1][1] - pair[0][1]).powi(2)).sqrt()).sum() } #[test] fn dashes_start_where_the_pattern_says_not_at_the_line_start() { let pieces = dash(&[vec![[0.0, 0.0], [100.0, 0.0]]], StrokeStyle::Dashed); assert_eq!(pieces.len(), 9); for (index, piece) in pieces.iter().enumerate() { let start = f32::from(u8::try_from(index).unwrap_or(0)) * 12.0; assert!((piece[0][0] - start).abs() < 1e-3, "dash {index} starts at {} not {start}", piece[0][0]); assert!(length(piece) <= 8.001, "dash {index} is {} long", length(piece)); } } #[test] fn dots_are_short_and_spaced_in_every_direction() { for (dx, dy) in [(80.0, 60.0), (-80.0, 60.0), (80.0, -60.0), (-80.0, -60.0), (0.0, 100.0), (-100.0, 0.0)] { let pieces = dash(&[vec![[0.0, 0.0], [dx, dy]]], StrokeStyle::Dotted); assert!(pieces.len() >= 20, "{dx},{dy}: {} dots", pieces.len()); assert!(pieces.iter().all(|piece| length(piece) <= 1.001), "{dx},{dy}: every dot is at most 1 long"); let gaps: Vec = pieces.windows(2).map(|pair| length(&[*pair[0].last().unwrap_or(&[0.0, 0.0]), pair[1][0]])).collect(); assert!(gaps.iter().all(|gap| (gap - 3.0).abs() < 0.05), "{dx},{dy}: uneven gaps {gaps:?}"); } } #[test] fn a_pattern_continues_around_a_corner() { let pieces = dash(&[vec![[0.0, 0.0], [5.0, 0.0], [5.0, 15.0]]], StrokeStyle::Dashed); let total: f32 = pieces.iter().map(|piece| length(piece)).sum(); // 20 units of line: 0..8 on, 8..12 off, 12..20 on. assert!((total - 16.0).abs() < 0.1, "drawn length {total}"); assert!(pieces.first().is_some_and(|piece| piece.contains(&[5.0, 0.0])), "the first dash bends around the corner"); }