From 85f83d683892e4413d3bace06a9006cab6f92860 Mon Sep 17 00:00:00 2001
From: Noam Rosenthal
-
(|outerRight| - |topRightHorizontalRadius|, |outerTop|, |topRightHorizontalRadius|, |topRightVerticalRadius|), |targetEdge|,
- 0, |targetTop| - |outerTop|, |outerRight| - |targetRight|, and |topRightShape|.
- 1. [=Add corner to path=] given |path|,
- the [=rectangle=] (|outerRight| - |bottomRightHorizontalRadius|, |outerBottom| - |bottomRightVerticalRadius|, |bottomRightHorizontalRadius|, |bottomRightVerticalRadius|), |targetEdge|,
- 1, |outerRight| - |targetRight|, |outerBottom| - |targetBottom|, and |bottomRightShape|.
- 1. [=Add corner to path=] given |path|,
- the [=rectangle=] (|outerLeft|, |outerBottom| - |bottomLeftVerticalRadius|, |bottomLeftHorizontalRadius|, |bottomLeftVerticalRadius|), |targetEdge|,
- 2, |outerBottom| - |targetBottom|, |targetLeft| - |outerLeft|, and |bottomLeftShape|.
- 1. [=Add corner to path=] given |path|,
- the [=rectangle=] (|outerLeft|, |outerTop|, |topLeftHorizontalRadius|, |topLeftVericalRadius|), |targetEdge|,
- 3, |targetLeft| - |outerLeft|, |targetTop| - |outerTop|, and |topLeftShape|.
- 1. Return |path|.
+An [=/element=]'s [=overflow clip edge=] is shaped by the [=border contour path=] given |element|, and an [=inset from a uniform outset=] given |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 an [=inset from a uniform outset=] given the shadow's [=used value|used=] 'box-shadow-spread'.
+
+An inset from a uniform outset given a number |outset| is an [=edge=] whose value is -|outset| in all directions.
+
+Vector Math Helpers
+
+A two-dimensional vector is a pair of numbers (, y).
+(|v|[0] ⋅ |factor|, |v|[1] ⋅ |factor|).
+
+hypot(|v|[0], |v|[1]).
+ 1. If |l| is 0, return |v|.
+ 1. Return (|v|[0] / |l|, |v|[1] / |l|).
+(|orienation| + 1) % 4].
- 1. Return.
-
- 1. Let |cornerQuad| be |cornerRect|'s [=clockwise quad=].
- 1. If |curvature| is -∞:
- 1. Extend |path| by drawing a line from |cornerQuad|[0] to |cornerQuad|[3], trimmed by |trimRect|.
- 1. Extend |path| by drawing a line from |cornerQuad|[3] to |cornerQuad|[2], trimmed by |trimRect|.
- 1. Return.
-
- 1. Let |clampedNormalizedHalfCorner| be the [=normalized superellipse half corner=] given clamp(|curvature|, -1, 1).
- 1. Let |equivalentQuadraticControlPointX| be |clampedNormalizedHalfCorner| * 2 - 0.5.
- 1. Let |curveStartPoint| be the [=aligned corner point=] given |cornerQuad|[|orienation|], the vector (|equivalentQuadraticControlPointX|, 1 - |equivalentQuadraticControlPointX|), |startThickness|, and |orientation| + 1.
- 1. Let |curveEndPoint| by the [=aligned corner point=] given |cornerQuad|[(|orientation| + 2) % 4], the vector (|equivalentQuadraticControlPointX| - 1, -|equivalentQuadraticControlPointX|), |endThickness|, and |orientation| + 3.
- 1. Let |alignedCornerRect| be a [=rectangle=] that includes the points |curveStartPoint| and |curveEndPoint|.
- 1. Let |projectionToCornerRect| be a [=transformation matrix=],
- translated by (|alignedCornerRect|'s [=x coordinate=], |alignedCornerRect|'s [=y coordinate=]),
- scaled by (|alignedCornerRect|'s [=width dimension=], |alignedCornerRect|'s [=height dimension=]),
- translated by (0.5, 0.5),
- rotated by 90deg * orientation,
- and translated by (-0.5, -0.5).
-
- 1. Let |K| be 0.5abs(|curvature|).
- 1. For each |T| between 0 and 1:
- 1. Let |A| be |T||K|.
- 1. Let |B| be 1 - (1 - |T|)|K|.
- 1. Let |normalizedPoint| be (|A|, |B|) if |curvature| is positive, otherwise (|B|, |A|).
- 1. Let |absolutePoint| be |normalizedPoint|, transformed by |projectionToCornerRect|.
- 1. If |absolutePoint| is within |trimRect|, extend |path| through |absolutePoint|.
-
- Note: User agents may approximate this algorithm, for instance, by using concatenated Bezier curves, to balance between performance and rendering accuracy.
-
-To compute the aligned corner point given a point |originalPoint|, a two-component vector |offsetFromControlPoint|, a number |thickness|, and a number |orientation|:
- 1. Let |length| be hypot(|offsetFromControlPoint|.x, |offsetFromControlPoint|.y).
- 1. Rotate |offsetFromControlPoint| by 90deg * |orientation|, and scale by |thickness|.
- 1. Translate |originalPoint| by |offsetFromControlPoint|.x / |length|, |offsetFromControlPoint|.y / |length|, and return the result.
-
-The clockwise quad given a [=rectangle=] |rect|, is a [=quadrilateral=] with the points
- (|rect|'s [=x coordinate=], |rect|'s [=y coordinate=]),
- (|rect|'s [=x coordinate=] + |rect|'s [=width dimension=], |rect|'s [=y coordinate=]),
- (|rect|'s [=x coordinate=] + |rect|'s [=width dimension=], |rect|'s [=y coordinate=] + |rect|'s [=height dimension=]),
- (|rect|'s [=x coordinate=], |rect|'s [=y coordinate=] + |rect|'s [=height dimension=]).
+The perpendicular of a [=two-dimensional vector=] |v| is (-|v|[1], |v|[0]).
+
+The vector between two points, given {{DOMPoint}}s |a| and |b|, is (|b|'s {{DOMPointReadOnly/x}} - |a|'s {{DOMPointReadOnly/x}}, |b|'s {{DOMPointReadOnly/y}} - |a|'s {{DOMPointReadOnly/y}}).
+
+Computing a contoured path
+
+This algorithm describes how to compute a path with shaped corners (a path that is either inset or outset from the original).
+To avoid the complexities of defining how superellipses intersect, the algorithm simplifies the process by specifying that each corner is "clipped out" of the path.
+The specific implementation details of this clipping operation are left to implementations.
+
+(|radius|'s [=width=] ⋅ |scaleFactor|, |radius|'s [=height=] ⋅ |scaleFactor|).
+ 1. Let |adjsutedRadiusInOutsetCoordinates| be the [=outset-adjusted border radius=] given |borderRect|'s size, |radius|, and (-|insetX|, -|insetY|).
+ 1. Return (|adjsutedRadiusInOutsetCoordinates|'s [=width=] - |insetX|, |adjsutedRadiusInOutsetCoordinates|'s [=height=] - |insetY|).
+
+ 1. Let |adjustedTopRightRadius| be the |adjustedRadius| given 'border-top-right-radius', |insetRight|, and |insetTop|.
+ 1. Let |adjustedBottomRightRadius| be the |adjustedRadius| given 'border-bottom-right-radius', |insetRight|, and |insetBottom|.
+ 1. Let |adjustedBottomLeftRadius| be the |adjustedRadius| given 'border-bottom-left-radius', |insetLeft|, and |insetBottom|.
+ 1. Let |adjustedTopLeftRadius| be the |adjustedRadius| given 'border-top-left-radius', |insetLeft|, and |insetTop|.
+
+ 1. Clip out from |path|, the [=border-aligned corner clip-out path=] given
+ |borderRect|'s right, |borderRect|'s top,
+ (-|adjustedTopRightRadius|[0], 0), (0, |adjustedTopRightRadius|[1]),
+ |element|'s [=computed value|computed=] 'corner-top-right-shape',
+ |topInset|, and |rightInset|.
+
+ 1. Clip out from |path|, the [=border-aligned corner clip-out path=] given
+ |borderRect|'s right, |borderRect|'s bottom,
+ (0, -|adjustedBottomRightRadius|[1]), (-|adjustedTopRightRadius|[0], 0),
+ |element|'s [=computed value|computed=] 'corner-bottom-right-shape',
+ |rightInset|, and |bottomInset|.
+
+ 1. Clip out from |path|, the [=border-aligned corner clip-out path=] given
+ |borderRect|'s left, |borderRect|'s bottom,
+ (|adjustedBottomLeftRadius|[0], 0), (0, -|adjustedBottomLeftRadius|[1]),
+ |element|'s [=computed value|computed=] 'corner-bottom-left-shape',
+ |bottomInset|, and |leftInset|.
+
+ 1. Clip out from |path|, the [=border-aligned corner clip-out path=] given
+ |borderRect|'s left, |borderRect|'s top,
+ (0, |adjustedTopLeftRadius|[1]), (|adjustedTopLeftRadius|[0], 0),
+ |element|'s [=computed value|computed=] 'corner-top-left-shape',
+ |leftInset|, and |topInset|.
+ 1. Return |path|.
+
+To get the border-aligned corner clip-out path given a {{DOMPointReadOnly}} |originalCornerOuter|, a [=two-dimensional vector=] |vectorTowardsStart|, a [=two-dimensional vector=] |vectorTowardsEnd|, a [=superellipse parameter=] |curvature|, and numbers |startInset| and |endInset|:
+ 1. If |curvature| is ∞, then return an empty path.
+ 1. Let |clampedHalfCorner| be the [=normalized superellipse half corner=] given clamp(|curvature|, -1, 1).
+ 1. Let |originalCornerStart| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |vectorTowardsStart| ».
+ 1. Let |originalCornerEnd| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |vectorTowardsEnd| ».
+ 1. Let |originalCornerCenter| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |vectorTowardsStart|, |vectorTowardsEnd| ».
+ 1. Let |extendStart| be a [=vector between two points|vector between=] |originalCornerStart| and |originalCornerCenter|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |startInset|.
+ 1. Let |extendEnd| be a [=vector between two points|vector between=] |originalCornerEnd| and |originalCornerCenter|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |endInset|.
+ 1. Let |clipStart| be |originalCornerStart|, [=point extended by vectors|extended by=] « |extendStart| ».
+ 1. Let |clipEnd| be |originalCornerEnd|, [=point extended by vectors|extended by=] « |extendEnd| ».
+ 1. Let |clipOuter| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |extendStart|, |extendEnd| ».
+ 1. Let |vectorFromStartToControlPoint| be the the [=two-dimensional vector=] (2 ⋅ |clampedHalfCorner| - 0.5, 1.5 - 2 ⋅ |clampedHalfCorner|).
+ 1. Let |singlePixelVectorFromStartToControlPoint| be the |vectorFromStartToControlPoint|, [=two-dimensional vector/normalize|normalized=].
+ 1. Let |strokeA|, |strokeB| be the [=two-dimensional vector/perpendicular=] of |singlePixelVectorFromStartToControlPoint|.
+ 1. Let |offset1| be [=vector between two points|the vector between=] |originalCornerStart| and |outerCorner|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |startInset| ⋅ |strokeA|.
+ 1. Let |offset2| be [=vector between two points|the vector between=] |outerCorner| and |originalCornerEnd|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |startInset| ⋅ |strokeB|.
+ 1. Let |offset3| be [=vector between two points|the vector between=] |originalCornerEnd| and |originalCornerCenter|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |endInset| ⋅ |strokeB|.
+ 1. Let |offset4| be [=vector between two points|the vector between=] |originalCornerCenter| and |originalCornerStart|, [=two-dimensional vector/normalize|normalized=] and [=two-dimensional vector/scale|scaled by=] |endInset| ⋅ |strokeA|.
+ 1. Let |adjustedCornerStart| be |originalCornerStart|, [=point extended by vectors|extended by=] « |offset1|, |offset2| ».
+ 1. Let |adjustedCornerEnd| be |originalCornerEnd|, [=point extended by vectors|extended by=] « |offset3|, |offset4| ».
+ 1. Let |adjustedCornerCenter| be |originalCornerCenter|, [=point extended by vectors|extended by=] « |offset4|, |offset1| ».
+ 1. Let |adjustedCornerOuter| be |originalCornerOuter|, [=point extended by vectors|extended by=] « |offset2|, |offset3| ».
+ 1. Let |curveCenter| be |adjustedCornerOuter| if |curvature| is less than 0, |adjustedCornerCenter| otherwise.
+ 1. Let |mapPointToCorner| be the following steps: given numbers |x| and |y|:
+ 1. Let |v1| be [=vector between two points|the vector between=] |curveCenter| and |adjustedCornerEnd|, [=two-dimensional vector/scale|scaled by=] |x|.
+ 1. Let |v2| be [=vector between two points|the vector between=] |curveCenter| and |adjustedCornerStart|, [=two-dimensional vector/scale|scaled by=] |y|.
+ 1. Return the {{DOMPointReadOnly}} at (|x|, |y|), [=point extended by vectors|extended by=] « |v1|, |v2| ».
+ 1. Let |controlPoint| be the result of calling |mapPointToCorner| given |unitVectorFromStartToControlPoint|'s {{DOMPointReadOnly/y}} and 1 - |unitVectorFromStartToControlPoint|'s {{DOMPointReadOnly/x}}.
+ 1. Let |axisAlignedCornerStart| be the intersection between the lines (|adjustedCornerStart|, |controlPoint|) and (|clipStart|, |clipOuter|). If the lines are parallel, let it be adjustedCornerStart.
+ 1. Let |axisAlignedCornerEnd| be the intersection between the lines (|adjustedCornerEnd|, |controlPoint|) and (|clipEnd|, |clipOuter|). If the lines are parallel, let it be adjustedCornerEnd.
+ 1. Let |path| be a path, starting at |axisAlignedCornerStart|.
+ 1. If |curvature| is -∞:
+ 1. Extend |path| to |adjustedCornerOuter|.
+ 1. Otherwise:
+ 1. Let |K| be 0.5-abs(|curvature|).
+ 1. For every |T| between 0 and 1, in an [=implementation-approximated=] manner,
+ extend |path| to the result of calling |mapPointToCorner| given |T||K| and (1 - |T|)|K|.
+ 1. Extend |path| to |axisAlignedCornerEnd|.
+ 1. Extend |path| to |clipOuter|.
+ 1. Return |path|.
0.5abs(|s|).
- 1. Let |convexHalfCorner| be 0.5|k|.
+ 1. Let |convexHalfCorner| be 0.51/|k|.
1. If |s| is less than 0, return 1 - |convexHalfCorner|.
1. Return |convexHalfCorner|.
From 7c6e5f51f6cde19ebab33340981046f100f55ed4 Mon Sep 17 00:00:00 2001
From: Noam Rosenthal (|x|, |y|), [=point extended by vectors|extended by=] « |v1|, |v2| ».
+
1. Let |controlPoint| be the result of calling |mapPointToCorner| given |unitVectorFromStartToControlPoint|'s {{DOMPointReadOnly/y}} and 1 - |unitVectorFromStartToControlPoint|'s {{DOMPointReadOnly/x}}.
1. Let |axisAlignedCornerStart| be the intersection between the lines (|adjustedCornerStart|, |controlPoint|) and (|clipStart|, |clipOuter|). If the lines are parallel, let it be adjustedCornerStart.
1. Let |axisAlignedCornerEnd| be the intersection between the lines (|adjustedCornerEnd|, |controlPoint|) and (|clipEnd|, |clipOuter|). If the lines are parallel, let it be adjustedCornerEnd.
+
+ Note: |axisAlignedCornerStart| and |axisAlignedCornerEnd| act as "miters" when rendering an outset.
+ They are a straight extension of the curve's tangent, intersecting with the unshaped target rect.
+
1. Let |path| be a path, starting at |axisAlignedCornerStart|.
1. If |curvature| is -∞:
1. Extend |path| to |adjustedCornerOuter|.
@@ -1596,6 +1601,7 @@ To get the border-aligned corner clip-out path given a {{DOMPointRead
1. Let |K| be 0.5-abs(|curvature|).
1. For every |T| between 0 and 1, in an [=implementation-approximated=] manner,
extend |path| to the result of calling |mapPointToCorner| given |T||K| and (1 - |T|)|K|.
+
1. Extend |path| to |axisAlignedCornerEnd|.
1. Extend |path| to |clipOuter|.
1. Return |path|.
From ca41d9cbdbb1399753ac3c17f59fea0ec8540681 Mon Sep 17 00:00:00 2001
From: Noam Rosenthal 0.5-abs(|curvature|).
1. For every |T| between 0 and 1, in an [=implementation-approximated=] manner,
From 907afe52ae989435e06ea51dc844d720d7844672 Mon Sep 17 00:00:00 2001
From: Noam Rosenthal 0.5-abs(|curvature|).
1. For every |T| between 0 and 1, in an [=implementation-approximated=] manner,