From 947b5813d7def12be84e3c2f3cf4af13a1eba5b3 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 27 Mar 2024 10:13:49 +0100 Subject: [PATCH] Fix visual glitch on the right side of highly rounded rectangles (#4244) * Part of https://github.com/emilk/egui/issues/4238 When one side of a rectangle is all rounding we need to take care not to produce duplicated vertices in the rectangle path generator. The old code only handled three sides, but forgot the last side (the right side). The new code handles the right side, and also handles the other sides more robustly (with a floating point eps) and efficiently (in a single pass). The glitch was most notable in shadows with a high blur width. Examples of the glitch: Screenshot 2024-03-26 at 20 15 38 Screenshot 2024-03-27 at 09 48 48 Screenshot 2024-03-27 at 09 49 21 --- crates/epaint/src/tessellator.rs | 36 +++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 49d5ab8a..f626b9f3 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -520,7 +520,7 @@ pub mod path { let min = rect.min; let max = rect.max; - let r = clamp_radius(rounding, rect); + let r = clamp_rounding(rounding, rect); if r == Rounding::ZERO { let min = rect.min; @@ -531,11 +531,33 @@ pub mod path { path.push(pos2(max.x, max.y)); // right bottom path.push(pos2(min.x, max.y)); // left bottom } else { - add_circle_quadrant(path, pos2(max.x - r.se, max.y - r.se), r.se, 0.0); - add_circle_quadrant(path, pos2(min.x + r.sw, max.y - r.sw), r.sw, 1.0); - add_circle_quadrant(path, pos2(min.x + r.nw, min.y + r.nw), r.nw, 2.0); - add_circle_quadrant(path, pos2(max.x - r.ne, min.y + r.ne), r.ne, 3.0); - path.dedup(); // We get duplicates for thin rectangles, producing visual artifats + // We need to avoid duplicated vertices, because that leads to visual artifacts later. + // Duplicated vertices can happen when one side is all rounding, with no straight edge between. + let eps = f32::EPSILON * rect.size().max_elem(); + + add_circle_quadrant(path, pos2(max.x - r.se, max.y - r.se), r.se, 0.0); // south east + + if rect.width() <= r.se + r.sw + eps { + path.pop(); // avoid duplicated vertex + } + + add_circle_quadrant(path, pos2(min.x + r.sw, max.y - r.sw), r.sw, 1.0); // south west + + if rect.height() <= r.sw + r.nw + eps { + path.pop(); // avoid duplicated vertex + } + + add_circle_quadrant(path, pos2(min.x + r.nw, min.y + r.nw), r.nw, 2.0); // north west + + if rect.width() <= r.nw + r.ne + eps { + path.pop(); // avoid duplicated vertex + } + + add_circle_quadrant(path, pos2(max.x - r.ne, min.y + r.ne), r.ne, 3.0); // north east + + if rect.height() <= r.ne + r.se + eps { + path.pop(); // avoid duplicated vertex + } } } @@ -589,7 +611,7 @@ pub mod path { } // Ensures the radius of each corner is within a valid range - fn clamp_radius(rounding: Rounding, rect: Rect) -> Rounding { + fn clamp_rounding(rounding: Rounding, rect: Rect) -> Rounding { let half_width = rect.width() * 0.5; let half_height = rect.height() * 0.5; let max_cr = half_width.min(half_height);