Dropdown menu and almost proper debugging

This commit is contained in:
Alexey 2025-09-01 12:50:36 +03:00
commit 8bdbe3dc69
7 changed files with 117 additions and 52 deletions

View file

@ -4,7 +4,7 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-08-25T12:35:44.992136184Z"> <DropdownSelection timestamp="2025-08-26T11:06:03.805342749Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/home/secondbeam/.config/.android/avd/Virtual_Pixel_3.avd" /> <DeviceId pluginId="LocalEmulator" identifier="path=/home/secondbeam/.config/.android/avd/Virtual_Pixel_3.avd" />

View file

@ -15,6 +15,8 @@ import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Build
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.colorScheme
@ -22,9 +24,13 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider 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.getValue
import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
@ -73,6 +79,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 canvasOffsetX = rememberSaveable { mutableFloatStateOf(-652.0647F) }
val canvasOffsetY = rememberSaveable { mutableFloatStateOf(-1145.693F) }
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val tileContainer = TileContainer(vm, coroutineScope) val tileContainer = TileContainer(vm, coroutineScope)
KtHeightMapTheme { KtHeightMapTheme {
@ -81,13 +89,16 @@ fun Main(vm: TileViewModel = viewModel()) {
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
.safeDrawingPadding() .safeDrawingPadding()
.align(Alignment.Center), .align(Alignment.Center),
floatingActionButton = { ToolButton() } floatingActionButton = { ToolButton(vm) }
) { innerPadding -> ) { innerPadding ->
MapCanvas( MapCanvas(
viewModel = vm,
gridColor = colorScheme.primary, gridColor = colorScheme.primary,
backColor = colorScheme.background, backColor = colorScheme.background,
scale = sliderValue, scale = sliderValue,
tileContainer = tileContainer, tileContainer = tileContainer,
offsetX = canvasOffsetX,
offsetY = canvasOffsetY,
modifier = Modifier.padding(innerPadding), modifier = Modifier.padding(innerPadding),
) )
} }
@ -100,7 +111,7 @@ fun Main(vm: TileViewModel = viewModel()) {
Slider( Slider(
value = sliderValue.floatValue, value = sliderValue.floatValue,
onValueChange = { sliderValue.floatValue = it }, onValueChange = { sliderValue.floatValue = it },
valueRange = 2F..14F, valueRange = 1F..14F,
modifier = Modifier.align(Alignment.CenterStart) modifier = Modifier.align(Alignment.CenterStart)
) )
} }
@ -109,33 +120,38 @@ fun Main(vm: TileViewModel = viewModel()) {
} }
@Composable @Composable
fun ToolButton() { fun ToolButton(viewModel: TileViewModel) {
val context = LocalContext.current val context = LocalContext.current
var expanded by remember{ mutableStateOf(false) }
FloatingActionButton( FloatingActionButton(
onClick = {
expanded = !expanded
}
) {
Icon(Icons.Filled.Build, contentDescription = "Tools")
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropdownMenuItem(
text = { Text("Toggle debug") },
onClick = { viewModel.debug = !viewModel.debug }
)
DropdownMenuItem(
text = { Text("Log") },
onClick = { viewModel.logRequested = true }
)
DropdownMenuItem(
text = { Text("Set location") },
onClick = {}
)
DropdownMenuItem(
text = { Text("Settings") },
onClick = { onClick = {
val intent = Intent(context, SettingsActivity::class.java) val intent = Intent(context, SettingsActivity::class.java)
context.startActivity(intent) context.startActivity(intent)
} }
) { Icon(Icons.Filled.Build, contentDescription = "Tools") }
}
@Preview(showBackground = true)
@Composable
fun MainPreview() {
KtHeightMapTheme {
Scaffold(
modifier = Modifier.safeDrawingPadding(),
floatingActionButton = { ToolButton() }
) { innerPadding ->
Box(
modifier = Modifier.padding(innerPadding)
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = "Gryadki"
) )
} }
} }
} }
}

View file

@ -1,6 +1,8 @@
package com.mirenkov.ktheightmap package com.mirenkov.ktheightmap
import android.util.Log
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -28,14 +30,18 @@ import kotlin.math.absoluteValue
@Composable @Composable
fun MapCanvas( fun MapCanvas(
viewModel: TileViewModel,
backColor: Color, backColor: Color,
gridColor: Color, gridColor: Color,
scale: MutableFloatState, scale: MutableFloatState,
offsetX: MutableFloatState,
offsetY: MutableFloatState,
tileContainer: TileContainer, tileContainer: TileContainer,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
var offsetX by rememberSaveable { mutableFloatStateOf(-TILE_SIZE) } var offsetX by rememberSaveable { offsetX }
var offsetY by rememberSaveable { mutableFloatStateOf(-TILE_SIZE) } var offsetY by rememberSaveable { offsetY }
val scale by rememberSaveable { scale }
val textMeasurer = rememberTextMeasurer() val textMeasurer = rememberTextMeasurer()
Canvas( Canvas(
modifier = modifier.fillMaxSize() modifier = modifier.fillMaxSize()
@ -50,8 +56,14 @@ fun MapCanvas(
color = backColor, color = backColor,
size = size size = size
) )
if (viewModel.logRequested) {
Log.i(TAG, "Offset: %.6f, %.6f".format(offsetX, offsetY))
viewModel.logRequested = false
}
val oldLevel = tileContainer.getLevel() val oldLevel = tileContainer.getLevel()
val level = scale.floatValue.toInt() val level = scale.toInt()
val levelDiff = level - oldLevel val levelDiff = level - oldLevel
if (levelDiff < 0) { if (levelDiff < 0) {
@ -72,6 +84,9 @@ fun MapCanvas(
val tileOffsetX = (offsetX / TILE_SIZE).toInt() val tileOffsetX = (offsetX / TILE_SIZE).toInt()
val tileOffsetY = (offsetY / TILE_SIZE).toInt() val tileOffsetY = (offsetY / TILE_SIZE).toInt()
val centerTileX = (1 + (offsetX + size.width / 2) / TILE_SIZE).toDouble()
val centerTileY = (1 + (offsetY + size.height / 2) / TILE_SIZE).toDouble()
val strippedOffsetX = offsetX % TILE_SIZE val strippedOffsetX = offsetX % TILE_SIZE
val strippedOffsetY = offsetY % TILE_SIZE val strippedOffsetY = offsetY % TILE_SIZE
@ -86,6 +101,10 @@ fun MapCanvas(
val crossRadius = 24F val crossRadius = 24F
val additionalSize = if (viewModel.debug) 96F else 0F
val latLonSize = Size(196F, 96F + additionalSize)
val latLonOffset = Offset(16F, 16F)
for (cellX in 0 .. gridWidth + 2) { for (cellX in 0 .. gridWidth + 2) {
val tileX = tileOffsetX + cellX val tileX = tileOffsetX + cellX
val localOffsetX = TILE_SIZE * (cellX - 1) val localOffsetX = TILE_SIZE * (cellX - 1)
@ -105,6 +124,7 @@ fun MapCanvas(
) )
} }
if (viewModel.debug) {
drawRect( drawRect(
color = gridColor, color = gridColor,
size = Size(TILE_SIZE, TILE_SIZE), size = Size(TILE_SIZE, TILE_SIZE),
@ -126,6 +146,7 @@ fun MapCanvas(
topLeft = totalOffset, topLeft = totalOffset,
size = Size(TILE_SIZE, TILE_SIZE) size = Size(TILE_SIZE, TILE_SIZE)
) )
}
val path = Path() val path = Path()
path.moveTo(size.width / 2 - crossRadius, size.height / 2) path.moveTo(size.width / 2 - crossRadius, size.height / 2)
@ -139,6 +160,30 @@ fun MapCanvas(
Color.White, Color.White,
style = Stroke(width = 6F) style = Stroke(width = 6F)
) )
drawRect(
color = backColor,
size = latLonSize,
topLeft = latLonOffset
)
drawText(
textMeasurer = textMeasurer,
text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
withStyle(SpanStyle(color = gridColor)) {
val lon = SphereMercator.mercateX(centerTileX, level)
val lat = SphereMercator.mercateY(centerTileY, level)
append("%.6f\n%.6f".format(lon, lat))
if (viewModel.debug) {
append("\n%.0f\n%.0f".format(offsetX, offsetY))
}
}
}
},
size = latLonSize,
topLeft = latLonOffset
)
} }
} }
} }

View file

@ -48,5 +48,9 @@ class SphereMercator {
fun lat2sy(latitude: Double, level: Int): Double { fun lat2sy(latitude: Double, level: Int): Double {
return lat2y(latitude) / scaledMetersPerPixel(level) return lat2y(latitude) / scaledMetersPerPixel(level)
} }
fun mercateX(x: Double, level: Int): Double = sx2lon((x * TILE_SIZE), level) - 180.0
fun mercateY(y: Double, level: Int): Double = -sy2lat((y - ((1 shl (level - 1)).toDouble() / 2.0)) * TILE_SIZE, level - 1)
} }
} }

View file

@ -47,7 +47,7 @@ class Tile {
return bitmap return bitmap
} }
fun mercateX(): Double = SphereMercator.sx2lon((x * TILE_SIZE).toDouble(), level) - 180.0 fun mercateX(): Double = SphereMercator.mercateX(x.toDouble(), level)
fun mercateY(): Double = -SphereMercator.sy2lat((y.toDouble() - ((1 shl (level - 1)).toDouble() / 2.0)) * TILE_SIZE, level - 1) fun mercateY(): Double = SphereMercator.mercateY(y.toDouble(), level)
} }

View file

@ -12,14 +12,12 @@ class TileRepository(private val tileDao: TileDao) {
fun pushTile(tile: Tile) { fun pushTile(tile: Tile) {
coroutineScope.launch(Dispatchers.IO) { tileDao.pushTile(tile) } coroutineScope.launch(Dispatchers.IO) { tileDao.pushTile(tile) }
} }
fun getTile(x: Int, y: Int, level: Int): Tile? { fun getTile(x: Int, y: Int, level: Int): Tile? {
val tileFuture = coroutineScope.future(Dispatchers.IO) { val tileFuture = coroutineScope.future(Dispatchers.IO) {
tileDao.getTile(x, y, level) tileDao.getTile(x, y, level)
} }
return tileFuture.join() return tileFuture.join()
} }
fun clearTiles() { fun clearTiles() {
coroutineScope.launch(Dispatchers.IO) { tileDao.clearTiles() } coroutineScope.launch(Dispatchers.IO) { tileDao.clearTiles() }
} }

View file

@ -6,6 +6,8 @@ import androidx.lifecycle.ViewModel
class TileViewModel(application: Application): ViewModel() { class TileViewModel(application: Application): ViewModel() {
val repository: TileRepository val repository: TileRepository
var debug = false
var logRequested = false
init { init {
val tileDb = TileDB.getInstance(application) val tileDb = TileDB.getInstance(application)