You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[css-borders-4] Tighten and improve corner-shape algorithm (#12313)
This reduces a lot of steps, and uses a couple of helper algorithms.
Also fixed a few minor bugs, and removed the "canonical superellipse formula" which is no longer needed.
@@ -609,33 +609,18 @@ The 'corner-*-shape' Shorthands And Longhands</h4>
609
609
<h4 id=corner-shape-rendering>
610
610
Rendering 'corner-shape'</h4>
611
611
612
+
When rendering elements with shaped corners, the element's path needs to be offset,
613
+
based on [=border=], [=outline=], 'box-shadow', 'overflow-clip-margin' and more.
612
614
615
+
When rendering borders or outlines, the offset is aligned to the curve of the element's shape,
616
+
while when rendering 'box-shadow' or offsetting for 'overflow-clip-margin', the offset is aligned to the axis.
613
617
614
-
The <dfn export>canonical superellipse formula</dfn> can be described in Cartesian coordinates, as follows,
615
-
where <code>s</code> is the [=superellipse parameter=]:
616
-
617
-
<pre>
618
-
k = 2<sup>abs(|s|)</sup>
619
-
x<sup>k</sup> + y<sup>k</sup> = 1
620
-
</pre>
621
-
622
-
The resulting |x| and |y| are later projected to CSS coordinates by scaling based on the 'border-radius' properties,
623
-
inversed if the [=superellipse parameter=] is negative. This creates symmetry between convex and concave shapes of the same absolute
624
-
[=superellipse parameter=].
625
-
626
-
627
-
628
-
629
-
Since stroking a superellipse accurately may be computationally intensive,
630
-
user agents may approximate the path using bezier curves,
631
-
as well as account for sharp edges and overlaps.
632
-
633
-
<figure>
634
-
<img src="images/corner-shape-adjusting.svg"
635
-
style="background: white;"
636
-
alt="Adjusting corner shapes">
637
-
<figcaption>Borders are aligned to the curve, shadows and clip are aligned to the axis.</figcaption>
638
-
</figure>
618
+
<figure>
619
+
<img src="images/corner-shape-adjusting.svg"
620
+
style="background: white;"
621
+
alt="Adjusting corner shapes">
622
+
<figcaption>Borders are aligned to the curve, shadows and clip are aligned to the axis.</figcaption>
623
+
</figure>
639
624
640
625
641
626
An [=/element=] |element|'s <dfn>outer contour</dfn> is the [=border contour path=] given |element| and |element|'s [=border edge=].
@@ -653,117 +638,80 @@ An [=/element=]'s [=overflow clip edge=] is shaped by the [=border contour path=
653
638
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'.
1. Translate |path| by <code>-|spread|, -|spread|</code>.
683
664
684
665
Note: this creates an effect where the resulting path has the same shape as the original path, but scaled to fit the given spread.
685
666
1. Return |path|.
686
667
687
-
To compute the <dfn>corner path</dfn> given a rectangle |cornerRect|, a rectangle |trimRect|, and numbers |startThickness|, |endThickness|, |orientation|, and |curvature|:
688
-
1. Assert: |orientation| is 0, 1, 2, or 3.
689
-
1. If |curvature| is less than zero, then:
690
-
1. Set |curvature| to <code>-|curvature|</code>.
691
-
1. Swap between |startThickness| and |endThickness|.
692
-
1. Set |orientation| to (|orientation| + 2) % 4.
693
-
1. Let |cornerPath| be a path that begins at <code>(0, 1)</code>.
694
-
1. Switch on |curvature|:
695
-
<dl class=switch>
696
-
: 0
697
-
:: Extend |cornerPath| by adding a straight line to <code>(1, 0)</code>.
698
-
699
-
: ∞
700
-
::
701
-
1. Extend |cornerPath| by adding a straight line to <code>(1, 1)</code>.
702
-
1. Extend |cornerPath| by adding a straight line to <code>(1, 0)</code>.
703
-
704
-
: Otherwise
705
-
::
706
-
1. Let |K| be <code>0.5<sup>|curvature|</sup></code>.
707
-
1. For each |T| between 0 and 1, extend |cornerPath| through <code>(|T|<sup>|K|</sup>, (1−|T|)<sup>|K|</sup>)</code>.
708
-
709
-
User agents may approximate this path, for instance, by using concatenated Bezier curves, to balance between performance and rendering accuracy.
710
-
</dl>
711
-
712
-
1. Let (|x|, |y|, |width|, |height|) be |targetRect|.
<figcaption>When the 'corner-shape' is ''corner-shape/round'' or more convex (<code>>= 1</code>), the unit vector is <code>1, 0</code>.
733
-
</figcaption>
734
-
</figure>
735
-
736
-
1. If |curvature| is less than 1:
737
-
1. Let |halfCorner| be the [=normalized superellipse half corner=] given |curvature|.
738
-
1. Let |offsetX| be <code>max(0, (|halfCorner| - 1) * 2 + √2)</code>.
739
-
1. Let |offsetY| be <code>max(0, √2 - |halfCorner| * 2)</code>.
740
-
741
-
Note: This formula defines the tangent of a quadratic Bezier curve that's equivalent to a superellipse quadrant.
742
-
Notably, convex hypoellipses (superellipses with a [=superellipse parameter|parameter=] between 0 and 1) can be very precisely represented by quadratic curves.
743
-
744
-
1. Let |length| be <code>hypot(|offsetX|, |offsetY|)</code>.
745
-
1. Set |tangentUnitVector| to <code>(|offsetX| / |length|, |offsetY| / |length|)</code>.
746
-
747
-
At this point |curvature| is guaranteed to be convex (>=1), so ther resulting |tangentUnitVector| would be in the range between <code>(1, 0)</code> and <code>(√2/2, √2/2)</code>.
To <dfn>add corner to path</dfn> given a path |path|, a rectangle |cornerRect|, a rectangle |trimRect|,
669
+
and numbers |orientation|, |startThickness|, |endThickness|, |curvature|:
670
+
671
+
1. If |cornerRect| is empty, or if |curvature| is ∞:
672
+
1. Let |innerQuad| be |trimRect|'s [=clockwise quad=] .
673
+
1. Extend |path| by drawing a line to |innerQuad|[<code>(|orienation| + 1) % 4</code>].
674
+
1. Return.
675
+
676
+
1. Let |cornerQuad| be |cornerRect|'s [=clockwise quad=].
677
+
1. If |curvature| is -∞:
678
+
1. Extend |path| by drawing a line from |cornerQuad|[0] to |cornerQuad|[3], trimmed by |trimRect|.
679
+
1. Extend |path| by drawing a line from |cornerQuad|[3] to |cornerQuad|[2], trimmed by |trimRect|.
680
+
1. Return.
681
+
682
+
1. Let |clampedNormalizedHalfCorner| be the [=normalized superellipse half corner=] given <code>clamp(|curvature|, -1, 1)</code>.
683
+
1. Let |equivalentQuadraticControlPointX| be <code>|clampedNormalizedHalfCorner| * 2 - 0.5</code>.
684
+
1. Let |curveStartPoint| be the [=aligned corner point=] given |cornerQuad|[|orienation|], the vector (|equivalentQuadraticControlPointX|, <code>1 - |equivalentQuadraticControlPointX|</code>), |startThickness|, and |orientation| + 1.
685
+
1. Let |curveEndPoint| by the [=aligned corner point=] given |cornerQuad|[(|orientation| + 2) % 4], the vector (<code>|equivalentQuadraticControlPointX| - 1</code>, <code>-|equivalentQuadraticControlPointX|</code>), |endThickness|, and |orientation| + 3.
686
+
1. Let |alignedCornerRect| be a [=rectangle=] that includes the points |curveStartPoint| and |curveEndPoint|.
687
+
1. Let |projectionToCornerRect| be a [=transformation matrix=],
688
+
translated by <code>(|alignedCornerRect|'s [=x coordinate=], |alignedCornerRect|'s [=y coordinate=])</code>,
689
+
scaled by <code>(|alignedCornerRect|'s [=width dimension=], |alignedCornerRect|'s [=height dimension=])</code>,
690
+
translated by <code>(0.5, 0.5)</code>,
691
+
rotated by <code>90deg * orientation</code>,
692
+
and translated by <code>(-0.5, -0.5)</code>.
693
+
694
+
1. Let |K| be <code>0.5<sup>abs(|curvature|)</sup></code>.
695
+
1. For each |T| between 0 and 1:
696
+
1. Let |A| be <code>|T|<sup>|K|</sup></code>.
697
+
1. Let |B| be <code>1 - (1 - |T|)<sup>|K|</sup></code>.
698
+
1. Let |normalizedPoint| be <code>(|A|, |B|)</code> if |curvature| is positive, otherwise <code>(|B|, |A|)</code>.
699
+
1. Let |absolutePoint| be |normalizedPoint|, transformed by |projectionToCornerRect|.
700
+
1. If |absolutePoint| is within |trimRect|, extend |path| through |absolutePoint|.
701
+
702
+
Note: User agents may approximate this algorithm, for instance, by using concatenated Bezier curves, to balance between performance and rendering accuracy.
703
+
704
+
To compute the <dfn>aligned corner point</dfn> given a point |originalPoint|, a two-component vector |offsetFromControlPoint|, a number |thickness|, and a number |orientation|:
705
+
1. Let |length| be <code>hypot(|offsetFromControlPoint|.x, |offsetFromControlPoint|.y)</code>.
706
+
1. Rotate |offsetFromControlPoint| by <code>90deg * |orientation|</code>, and scale by |thickness|.
707
+
1. Translate |originalPoint| by <code>|offsetFromControlPoint|.x / |length|, |offsetFromControlPoint|.y / |length|</code>, and return the result.
708
+
709
+
The <dfn>clockwise quad</dfn> given a [=rectangle=] |rect|, is a [=quadrilateral=] with the points
0 commit comments