Fast scroll bug fix

This commit is contained in:
Alexander Nozik 2022-07-10 17:50:25 +03:00
parent 18b7b91b6f
commit 6258e25669
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
3 changed files with 56 additions and 15 deletions

View File

@ -1,24 +1,25 @@
package centre.sciprog.maps.compose package centre.sciprog.maps.compose
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
//TODO replace zoom range with zoom-based representation change //TODO replace zoom range with zoom-based representation change
sealed class MapFeature(val zoomRange: ClosedFloatingPointRange<Double>) sealed class MapFeature(val zoomRange: ClosedFloatingPointRange<Double>)
private val defaultRange = 0.0..18.0 private val defaultZoomRange = 1.0..18.0
private fun Pair<Double, Double>.toCoordinates() = GeodeticMapCoordinates.ofDegrees(first, second) private fun Pair<Double, Double>.toCoordinates() = GeodeticMapCoordinates.ofDegrees(first, second)
class MapCircleFeature( class MapCircleFeature(
val center: GeodeticMapCoordinates, val center: GeodeticMapCoordinates,
zoomRange: ClosedFloatingPointRange<Double> = defaultRange, zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
val size: Float = 5f, val size: Float = 5f,
val color: Color = Color.Red, val color: Color = Color.Red,
) : MapFeature(zoomRange) ) : MapFeature(zoomRange)
fun MapCircleFeature( fun MapCircleFeature(
centerCoordinates: Pair<Double, Double>, centerCoordinates: Pair<Double, Double>,
zoomRange: ClosedFloatingPointRange<Double> = defaultRange, zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
size: Float = 5f, size: Float = 5f,
color: Color = Color.Red, color: Color = Color.Red,
) = MapCircleFeature( ) = MapCircleFeature(
@ -28,17 +29,30 @@ fun MapCircleFeature(
color color
) )
class MapLineFeature( class MapLineFeature(
val a: GeodeticMapCoordinates, val a: GeodeticMapCoordinates,
val b: GeodeticMapCoordinates, val b: GeodeticMapCoordinates,
zoomRange: ClosedFloatingPointRange<Double> = defaultRange, zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
val color: Color = Color.Red, val color: Color = Color.Red,
) : MapFeature(zoomRange) ) : MapFeature(zoomRange)
fun MapLineFeature( fun MapLineFeature(
aCoordinates: Pair<Double, Double>, aCoordinates: Pair<Double, Double>,
bCoordinates: Pair<Double, Double>, bCoordinates: Pair<Double, Double>,
zoomRange: ClosedFloatingPointRange<Double> = defaultRange, zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
color: Color = Color.Red, color: Color = Color.Red,
) = MapLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), zoomRange, color) ) = MapLineFeature(aCoordinates.toCoordinates(), bCoordinates.toCoordinates(), zoomRange, color)
class MapTextFeature(
val position: GeodeticMapCoordinates,
val text: String,
zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
val color: Color = Color.Red,
): MapFeature(zoomRange)
class MapImageFeature(
val position: GeodeticMapCoordinates,
val image: ImageBitmap,
zoomRange: ClosedFloatingPointRange<Double> = defaultZoomRange,
val color: Color = Color.Red,
): MapFeature(zoomRange)

View File

@ -9,8 +9,11 @@ import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier 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.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.drawscope.clipRect import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.graphics.toComposeImageBitmap import androidx.compose.ui.graphics.toComposeImageBitmap
import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputChange
@ -30,6 +33,7 @@ import java.net.URL
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.* import kotlin.io.path.*
import kotlin.math.floor import kotlin.math.floor
import kotlin.math.pow
private const val TILE_SIZE = 256 private const val TILE_SIZE = 256
@ -89,6 +93,14 @@ private class OsMapCache(val scope: CoroutineScope, val client: HttpClient, priv
} }
} }
private fun Color.toPaint(): org.jetbrains.skia.Paint = org.jetbrains.skia.Paint().setARGB(
(alpha * 256).toInt(),
(red * 256).toInt(),
(green * 256).toInt(),
(blue * 256).toInt()
)
private fun Double.toIndex(): Int = floor(this / TILE_SIZE).toInt() private fun Double.toIndex(): Int = floor(this / TILE_SIZE).toInt()
private fun Int.toCoordinate(): Double = (this * TILE_SIZE).toDouble() private fun Int.toCoordinate(): Double = (this * TILE_SIZE).toDouble()
@ -115,6 +127,7 @@ fun MapView(
val centerCoordinates by derivedStateOf { viewPoint.toMercator() } val centerCoordinates by derivedStateOf { viewPoint.toMercator() }
LaunchedEffect(viewPoint, canvasSize) { LaunchedEffect(viewPoint, canvasSize) {
val z = viewPoint.zoom.toInt()
val left = centerCoordinates.x - canvasSize.width / 2 val left = centerCoordinates.x - canvasSize.width / 2
val right = centerCoordinates.x + canvasSize.width / 2 val right = centerCoordinates.x + canvasSize.width / 2
val horizontalIndices = left.toIndex()..right.toIndex() val horizontalIndices = left.toIndex()..right.toIndex()
@ -125,13 +138,17 @@ fun MapView(
mapTiles.clear() mapTiles.clear()
val indexRange = 0 until 2.0.pow(z).toInt()
for (j in verticalIndices) { for (j in verticalIndices) {
for (i in horizontalIndices) { for (i in horizontalIndices) {
val tileId = OsMapTileId(viewPoint.zoom.toInt(), i, j) if(z == viewPoint.zoom.toInt() && i in indexRange && j in indexRange) {
val tileId = OsMapTileId(z, i, j)
val tile = mapCache.loadTile(tileId) val tile = mapCache.loadTile(tileId)
mapTiles.add(tile) mapTiles.add(tile)
} }
} }
}
} }
@ -186,10 +203,20 @@ fun MapView(
topLeft = offset topLeft = offset
) )
} }
features.filter { viewPoint.zoom in it.zoomRange }.forEach { features.filter { viewPoint.zoom in it.zoomRange }.forEach { feature ->
when (it) { when (feature) {
is MapCircleFeature -> drawCircle(it.color, it.size, center = it.center.toOffset()) is MapCircleFeature -> drawCircle(
is MapLineFeature -> drawLine(it.color, it.a.toOffset(), it.b.toOffset()) feature.color,
feature.size,
center = feature.center.toOffset()
)
is MapLineFeature -> drawLine(feature.color, feature.a.toOffset(), feature.b.toOffset())
is MapImageFeature -> drawImage(feature.image, feature.position.toOffset())
is MapTextFeature -> drawIntoCanvas {
val offset = feature.position.toOffset()
it.nativeCanvas.drawString(feature.text, offset.x, offset.y, null, feature.color.toPaint())
}
} }
} }
} }

View File

@ -33,7 +33,7 @@ fun MapViewPoint.move(delta: GeodeticMapCoordinates): MapViewPoint {
} }
fun MapViewPoint.zoom(zoomDelta: Double): MapViewPoint { fun MapViewPoint.zoom(zoomDelta: Double): MapViewPoint {
return copy(zoom = (zoom + zoomDelta).coerceIn(1.0, 18.0)) return copy(zoom = (zoom + zoomDelta).coerceIn(2.0, 18.0))
} }
fun MapViewPoint.toMercator() = WebMercatorProjection.toMercator(focus, zoom) fun MapViewPoint.toMercator() = WebMercatorProjection.toMercator(focus, zoom)