mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-24 03:53:12 -04:00
Map pinching, revised (#12776)
* Improved precision of map pinching, was inaccurate * Consolidated zoom() callback with pinch() to simplify. Added comments on the math. --------- Co-authored-by: M. Rittweger <m.rittweger@mvolution.de>
This commit is contained in:
parent
a5e6622414
commit
08d59dfc2d
@ -7,12 +7,22 @@ import com.badlogic.gdx.scenes.scene2d.EventListener
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent
|
||||
|
||||
open class ZoomGestureListener(
|
||||
halfTapSquareSize: Float, tapCountInterval: Float, longPressDuration: Float, maxFlingDelay: Float
|
||||
halfTapSquareSize: Float,
|
||||
tapCountInterval: Float,
|
||||
longPressDuration: Float,
|
||||
maxFlingDelay: Float,
|
||||
stageSize: () -> Vector2
|
||||
) : EventListener {
|
||||
|
||||
private val detector: GestureDetector
|
||||
|
||||
constructor() : this(20f, 0.4f, 1.1f, Int.MAX_VALUE.toFloat())
|
||||
constructor(stageSize: () -> Vector2) : this(
|
||||
20f,
|
||||
0.4f,
|
||||
1.1f,
|
||||
Int.MAX_VALUE.toFloat(),
|
||||
stageSize
|
||||
)
|
||||
|
||||
init {
|
||||
detector = GestureDetector(
|
||||
@ -22,8 +32,10 @@ open class ZoomGestureListener(
|
||||
maxFlingDelay,
|
||||
object : GestureDetector.GestureAdapter() {
|
||||
|
||||
private var pinchCenter: Vector2? = null
|
||||
private var initialDistance = 0f
|
||||
// focal point, the center of the two pointers where zooming should be directed to
|
||||
private var lastFocus: Vector2? = null
|
||||
// distance between the two pointers performing a pinch
|
||||
private var lastDistance = 0f
|
||||
|
||||
override fun pinch(
|
||||
stageInitialPointer1: Vector2,
|
||||
@ -31,20 +43,44 @@ open class ZoomGestureListener(
|
||||
stagePointer1: Vector2,
|
||||
stagePointer2: Vector2
|
||||
): Boolean {
|
||||
if (pinchCenter == null) {
|
||||
pinchCenter = stageInitialPointer1.cpy().add(stageInitialPointer2).scl(.5f)
|
||||
initialDistance = stageInitialPointer1.dst(stageInitialPointer2)
|
||||
|
||||
if (lastFocus == null) {
|
||||
lastFocus = stageInitialPointer1.cpy().add(stageInitialPointer2).scl(.5f)
|
||||
lastDistance = stageInitialPointer1.dst(stageInitialPointer2)
|
||||
}
|
||||
val currentCenter = stagePointer1.cpy().add(stagePointer2).scl(.5f)
|
||||
val delta = currentCenter.cpy().sub(pinchCenter)
|
||||
pinchCenter = currentCenter.cpy()
|
||||
this@ZoomGestureListener.pinch(delta)
|
||||
this@ZoomGestureListener.zoom(initialDistance, stagePointer1.dst(stagePointer2))
|
||||
|
||||
// the current focal point is the center of the two pointers
|
||||
val currentFocus = stagePointer1.cpy().add(stagePointer2).scl(.5f)
|
||||
|
||||
// translation caused by moving the focal point
|
||||
val translation = currentFocus.cpy().sub(lastFocus)
|
||||
lastFocus = currentFocus.cpy()
|
||||
|
||||
// scale change caused by changing distance of the two pointers
|
||||
val currentDistance = stagePointer1.dst(stagePointer2)
|
||||
val scaleChange = currentDistance / lastDistance
|
||||
lastDistance = currentDistance
|
||||
|
||||
// Calculate the translation (dx, dy) needed to direct the zoom towards to
|
||||
// current focal point. Without this correction, the zoom would be directed
|
||||
// towards the center of the stage.
|
||||
// - First we calculate the distance from the stage center to the focal point.
|
||||
// - We then calculate how much the scaling affects that distance by multiplying
|
||||
// with scaleChange - 1.
|
||||
val dx = (stageSize().x / 2 - currentFocus.x) * (scaleChange - 1)
|
||||
val dy = (stageSize().y / 2 - currentFocus.y) * (scaleChange - 1)
|
||||
|
||||
// Add the translation caused by changing the scale (dx, dy) to the translation
|
||||
// caused by changing the position of the focal point.
|
||||
translation.add(dx, dy)
|
||||
|
||||
this@ZoomGestureListener.pinch(translation, scaleChange)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun pinchStop() {
|
||||
pinchCenter = null
|
||||
lastFocus = null
|
||||
this@ZoomGestureListener.pinchStop()
|
||||
}
|
||||
})
|
||||
@ -83,7 +119,7 @@ open class ZoomGestureListener(
|
||||
}
|
||||
|
||||
open fun scrolled(amountX: Float, amountY: Float): Boolean { return false }
|
||||
open fun zoom(initialDistance: Float, distance: Float) {}
|
||||
open fun pinch(delta: Vector2) {}
|
||||
/** [translation] in stage coordinates */
|
||||
open fun pinch(translation: Vector2, scaleChange: Float) {}
|
||||
open fun pinchStop() {}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import com.unciv.UncivGame
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.ui.components.ZoomGestureListener
|
||||
import com.unciv.ui.components.input.KeyboardPanningListener
|
||||
import kotlin.math.sqrt
|
||||
|
||||
|
||||
open class ZoomableScrollPane(
|
||||
@ -174,7 +173,7 @@ open class ZoomableScrollPane(
|
||||
return zoomListener.isZooming
|
||||
}
|
||||
|
||||
inner class ZoomListener : ZoomGestureListener() {
|
||||
inner class ZoomListener : ZoomGestureListener({ Vector2(stage.width, stage.height) }) {
|
||||
|
||||
inner class ZoomAction : TemporalAction() {
|
||||
|
||||
@ -204,8 +203,6 @@ open class ZoomableScrollPane(
|
||||
}
|
||||
|
||||
private var zoomAction: ZoomAction? = null
|
||||
private var lastInitialDistance = 0f
|
||||
var lastScale = 1f
|
||||
var isZooming = false
|
||||
|
||||
fun zoomOut(zoomMultiplier: Float = 0.82f) {
|
||||
@ -246,16 +243,17 @@ open class ZoomableScrollPane(
|
||||
}
|
||||
}
|
||||
|
||||
override fun pinch(delta: Vector2) {
|
||||
override fun pinch(translation: Vector2, scaleChange: Float) {
|
||||
if (!isZooming) {
|
||||
isZooming = true
|
||||
onZoomStartListener?.invoke()
|
||||
}
|
||||
scrollTo(
|
||||
scrollX - delta.x,
|
||||
scrollY + delta.y,
|
||||
scrollX - translation.x / scaleX,
|
||||
scrollY + translation.y / scaleY,
|
||||
true
|
||||
)
|
||||
zoom(scaleX * scaleChange)
|
||||
}
|
||||
|
||||
override fun pinchStop() {
|
||||
@ -263,15 +261,6 @@ open class ZoomableScrollPane(
|
||||
onZoomStopListener?.invoke()
|
||||
}
|
||||
|
||||
override fun zoom(initialDistance: Float, distance: Float) {
|
||||
if (lastInitialDistance != initialDistance) {
|
||||
lastInitialDistance = initialDistance
|
||||
lastScale = scaleX
|
||||
}
|
||||
val scale: Float = sqrt((distance / initialDistance).toDouble()).toFloat() * lastScale
|
||||
zoom(scale)
|
||||
}
|
||||
|
||||
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
||||
if (amountX > 0 || amountY > 0)
|
||||
zoomOut()
|
||||
|
Loading…
x
Reference in New Issue
Block a user