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 }