Very buggy map drawing

This commit is contained in:
Alexey 2025-08-08 12:29:35 +03:00
commit 9c65627445
4 changed files with 99 additions and 11 deletions

View file

@ -23,6 +23,7 @@ import androidx.compose.material3.Slider
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -72,6 +73,8 @@ class MainActivity : ComponentActivity() {
@Composable @Composable
fun Main(vm: TileViewModel = viewModel()) { fun Main(vm: TileViewModel = viewModel()) {
val sliderValue = rememberSaveable { mutableFloatStateOf(1F) } val sliderValue = rememberSaveable { mutableFloatStateOf(1F) }
val coroutineScope = rememberCoroutineScope()
val tileContainer = TileContainer(vm, coroutineScope)
KtHeightMapTheme { KtHeightMapTheme {
Box(modifier = Modifier.background(color = colorScheme.background)) { Box(modifier = Modifier.background(color = colorScheme.background)) {
Scaffold( Scaffold(
@ -84,6 +87,7 @@ fun Main(vm: TileViewModel = viewModel()) {
gridColor = colorScheme.primary, gridColor = colorScheme.primary,
backColor = colorScheme.background, backColor = colorScheme.background,
scale = sliderValue, scale = sliderValue,
tileContainer = tileContainer,
modifier = Modifier.padding(innerPadding), modifier = Modifier.padding(innerPadding),
) )
} }

View file

@ -2,8 +2,6 @@ package com.mirenkov.ktheightmap
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.rememberTransformableState
import androidx.compose.foundation.gestures.transformable
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableFloatState import androidx.compose.runtime.MutableFloatState
@ -13,6 +11,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
@ -21,6 +20,7 @@ fun MapCanvas(
backColor: Color, backColor: Color,
gridColor: Color, gridColor: Color,
scale: MutableFloatState, scale: MutableFloatState,
tileContainer: TileContainer,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val offsetX = rememberSaveable { mutableFloatStateOf(0F) } val offsetX = rememberSaveable { mutableFloatStateOf(0F) }
@ -40,25 +40,43 @@ fun MapCanvas(
size = size size = size
) )
val tileOffsetX = offsetX.floatValue.toInt() / TILE_SIZE.toInt()
val tileOffsetY = offsetY.floatValue.toInt() / TILE_SIZE.toInt()
val strippedOffsetX = offsetX.floatValue % TILE_SIZE val strippedOffsetX = offsetX.floatValue % TILE_SIZE
val strippedOffsetY = offsetY.floatValue % TILE_SIZE val strippedOffsetY = offsetY.floatValue % TILE_SIZE
val level = scale.floatValue.toInt() val level = scale.floatValue.toInt()
val strippedScale = scale.floatValue + 1 - level
val actualTileSize = TILE_SIZE * strippedScale
val offset = Offset(strippedOffsetX, strippedOffsetY) val offset = Offset(strippedOffsetX, strippedOffsetY)
val grid = size / actualTileSize
val grid = size / TILE_SIZE
val gridWidth = grid.width.toInt() val gridWidth = grid.width.toInt()
val gridHeight = grid.height.toInt() val gridHeight = grid.height.toInt()
val tiles = tileContainer.getTiles(tileOffsetX, tileOffsetY, tileOffsetX + gridWidth, tileOffsetY + gridHeight, level)
for (cellX in 0 .. gridWidth + 2) { for (cellX in 0 .. gridWidth + 2) {
val offsetX = actualTileSize * (cellX - 1) val tileX = tileOffsetX + cellX
val offsetX = TILE_SIZE * (cellX - 1)
for (cellY in 0 .. gridHeight + 2) { for (cellY in 0 .. gridHeight + 2) {
val offsetY = actualTileSize * (cellY - 1) val tileY = tileOffsetY + cellY
val offsetY = TILE_SIZE * (cellY - 1)
val bitmap = tiles.find { it.x == tileX && it.y == tileY && it.level == level }?.getBitmap()
bitmap?.let {
val imageBitmap = bitmap.asImageBitmap()
drawImage(
image = imageBitmap,
topLeft = offset + Offset(offsetX, offsetY)
)
}
drawRect( drawRect(
color = gridColor, color = gridColor,
size = Size(actualTileSize, actualTileSize), size = Size(TILE_SIZE, TILE_SIZE),
topLeft = offset + Offset(offsetX, offsetY), topLeft = offset + Offset(offsetX, offsetY),
style = Stroke(width = 4F) style = Stroke(width = 4F)
) )

View file

@ -0,0 +1,65 @@
package com.mirenkov.ktheightmap
import kotlinx.coroutines.CoroutineScope
class TileContainer(private val vm: TileViewModel, private val coroutineScope: CoroutineScope) {
private val tiles: MutableList<Tile> = mutableListOf()
private var startX = 0
private var startY = 0
private var endX = 0
private var endY = 0
private var level = 1
fun getTiles(_startX: Int, _startY: Int, _endX: Int, _endY: Int, _level: Int): List<Tile> {
val newParams = arrayOf( _startX, _startY, _endX, _endY, _level )
val oldParams = arrayOf( startX, startY, endX, endY, level )
var isInvalid = false
for (i in 0 until newParams.size) {
if (oldParams[i] != newParams[i]) {
isInvalid = true
break
}
}
if (isInvalid) {
updateTileList(_startX, _startY, _endX, _endY, _level)
}
updateValues(_startX, _startY, _endX, _endY, _level)
return tiles
}
private fun updateTileList(_startX: Int, _startY: Int, _endX: Int, _endY: Int, _level: Int) {
tiles.removeIf {
it.level != _level ||
it.x < _startX ||
it.x > _endX ||
it.y < _startY ||
it.y > _endY
}
for (x in _startX .. _endX) {
for (y in _startY .. _endY) {
if (tiles.find { it.x == x && it.y == y && it.level == _level } == null) {
val tile = vm.repository.getTile(x, y, _level)
if (tile == null) {
tiles.add(Tile(x, y, _level))
} else {
tiles.add(tile)
}
}
}
}
}
private fun updateValues(_startX: Int, _startY: Int, _endX: Int, _endY: Int, _level: Int) {
startX = _startX
startY = _startY
endX = _endX
endY = _endY
level = _level
}
}

View file

@ -3,6 +3,7 @@ package com.mirenkov.ktheightmap
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.future.future
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class TileRepository(private val tileDao: TileDao) { class TileRepository(private val tileDao: TileDao) {
@ -12,11 +13,11 @@ class TileRepository(private val tileDao: TileDao) {
coroutineScope.launch(Dispatchers.IO) { tileDao.pushTile(tile) } coroutineScope.launch(Dispatchers.IO) { tileDao.pushTile(tile) }
} }
suspend fun getTile(x: Int, y: Int, level: Int): Tile? { fun getTile(x: Int, y: Int, level: Int): Tile? {
val tileDeferred = coroutineScope.async(Dispatchers.IO) { val tileFuture = coroutineScope.future(Dispatchers.IO) {
tileDao.getTile(x, y, level) tileDao.getTile(x, y, level)
} }
return tileDeferred.await() return tileFuture.join()
} }
fun clearTiles() { fun clearTiles() {