package world import ( "image" "image/color" "testing" "github.com/stretchr/testify/require" ) type testTheme struct{} func (testTheme) ID() string { return "t1" } func (testTheme) Name() string { return "Theme1" } func (testTheme) BackgroundColor() color.Color { return color.RGBA{R: 1, G: 2, B: 3, A: 255} } func (testTheme) BackgroundImage() image.Image { return nil } func (testTheme) BackgroundTileMode() BackgroundTileMode { return BackgroundTileRepeat } func (testTheme) BackgroundScaleMode() BackgroundScaleMode { return BackgroundScaleNone } func (testTheme) BackgroundAnchorMode() BackgroundAnchorMode { return BackgroundAnchorWorld } func (testTheme) PointClassOverride(PointClassID) (StyleOverride, bool) { return StyleOverride{}, false } func (testTheme) LineClassOverride(LineClassID) (StyleOverride, bool) { return StyleOverride{}, false } func (testTheme) CircleClassOverride(CircleClassID) (StyleOverride, bool) { return StyleOverride{}, false } func (testTheme) PointStyle() Style { return Style{ FillColor: color.RGBA{R: 9, A: 255}, StrokeColor: nil, StrokeWidthPx: 0, StrokeDashes: nil, StrokeDashOffset: 0, PointRadiusPx: 4, } } func (testTheme) LineStyle() Style { return Style{ FillColor: nil, StrokeColor: color.RGBA{G: 9, A: 255}, StrokeWidthPx: 3, StrokeDashes: []float64{2, 2}, StrokeDashOffset: 1, PointRadiusPx: 0, } } func (testTheme) CircleStyle() Style { return Style{ FillColor: color.RGBA{B: 9, A: 255}, StrokeColor: color.RGBA{A: 255}, StrokeWidthPx: 2, StrokeDashes: nil, StrokeDashOffset: 0, PointRadiusPx: 0, } } func TestWorldSetTheme_MaterializesThemeDefaultStyles(t *testing.T) { t.Parallel() w := NewWorld(10, 10) // Built-ins should remain stable (1/2/3). require.Equal(t, StyleIDDefaultLine, StyleID(1)) require.Equal(t, StyleIDDefaultCircle, StyleID(2)) require.Equal(t, StyleIDDefaultPoint, StyleID(3)) // Set a custom theme. w.SetTheme(testTheme{}) // Theme defaults should NOT be built-in IDs anymore. require.NotEqual(t, StyleIDDefaultLine, w.themeDefaultLineStyleID) require.NotEqual(t, StyleIDDefaultCircle, w.themeDefaultCircleStyleID) require.NotEqual(t, StyleIDDefaultPoint, w.themeDefaultPointStyleID) ls, ok := w.styles.Get(w.themeDefaultLineStyleID) require.True(t, ok) require.Equal(t, 3.0, ls.StrokeWidthPx) require.Equal(t, []float64{2, 2}, ls.StrokeDashes) require.Equal(t, 1.0, ls.StrokeDashOffset) cs, ok := w.styles.Get(w.themeDefaultCircleStyleID) require.True(t, ok) require.Equal(t, 2.0, cs.StrokeWidthPx) ps, ok := w.styles.Get(w.themeDefaultPointStyleID) require.True(t, ok) require.Equal(t, 4.0, ps.PointRadiusPx) } func TestRender_UsesThemeBackgroundColor_WhenNoOptionOverride(t *testing.T) { t.Parallel() w := NewWorld(10, 10) w.SetTheme(testTheme{}) // Minimal index. w.resetGrid(2 * SCALE) params := RenderParams{ ViewportWidthPx: 10, ViewportHeightPx: 10, MarginXPx: 0, MarginYPx: 0, CameraXWorldFp: 5 * SCALE, CameraYWorldFp: 5 * SCALE, CameraZoom: 1.0, } d := &fakePrimitiveDrawer{} require.NoError(t, w.Render(d, params)) // Should clear with theme background color via ClearAllTo(bg). require.NotEmpty(t, d.CommandsByName("ClearAllTo")) } func TestRender_OptionsBackgroundColor_OverridesThemeBackgroundColor(t *testing.T) { t.Parallel() w := NewWorld(10, 10) w.SetTheme(testTheme{}) w.resetGrid(2 * SCALE) params := RenderParams{ ViewportWidthPx: 10, ViewportHeightPx: 10, MarginXPx: 0, MarginYPx: 0, CameraXWorldFp: 5 * SCALE, CameraYWorldFp: 5 * SCALE, CameraZoom: 1.0, Options: &RenderOptions{ BackgroundColor: color.RGBA{R: 200, A: 255}, }, } d := &fakePrimitiveDrawer{} require.NoError(t, w.Render(d, params)) require.NotEmpty(t, d.CommandsByName("ClearAllTo")) }