@@ -11,71 +11,87 @@ export function getSvgPath({
1111 width,
1212 height,
1313} : FigmaSquircleParams ) {
14- cornerRadius = Math . min ( cornerRadius , width / 2 , height / 2 )
14+ const maxRadius = Math . min ( width , height ) / 2
15+ cornerRadius = Math . min ( cornerRadius , maxRadius )
1516
16- // Keeping these variable names the same as the original code for now
17+ // The article from figma's blog
18+ // https://www.figma.com/blog/desperately-seeking-squircles/
19+ //
20+ // The original code
1721 // https://github.com/MartinRGB/Figma_Squircles_Approximation/blob/bf29714aab58c54329f3ca130ffa16d39a2ff08c/js/rounded-corners.js#L64
18- const shortest_l = Math . min ( width , height )
1922
20- const p = Math . min ( shortest_l / 2 , ( 1 + cornerSmoothing ) * cornerRadius )
23+ // 12.2 from the article
24+ const p = Math . min ( ( 1 + cornerSmoothing ) * cornerRadius , maxRadius )
2125
22- let angle_alpha : number , angle_beta : number
23- if ( cornerRadius > shortest_l / 4 ) {
24- const change_percentage = ( cornerRadius - shortest_l / 4 ) / ( shortest_l / 4 )
25- angle_beta = 90 * ( 1 - cornerSmoothing * ( 1 - change_percentage ) )
26- angle_alpha = 45 * cornerSmoothing * ( 1 - change_percentage )
26+ let angleAlpha : number , angleBeta : number
27+
28+ if ( cornerRadius <= maxRadius / 2 ) {
29+ angleBeta = 90 * ( 1 - cornerSmoothing )
30+ angleAlpha = 45 * cornerSmoothing
2731 } else {
28- angle_beta = 90 * ( 1 - cornerSmoothing )
29- angle_alpha = 45 * cornerSmoothing
32+ // When `cornerRadius` is larger and `maxRadius / 2`,
33+ // these angles also depend on `cornerRadius` and `maxRadius / 2`
34+ //
35+ // I did a few tests in Figma and this code generated similar but not identical results
36+ // `diffRatio` was called `change_percentage` in the orignal code
37+ const diffRatio = ( cornerRadius - maxRadius / 2 ) / ( maxRadius / 2 )
38+
39+ angleBeta = 90 * ( 1 - cornerSmoothing * ( 1 - diffRatio ) )
40+ angleAlpha = 45 * cornerSmoothing * ( 1 - diffRatio )
3041 }
3142
32- const angle_theta = ( 90 - angle_beta ) / 2
43+ const angleTheta = ( 90 - angleBeta ) / 2
44+
45+ // This was called `h_longest` in the original code
46+ // In the article this is the distance between 2 control points: P3 and P4
47+ const p3ToP4Distance = cornerRadius * Math . tan ( toRadians ( angleTheta / 2 ) )
3348
34- const d_div_c = Math . tan ( toRadians ( angle_alpha ) )
35- const h_longest = cornerRadius * Math . tan ( toRadians ( angle_theta / 2 ) )
49+ // This was called `l` in the original code
50+ const circularSectionLength =
51+ Math . sin ( toRadians ( angleBeta / 2 ) ) * cornerRadius * Math . sqrt ( 2 )
3652
37- const l =
38- Math . sin ( toRadians ( angle_beta / 2 ) ) * cornerRadius * Math . pow ( 2 , 1 / 2 )
39- const c = h_longest * Math . cos ( toRadians ( angle_alpha ) )
40- const d = c * d_div_c
41- const b = ( p - l - ( 1 + d_div_c ) * c ) / 3
53+ // a, b, c and d are from 11.1 in the article
54+ const c = p3ToP4Distance * Math . cos ( toRadians ( angleAlpha ) )
55+ const d = c * Math . tan ( toRadians ( angleAlpha ) )
56+ const b = ( p - circularSectionLength - c - d ) / 3
4257 const a = 2 * b
4358
4459 return `
45- M ${ width / 2 } 0
46- L ${ Math . max ( width / 2 , width - p ) } 0
60+ M ${ Math . max ( width / 2 , width - p ) } 0
4761 C ${ width - ( p - a ) } 0 ${ width - ( p - a - b ) } 0 ${ width -
4862 ( p - a - b - c ) } ${ d }
49- a ${ cornerRadius } ${ cornerRadius } 0 0 1 ${ l } ${ l }
63+ a ${ cornerRadius } ${ cornerRadius } 0 0 1 ${ circularSectionLength } ${ circularSectionLength }
5064 C ${ width } ${ p - a - b }
5165 ${ width } ${ p - a }
5266 ${ width } ${ Math . min ( height / 2 , p ) }
5367 L ${ width } ${ Math . max ( height / 2 , height - p ) }
5468 C ${ width } ${ height - ( p - a ) }
5569 ${ width } ${ height - ( p - a - b ) }
5670 ${ width - d } ${ height - ( p - a - b - c ) }
57- a ${ cornerRadius } ${ cornerRadius } 0 0 1 -${ l } ${ l }
71+ a ${ cornerRadius } ${ cornerRadius } 0 0 1 -${ circularSectionLength } ${ circularSectionLength }
5872 C ${ width - ( p - a - b ) } ${ height }
5973 ${ width - ( p - a ) } ${ height }
6074 ${ Math . max ( width / 2 , width - p ) } ${ height }
6175 L ${ Math . min ( width / 2 , p ) } ${ height }
6276 C ${ p - a } ${ height }
6377 ${ p - a - b } ${ height }
6478 ${ p - a - b - c } ${ height - d }
65- a ${ cornerRadius } ${ cornerRadius } 0 0 1 -${ l } -${ l }
79+ a ${ cornerRadius } ${ cornerRadius } 0 0 1 -${ circularSectionLength } -${ circularSectionLength }
6680 C 0 ${ height - ( p - a - b ) }
6781 0 ${ height - ( p - a ) }
6882 0 ${ Math . max ( height / 2 , height - p ) }
6983 L 0 ${ Math . min ( height / 2 , p ) }
7084 C 0 ${ p - a }
7185 0 ${ p - a - b }
7286 ${ d } ${ p - a - b - c }
73- a ${ cornerRadius } ${ cornerRadius } 0 0 1 ${ l } -${ l }
87+ a ${ cornerRadius } ${ cornerRadius } 0 0 1 ${ circularSectionLength } -${ circularSectionLength }
7488 C ${ p - a - b } 0
7589 ${ p - a } 0
7690 ${ + Math . min ( width / 2 , p ) } 0
7791 Z
78- ` . replace ( / [ \t \s \n ] + / g, ' ' )
92+ `
93+ . replace ( / [ \t \s \n ] + / g, ' ' )
94+ . trim ( )
7995}
8096
8197function toRadians ( degrees : number ) {
0 commit comments