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>
<SelectionState runConfigName="app">
<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">
<handle>
<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.material.icons.Icons
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.Icon
import androidx.compose.material3.MaterialTheme.colorScheme
@ -22,9 +24,13 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
@ -73,6 +79,8 @@ class MainActivity : ComponentActivity() {
@Composable
fun Main(vm: TileViewModel = viewModel()) {
val sliderValue = rememberSaveable { mutableFloatStateOf(1F) }
val canvasOffsetX = rememberSaveable { mutableFloatStateOf(-652.0647F) }
val canvasOffsetY = rememberSaveable { mutableFloatStateOf(-1145.693F) }
val coroutineScope = rememberCoroutineScope()
val tileContainer = TileContainer(vm, coroutineScope)
KtHeightMapTheme {
@ -81,13 +89,16 @@ fun Main(vm: TileViewModel = viewModel()) {
modifier = Modifier.fillMaxSize()
.safeDrawingPadding()
.align(Alignment.Center),
floatingActionButton = { ToolButton() }
floatingActionButton = { ToolButton(vm) }
) { innerPadding ->
MapCanvas(
viewModel = vm,
gridColor = colorScheme.primary,
backColor = colorScheme.background,
scale = sliderValue,
tileContainer = tileContainer,
offsetX = canvasOffsetX,
offsetY = canvasOffsetY,
modifier = Modifier.padding(innerPadding),
)
}
@ -100,7 +111,7 @@ fun Main(vm: TileViewModel = viewModel()) {
Slider(
value = sliderValue.floatValue,
onValueChange = { sliderValue.floatValue = it },
valueRange = 2F..14F,
valueRange = 1F..14F,
modifier = Modifier.align(Alignment.CenterStart)
)
}
@ -109,33 +120,38 @@ fun Main(vm: TileViewModel = viewModel()) {
}
@Composable
fun ToolButton() {
fun ToolButton(viewModel: TileViewModel) {
val context = LocalContext.current
var expanded by remember{ mutableStateOf(false) }
FloatingActionButton(
onClick = {
val intent = Intent(context, SettingsActivity::class.java)
context.startActivity(intent)
expanded = !expanded
}
) { 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"
)
}
) {
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 = {
val intent = Intent(context, SettingsActivity::class.java)
context.startActivity(intent)
}
)
}
}
}

View file

@ -1,6 +1,8 @@
package com.mirenkov.ktheightmap
import android.util.Log
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
@ -28,14 +30,18 @@ import kotlin.math.absoluteValue
@Composable
fun MapCanvas(
viewModel: TileViewModel,
backColor: Color,
gridColor: Color,
scale: MutableFloatState,
offsetX: MutableFloatState,
offsetY: MutableFloatState,
tileContainer: TileContainer,
modifier: Modifier = Modifier
) {
var offsetX by rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
var offsetY by rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
var offsetX by rememberSaveable { offsetX }
var offsetY by rememberSaveable { offsetY }
val scale by rememberSaveable { scale }
val textMeasurer = rememberTextMeasurer()
Canvas(
modifier = modifier.fillMaxSize()
@ -50,8 +56,14 @@ fun MapCanvas(
color = backColor,
size = size
)
if (viewModel.logRequested) {
Log.i(TAG, "Offset: %.6f, %.6f".format(offsetX, offsetY))
viewModel.logRequested = false
}
val oldLevel = tileContainer.getLevel()
val level = scale.floatValue.toInt()
val level = scale.toInt()
val levelDiff = level - oldLevel
if (levelDiff < 0) {
@ -72,6 +84,9 @@ fun MapCanvas(
val tileOffsetX = (offsetX / 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 strippedOffsetY = offsetY % TILE_SIZE
@ -86,6 +101,10 @@ fun MapCanvas(
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) {
val tileX = tileOffsetX + cellX
val localOffsetX = TILE_SIZE * (cellX - 1)
@ -105,27 +124,29 @@ fun MapCanvas(
)
}
drawRect(
color = gridColor,
size = Size(TILE_SIZE, TILE_SIZE),
topLeft = totalOffset,
style = Stroke(width = 4F)
)
if (viewModel.debug) {
drawRect(
color = gridColor,
size = Size(TILE_SIZE, TILE_SIZE),
topLeft = totalOffset,
style = Stroke(width = 4F)
)
drawText(
textMeasurer = textMeasurer,
text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
withStyle(SpanStyle(color = gridColor)) {
val mercX = tile.mercateX()
val mercY = tile.mercateY()
append("%.6f, %.6f,\n%d, %d".format(mercX, mercY, tileX, tileY))
drawText(
textMeasurer = textMeasurer,
text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
withStyle(SpanStyle(color = gridColor)) {
val mercX = tile.mercateX()
val mercY = tile.mercateY()
append("%.6f, %.6f,\n%d, %d".format(mercX, mercY, tileX, tileY))
}
}
}
},
topLeft = totalOffset,
size = Size(TILE_SIZE, TILE_SIZE)
)
},
topLeft = totalOffset,
size = Size(TILE_SIZE, TILE_SIZE)
)
}
val path = Path()
path.moveTo(size.width / 2 - crossRadius, size.height / 2)
@ -139,6 +160,30 @@ fun MapCanvas(
Color.White,
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 {
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
}
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) {
coroutineScope.launch(Dispatchers.IO) { tileDao.pushTile(tile) }
}
fun getTile(x: Int, y: Int, level: Int): Tile? {
val tileFuture = coroutineScope.future(Dispatchers.IO) {
tileDao.getTile(x, y, level)
}
return tileFuture.join()
}
fun clearTiles() {
coroutineScope.launch(Dispatchers.IO) { tileDao.clearTiles() }
}

View file

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