From 96f3a237f4561a48c02670b79fe3eaf0dca79a49 Mon Sep 17 00:00:00 2001 From: jrmoulton Date: Tue, 18 Feb 2025 01:12:17 -0700 Subject: [PATCH] fix border when there is no border radius --- src/border_path_iter.rs | 108 +++++++++++++++++++++++----------------- src/view.rs | 8 ++- 2 files changed, 68 insertions(+), 48 deletions(-) diff --git a/src/border_path_iter.rs b/src/border_path_iter.rs index 0ae5c74c..672e7d04 100644 --- a/src/border_path_iter.rs +++ b/src/border_path_iter.rs @@ -109,10 +109,8 @@ impl<'a> Iterator for BorderPathIter<'a> { } assert!(self.current_iter.is_none()); - const EPSILON: f64 = 1e-4; - // end condition: we have reached the target percentage - if (self.current_len / self.total_len - end).abs() <= EPSILON + if (self.current_len / self.total_len - end).abs() <= f64::EPSILON || (self.current_len / self.total_len) >= end { return if self.emitted_last_stroke { @@ -176,10 +174,10 @@ impl<'a> Iterator for BorderPathIter<'a> { let subseg = path_seg.subsegment(0.0..t); self.current_len += subseg.arclen(self.tolerance); - self.current_iter = Some(Box::new(std::iter::once(subseg.as_path_el()))); + self.current_iter = Some(Box::new(subseg.path_elements(self.tolerance))); } else { self.current_len += seg_len; - self.current_iter = Some(Box::new(std::iter::once(path_seg.as_path_el()))); + self.current_iter = Some(Box::new(path_seg.path_elements(self.tolerance))); } break; } @@ -383,6 +381,7 @@ pub struct RoundedRectPathIter { rect: RectPathIter, arcs: [Arc; 8], } + #[derive(Debug)] pub enum ArcOrPath { Arc(Arc), @@ -394,48 +393,63 @@ impl Iterator for RoundedRectPathIter { type Item = ArcOrPath; fn next(&mut self) -> Option { - // The total sequence is: - // 0. Arc 1 - // 1. LineTo from rect - // 2. Arc 2 (top right) - // 3. Corner - // 4. Arc 3 (right top) - // 5. LineTo from rect - // 6. Arc 4 (right bottom) - // 7. Corner - // 8. Arc 5 (bottom right) - // 9. LineTo from rect - // 10. Arc 6 (bottom left) - // 11. Corner - // 12. Arc 7 (left bottom) - // 13. Final LineTo from rect - // 14. Arc 8 (left top) - // 15. Corner - - if self.idx >= 16 { - return None; - } - - let result = match self.idx { - // Arc segments (even indices except 2,5,8) - 0 => Some(ArcOrPath::Arc(self.arcs[1])), - 2 => Some(ArcOrPath::Arc(self.arcs[2])), - 4 => Some(ArcOrPath::Arc(self.arcs[3])), - 6 => Some(ArcOrPath::Arc(self.arcs[4])), - 8 => Some(ArcOrPath::Arc(self.arcs[5])), - 10 => Some(ArcOrPath::Arc(self.arcs[6])), - 12 => Some(ArcOrPath::Arc(self.arcs[7])), - 14 => Some(ArcOrPath::Arc(self.arcs[0])), - - // Line segments (odd indices) - 1 | 5 | 9 | 13 => Some(ArcOrPath::Path(self.rect.next().unwrap())), - - 3 | 7 | 11 | 15 => Some(ArcOrPath::Corner), - - 16.. => None, - }; + while self.idx < 16 { + let idx = self.idx; + let mut skip_arc = false; + let mut check_arc = |radius: f64, arc_idx: usize| { + if radius > f64::EPSILON { + Some(ArcOrPath::Arc(self.arcs[arc_idx])) + } else { + skip_arc = true; + None + } + }; + let mut line_to = || Some(ArcOrPath::Path(self.rect.next().unwrap())); + let result = match idx { + // Arc 1 (top left) + 0 => check_arc(self.rect.radii.top_left, 1), + // LineTo from rect + 1 => line_to(), + // Arc 2 (top right) + 2 => check_arc(self.rect.radii.top_right, 2), + // Corner + 3 => Some(ArcOrPath::Corner), + // Arc 3 (right top) + 4 => check_arc(self.rect.radii.top_right, 3), + // LineTo from rect + 5 => line_to(), + // Arc 4 (right bottom) + 6 => check_arc(self.rect.radii.bottom_right, 4), + // Corner + 7 => Some(ArcOrPath::Corner), + // Arc 5 (bottom right) + 8 => check_arc(self.rect.radii.bottom_right, 5), + // LineTo from rect + 9 => line_to(), + // Arc 6 (bottom left) + 10 => check_arc(self.rect.radii.bottom_left, 6), + // Corner + 11 => Some(ArcOrPath::Corner), + // Arc 7 (left bottom) + 12 => check_arc(self.rect.radii.bottom_left, 7), + // Final LineTo from rect + 13 => line_to(), + // Arc 8 (left top) + 14 => check_arc(self.rect.radii.top_left, 0), + // Corner + 15 => Some(ArcOrPath::Corner), + 16.. => None, + }; - self.idx += 1; - result + self.idx += 1; + if skip_arc { + continue; + } + if let Some(result) = result { + return Some(result); + } + continue; + } + None } } diff --git a/src/view.rs b/src/view.rs index 293c1bf8..09e503e4 100644 --- a/src/view.rs +++ b/src/view.rs @@ -694,7 +694,13 @@ pub(crate) fn paint_border( let mut current_path = Vec::new(); for event in border_path.path_elements(&borders, 0.1) { match event { - BorderPathEvent::PathElement(el) => current_path.push(el), + BorderPathEvent::PathElement(el) => { + if !current_path.is_empty() && matches!(el, PathEl::MoveTo(_)) { + // extra move to's will mess up dashed patterns + continue; + } + current_path.push(el) + } BorderPathEvent::NewStroke(stroke) => { // Render current path with previous stroke if any if !current_path.is_empty() {