diff --git a/css-borders-4/Overview.bs b/css-borders-4/Overview.bs index 3e311e41d5c..3b92ae024f1 100644 --- a/css-borders-4/Overview.bs +++ b/css-borders-4/Overview.bs @@ -20,6 +20,12 @@ Warning: Not Ready spec:css-text-4; type:value; text:collapse spec:css-shapes-2; type:function; text:path() spec:css-shapes-2; type:property; text:shape-inside +spec:geometry-1; type: dfn; text: width dimension +spec:geometry-1; type: dfn; text: height dimension +spec:geometry-1; type: dfn; text: x coordinate; for: rectangle +spec:geometry-1; type: dfn; text: y coordinate; for: rectangle +spec:geometry-1; type: dfn; text: rectangle +spec:dom; type: dfn; text: element; @@ -358,10 +364,144 @@ Like 'border-radius', 'corner-shape' clips elements according to the [=overflow= Since stroking a superellipse accurately may be computationally intensive, user agents may approximate the path using bezier curves, as well as account for sharp edges and overlaps. -Issue: 'border-radius' already handles *adjacent* corners overlapping by shrinking the radiuses proportionally. -A negative ''superellipse()'' parameter allows for *opposite* corners to sometimes overlap, and needs additional restrictions defined. - -Issue #11610: check if we need additional rendering restrictions. +When rendering a [=border=] for a box that has a 'border-radius' and a 'corner-shape', +the inner border follows the curve of the outer shape, with a nearly consistent distance from the outer path throughout, +or a linearly increasing distance if the 'border-width' of the two edges of the corner is not uniform. + +In contrast, when rendering a 'box shadow' or when extending the [=overflow clip edge=], the resulting path does not trace border contour. +Instead, it preserves the original shape and scales it in an axis-aligned manner. + +
+ Adjusting corner shapes +
Borders are aligned to the curve, shadows and clip are aligned to the axis.
+
+ + +An [=/element=] |element|'s outer contour is the [=border contour path=] given |element| and |element|'s [=border edge=]. + +An [=/element=] |element|'s inner contour is the [=border contour path=] given |element| and |element|'s [=padding edge=]. + +An [=/element=]'s [=border=] is rendered in the area between its [=outer contour=] and its [=inner contour=]. + +An [=/element=]'s [=overflow=] area is shaped by its [=inner contour=]. +An [=/element=]'s [=overflow clip edge=] is shaped by the [=border contour path=] given |element|, and |element|'s [=padding edge=], and |element|'s [=used value|used=] 'overflow-clip-margin'. + +Each shadow of [=/element=]'s 'box shadow' is shaped by the [=border contour path=] given |element|, and |element|'s [=border edge=], and the shadow's [=used value|used=] 'box-shadow-spread'. + +
+To compute an [=/element=] |element|'s border contour path given an an [=edge=] |targetEdge| and an optional number |spread| (default 0): + 1. Let |outerLeft|, |outerTop|, |outerRight|, |outerBottom| be |element|'s [=unshaped edge|unshaped=] [=border edge=]. + 1. Let |topLeftHorizontalRadius|, |topLeftVericalRadius|, |topRightHorizontalRadius|, |topRightVerticalRadius|, |bottomRightHorizontalRadius|, + |bottomRightVerticalRadius|, |bottomLeftHorizontalRadius|, and |bottomLeftVerticalRadius| be |element| [=border edge=]'s radii. + 1. Let |topLeftShape|, |topRightShape|, |bottomRightShape|, and |bottomLeftShape| be |element|'s [=computed value|computed=] 'corner-*-shape' values. + 1. Let |targetLeft|, |targetTop|, |targetRight|, |targetBottom| [=unshaped edge|unshaped=] |targetEdge|. + 1. Let |path| be a new path [[SVG2]]. + 1. Compute a [=corner path=] given + the [=rectangle=] (|outerRight| - |topRightHorizontalRadius|, |outerTop|, |topRightHorizontalRadius|, |topRightVerticalRadius|), + 0, |targetTop| - |outerTop|, |outerRight| - |targetRight|, and |topRightShape|, + and append it to |path|. + 1. Compute a [=corner path=] given + the rectangle (|outerRight| - |bottomRightHorizontalRadius|, |outerBottom| - |bottomRightVerticalRadius|, |bottomRightHorizontalRadius|, |bottomRightVerticalRadius|), |targetEdge|, + 1, |outerRight| - |targetRight|, |outerBottom| - |targetBottom|, and |bottomRightShape|, + and append it to |path|. + 1. Compute a [=corner path=] given + the rectangle (|outerLeft|, |outerBottom| - |bottomLeftVerticalRadius|, |bottomLeftHorizontalRadius|, |bottomLeftVerticalRadius|), |targetEdge|, + 2, |outerBottom| - |targetBottom|, |targetLeft| - |outerLeft|, and |bottomLeftShape|, + and append it to |path|. + 1. Compute a [=corner path=] given + the rectangle (|outerLeft|, |outerTop|, |topLeftHorizontalRadius|, |topLeftVericalRadius|), |targetEdge|, + 3, |targetLeft| - |outerLeft|, |targetTop| - |outerTop|, and |topLeftShape|, + and append it to |path|. + 1. If |spread| is not 0, then: + 1. Scale |path| by 1 + (|spread| * 2) / (|targetRect|'s [=width dimension|width=]), 1 + (|spread| * 2) / (|targetEdge|'s [=height dimension|height=]). + 1. Translate |path| by -|spread|, -|spread|. + + Note: this creates an effect where the resulting path has the same shape as the original path, but scaled to fit the given spread. + 1. Return |path|. + +To compute the corner path given a rectangle |cornerRect|, a rectangle |trimRect|, and numbers |startThickness|, |endThickness|, |orientation|, and |curvature|: + 1. Assert: |orientation| is 0, 1, 2, or 3. + 1. If |curvature| is less than zero, then: + 1. Set |curvature| to -|curvature|. + 1. Swap between |startThickness| and |endThickness|. + 1. Set |orientation| to (|orientation| + 2) % 4. + 1. Let |cornerPath| be a path that begins at (0, 1). + 1. Switch on |curvature|: +
+ : 0 + :: Extend |cornerPath| by adding a straight line to (1, 0). + + : ∞ + :: + 1. Extend |cornerPath| by adding a straight line to (1, 1). + 1. Extend |cornerPath| by adding a straight line to (1, 0). + + : Otherwise + :: + 1. Let |K| be 0.5|curvature|. + 1. For each |T| between 0 and 1, extend |cornerPath| through (|T||K|, (1−|T|)|K|). + + User agents may approximate this path, for instance, by using concatenated Bezier curves, to balance between performance and rendering accuracy. +
+ + 1. Let (|x|, |y|, |width|, |height|) be |targetRect|. + 1. Let |clockwiseRectQuad| be « (|x|, |y|), (|x| + |width|, |y|), (|x| + |width|, |y| + |height|), (|x|, |y| + height|) ». + 1. Let |curveStartPoint| be |clockwiseRectQuad|[|orientation|]. + 1. Let |curveEndPoint| be |clockwiseRectQuad|[(|orientation| + 2) % 4]. + 1. If either |startThickness| or |endThickness| is greater than 0, then: + + Note: the following substeps compute a new |curveStartPoint| and |curveEndPoint|, based on the thickness and |curvature|. + The start and end points are offset inwards, perpendicular to the direction of the curve, with the corresponding |startThickness| or |endThickness|. + + 1. Let |tangentUnitVector| be (1, 0). + + Note: |tangentUnitVector| is a unit vector (length of 1 pixel) that points along a curve with both positive X and Y components + (like a top-right corner) and reflects the given |curvature|. This base vector can then be rotated to align with the specific corner's orientation + and scaled to match the required border thickness. + For round curvatures, or for hyperellipses (|curvature| greater than 1), the tangent is a horizontal line to the right. + +
+ Tangent unit vector with round (s=1) +
When the 'corner-shape' is ''corner-shape/round'' or more convex (>= 1), the unit vector is 1, 0. +
+
+ + 1. If |curvature| is less than 1: + 1. Let |halfCorner| be the [=normalized superellipse half corner=] given |curvature|. + 1. Let |offsetX| be max(0, (|halfCorner| - 1) * 2 + √2). + 1. Let |offsetY| be max(0, √2 - |halfCorner| * 2). + + Note: This formula defines the tangent of a quadratic Bezier curve that's equivalent to a superellipse quadrant. + Notably, convex hypoellipses (superellipses with a [=superellipse parameter|parameter=] between 0 and 1) can be very precisely represented by quadratic curves. + + 1. Let |length| be hypot(|offsetX|, |offsetY|). + 1. Set |tangentUnitVector| to (|offsetX| / |length|, |offsetY| / |length|). + + At this point |curvature| is guaranteed to be convex (>=1), so ther resulting |tangentUnitVector| would be in the range between (1, 0) and (√2/2, √2/2). + +
+ Tangent unit vector with bevel (s=0) +
When the 'corner-shape' is ''corner-shape/bevel'' (0), the unit vector is √2/2, √2/2. +
+
+ + 1. Let |startOffset| be |tangentUnitVector|, scaled by |startThickness| and rotated 90° * ((|orientation| + 1) % 4) clockwise. + 1. Let |endOffset| be |tangentUnitVector|, scaled by |endThickness| and rotated by 90° * ((|orientation| + 2) % 4) clockwise. + 1. Translate |curveStartPoint| by |startOffset|. + 1. Translate |curveEndPoint| by |endOffset|. + 1. Set |cornerRect| to a rectangle that contains |curveStartPoint| and |curveEndPoint|. + 1. Rotate |cornerPath| by 90° * |orientation|, with (0.5, 0.5) as the origin, as described [=transformation matrix|here=]. + 1. Scale |cornerPath| by |cornerRect|'s [=width dimension|width=], |cornerRect|'s [=width dimension|height=]. + 1. translate |cornerPath| by |cornerRect|'s [=x coordinate|x=], |cornerRect|'s [=y coordinate|y=]. + 1. Trim |cornerPath| to |trimRect|. + 1. Return |cornerPath|. +

'corner-shape' values

@@ -396,7 +536,7 @@ Issue #11610: che It is a number between -infinity and infinity, with -infinity corresponding to a straight concave corner, infinity corresponding to a square convex corner. - The canonical superellipse formula can be described in Cartesian coordinates, as follows, + The canonical superellipse formula can be described in Cartesian coordinates, as follows, where s is the [=superellipse parameter=]:
@@ -415,10 +555,10 @@ Issue #11610: che
 
 		
Rendering of different superellipse parameter values. + width="320" height="240" + style="background: white; padding: 8px;" + title="rendering of different superellipse parameter values" + alt="Rendering of different superellipse parameter values.">
Rendering examples of different ''superellipse()'' values.
@@ -504,7 +644,7 @@ Since it uses a log2, interpolating it linearly would result in an To balance that, the superellipse interpolation formula describes how a [=superellipse parameter=] is converted to a value between 0 and 1, and vice versa:
-To interpolate a <> |s| to an interpolation value between 0 and 1, return the first matching statement, switch on |s|: +To compute the normalized superellipse half corner given a [=superellipse parameter=] |s|, return the first matching statement, switching on |s|:
: -∞ :: Return 0. @@ -518,9 +658,10 @@ To interpolate a <> |s| to an interpolation value bet 1. Let |convexHalfCorner| be 0.5|k|. 1. If |param| is less than 0, return 1 - |convexHalfCorner|. 1. Return |convexHalfCorner|. -
+To interpolate a [=superellipse parameter=] |s| to an interpolation value between 0 and 1, return the [=normalized superellipse half corner=] given |s|. + To convert a <> |interpolationValue| back to a [=superellipse parameter=], switch on |interpolationValue|:
: 0 diff --git a/css-borders-4/images/corner-shape-adjusting.svg b/css-borders-4/images/corner-shape-adjusting.svg new file mode 100644 index 00000000000..68de0460feb --- /dev/null +++ b/css-borders-4/images/corner-shape-adjusting.svg @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + Shadow/clip Spread direction + Borderwidthdirection + Borderwidthdirection + + + + + + + + diff --git a/css-borders-4/images/corner-shape-border.svg b/css-borders-4/images/corner-shape-border.svg new file mode 100644 index 00000000000..dc09d66631d --- /dev/null +++ b/css-borders-4/images/corner-shape-border.svg @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + < border-left-width > + < border-left-width > + < border-top-width > + < border-top-width > + + Hull ofsuperellipse + 90°from hull + 90°from hull + + + + + Meeting point of the border and the corner + Meeting point of the border and the corner + + + + diff --git a/css-borders-4/images/corner-shape-target-unit-vector-bevel.svg b/css-borders-4/images/corner-shape-target-unit-vector-bevel.svg new file mode 100644 index 00000000000..909d80cc688 --- /dev/null +++ b/css-borders-4/images/corner-shape-target-unit-vector-bevel.svg @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + Bevel:√2/2, √2/2 + + diff --git a/css-borders-4/images/corner-shape-target-unit-vector-round.svg b/css-borders-4/images/corner-shape-target-unit-vector-round.svg new file mode 100644 index 00000000000..41494140df4 --- /dev/null +++ b/css-borders-4/images/corner-shape-target-unit-vector-round.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + Round: 1,0 + + +