package world import ( "testing" "github.com/stretchr/testify/require" ) func TestWorldDeltaFixedToCanvasPx_RemainderAccumulatesPositive(t *testing.T) { t.Parallel() // zoom=1: px = (deltaWorldFp * 1000) / 1e6 // For deltaWorldFp=1, each step contributes 0 px with remainder, // and after 1000 steps it must become 1 px total. zoomFp := SCALE var rem int64 sum := 0 for i := 0; i < 1000; i++ { sum += worldDeltaFixedToCanvasPx(1, zoomFp, &rem) } require.Equal(t, 1, sum) } func TestWorldDeltaFixedToCanvasPx_RemainderAccumulatesNegative(t *testing.T) { t.Parallel() zoomFp := SCALE var rem int64 sum := 0 for i := 0; i < 1000; i++ { sum += worldDeltaFixedToCanvasPx(-1, zoomFp, &rem) } require.Equal(t, -1, sum) } func TestComputePanShiftPx_FirstCallRequiresFullRedraw(t *testing.T) { t.Parallel() w := NewWorld(10, 10) params := RenderParams{ ViewportWidthPx: 100, ViewportHeightPx: 80, MarginXPx: 25, MarginYPx: 20, CameraXWorldFp: 5 * SCALE, CameraYWorldFp: 5 * SCALE, CameraZoom: 1.0, } _, _, err := w.ComputePanShiftPx(params) require.ErrorIs(t, err, errIncrementalStateNotReady) } func TestComputePanShiftPx_ZoomOrViewportChangeForcesFullRedraw(t *testing.T) { t.Parallel() w := NewWorld(10, 10) base := RenderParams{ ViewportWidthPx: 100, ViewportHeightPx: 80, MarginXPx: 25, MarginYPx: 20, CameraXWorldFp: 5 * SCALE, CameraYWorldFp: 5 * SCALE, CameraZoom: 1.0, } require.NoError(t, w.CommitFullRedrawState(base)) changed := base changed.CameraZoom = 2.0 _, _, err := w.ComputePanShiftPx(changed) require.ErrorIs(t, err, errIncrementalZoomMismatch) changed2 := base changed2.ViewportWidthPx = 101 _, _, err = w.ComputePanShiftPx(changed2) require.ErrorIs(t, err, errIncrementalZoomMismatch) } func TestComputePanShiftPx_PanRightShiftsImageLeft(t *testing.T) { t.Parallel() w := NewWorld(10, 10) params := RenderParams{ ViewportWidthPx: 100, ViewportHeightPx: 80, MarginXPx: 25, MarginYPx: 20, CameraXWorldFp: 5 * SCALE, CameraYWorldFp: 5 * SCALE, CameraZoom: 1.0, } require.NoError(t, w.CommitFullRedrawState(params)) // Move camera right by 1 world unit => world rect minX increases by 1 unit, // so content moves left by 1px at zoom=1 => image shift should be -1. params2 := params params2.CameraXWorldFp += 1 * SCALE dx, dy, err := w.ComputePanShiftPx(params2) require.NoError(t, err) require.Equal(t, -1, dx) require.Equal(t, 0, dy) } func TestComputePanShiftPx_PanUpShiftsImageDown(t *testing.T) { t.Parallel() w := NewWorld(10, 10) params := RenderParams{ ViewportWidthPx: 100, ViewportHeightPx: 80, MarginXPx: 25, MarginYPx: 20, CameraXWorldFp: 5 * SCALE, CameraYWorldFp: 5 * SCALE, CameraZoom: 1.0, } require.NoError(t, w.CommitFullRedrawState(params)) // Move camera up by 1 world unit => world rect minY decreases by 1 unit, // so content moves down by 1px => image shift should be +1 in dy. params2 := params params2.CameraYWorldFp -= 1 * SCALE dx, dy, err := w.ComputePanShiftPx(params2) require.NoError(t, err) require.Equal(t, 0, dx) require.Equal(t, 1, dy) } func TestComputePanShiftPx_SubPixelPanAccumulatesToOnePixel(t *testing.T) { t.Parallel() w := NewWorld(10, 10) params := RenderParams{ ViewportWidthPx: 100, ViewportHeightPx: 80, MarginXPx: 25, MarginYPx: 20, CameraXWorldFp: 5 * SCALE, CameraYWorldFp: 5 * SCALE, CameraZoom: 1.0, } require.NoError(t, w.CommitFullRedrawState(params)) // Pan camera right by 0.001 world units (1 fixed-point) 1000 times. // At zoom=1 this should accumulate to a 1px content shift left, hence image shift -1. totalDx := 0 p := params for i := 0; i < 1000; i++ { p.CameraXWorldFp += 1 dx, dy, err := w.ComputePanShiftPx(p) require.NoError(t, err) require.Equal(t, 0, dy) totalDx += dx } require.Equal(t, -1, totalDx) }