33
44package top.yukonga.miuix.kmp.utils
55
6+ import androidx.annotation.FloatRange
67import androidx.compose.foundation.shape.CornerBasedShape
78import androidx.compose.foundation.shape.CornerSize
89import androidx.compose.ui.geometry.Size
@@ -11,7 +12,9 @@ import androidx.compose.ui.graphics.Path
1112import androidx.compose.ui.unit.Dp
1213import androidx.compose.ui.unit.LayoutDirection
1314import androidx.graphics.shapes.CornerRounding
15+ import androidx.graphics.shapes.Cubic
1416import androidx.graphics.shapes.RoundedPolygon
17+ import kotlin.jvm.JvmOverloads
1518
1619/* *
1720 * Creates a [Path] for a rectangle with smoothly rounded corners.
@@ -24,15 +27,14 @@ import androidx.graphics.shapes.RoundedPolygon
2427 * @param bottomRight The radius of the bottom-right corner.
2528 */
2629fun Path.Companion.smoothRoundedRectangle (
27- smoothing : Float ,
30+ @FloatRange(from = 0.0 , 1.0 ) smoothing : Float ,
2831 size : Size ,
2932 topLeft : Float ,
3033 topRight : Float ,
3134 bottomLeft : Float ,
3235 bottomRight : Float
3336): Path {
3437 if (size.width <= 0f || size.height <= 0f ) return Path ()
35- val clampedSmoothing = smoothing.coerceIn(0f , 1f )
3638
3739 return RoundedPolygon (
3840 vertices = floatArrayOf(
@@ -42,12 +44,12 @@ fun Path.Companion.smoothRoundedRectangle(
4244 0f , size.height
4345 ),
4446 perVertexRounding = listOf (
45- CornerRounding (radius = topLeft, smoothing = clampedSmoothing ),
46- CornerRounding (radius = topRight, smoothing = clampedSmoothing ),
47- CornerRounding (radius = bottomRight, smoothing = clampedSmoothing ),
48- CornerRounding (radius = bottomLeft, smoothing = clampedSmoothing ),
47+ CornerRounding (radius = topLeft, smoothing = smoothing ),
48+ CornerRounding (radius = topRight, smoothing = smoothing ),
49+ CornerRounding (radius = bottomRight, smoothing = smoothing ),
50+ CornerRounding (radius = bottomLeft, smoothing = smoothing ),
4951 )
50- ).toComposePath ()
52+ ).toPath ()
5153}
5254
5355/* *
@@ -58,7 +60,7 @@ fun Path.Companion.smoothRoundedRectangle(
5860 */
5961fun SmoothRoundedCornerShape (
6062 corner : Dp ,
61- smoothing : Float = DefaultSmoothing
63+ @FloatRange(from = 0.0 , 1.0 ) smoothing : Float = DefaultSmoothing
6264): SmoothRoundedCornerShape = SmoothRoundedCornerShape (
6365 smoothing = smoothing,
6466 topStart = corner,
@@ -100,7 +102,7 @@ class SmoothRoundedCornerShape(
100102 * @param bottomStart The Dp value for the bottom-start corner.
101103 */
102104 constructor (
103- smoothing: Float = DefaultSmoothing ,
105+ @FloatRange(from = 0.0 , 1.0 ) smoothing: Float = DefaultSmoothing ,
104106 topStart: Dp ,
105107 topEnd: Dp ,
106108 bottomEnd: Dp ,
@@ -152,26 +154,40 @@ class SmoothRoundedCornerShape(
152154}
153155
154156/* *
155- * Converts a [RoundedPolygon] from the AndroidX graphics shapes library to a Jetpack Compose [Path].
157+ * Gets a [Path] representation for a [RoundedPolygon] shape. Note that there is some rounding
158+ * happening (to the nearest thousandth), to work around rendering artifacts introduced by some
159+ * points being just slightly off from each other (far less than a pixel). This also allows for a
160+ * more optimal path, as redundant curves (usually a single point) can be detected and not added to
161+ * the resulting path.
156162 *
157- * @param path An optional existing [Path] to reuse. If provided, it will be rewound and populated.
158- * Otherwise, a new [Path] will be created.
163+ * @param path an optional [Path] object which, if supplied, will avoid the function having to
164+ * create a new [Path] object
159165 */
160- fun RoundedPolygon.toComposePath (path : Path = Path ()): Path {
161- path.rewind()
166+ @JvmOverloads
167+ fun RoundedPolygon.toPath (path : Path = Path ()): Path {
168+ pathFromCubics(path, cubics)
169+ return path
170+ }
162171
163- if (cubics.isEmpty()) return path
164- path.moveTo(cubics[0 ].anchor0X, cubics[0 ].anchor0Y)
165- for (cubic in cubics) {
172+ private fun pathFromCubics (path : Path , cubics : List <Cubic >) {
173+ var first = true
174+ path.rewind()
175+ for (i in 0 until cubics.size) {
176+ val cubic = cubics[i]
177+ if (first) {
178+ path.moveTo(cubic.anchor0X, cubic.anchor0Y)
179+ first = false
180+ }
166181 path.cubicTo(
167- cubic.control0X, cubic.control0Y,
168- cubic.control1X, cubic.control1Y,
169- cubic.anchor1X, cubic.anchor1Y
182+ cubic.control0X,
183+ cubic.control0Y,
184+ cubic.control1X,
185+ cubic.control1Y,
186+ cubic.anchor1X,
187+ cubic.anchor1Y
170188 )
171189 }
172-
173190 path.close()
174- return path
175191}
176192
177193/* *
0 commit comments