ui: basic map scroller
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// rendererTestEnv groups the common mutable inputs used by renderer tests.
|
||||
// The environment stores independent horizontal and vertical margins because
|
||||
// the expanded canvas geometry is derived separately on each axis.
|
||||
type rendererTestEnv struct {
|
||||
world *World
|
||||
drawer *fakePrimitiveDrawer
|
||||
|
||||
// Viewport origin and size in canvas pixel coordinates.
|
||||
viewportX int
|
||||
viewportY int
|
||||
viewportW int
|
||||
viewportH int
|
||||
|
||||
// Independent margins around the viewport in canvas pixels.
|
||||
marginXPx int
|
||||
marginYPx int
|
||||
|
||||
// Final expanded canvas size in pixels.
|
||||
// In the default setup:
|
||||
// canvasW = viewportW + 2*marginXPx
|
||||
// canvasH = viewportH + 2*marginYPx
|
||||
canvasW int
|
||||
canvasH int
|
||||
|
||||
// Camera center in fixed-point world coordinates.
|
||||
cameraX int
|
||||
cameraY int
|
||||
|
||||
// Camera zoom in fixed-point representation, if needed by renderer internals.
|
||||
zoomFp int
|
||||
}
|
||||
|
||||
// newRendererTestEnv returns a baseline renderer test environment.
|
||||
// The default margins are derived independently from viewport width and height.
|
||||
func newRendererTestEnv() *rendererTestEnv {
|
||||
viewportW := 100
|
||||
viewportH := 80
|
||||
|
||||
marginXPx := viewportW / 4
|
||||
marginYPx := viewportH / 4
|
||||
|
||||
return &rendererTestEnv{
|
||||
world: NewWorld(10, 10),
|
||||
drawer: &fakePrimitiveDrawer{},
|
||||
viewportX: marginXPx,
|
||||
viewportY: marginYPx,
|
||||
viewportW: viewportW,
|
||||
viewportH: viewportH,
|
||||
marginXPx: marginXPx,
|
||||
marginYPx: marginYPx,
|
||||
canvasW: viewportW + 2*marginXPx,
|
||||
canvasH: viewportH + 2*marginYPx,
|
||||
cameraX: 5 * SCALE,
|
||||
cameraY: 5 * SCALE,
|
||||
zoomFp: SCALE,
|
||||
}
|
||||
}
|
||||
|
||||
// setViewport resets viewport-dependent fields and recomputes margins
|
||||
// using the default test formula:
|
||||
//
|
||||
// marginXPx = viewportW / 4
|
||||
// marginYPx = viewportH / 4
|
||||
func (env *rendererTestEnv) setViewport(viewportW, viewportH int) {
|
||||
env.viewportW = viewportW
|
||||
env.viewportH = viewportH
|
||||
|
||||
env.marginXPx = viewportW / 4
|
||||
env.marginYPx = viewportH / 4
|
||||
|
||||
env.viewportX = env.marginXPx
|
||||
env.viewportY = env.marginYPx
|
||||
|
||||
env.canvasW = env.viewportW + 2*env.marginXPx
|
||||
env.canvasH = env.viewportH + 2*env.marginYPx
|
||||
}
|
||||
|
||||
// setViewportAndMargins overrides viewport and margins explicitly.
|
||||
// This is useful for edge cases where the expanded canvas geometry
|
||||
// must be controlled exactly.
|
||||
func (env *rendererTestEnv) setViewportAndMargins(viewportW, viewportH, marginXPx, marginYPx int) {
|
||||
env.viewportW = viewportW
|
||||
env.viewportH = viewportH
|
||||
|
||||
env.marginXPx = marginXPx
|
||||
env.marginYPx = marginYPx
|
||||
|
||||
env.viewportX = env.marginXPx
|
||||
env.viewportY = env.marginYPx
|
||||
|
||||
env.canvasW = env.viewportW + 2*env.marginXPx
|
||||
env.canvasH = env.viewportH + 2*env.marginYPx
|
||||
}
|
||||
|
||||
// viewportRect returns the viewport rectangle in canvas pixel coordinates.
|
||||
func (env *rendererTestEnv) viewportRect() (x, y, w, h float64) {
|
||||
return float64(env.viewportX), float64(env.viewportY), float64(env.viewportW), float64(env.viewportH)
|
||||
}
|
||||
|
||||
// canvasRect returns the full expanded canvas rectangle in canvas pixel coordinates.
|
||||
func (env *rendererTestEnv) canvasRect() (x, y, w, h float64) {
|
||||
return 0, 0, float64(env.canvasW), float64(env.canvasH)
|
||||
}
|
||||
|
||||
// worldMustAddPoint adds a point to the test world and fails the test on error.
|
||||
func worldMustAddPoint(t *testing.T, w *World, x, y float64) {
|
||||
t.Helper()
|
||||
|
||||
_, err := w.AddPoint(x, y)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// worldMustAddCircle adds a circle to the test world and fails the test on error.
|
||||
func worldMustAddCircle(t *testing.T, w *World, x, y, r float64) {
|
||||
t.Helper()
|
||||
|
||||
_, err := w.AddCircle(x, y, r)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// worldMustAddLine adds a line to the test world and fails the test on error.
|
||||
func worldMustAddLine(t *testing.T, w *World, x1, y1, x2, y2 float64) {
|
||||
t.Helper()
|
||||
|
||||
_, err := w.AddLine(x1, y1, x2, y2)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// requireNoDrawerCommands asserts that the renderer produced no drawing commands.
|
||||
func requireNoDrawerCommands(t *testing.T, d *fakePrimitiveDrawer) {
|
||||
t.Helper()
|
||||
|
||||
require.Empty(t, d.Commands())
|
||||
}
|
||||
|
||||
// requireStrokeCommandAt returns a command and asserts that it is Stroke.
|
||||
func requireStrokeCommandAt(t *testing.T, d *fakePrimitiveDrawer, index int) fakeDrawerCommand {
|
||||
t.Helper()
|
||||
|
||||
cmd := requireDrawerCommandAt(t, d, index)
|
||||
requireCommandName(t, cmd, "Stroke")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// requireFillCommandAt returns a command and asserts that it is Fill.
|
||||
func requireFillCommandAt(t *testing.T, d *fakePrimitiveDrawer, index int) fakeDrawerCommand {
|
||||
t.Helper()
|
||||
|
||||
cmd := requireDrawerCommandAt(t, d, index)
|
||||
requireCommandName(t, cmd, "Fill")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// requireAddPointCommandAt returns a command and asserts that it is AddPoint.
|
||||
func requireAddPointCommandAt(t *testing.T, d *fakePrimitiveDrawer, index int) fakeDrawerCommand {
|
||||
t.Helper()
|
||||
|
||||
cmd := requireDrawerCommandAt(t, d, index)
|
||||
requireCommandName(t, cmd, "AddPoint")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// requireAddLineCommandAt returns a command and asserts that it is AddLine.
|
||||
func requireAddLineCommandAt(t *testing.T, d *fakePrimitiveDrawer, index int) fakeDrawerCommand {
|
||||
t.Helper()
|
||||
|
||||
cmd := requireDrawerCommandAt(t, d, index)
|
||||
requireCommandName(t, cmd, "AddLine")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// requireAddCircleCommandAt returns a command and asserts that it is AddCircle.
|
||||
func requireAddCircleCommandAt(t *testing.T, d *fakePrimitiveDrawer, index int) fakeDrawerCommand {
|
||||
t.Helper()
|
||||
|
||||
cmd := requireDrawerCommandAt(t, d, index)
|
||||
requireCommandName(t, cmd, "AddCircle")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// requireSingleClipRectOnCommand asserts that the command was issued under exactly one clip rect.
|
||||
func requireSingleClipRectOnCommand(t *testing.T, cmd fakeDrawerCommand, x, y, w, h float64) {
|
||||
t.Helper()
|
||||
|
||||
requireCommandClipRects(t, cmd, fakeClipRect{
|
||||
X: x,
|
||||
Y: y,
|
||||
W: w,
|
||||
H: h,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user