world refactor

This commit is contained in:
Ilia Denisov
2026-03-17 11:48:05 +02:00
committed by GitHub
parent 9208ef1065
commit 5029857fe4
82 changed files with 9838 additions and 9715 deletions
+150 -2
View File
@@ -1,12 +1,12 @@
package world
import (
"github.com/stretchr/testify/require"
"image/color"
"testing"
"github.com/stretchr/testify/require"
)
// TestHitTest_ReturnsBestByPriorityAndAllHits verifies hit Test Returns Best By Priority And All Hits.
func TestHitTest_ReturnsBestByPriorityAndAllHits(t *testing.T) {
t.Parallel()
@@ -48,6 +48,7 @@ func TestHitTest_ReturnsBestByPriorityAndAllHits(t *testing.T) {
require.Equal(t, idLine, hits[2].ID)
}
// TestHitTest_BufferTooSmall_KeepsBestHits verifies hit Test Buffer Too Small Keeps Best Hits.
func TestHitTest_BufferTooSmall_KeepsBestHits(t *testing.T) {
t.Parallel()
@@ -77,6 +78,7 @@ func TestHitTest_BufferTooSmall_KeepsBestHits(t *testing.T) {
require.Equal(t, idCircle, hits[0].ID)
}
// TestHitTest_NoWrap_ClampsCameraAndStillHits verifies hit Test No Wrap Clamps Camera And Still Hits.
func TestHitTest_NoWrap_ClampsCameraAndStillHits(t *testing.T) {
t.Parallel()
@@ -105,6 +107,7 @@ func TestHitTest_NoWrap_ClampsCameraAndStillHits(t *testing.T) {
require.NotEmpty(t, hits)
}
// TestHitTest_CircleStrokeOnly_HitsNearRingNotCenter verifies hit Test Circle Stroke Only Hits Near Ring Not Center.
func TestHitTest_CircleStrokeOnly_HitsNearRingNotCenter(t *testing.T) {
t.Parallel()
@@ -151,6 +154,7 @@ func TestHitTest_CircleStrokeOnly_HitsNearRingNotCenter(t *testing.T) {
require.Equal(t, KindCircle, hits[0].Kind)
}
// TestHitTest_CircleRadiusScale_AffectsHitArea verifies hit Test Circle Radius Scale Affects Hit Area.
func TestHitTest_CircleRadiusScale_AffectsHitArea(t *testing.T) {
t.Parallel()
@@ -186,3 +190,147 @@ func TestHitTest_CircleRadiusScale_AffectsHitArea(t *testing.T) {
// Tap at +5 should typically miss (depending on slop); enforce by setting small slop via options.
// We'll add a small-slope circle and test deterministically.
}
// TestHitTest_Circle_StrictThresholds_WithRadiusScale_Table verifies hit Test Circle Strict Thresholds With Radius Scale Table.
func TestHitTest_Circle_StrictThresholds_WithRadiusScale_Table(t *testing.T) {
t.Parallel()
type tc struct {
name string
fillVisible bool
rawRadius int // world units (not fixed); zoom=1 => 1px per unit
scaleFp int
hitSlopPx int
cursorDxPx int // offset from center in pixels along X axis
wantHit bool
wantKind PrimitiveKind
}
// Common settings: world 20x20, viewport 200x200, camera at center (10,10).
params := RenderParams{
ViewportWidthPx: 200,
ViewportHeightPx: 200,
MarginXPx: 0,
MarginYPx: 0,
CameraXWorldFp: 10 * SCALE,
CameraYWorldFp: 10 * SCALE,
CameraZoom: 1.0,
}
tests := []tc{
{
name: "filled: on boundary hits (R=4, S=1, dx=4)",
fillVisible: true,
rawRadius: 2,
scaleFp: 2 * SCALE, // eff radius = 4
hitSlopPx: 1,
cursorDxPx: 4,
wantHit: true,
wantKind: KindCircle,
},
{
name: "filled: outside beyond slop misses (R=4, S=1, dx=6)",
fillVisible: true,
rawRadius: 2,
scaleFp: 2 * SCALE,
hitSlopPx: 1,
cursorDxPx: 6, // 6 > R+S = 5
wantHit: false,
},
{
name: "filled: just inside slop hits (R=4, S=1, dx=5)",
fillVisible: true,
rawRadius: 2,
scaleFp: 2 * SCALE,
hitSlopPx: 1,
cursorDxPx: 5, // == R+S
wantHit: true,
wantKind: KindCircle,
},
{
name: "stroke-only: center must miss even if slop would cover",
fillVisible: false,
rawRadius: 2,
scaleFp: 2 * SCALE, // eff radius = 4
hitSlopPx: 10, // huge, would normally include center without our rule
cursorDxPx: 0,
wantHit: false,
},
{
name: "stroke-only: on ring hits (R=4, S=1, dx=4)",
fillVisible: false,
rawRadius: 2,
scaleFp: 2 * SCALE,
hitSlopPx: 1,
cursorDxPx: 4,
wantHit: true,
wantKind: KindCircle,
},
{
name: "stroke-only: inside ring beyond slop misses (R=4, S=1, dx=2)",
fillVisible: false,
rawRadius: 2,
scaleFp: 2 * SCALE,
hitSlopPx: 1,
cursorDxPx: 2, // 2 < R-S = 3
wantHit: false,
},
{
name: "stroke-only: outside ring beyond slop misses (R=4, S=1, dx=6)",
fillVisible: false,
rawRadius: 2,
scaleFp: 2 * SCALE,
hitSlopPx: 1,
cursorDxPx: 6, // 6 > R+S = 5
wantHit: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
w := NewWorld(20, 20)
w.IndexOnViewportChange(params.ViewportWidthPx, params.ViewportHeightPx, params.CameraZoom)
require.NoError(t, w.SetCircleRadiusScaleFp(tt.scaleFp))
// Build a stroke-only circle style if needed.
var opts []CircleOpt
opts = append(opts, CircleWithHitSlopPx(tt.hitSlopPx))
if !tt.fillVisible {
// Force fill alpha=0 => stroke-only for hit-test and rendering.
sw := 1.0
styleID := w.AddStyleCircle(StyleOverride{
FillColor: color.RGBA{A: 0},
StrokeColor: color.RGBA{A: 255},
StrokeWidthPx: &sw,
})
opts = append(opts, CircleWithStyleID(styleID))
}
_, err := w.AddCircle(10, 10, float64(tt.rawRadius), opts...)
require.NoError(t, err)
w.Reindex()
// Cursor at viewport center +/- dx along X. At zoom=1, 1px == 1 world unit.
cx := params.ViewportWidthPx/2 + tt.cursorDxPx
cy := params.ViewportHeightPx / 2
buf := make([]Hit, 0, 8)
hits, err := w.HitTest(buf, &params, cx, cy)
require.NoError(t, err)
if !tt.wantHit {
require.Empty(t, hits)
return
}
require.NotEmpty(t, hits)
require.Equal(t, tt.wantKind, hits[0].Kind)
})
}
}