Skip to content

Commit

Permalink
fix border when there is no border radius
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmoulton committed Feb 18, 2025
1 parent c9f9b16 commit 7aec5f7
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 18 deletions.
3 changes: 3 additions & 0 deletions examples/widget-gallery/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ im.workspace = true
floem = { path = "../..", features = ["rfd-async-std"] }
strum = { version = "0.25.0", features = ["derive"] }
files = { path = "../files/" }

[features]
vello = ["floem/vello"]
54 changes: 38 additions & 16 deletions src/border_path_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub struct BorderPath {

impl BorderPath {
pub fn new(rect: Rect, radii: RoundedRectRadii) -> Self {
let rect = rect.abs();
let shortest_side_length = (rect.width()).min(rect.height());
let radii = radii.abs().clamp(shortest_side_length / 2.0);
let rounded_path = RectPathIter {
idx: 0,
rect,
Expand Down Expand Up @@ -90,7 +93,11 @@ impl<'a> Iterator for BorderPathIter<'a> {
type Item = BorderPathEvent<'a>;

fn next(&mut self) -> Option<Self::Item> {
assert!(self.total_len > 0.0, "Total length must be positive");
assert!(
self.total_len >= 0.0,
"Total length must be positive. Total_len: {}",
self.total_len
);
assert!(
self.current_len <= self.total_len,
"Current length cannot exceed total length"
Expand All @@ -109,10 +116,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 {
Expand All @@ -136,6 +141,9 @@ impl<'a> Iterator for BorderPathIter<'a> {
Some(ArcOrPath::Arc(arc)) => {
// Set corner flag based on arc transition
let arc_len = arc.perimeter(self.tolerance);
if arc_len < f64::EPSILON {
continue;
}
let normalized_current = self.current_len / self.total_len;
let normalized_seg_len = arc_len / self.total_len;

Expand All @@ -151,7 +159,11 @@ impl<'a> Iterator for BorderPathIter<'a> {
assert!(t > 0.0 && t <= 1.0, "Invalid subsegment parameter");

let subseg = arc_subsegment(&arc, 0.0..t);
self.current_len += subseg.perimeter(self.tolerance);
let seg_len = subseg.perimeter(self.tolerance);
if seg_len < f64::EPSILON {
continue;
}
self.current_len += seg_len;
self.current_iter = Some(Box::new(subseg.path_elements(self.tolerance)));
} else {
self.current_len += arc_len;
Expand All @@ -161,6 +173,9 @@ impl<'a> Iterator for BorderPathIter<'a> {
}
Some(ArcOrPath::Path(path_seg)) => {
let seg_len = path_seg.arclen(self.tolerance);
if seg_len < f64::EPSILON {
continue;
}
let normalized_current = self.current_len / self.total_len;
let normalized_seg_len = seg_len / self.total_len;

Expand All @@ -175,11 +190,16 @@ impl<'a> Iterator for BorderPathIter<'a> {
assert!(t > 0.0 && t <= 1.0, "Invalid subsegment parameter");

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())));

let seg_len = subseg.arclen(self.tolerance);
if seg_len < f64::EPSILON {
continue;
}
self.current_len += seg_len;
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;
}
Expand Down Expand Up @@ -324,23 +344,24 @@ impl RectPathIter {
}

fn total_len(&self, tolerance: f64) -> f64 {
// Calculate arc lengths - one for each corner
// Calculate arc lengths with clamped radii
let arc_lengths: f64 = (0..4)
.map(|i| self.build_corner_arc(i).perimeter(tolerance))
.sum();

// Calculate straight segment lengths
// Calculate straight segment lengths with clamped radii
let straight_lengths = {
let rect = self.rect;
let radii = self.radii;
let width = self.rect.width();
let height = self.rect.height();
let radii = &self.radii;
// Top edge (minus the arc segments)
let top = rect.x1 - rect.x0 - radii.top_left - radii.top_right;
let top = width - radii.top_left - radii.top_right;
// Right edge
let right = rect.y1 - rect.y0 - radii.top_right - radii.bottom_right;
let right = height - radii.top_right - radii.bottom_right;
// Bottom edge
let bottom = rect.x1 - rect.x0 - radii.bottom_left - radii.bottom_right;
let bottom = width - radii.bottom_left - radii.bottom_right;
// Left edge
let left = rect.y1 - rect.y0 - radii.top_left - radii.bottom_left;
let left = height - radii.top_left - radii.bottom_left;

top + right + bottom + left
};
Expand Down Expand Up @@ -383,6 +404,7 @@ pub struct RoundedRectPathIter {
rect: RectPathIter,
arcs: [Arc; 8],
}

#[derive(Debug)]
pub enum ArcOrPath {
Arc(Arc),
Expand Down
11 changes: 9 additions & 2 deletions src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,10 +691,17 @@ pub(crate) fn paint_border(
border_path.subsegment(0.0..(border_progress.clamp(0.0, 100.) / 100.));
}

let mut current_path = Vec::new();
// optimize for maximum which is 12 paths and a single move to
let mut current_path = smallvec::SmallVec::<[_; 13]>::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() {
Expand Down

0 comments on commit 7aec5f7

Please sign in to comment.