package world import ( "github.com/stretchr/testify/require" "math" "testing" ) // TestWrap verifies wrap. func TestWrap(t *testing.T) { t.Parallel() tests := []struct { name string value int size int want int }{ {name: "zero", value: 0, size: 10, want: 0}, {name: "inside range", value: 7, size: 10, want: 7}, {name: "equal to size", value: 10, size: 10, want: 0}, {name: "greater than size", value: 27, size: 10, want: 7}, {name: "negative one", value: -1, size: 10, want: 9}, {name: "negative many", value: -23, size: 10, want: 7}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := wrap(tt.value, tt.size); got != tt.want { t.Fatalf("wrap(%d, %d) = %d, want %d", tt.value, tt.size, got, tt.want) } }) } } // TestClamp verifies clamp. func TestClamp(t *testing.T) { t.Parallel() tests := []struct { name string value int minValue int maxValue int want int }{ {name: "below range", value: -5, minValue: 0, maxValue: 10, want: 0}, {name: "inside range", value: 5, minValue: 0, maxValue: 10, want: 5}, {name: "above range", value: 15, minValue: 0, maxValue: 10, want: 10}, {name: "equal min", value: 0, minValue: 0, maxValue: 10, want: 0}, {name: "equal max", value: 10, minValue: 0, maxValue: 10, want: 10}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := clamp(tt.value, tt.minValue, tt.maxValue); got != tt.want { t.Fatalf("clamp(%d, %d, %d) = %d, want %d", tt.value, tt.minValue, tt.maxValue, got, tt.want) } }) } } // TestCeilDiv verifies ceil Div. func TestCeilDiv(t *testing.T) { t.Parallel() tests := []struct { a int b int want int }{ {a: 1, b: 1, want: 1}, {a: 1, b: 2, want: 1}, {a: 2, b: 2, want: 1}, {a: 3, b: 2, want: 2}, {a: 10, b: 3, want: 4}, {a: 16, b: 8, want: 2}, {a: 17, b: 8, want: 3}, } for _, tt := range tests { if got := ceilDiv(tt.a, tt.b); got != tt.want { t.Fatalf("ceilDiv(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want) } } } // TestFloorDiv verifies floor Div. func TestFloorDiv(t *testing.T) { t.Parallel() require.Equal(t, 0, floorDiv(0, 10)) require.Equal(t, 0, floorDiv(1, 10)) require.Equal(t, 0, floorDiv(9, 10)) require.Equal(t, 1, floorDiv(10, 10)) require.Equal(t, 1, floorDiv(19, 10)) require.Equal(t, -1, floorDiv(-1, 10)) require.Equal(t, -1, floorDiv(-9, 10)) require.Equal(t, -1, floorDiv(-10, 10)) require.Equal(t, -2, floorDiv(-11, 10)) require.Panics(t, func() { _ = floorDiv(1, 0) }) require.Panics(t, func() { _ = floorDiv(1, -1) }) } // TestFixedPoint verifies fixed Point. func TestFixedPoint(t *testing.T) { t.Parallel() tests := []struct { name string v float64 want int }{ {name: "zero", v: 0, want: 0}, {name: "integer", v: 3, want: 3000}, {name: "fraction", v: 1.234, want: 1234}, {name: "round down", v: 1.2344, want: 1234}, {name: "round up", v: 1.2345, want: 1235}, {name: "negative", v: -1.2345, want: -1235}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := fixedPoint(tt.v); got != tt.want { t.Fatalf("fixedPoint(%f) = %d, want %d", tt.v, got, tt.want) } }) } } // TestAbs verifies abs. func TestAbs(t *testing.T) { t.Parallel() tests := []struct { v int want int }{ {v: 0, want: 0}, {v: 7, want: 7}, {v: -7, want: 7}, } for _, tt := range tests { if got := abs(tt.v); got != tt.want { t.Fatalf("abs(%d) = %d, want %d", tt.v, got, tt.want) } } } // TestPixelSpanToWorldFixed verifies pixel Span To World Fixed. func TestPixelSpanToWorldFixed(t *testing.T) { t.Parallel() tests := []struct { name string spanPx int zoomFp int want int }{ {name: "1x zoom", spanPx: 100, zoomFp: SCALE, want: 100 * SCALE}, {name: "2x zoom", spanPx: 100, zoomFp: 2 * SCALE, want: 50 * SCALE}, {name: "half zoom", spanPx: 100, zoomFp: SCALE / 2, want: 200 * SCALE}, {name: "fractional result truncation", spanPx: 1, zoomFp: 3 * SCALE, want: 333}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := PixelSpanToWorldFixed(tt.spanPx, tt.zoomFp); got != tt.want { t.Fatalf("PixelSpanToWorldFixed(%d, %d) = %d, want %d", tt.spanPx, tt.zoomFp, got, tt.want) } }) } } // TestWorldToCellPanicsOnInvalidGrid verifies world To Cell Panics On Invalid Grid. func TestWorldToCellPanicsOnInvalidGrid(t *testing.T) { t.Parallel() tests := []struct { name string cells int cellSize int }{ {name: "zero cells", cells: 0, cellSize: 1}, {name: "negative cells", cells: -1, cellSize: 1}, {name: "zero cell size", cells: 1, cellSize: 0}, {name: "negative cell size", cells: 1, cellSize: -1}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() defer func() { if recover() == nil { t.Fatalf("worldToCell did not panic for cells=%d cellSize=%d", tt.cells, tt.cellSize) } }() _ = worldToCell(0, 1000, tt.cells, tt.cellSize) }) } } // TestShortestWrappedDelta verifies shortest Wrapped Delta. func TestShortestWrappedDelta(t *testing.T) { t.Parallel() tests := []struct { name string from int to int size int wantA int wantB int }{ {name: "no wrap forward", from: 1000, to: 3000, size: 10000, wantA: 1000, wantB: 3000}, {name: "no wrap backward", from: 3000, to: 1000, size: 10000, wantA: 3000, wantB: 1000}, {name: "wrap forward over half", from: 1000, to: 7000, size: 10000, wantA: 11000, wantB: 7000}, {name: "wrap backward over half", from: 7000, to: 1000, size: 10000, wantA: 7000, wantB: 11000}, {name: "tie positive half wraps", from: 1000, to: 6000, size: 10000, wantA: 11000, wantB: 6000}, {name: "tie negative half stays", from: 6000, to: 1000, size: 10000, wantA: 6000, wantB: 1000}, {name: "just below positive half does not wrap", from: 1000, to: 5999, size: 10000, wantA: 1000, wantB: 5999}, {name: "just beyond negative half wraps", from: 6001, to: 1000, size: 10000, wantA: 6001, wantB: 11000}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() gotA, gotB := shortestWrappedDelta(tt.from, tt.to, tt.size) if gotA != tt.wantA || gotB != tt.wantB { t.Fatalf("shortestWrappedDelta(%d, %d, %d) = (%d, %d), want (%d, %d)", tt.from, tt.to, tt.size, gotA, gotB, tt.wantA, tt.wantB) } delta := gotB - gotA half := tt.size / 2 if delta < -half || delta >= half { t.Fatalf("normalized delta %d is outside [-%d, %d)", delta, half, half) } }) } } // TestCameraZoomToWorldFixed verifies camera Zoom To World Fixed. func TestCameraZoomToWorldFixed(t *testing.T) { t.Parallel() tests := []struct { name string cameraZoom float64 want int }{ { name: "neutral zoom", cameraZoom: 1.0, want: SCALE, }, { name: "integer zoom", cameraZoom: 2.0, want: 2 * SCALE, }, { name: "fractional zoom", cameraZoom: 1.25, want: 1250, }, { name: "minimum configured zoom value shape", cameraZoom: 0.25, want: SCALE / 4, }, { name: "round down", cameraZoom: 1.2344, want: 1234, }, { name: "round up", cameraZoom: 1.2345, want: 1235, }, { name: "very small but still positive after rounding", cameraZoom: 0.0006, want: 1, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() got, err := CameraZoomToWorldFixed(tt.cameraZoom) require.NoError(t, err) require.Equal(t, tt.want, got) }) } } // TestCameraZoomToWorldFixedReturnsError verifies camera Zoom To World Fixed Returns Error. func TestCameraZoomToWorldFixedReturnsError(t *testing.T) { t.Parallel() tests := []struct { name string cameraZoom float64 }{ { name: "zero", cameraZoom: 0, }, { name: "negative", cameraZoom: -1, }, { name: "nan", cameraZoom: math.NaN(), }, { name: "positive infinity", cameraZoom: math.Inf(1), }, { name: "negative infinity", cameraZoom: math.Inf(-1), }, { name: "positive but rounds to zero", cameraZoom: 0.0004, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() got, err := CameraZoomToWorldFixed(tt.cameraZoom) require.ErrorIs(t, err, errInvalidCameraZoom) require.Zero(t, got) }) } } // TestMustCameraZoomToWorldFixed verifies must Camera Zoom To World Fixed. func TestMustCameraZoomToWorldFixed(t *testing.T) { t.Parallel() require.Equal(t, 1250, mustCameraZoomToWorldFixed(1.25)) require.Panics(t, func() { _ = mustCameraZoomToWorldFixed(0) }) } // TestWorldFixedToCameraZoom verifies world Fixed To Camera Zoom. func TestWorldFixedToCameraZoom(t *testing.T) { t.Parallel() tests := []struct { name string zoomFp int want float64 }{ {name: "zero", zoomFp: 0, want: 0}, {name: "neutral", zoomFp: SCALE, want: 1.0}, {name: "fractional", zoomFp: 1250, want: 1.25}, {name: "integer multiple", zoomFp: 3 * SCALE, want: 3.0}, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() got := worldFixedToCameraZoom(tt.zoomFp) require.Equal(t, tt.want, got) }) } } // TestRequiredZoomToFitWorld verifies required Zoom To Fit World. func TestRequiredZoomToFitWorld(t *testing.T) { t.Parallel() tests := []struct { name string viewportSpanPx int worldSpanFp int want int }{ { name: "zero viewport span", viewportSpanPx: 0, worldSpanFp: 10 * SCALE, want: 0, }, { name: "exact neutral fit", viewportSpanPx: 10, worldSpanFp: 10 * SCALE, want: SCALE, }, { name: "exact 2x fit", viewportSpanPx: 20, worldSpanFp: 10 * SCALE, want: 2 * SCALE, }, { name: "fractional fit rounded up", viewportSpanPx: 11, worldSpanFp: 10 * SCALE, want: 1100, }, { name: "small world requires larger zoom", viewportSpanPx: 320, worldSpanFp: 80 * SCALE, want: 4 * SCALE, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() got := requiredZoomToFitWorld(tt.viewportSpanPx, tt.worldSpanFp) require.Equal(t, tt.want, got) }) } } // TestRequiredZoomToFitWorldPanics verifies required Zoom To Fit World Panics. func TestRequiredZoomToFitWorldPanics(t *testing.T) { t.Parallel() tests := []struct { name string viewportSpanPx int worldSpanFp int }{ { name: "negative viewport span", viewportSpanPx: -1, worldSpanFp: 10 * SCALE, }, { name: "zero world span", viewportSpanPx: 10, worldSpanFp: 0, }, { name: "negative world span", viewportSpanPx: 10, worldSpanFp: -1, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() require.Panics(t, func() { _ = requiredZoomToFitWorld(tt.viewportSpanPx, tt.worldSpanFp) }) }) } } // TestCorrectCameraZoomFpReturnsCurrentWhenNoCorrectionNeeded verifies correct Camera Zoom Fp Returns Current When No Correction Needed. func TestCorrectCameraZoomFpReturnsCurrentWhenNoCorrectionNeeded(t *testing.T) { t.Parallel() got := correctCameraZoomFp( 2*SCALE, 40, 30, 100*SCALE, 100*SCALE, MIN_ZOOM, MAX_ZOOM, ) require.Equal(t, 2*SCALE, got) } // TestCorrectCameraZoomFpRaisesZoomToFitWorldWidth verifies correct Camera Zoom Fp Raises Zoom To Fit World Width. func TestCorrectCameraZoomFpRaisesZoomToFitWorldWidth(t *testing.T) { t.Parallel() got := correctCameraZoomFp( SCALE, 120, 20, 100*SCALE, 100*SCALE, 0, 0, ) require.Equal(t, 1200, got) } // TestCorrectCameraZoomFpRaisesZoomToFitWorldHeight verifies correct Camera Zoom Fp Raises Zoom To Fit World Height. func TestCorrectCameraZoomFpRaisesZoomToFitWorldHeight(t *testing.T) { t.Parallel() got := correctCameraZoomFp( SCALE, 20, 150, 100*SCALE, 100*SCALE, 0, 0, ) require.Equal(t, 1500, got) } // TestCorrectCameraZoomFpUsesMaxFitAcrossAxes verifies correct Camera Zoom Fp Uses Max Fit Across Axes. func TestCorrectCameraZoomFpUsesMaxFitAcrossAxes(t *testing.T) { t.Parallel() got := correctCameraZoomFp( SCALE, 120, 150, 100*SCALE, 100*SCALE, 0, 0, ) require.Equal(t, 1500, got) } // TestCorrectCameraZoomFpAppliesMinZoomWhenLargerThanCurrentAndFit verifies correct Camera Zoom Fp Applies Min Zoom When Larger Than Current And Fit. func TestCorrectCameraZoomFpAppliesMinZoomWhenLargerThanCurrentAndFit(t *testing.T) { t.Parallel() got := correctCameraZoomFp( SCALE, 20, 20, 100*SCALE, 100*SCALE, 1500, 0, ) require.Equal(t, 1500, got) } // TestCorrectCameraZoomFpAppliesMaxZoomWhenNoFitConflict verifies correct Camera Zoom Fp Applies Max Zoom When No Fit Conflict. func TestCorrectCameraZoomFpAppliesMaxZoomWhenNoFitConflict(t *testing.T) { t.Parallel() got := correctCameraZoomFp( 4*SCALE, 20, 20, 100*SCALE, 100*SCALE, 0, 3*SCALE, ) require.Equal(t, 3*SCALE, got) } // TestCorrectCameraZoomFpIgnoresMaxZoomWhenFitNeedsMore verifies correct Camera Zoom Fp Ignores Max Zoom When Fit Needs More. func TestCorrectCameraZoomFpIgnoresMaxZoomWhenFitNeedsMore(t *testing.T) { t.Parallel() got := correctCameraZoomFp( SCALE, 200, 20, 100*SCALE, 100*SCALE, 0, 1500, ) require.Equal(t, 2*SCALE, got) } // TestCorrectCameraZoomFpAppliesMinThenMaxWhenBothValid verifies correct Camera Zoom Fp Applies Min Then Max When Both Valid. func TestCorrectCameraZoomFpAppliesMinThenMaxWhenBothValid(t *testing.T) { t.Parallel() got := correctCameraZoomFp( SCALE, 20, 20, 100*SCALE, 100*SCALE, 1500, 1600, ) require.Equal(t, 1500, got) } // TestCorrectCameraZoomFpCurrentAboveMaxGetsClamped verifies correct Camera Zoom Fp Current Above Max Gets Clamped. func TestCorrectCameraZoomFpCurrentAboveMaxGetsClamped(t *testing.T) { t.Parallel() got := correctCameraZoomFp( 5*SCALE, 20, 20, 100*SCALE, 100*SCALE, 0, 3*SCALE, ) require.Equal(t, 3*SCALE, got) } // TestCorrectCameraZoomFpZeroViewportUsesOnlyBounds verifies correct Camera Zoom Fp Zero Viewport Uses Only Bounds. func TestCorrectCameraZoomFpZeroViewportUsesOnlyBounds(t *testing.T) { t.Parallel() got := correctCameraZoomFp( SCALE, 0, 0, 100*SCALE, 100*SCALE, 1500, 0, ) require.Equal(t, 1500, got) } // TestCorrectCameraZoomFpZeroBoundsAreIgnored verifies correct Camera Zoom Fp Zero Bounds Are Ignored. func TestCorrectCameraZoomFpZeroBoundsAreIgnored(t *testing.T) { t.Parallel() got := correctCameraZoomFp( 1250, 20, 20, 100*SCALE, 100*SCALE, 0, 0, ) require.Equal(t, 1250, got) } // TestCorrectCameraZoomFpPanics verifies correct Camera Zoom Fp Panics. func TestCorrectCameraZoomFpPanics(t *testing.T) { t.Parallel() tests := []struct { name string fn func() }{ { name: "non-positive current zoom", fn: func() { _ = correctCameraZoomFp(0, 10, 10, 100*SCALE, 100*SCALE, 0, 0) }, }, { name: "negative viewport width", fn: func() { _ = correctCameraZoomFp(SCALE, -1, 10, 100*SCALE, 100*SCALE, 0, 0) }, }, { name: "negative viewport height", fn: func() { _ = correctCameraZoomFp(SCALE, 10, -1, 100*SCALE, 100*SCALE, 0, 0) }, }, { name: "non-positive world width", fn: func() { _ = correctCameraZoomFp(SCALE, 10, 10, 0, 100*SCALE, 0, 0) }, }, { name: "non-positive world height", fn: func() { _ = correctCameraZoomFp(SCALE, 10, 10, 100*SCALE, 0, 0, 0) }, }, { name: "negative min zoom", fn: func() { _ = correctCameraZoomFp(SCALE, 10, 10, 100*SCALE, 100*SCALE, -1, 0) }, }, { name: "negative max zoom", fn: func() { _ = correctCameraZoomFp(SCALE, 10, 10, 100*SCALE, 100*SCALE, 0, -1) }, }, { name: "min greater than max", fn: func() { _ = correctCameraZoomFp(SCALE, 10, 10, 100*SCALE, 100*SCALE, 2000, 1500) }, }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() require.Panics(t, tt.fn) }) } } // TestWorldCorrectCameraZoomReturnsFloatValue verifies world Correct Camera Zoom Returns Float Value. func TestWorldCorrectCameraZoomReturnsFloatValue(t *testing.T) { t.Parallel() w := NewWorld(100, 100) got := w.CorrectCameraZoom(1.0, 120, 20) require.Equal(t, 1.2, got) } // TestWorldCorrectCameraZoomAppliesDefaultBounds verifies world Correct Camera Zoom Applies Default Bounds. func TestWorldCorrectCameraZoomAppliesDefaultBounds(t *testing.T) { t.Parallel() w := NewWorld(100, 100) got := w.CorrectCameraZoom(100.0, 20, 20) require.Equal(t, worldFixedToCameraZoom(MAX_ZOOM), got) } // TestWorldCorrectCameraZoomFitBeatsDefaultMaxBound verifies world Correct Camera Zoom Fit Beats Default Max Bound. func TestWorldCorrectCameraZoomFitBeatsDefaultMaxBound(t *testing.T) { t.Parallel() w := NewWorld(1, 100) got := w.CorrectCameraZoom(1.0, 40, 10) require.Equal(t, 40.0, got) } // TestCorrectCameraZoomFp_DoesNotLowerZoomWhenViewportIsSmallerThanWorld verifies correct Camera Zoom Fp Does Not Lower Zoom When Viewport Is Smaller Than World. func TestCorrectCameraZoomFp_DoesNotLowerZoomWhenViewportIsSmallerThanWorld(t *testing.T) { t.Parallel() got := correctCameraZoomFp( SCALE, // currentZoomFp = 1.0x 80, 80, // viewport px 100*SCALE, 100*SCALE, // world fp 0, 0, ) // No anti-wrap needed, and we do not auto-fit by lowering zoom. require.Equal(t, SCALE, got) } // TestCorrectCameraZoomFp_RaisesZoomToPreventWrapWhenViewportIsLarger verifies correct Camera Zoom Fp Raises Zoom To Prevent Wrap When Viewport Is Larger. func TestCorrectCameraZoomFp_RaisesZoomToPreventWrapWhenViewportIsLarger(t *testing.T) { t.Parallel() // World width = 100 units, viewport width = 120 px, at zoom=1 visible span = 120 units => too large. got := correctCameraZoomFp( SCALE, 120, 20, 100*SCALE, 100*SCALE, 0, 0, ) require.Equal(t, 1200, got) // 1.2x } // TestCorrectCameraZoomFp_AppliesMaxZoomWhenNoWrapConflict verifies correct Camera Zoom Fp Applies Max Zoom When No Wrap Conflict. func TestCorrectCameraZoomFp_AppliesMaxZoomWhenNoWrapConflict(t *testing.T) { t.Parallel() got := correctCameraZoomFp( 4*SCALE, // user wants 4x 20, 20, 100*SCALE, 100*SCALE, 0, 3*SCALE, // max 3x ) require.Equal(t, 3*SCALE, got) } // TestCorrectCameraZoomFp_AntiWrapBeatsMaxZoom verifies correct Camera Zoom Fp Anti Wrap Beats Max Zoom. func TestCorrectCameraZoomFp_AntiWrapBeatsMaxZoom(t *testing.T) { t.Parallel() // requiredFit = 2x, but max is 1.5x => must return 2x. got := correctCameraZoomFp( SCALE, 200, 20, 100*SCALE, 100*SCALE, 0, 1500, ) require.Equal(t, 2*SCALE, got) } // TestCorrectCameraZoomFp_AppliesMinZoom verifies correct Camera Zoom Fp Applies Min Zoom. func TestCorrectCameraZoomFp_AppliesMinZoom(t *testing.T) { t.Parallel() got := correctCameraZoomFp( 800, // 0.8x 20, 20, 100*SCALE, 100*SCALE, SCALE, 0, // min 1.0x ) require.Equal(t, SCALE, got) } // TestCorrectCameraZoomFp_ZeroViewportUsesOnlyBounds verifies correct Camera Zoom Fp Zero Viewport Uses Only Bounds. func TestCorrectCameraZoomFp_ZeroViewportUsesOnlyBounds(t *testing.T) { t.Parallel() got := correctCameraZoomFp( SCALE, 0, 0, 100*SCALE, 100*SCALE, 1500, 0, ) require.Equal(t, 1500, got) } // TestClampCameraNoWrapViewport_ClampsToKeepViewportInsideWorld verifies clamp Camera No Wrap Viewport Clamps To Keep Viewport Inside World. func TestClampCameraNoWrapViewport_ClampsToKeepViewportInsideWorld(t *testing.T) { t.Parallel() worldW := 100 * SCALE worldH := 100 * SCALE zoomFp := SCALE // 1.0x // viewport 40px => span 40 units => half 20. viewportW, viewportH := 40, 40 // Too far left/up => clamp to minCam=20 cx, cy := ClampCameraNoWrapViewport( 0, 0, viewportW, viewportH, zoomFp, worldW, worldH, ) require.Equal(t, 20*SCALE, cx) require.Equal(t, 20*SCALE, cy) // Too far right/down => clamp to maxCam=world-half=80 cx, cy = ClampCameraNoWrapViewport( 99*SCALE, 99*SCALE, viewportW, viewportH, zoomFp, worldW, worldH, ) require.Equal(t, 80*SCALE, cx) require.Equal(t, 80*SCALE, cy) // Inside range => unchanged cx, cy = ClampCameraNoWrapViewport( 50*SCALE, 60*SCALE, viewportW, viewportH, zoomFp, worldW, worldH, ) require.Equal(t, 50*SCALE, cx) require.Equal(t, 60*SCALE, cy) } // TestClampCameraNoWrapViewport_WhenViewportLargerThanWorld_ForcesCenter verifies clamp Camera No Wrap Viewport When Viewport Larger Than World Forces Center. func TestClampCameraNoWrapViewport_WhenViewportLargerThanWorld_ForcesCenter(t *testing.T) { t.Parallel() worldW := 50 * SCALE worldH := 50 * SCALE zoomFp := SCALE // viewport 60px => span 60 units > world 50 viewportW, viewportH := 60, 60 cx, cy := ClampCameraNoWrapViewport( 0, 0, viewportW, viewportH, zoomFp, worldW, worldH, ) require.Equal(t, worldW/2, cx) require.Equal(t, worldH/2, cy) } // TestWorldClampRenderParamsNoWrap_UsesViewportClamp verifies world Clamp Render Params No Wrap Uses Viewport Clamp. func TestWorldClampRenderParamsNoWrap_UsesViewportClamp(t *testing.T) { t.Parallel() w := NewWorld(100, 100) p := RenderParams{ ViewportWidthPx: 40, ViewportHeightPx: 40, MarginXPx: 10, MarginYPx: 10, CameraZoom: 1.0, CameraXWorldFp: 0, CameraYWorldFp: 0, Options: &RenderOptions{DisableWrapScroll: true}, } w.ClampRenderParamsNoWrap(&p) // viewport half is 20, not 30 (margins ignored) require.Equal(t, 20*SCALE, p.CameraXWorldFp) require.Equal(t, 20*SCALE, p.CameraYWorldFp) } // TestPivotZoom_CursorAtCenter_KeepsCamera verifies pivot Zoom Cursor At Center Keeps Camera. func TestPivotZoom_CursorAtCenter_KeepsCamera(t *testing.T) { t.Parallel() cx, cy := PivotZoomCameraNoWrap( 50*SCALE, 60*SCALE, 100, 80, 50, 40, // cursor at center SCALE, 2*SCALE, ) require.Equal(t, 50*SCALE, cx) require.Equal(t, 60*SCALE, cy) } // TestPivotZoom_RightEdge_ZoomInMovesCameraRight verifies pivot Zoom Right Edge Zoom In Moves Camera Right. func TestPivotZoom_RightEdge_ZoomInMovesCameraRight(t *testing.T) { t.Parallel() // viewport 100px, cursor at x=100 (right edge), center is 50 => offX=50px // zoom 1->2 halves worldPerPx, so camera must move towards the cursor to keep the same world point. cx0 := 50 * SCALE cx, _ := PivotZoomCameraNoWrap( cx0, 50*SCALE, 100, 100, 100, 50, SCALE, 2*SCALE, ) require.Greater(t, cx, cx0) } // TestPivotZoom_LeftEdge_ZoomInMovesCameraLeft verifies pivot Zoom Left Edge Zoom In Moves Camera Left. func TestPivotZoom_LeftEdge_ZoomInMovesCameraLeft(t *testing.T) { t.Parallel() cx0 := 50 * SCALE cx, _ := PivotZoomCameraNoWrap( cx0, 50*SCALE, 100, 100, 0, 50, SCALE, 2*SCALE, ) require.Less(t, cx, cx0) }