0

I'm making an isometric grid and using padding for it cells. Currently as parameters I'm waiting for its items width and height. But how can I avoid this and use final cells size in an optimal way?

I tried some solutions (commented code is the original way) from this question but it doesn't look legit for the case: I have extra multiple calculations, an error: Padding must be non-negative and it doesn't show correctly in Android Studio preview.

Modifier.onGloballyPositioned also looks same and incorrect for the case, what's the right way?

@Composable
fun <T> IsometricGrid(
    gridWidth: Int,
    gridHeight: Int,
    cellWidth: Int,
    cellHeight: Int,
    data: List<T>,
    itemContent: @Composable (Int, T) -> Unit
) {
    var width by remember { mutableStateOf(0) }
    var height by remember { mutableStateOf(0) }

    for (y in 0 until gridHeight) {
        for (x in 0 until gridWidth) {
//            val start = (y % 2 * 0.5 + x) * cellWidth
//            val top = y * cellHeight * 0.5
            val index = x * gridHeight + y

            Box(
                modifier = Modifier
                    .onSizeChanged {
                        width = it.width
                        height = it.height
                        Timber.i("$width, $height")
                    }
//                    .padding(start = start.dp, top = top.dp)
                    .padding(start = ((y % 2 * 0.5 + x) * cellWidth).dp, top = (y * height * 0.5).dp)
            ) {
                itemContent(index, data[index])
            }
        }
    }
}

Added usage:

IsometricGrid(4, 4, 100, 50, listOf<Foo>(...)) { index: Int, foo: Foo ->
    Icon(...)
}
Psijic
  • 743
  • 7
  • 20

1 Answers1

2
start = (y % 2 * 0.5 + x * width).dp, top = (y * height * 0.5).dp

This line is not correct because you add dp extension to pixel instead of converting pixel to dp

What you should be doing

val density = LocalDensity.current
density.run{(y % 2 * 0.5 + x * width).toDp()}

because dp value of any pixel values is calculated as

dpValue = valueInPixel/density

let's say you have 100px with a density = 2.0f

your dp value should be 50.dp. If you calculate it as in your question you find that it returns 100.dp

Try this. I don't have data, so i'm not able to try your function.

@Composable
fun <T> IsometricGrid(
    gridWidth: Int,
    gridHeight: Int,
    cellWidth: Int,
    cellHeight: Int,
    data: List<T>,
    itemContent: @Composable (Int, T) -> Unit
) {

    val density = LocalDensity.current

    var width by remember { mutableStateOf(0) }
    var height by remember { mutableStateOf(0) }

    for (y in 0 until gridHeight) {
        for (x in 0 until gridWidth) {
//            val start = (y % 2 * 0.5 + x) * cellWidth
//            val top = y * cellHeight * 0.5
            val index = x * gridHeight + y

            val startInDp = density.run { ((y % 2 * 0.5f + x) * cellWidth).toDp() }
            val topInDp = density.run { (y * height * 0.5f).toDp() }

            Box(
                modifier = Modifier
                    .onSizeChanged {
                        width = it.width
                        height = it.height
                    }
//                    .padding(start = start.dp, top = top.dp)
                    .padding(
                        start = startInDp,
                        top = topInDp
                    )
            ) {
                itemContent(index, data[index])
            }
        }
    }
}

toDp() is extension for Float in Density interface

/** Convert a [Float] pixel value to a Dp */
@Stable
fun Float.toDp(): Dp = (this / density).dp
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • Yep, found ((y % 2 * 0.5 + x) * cellWidth).dp in my code is correct. But it didn't change the case. – Psijic Sep 06 '22 at 12:33
  • It's in px. You need to convert it to dp as i said. It's correct in pixel – Thracian Sep 06 '22 at 12:34
  • Ok, what's the correct final code should be? Could you update your answer? – Psijic Sep 06 '22 at 12:34
  • Nope, it has same errors: `Padding must be non-negative`. For Android preview I suppose, it's possible to set the default size above 0. – Psijic Sep 06 '22 at 13:18
  • You can try changing order of `onSizeChanged ` and `padding` it also changes what you get as result. Also if you check comment in accept answer in link you provided you will see that you need to convert what you get from `onSizeChanged `. You can't just convert px to dp by adding dp to it's end – Thracian Sep 06 '22 at 13:21
  • Added usage sample. Moving padding first runs code without the error but brokes cells positions – Psijic Sep 06 '22 at 13:24