Skip to content

Instantly share code, notes, and snippets.

@pepijntb
Created February 26, 2024 20:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pepijntb/42b1db2188a22d9596c9885ade0d91b9 to your computer and use it in GitHub Desktop.
Save pepijntb/42b1db2188a22d9596c9885ade0d91b9 to your computer and use it in GitHub Desktop.
package com.example.custom_component_test
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.vector.PathData
import androidx.compose.ui.graphics.vector.toPath
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlin.math.cos
import kotlin.math.sin
@Composable
fun OrientationPicker(
modifier: Modifier = Modifier,
) {
var selectedAngle by remember {
mutableStateOf(0)
}
BoxWithConstraints(
modifier = Modifier.size(200.dp),
) {
val density = LocalDensity.current
val widthInPixels = remember {
with(density) {
maxWidth.toPx()
}
}
val heightInPixels = remember {
with(density) {
maxHeight.toPx()
}
}
val paths = remember {
buildList {
for (angle in 0..315 step 45) {
add(
angle to getPathForAngle(
Size(widthInPixels, heightInPixels),
angle.toDouble()
)
)
}
}
}
Canvas(
modifier = Modifier
.fillMaxSize()
.pointerInput(Unit) {
detectTapGestures { offset ->
paths
.firstOrNull {
it.second
.getBounds()
.contains(offset)
}
?.let {
selectedAngle = it.first
}
}
},
) {
for (path in paths) {
drawPath(
path = path.second,
color = if (path.first == selectedAngle) Color.Green else Color.Red
)
}
}
}
}
fun getX(centerX: Float, radius: Float, angle: Double): Float =
centerX + radius * cos(angle.toFloat())
fun getY(centerY: Float, radius: Float, angle: Double): Float =
centerY + radius * sin(angle.toFloat())
fun getPathForAngle(
size: Size,
angle: Double,
): Path = PathData {
val angleStart = Math.toRadians(angle - 20.5)
val angleEnd = Math.toRadians(angle + 22.0)
val centerX = size.center.x
val centerY = size.center.y
val radiusInner = size.width * .25f
val radiusOuter = size.width * .45f
moveTo(
x = getX(centerX, radiusInner, angleStart),
y = getY(centerY, radiusInner, angleStart)
)
lineTo(
x = getX(centerX, radiusOuter, angleStart),
y = getY(centerY, radiusOuter, angleStart)
)
arcTo(
horizontalEllipseRadius = radiusInner * 2,
verticalEllipseRadius = radiusInner * 2,
theta = 0f,
isMoreThanHalf = false,
isPositiveArc = true,
x1 = getX(centerX, radiusOuter, angleEnd),
y1 = getY(centerY, radiusOuter, angleEnd)
)
lineTo(
x = getX(centerX, radiusInner, angleEnd),
y = getY(centerY, radiusInner, angleEnd)
)
arcTo(
horizontalEllipseRadius = radiusInner,
verticalEllipseRadius = radiusInner,
theta = 0f,
isMoreThanHalf = false,
isPositiveArc = false,
x1 = getX(centerX, radiusInner, angleStart),
y1 = getY(centerY, radiusInner, angleStart)
)
}.toPath()
@Composable
@Preview
fun Preview() {
OrientationPicker()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment