mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 10:27:00 -06:00
Batch r2-1 review: per-side strokes follow smoothed corners for every alignment
This commit is contained in:
@@ -456,8 +456,9 @@ pub fn rect_side_strokes(w: f64, h: f64, sides: [f64; 4], align: StrokeAlign) ->
|
||||
/// Per-side stroke of a rectangle with corner radii `[tl, tr, br, bl]`, like CSS borders: the outer
|
||||
/// edge is the rounded rectangle (center and outside grow its box by half or all of each side's
|
||||
/// weight, and each non-zero radius with it); the inner edge is inset by each side's weight, with
|
||||
/// elliptical corners `max(0, R − weight)` across and down (square where either is 0). An even-odd
|
||||
/// ring whose inner edge runs against the outer one, so it is a ring under non-zero too.
|
||||
/// elliptical corners `max(0, R − weight)` across and down (square where either is 0). Corners
|
||||
/// that stay circular are smoothed like the shape's. An even-odd ring whose inner edge runs against
|
||||
/// the outer one, so it is a ring under non-zero too.
|
||||
pub fn rounded_side_strokes(w: f64, h: f64, radii: [f64; 4], smoothing: f64, sides: [f64; 4], align: StrokeAlign) -> BezPath {
|
||||
let [t, r, b, l] = sides.map(non_negative);
|
||||
if t + r + b + l <= 0.0 {
|
||||
@@ -477,7 +478,7 @@ pub fn rounded_side_strokes(w: f64, h: f64, radii: [f64; 4], smoothing: f64, sid
|
||||
figcraft_geom::rounded_rect(w, h, radii, smoothing)
|
||||
} else {
|
||||
let grown = corners.map(|(rr, x, y)| if rr > 0.0 { (rr + x * f, rr + y * f) } else { (0.0, 0.0) });
|
||||
elliptical_rect(figcraft_geom::Rect::new(-l * f, -t * f, w + r * f, h + b * f), grown)
|
||||
corner_rect(figcraft_geom::Rect::new(-l * f, -t * f, w + r * f, h + b * f), grown, smoothing)
|
||||
};
|
||||
let k = 1.0 - f;
|
||||
let inner = figcraft_geom::Rect::new(l * k, t * k, w - r * k, h - b * k);
|
||||
@@ -485,9 +486,18 @@ pub fn rounded_side_strokes(w: f64, h: f64, radii: [f64; 4], smoothing: f64, sid
|
||||
return outer;
|
||||
}
|
||||
let (inset, fit) = fit_corners(inner, corners.map(|(rr, x, y)| ((rr - x * k).max(0.0), (rr - y * k).max(0.0))));
|
||||
// The hole's corners sit on the outer ones, inside them, unless they were shrunk to fit or the
|
||||
// outer ones are smoothed: then it may leave the outer edge.
|
||||
ring(outer, &elliptical_rect(inner, inset), fit < 1.0 || (align == StrokeAlign::Inside && smoothing > 0.0))
|
||||
// The hole's corners sit on the outer ones, inside them, unless they were shrunk to fit or
|
||||
// smoothed: then it may leave the outer edge.
|
||||
ring(outer, &corner_rect(inner, inset, smoothing), fit < 1.0 || smoothing > 0.0)
|
||||
}
|
||||
|
||||
/// Rectangle `r` with `(rx, ry)` corners, smoothed like `figcraft_geom::rounded_rect` where every
|
||||
/// corner is circular (smoothing is defined for those only).
|
||||
fn corner_rect(r: figcraft_geom::Rect, corners: [(f64, f64); 4], smoothing: f64) -> BezPath {
|
||||
if smoothing > 0.0 && corners.iter().all(|(rx, ry)| rx == ry) {
|
||||
return kurbo::Affine::translate((r.x0, r.y0)) * figcraft_geom::rounded_rect(r.width(), r.height(), corners.map(|c| c.0), smoothing);
|
||||
}
|
||||
elliptical_rect(r, corners)
|
||||
}
|
||||
|
||||
fn non_negative(v: f64) -> f64 {
|
||||
|
||||
@@ -100,6 +100,16 @@ fn smoothed_corners_keep_the_stroke_inside_the_fill() {
|
||||
assert!(painted(&smooth, 60.0, 59.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn center_and_outside_strokes_follow_smoothed_corners() {
|
||||
for (align, out, inset) in [(StrokeAlign::Center, 1.0, 1.0), (StrokeAlign::Outside, 2.0, 0.0)] {
|
||||
let a = area(&rounded_side_strokes(W, H, [16.0; 4], 0.6, [2.0; 4], align));
|
||||
let outer = area(&rounded_rect(W + 2.0 * out, H + 2.0 * out, [16.0 + out; 4], 0.6));
|
||||
let hole = area(&rounded_rect(W - 2.0 * inset, H - 2.0 * inset, [16.0 - inset; 4], 0.6));
|
||||
assert!((a - (outer - hole)).abs() < 1e-3, "{align:?}: {a} vs {}", outer - hole);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weights_wider_than_the_box_leave_the_outer_edge() {
|
||||
let full = rounded_side_strokes(20.0, 10.0, [4.0; 4], 0.0, [6.0; 4], StrokeAlign::Inside);
|
||||
|
||||
Reference in New Issue
Block a user