no-wrap option; pivoted exponential zoom

This commit is contained in:
Ilia Denisov
2026-03-07 11:35:18 +02:00
committed by GitHub
parent 1de621c743
commit 477e656008
22 changed files with 605 additions and 81 deletions
+44
View File
@@ -0,0 +1,44 @@
package world
// PivotZoomCameraNoWrap adjusts camera center so that the world point under the cursor remains fixed
// when zoom changes from oldZoomFp to newZoomFp.
//
// Coordinate conventions:
// - CameraXWorldFp/YWorldFp is the center of the viewport in world-fixed units.
// - cursorXPx/cursorYPx are pixel coordinates relative to the top-left of the viewport.
// - viewportW/H are viewport size in pixels.
//
// This function does not clamp the result; caller should clamp for no-wrap mode using ClampCameraNoWrapViewport.
func PivotZoomCameraNoWrap(
cameraXWorldFp, cameraYWorldFp int,
viewportW, viewportH int,
cursorXPx, cursorYPx int,
oldZoomFp, newZoomFp int,
) (newCamX, newCamY int) {
if oldZoomFp <= 0 || newZoomFp <= 0 {
panic("PivotZoomCameraNoWrap: invalid zoom")
}
if viewportW <= 0 || viewportH <= 0 {
panic("PivotZoomCameraNoWrap: invalid viewport")
}
// Offset of cursor from viewport center in pixels.
offXPx := cursorXPx - viewportW/2
offYPx := cursorYPx - viewportH/2
// World-fixed per 1 pixel at each zoom.
// (Conservative: integer arithmetic, consistent with PixelSpanToWorldFixed.)
oldWorldPerPx := PixelSpanToWorldFixed(1, oldZoomFp)
newWorldPerPx := PixelSpanToWorldFixed(1, newZoomFp)
// World point under cursor before zoom:
// world = camera + offsetPx * worldPerPx
worldX := cameraXWorldFp + offXPx*oldWorldPerPx
worldY := cameraYWorldFp + offYPx*oldWorldPerPx
// Choose new camera so that the same world point stays under cursor:
// camera' = world - offsetPx * newWorldPerPx
newCamX = worldX - offXPx*newWorldPerPx
newCamY = worldY - offYPx*newWorldPerPx
return
}