feat: squircle -> circular blob merging

More consistent with the rest of the rounding
This commit is contained in:
2 * r + 2 * t 2026-06-04 17:25:19 +10:00
parent 7403004c73
commit a0acd1a22c
3 changed files with 15 additions and 14 deletions

View file

@ -19,7 +19,7 @@ Item {
id: blobGroup id: blobGroup
color: Colours.palette.m3surfaceContainerHighest color: Colours.palette.m3surfaceContainerHighest
smoothing: 16 smoothing: Tokens.rounding.medium
cornerFill: false cornerFill: false
Behavior on color { Behavior on color {

View file

@ -41,22 +41,23 @@ float sdBox(vec2 p, vec2 center, vec2 halfSize) {
} }
float smin(float a, float b, float k) { float smin(float a, float b, float k) {
// Cubic smooth min (C2 continuous — no curvature kinks at blend boundary) // Circular smooth min — the blend fillet is a true circular arc of radius k,
float h = max(k - abs(a - b), 0.0) / k; // tangent to both surfaces (not a polynomial/squircle curve). Deviation region
return min(a, b) - h * h * h * k * (1.0/6.0); // width = k (deviates only where both a < k and b < k), matching the cubic it
// replaces. Always <= min(a, b); max blend depth at a == b is (sqrt(2) - 1) * k.
return max(k, min(a, b)) - length(max(vec2(k) - vec2(a, b), vec2(0.0)));
} }
float smax(float a, float b, float k) { float smax(float a, float b, float k) {
float h = max(k - abs(a - b), 0.0) / k; // Circular smooth max — dual of smin: -smin(-a, -b, k). Always >= max(a, b).
return max(a, b) + h * h * h * k * (1.0/6.0); return min(-k, max(a, b)) + length(max(vec2(a, b) + vec2(k), vec2(0.0)));
} }
float smaxSharpA(float a, float b, float k) { float smaxSharpA(float a, float b, float k) {
// smax variant that keeps a's boundary sharp (no inward rounding at a = 0). // Circular smax variant that keeps a's boundary sharp (no inward rounding at
// Used for the frame outer edge so it always fills to the edges. // a = 0). Used for the frame outer edge so it always fills to the edges.
float h = max(k - abs(a - b), 0.0) / k; float sm = min(-k, max(a, b)) + length(max(vec2(a, b) + vec2(k), vec2(0.0)));
float blend = h * h * h * k * (1.0/6.0); float blend = (sm - max(a, b)) * smoothstep(0.0, k * 0.5, -a);
blend *= smoothstep(0.0, k * 0.5, -a);
return max(a, b) + blend; return max(a, b) + blend;
} }
@ -193,8 +194,8 @@ void main() {
// Screen-space center (with offset) and pre-computed AABB half-extents // Screen-space center (with offset) and pre-computed AABB half-extents
vec2 ctr = rect.xy + sinkProps.yz; vec2 ctr = rect.xy + sinkProps.yz;
// Delay sink to absorb smin blend depth (cubic smin max = k/6) // Delay sink to absorb smin blend depth (circular smin max = (sqrt(2)-1)*k)
float preOff = smoothFactor * (1.0/6.0); float preOff = smoothFactor * (sqrt(2.0) - 1.0);
// Top border: track rect's BOTTOM edge, only within border thickness // Top border: track rect's BOTTOM edge, only within border thickness
float topPen = clamp(innerTop - (ctr.y + sinkSh.y) - preOff, 0.0, innerTop - outerTop); float topPen = clamp(innerTop - (ctr.y + sinkSh.y) - preOff, 0.0, innerTop - outerTop);

View file

@ -12,7 +12,7 @@ class BorderConfig : public ConfigObject {
CONFIG_PROPERTY(int, thickness, 10) CONFIG_PROPERTY(int, thickness, 10)
CONFIG_PROPERTY(int, rounding, 25) CONFIG_PROPERTY(int, rounding, 25)
CONFIG_PROPERTY(int, smoothing, 32) CONFIG_PROPERTY(int, smoothing, 20)
Q_PROPERTY(int minThickness READ minThickness CONSTANT) Q_PROPERTY(int minThickness READ minThickness CONSTANT)
Q_PROPERTY(int clampedThickness READ clampedThickness NOTIFY thicknessChanged) Q_PROPERTY(int clampedThickness READ clampedThickness NOTIFY thicknessChanged)