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:

<img width="203" alt="Screenshot 2024-03-26 at 20 15 38"
src="https://github.com/emilk/egui/assets/1148717/dc1c0a06-35f0-4fda-a011-0e37d18454a0">

<img width="220" alt="Screenshot 2024-03-27 at 09 48 48"
src="https://github.com/emilk/egui/assets/1148717/c278b28e-c3f9-4c82-ba20-0480621efd2f">

<img width="33" alt="Screenshot 2024-03-27 at 09 49 21"
src="https://github.com/emilk/egui/assets/1148717/379ddf77-6590-4444-9c2e-67ab1e071f0f">
This commit is contained in:
Emil Ernerfeldt 2024-03-27 10:13:49 +01:00 committed by GitHub
parent 4d4cb3d20d
commit 947b5813d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 29 additions and 7 deletions

View File

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