Shapes: per-corner roundness for Rectangle Path (#1896)

Add Per-Corner Roundness to Rectangle Path: a checkbox (off by default) and four Pixels properties (top-left, top-right, bottom-right, bottom-left). New rectangles initialise the four values from their Roundness, so ticking the box keeps the shape; the radii are animatable and expression-driven.\n\nrect_radii() builds the path clockwise from the top-left with kappa quarter-circle arcs. Overlapping radii scale down like CSS border-radius; negative and non-finite radii are square; rect() delegates to it, so uniform roundness clamps to half the shorter side as before.\n\nLottie rc has one r: four equal static radii export as that value; mixed or animated radii export roundness 0 with a warning. Import puts r into the four per-corner values so ticking the box on an imported rectangle keeps the shape.\n\nTests: path geometry and clamping, rendered per-corner pixels, Lottie equal/mixed/checkbox-off export.
This commit is contained in:
Camille Moine
2026-10-30 01:24:59 +01:00
parent 60f246d1a7
commit 3bc1caaedb
10 changed files with 304 additions and 22 deletions
+7
View File
@@ -385,11 +385,18 @@ pub fn shape_group(ids: &mut Ids, name: &str, items: Vec<PropGroup>) -> PropGrou
}
pub fn shape_rect(ids: &mut Ids, size: [f64; 2], position: [f64; 2], roundness: f64) -> PropGroup {
// Per-corner radii start from the uniform Roundness, so ticking the box keeps the shape.
let r = if roundness.is_finite() { roundness.max(0.0) } else { 0.0 };
indexed(ids.group("rect", "Rectangle Path 1"))
.with(ids.prop("direction", "Path Direction", Value::Enum(0)).with_ui(ParamUi::Hidden))
.with(ids.prop("size", "Size", Value::Vec2(size)).with_ui(ParamUi::Pixels))
.with(ids.prop("position", "Position", Value::Vec2(position)).with_ui(ParamUi::Point))
.with(ids.prop("roundness", "Roundness", Value::Scalar(roundness)).with_ui(ParamUi::Pixels))
.with(ids.prop("perCorner", "Per-Corner Roundness", Value::Bool(false)).with_ui(ParamUi::Checkbox))
.with(ids.prop("topLeftRoundness", "Top Left Roundness", Value::Scalar(r)).with_ui(ParamUi::Pixels))
.with(ids.prop("topRightRoundness", "Top Right Roundness", Value::Scalar(r)).with_ui(ParamUi::Pixels))
.with(ids.prop("bottomRightRoundness", "Bottom Right Roundness", Value::Scalar(r)).with_ui(ParamUi::Pixels))
.with(ids.prop("bottomLeftRoundness", "Bottom Left Roundness", Value::Scalar(r)).with_ui(ParamUi::Pixels))
}
pub fn shape_ellipse(ids: &mut Ids, size: [f64; 2], position: [f64; 2]) -> PropGroup {