import androidx.annotation.FloatRange import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Outline import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.Shape import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.LayoutDirection import kotlin.math.atan2 import kotlin.math.cos import kotlin.math.pow import kotlin.math.sin import kotlin.math.sqrt enum class BeakDirection { Top, Bottom, Left, Right; } class BeakShape( @FloatRange(from = .0, to = 1.0) private val ratio: Float, private val direction: BeakDirection = BeakDirection.Top, ) : Shape { override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline { val width = size.width.toDouble() val height = size.height.toDouble() val centerX = width / 2 val centerY = height / 2 val path = createBeakPath(width.toFloat(), height.toFloat(), centerX.toFloat(), centerY.toFloat(), direction) return Outline.Generic(path) } private fun createBeakPath( width: Float, height: Float, centerX: Float, centerY: Float, direction: BeakDirection ): Path { val diagonal = when (direction) { BeakDirection.Left, BeakDirection.Right -> sqrt(width.pow(2) + centerY.pow(2)) BeakDirection.Top, BeakDirection.Bottom -> sqrt(centerX.pow(2) + height.pow(2)) } val targetDiagonal = diagonal * (1 - ratio) val radian = atan2( when (direction) { BeakDirection.Left, BeakDirection.Right -> width BeakDirection.Top, BeakDirection.Bottom -> centerX }, when (direction) { BeakDirection.Left, BeakDirection.Right -> centerY BeakDirection.Top, BeakDirection.Bottom -> height } ) val curvePointX = sin(radian) * targetDiagonal val curvePointY = cos(radian) * targetDiagonal return Path().apply { when (direction) { BeakDirection.Top -> { moveTo(0f, height) cubicTo(centerX, 0f, centerX, 0f, (centerX - curvePointX), curvePointY) cubicTo(centerX, 0f, centerX, 0f, (centerX + curvePointX), curvePointY) lineTo(width, height) } BeakDirection.Bottom -> { moveTo(0f, 0f) cubicTo(centerX, height, centerX, height, (centerX - curvePointX), height - curvePointY) cubicTo(centerX, height, centerX, height, (centerX + curvePointX), height - curvePointY) lineTo(width, 0f) } BeakDirection.Left -> { moveTo(width, 0f) cubicTo(0f, centerY, 0f, centerY, curvePointX, (centerY - curvePointY)) cubicTo(0f, centerY, 0f, centerY, curvePointX, (centerY + curvePointY)) lineTo(width, height) } BeakDirection.Right -> { moveTo(0f, 0f) cubicTo(width, centerY, width, centerY, width - curvePointX, (centerY - curvePointY)) cubicTo(width, centerY, width, centerY, width - curvePointX, (centerY + curvePointY)) lineTo(0f, height) } } close() } } }