From bebd33f8593a614b2a7fbabeb0e19097ec6f2f3c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 13 Jul 2022 21:32:28 +0300 Subject: [PATCH] Fix for TAVRIDA-T-5 --- .../centre/sciprog/maps/compose/MapViewJvm.kt | 88 +++++++++++++++---- 1 file changed, 70 insertions(+), 18 deletions(-) diff --git a/src/jvmMain/kotlin/centre/sciprog/maps/compose/MapViewJvm.kt b/src/jvmMain/kotlin/centre/sciprog/maps/compose/MapViewJvm.kt index 8da587e..09cee4c 100644 --- a/src/jvmMain/kotlin/centre/sciprog/maps/compose/MapViewJvm.kt +++ b/src/jvmMain/kotlin/centre/sciprog/maps/compose/MapViewJvm.kt @@ -1,23 +1,20 @@ package centre.sciprog.maps.compose import androidx.compose.foundation.Canvas -import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.gestures.drag +import androidx.compose.foundation.gestures.forEachGesture import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.* import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.drawscope.clipRect -import androidx.compose.ui.graphics.drawscope.drawIntoCanvas -import androidx.compose.ui.graphics.drawscope.translate +import androidx.compose.ui.graphics.PathEffect +import androidx.compose.ui.graphics.drawscope.* import androidx.compose.ui.graphics.nativeCanvas import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.input.pointer.PointerEventType -import androidx.compose.ui.input.pointer.PointerInputChange -import androidx.compose.ui.input.pointer.onPointerEvent -import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.input.pointer.* import androidx.compose.ui.unit.* import centre.sciprog.maps.* import mu.KotlinLogging @@ -25,8 +22,8 @@ import org.jetbrains.skia.Font import org.jetbrains.skia.Paint import kotlin.math.ceil import kotlin.math.floor +import kotlin.math.log2 import kotlin.math.pow -import kotlin.math.roundToInt private fun Color.toPaint(): Paint = Paint().apply { @@ -39,6 +36,7 @@ private val logger = KotlinLogging.logger("MapView") /** * A component that renders map and provides basic map manipulation capabilities */ +@OptIn(ExperimentalComposeUiApi::class) @Composable actual fun MapView( initialViewPoint: MapViewPoint, @@ -73,20 +71,62 @@ actual fun MapView( */ fun DpOffset.toGeodetic() = WebMercatorProjection.toGeodetic(toMercator()) - @OptIn(ExperimentalComposeUiApi::class) - val canvasModifier = modifier.onPointerEvent(PointerEventType.Press) { - val (xPos, yPos) = it.changes.first().position - onClick(DpOffset(xPos.toDp(), yPos.toDp()).toGeodetic()) + // Selection rectangle. If null - no selection + var selectRect by remember { mutableStateOf(null) } + + val canvasModifier = modifier.pointerInput(Unit) { + forEachGesture { + awaitPointerEventScope { + val event: PointerEvent = awaitPointerEvent() + event.changes.forEach { change -> + if (event.buttons.isPrimaryPressed) { + //Evaluating selection frame + if (event.keyboardModifiers.isShiftPressed) { + selectRect = Rect(change.position, change.position) + drag(change.id) { dragChange -> + selectRect?.let { rect -> + val offset = dragChange.position + selectRect = Rect( + kotlin.math.min(offset.x, rect.left), + kotlin.math.min(offset.y, rect.top), + kotlin.math.max(offset.x, rect.right), + kotlin.math.max(offset.y, rect.bottom) + ) + } + } + selectRect?.let { rect -> + val (centerX, centerY) = rect.center + val centerGmc = DpOffset(centerX.toDp(), centerY.toDp()).toGeodetic() + + val horizontalZoom: Float = log2(canvasSize.width.toPx() / rect.width) + val verticalZoom: Float = log2(canvasSize.height.toPx() / rect.height) + + + viewPoint = MapViewPoint(centerGmc, viewPoint.zoom + kotlin.math.min(verticalZoom, horizontalZoom)) + selectRect = null + } + } else { + val dragStart = change.position + val dpPos = DpOffset(dragStart.x.toDp(), dragStart.y.toDp()) + onClick(dpPos.toGeodetic()) + drag(change.id) { dragChange -> + val dragAmount = dragChange.position - dragChange.previousPosition + viewPoint = viewPoint.move( + -dragAmount.x.toDp().value / tileScale, + +dragAmount.y.toDp().value / tileScale + ) + } + } + } + } + } + } }.onPointerEvent(PointerEventType.Scroll) { val change = it.changes.first() val (xPos, yPos) = change.position //compute invariant point of translation val invariant = DpOffset(xPos.toDp(), yPos.toDp()).toGeodetic() viewPoint = viewPoint.zoom(-change.scrollDelta.y.toDouble() * config.zoomSpeed, invariant) - }.pointerInput(Unit) { - detectDragGestures { _: PointerInputChange, dragAmount: Offset -> - viewPoint = viewPoint.move(-dragAmount.x.toDp().value / tileScale, +dragAmount.y.toDp().value / tileScale) - } }.fillMaxSize() @@ -189,5 +229,17 @@ actual fun MapView( drawFeature(zoom, feature) } } + selectRect?.let { rect -> + drawRect( + color = Color.Blue, + topLeft = rect.topLeft, + size = rect.size, + alpha = 0.5f, + style = Stroke( + width = 2f, + pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f) + ) + ) + } } } \ No newline at end of file