ui: basic map scroller

This commit is contained in:
Ilia Denisov
2026-03-06 23:29:06 +02:00
committed by GitHub
parent 29d188969b
commit 1de621c743
68 changed files with 9861 additions and 118 deletions
+442
View File
@@ -0,0 +1,442 @@
package world
import (
"testing"
"github.com/stretchr/testify/require"
)
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)
})
}
}
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)
})
}
}
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)
})
})
}
}
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)
}
func TestCorrectCameraZoomFpRaisesZoomToFitWorldWidth(t *testing.T) {
t.Parallel()
got := correctCameraZoomFp(
SCALE,
120, 20,
100*SCALE, 100*SCALE,
0, 0,
)
require.Equal(t, 1200, got)
}
func TestCorrectCameraZoomFpRaisesZoomToFitWorldHeight(t *testing.T) {
t.Parallel()
got := correctCameraZoomFp(
SCALE,
20, 150,
100*SCALE, 100*SCALE,
0, 0,
)
require.Equal(t, 1500, got)
}
func TestCorrectCameraZoomFpUsesMaxFitAcrossAxes(t *testing.T) {
t.Parallel()
got := correctCameraZoomFp(
SCALE,
120, 150,
100*SCALE, 100*SCALE,
0, 0,
)
require.Equal(t, 1500, got)
}
func TestCorrectCameraZoomFpAppliesMinZoomWhenLargerThanCurrentAndFit(t *testing.T) {
t.Parallel()
got := correctCameraZoomFp(
SCALE,
20, 20,
100*SCALE, 100*SCALE,
1500, 0,
)
require.Equal(t, 1500, got)
}
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)
}
func TestCorrectCameraZoomFpIgnoresMaxZoomWhenFitNeedsMore(t *testing.T) {
t.Parallel()
got := correctCameraZoomFp(
SCALE,
200, 20,
100*SCALE, 100*SCALE,
0, 1500,
)
require.Equal(t, 2*SCALE, got)
}
func TestCorrectCameraZoomFpAppliesMinThenMaxWhenBothValid(t *testing.T) {
t.Parallel()
got := correctCameraZoomFp(
SCALE,
20, 20,
100*SCALE, 100*SCALE,
1500, 1600,
)
require.Equal(t, 1500, got)
}
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)
}
func TestCorrectCameraZoomFpZeroViewportUsesOnlyBounds(t *testing.T) {
t.Parallel()
got := correctCameraZoomFp(
SCALE,
0, 0,
100*SCALE, 100*SCALE,
1500, 0,
)
require.Equal(t, 1500, got)
}
func TestCorrectCameraZoomFpZeroBoundsAreIgnored(t *testing.T) {
t.Parallel()
got := correctCameraZoomFp(
1250,
20, 20,
100*SCALE, 100*SCALE,
0, 0,
)
require.Equal(t, 1250, got)
}
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)
})
}
}
func TestWorldCorrectCameraZoomReturnsFloatValue(t *testing.T) {
t.Parallel()
w := NewWorld(100, 100)
got := w.CorrectCameraZoom(1.0, 120, 20)
require.Equal(t, 1.2, got)
}
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)
}
func TestWorldCorrectCameraZoomFitBeatsDefaultMaxBound(t *testing.T) {
t.Parallel()
w := NewWorld(1, 100)
got := w.CorrectCameraZoom(1.0, 40, 10)
require.Equal(t, 40.0, got)
}
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)
}
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
}
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)
}
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)
}
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)
}
func TestCorrectCameraZoomFp_ZeroViewportUsesOnlyBounds(t *testing.T) {
t.Parallel()
got := correctCameraZoomFp(
SCALE,
0, 0,
100*SCALE, 100*SCALE,
1500, 0,
)
require.Equal(t, 1500, got)
}