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, }) }