45 lines
1.6 KiB
Go
45 lines
1.6 KiB
Go
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
|
|
}
|