package engine import ( "math" "testing" ) func TestNewIdentityMatrix(t *testing.T) { m := NewIdentityMatrix() expected := Matrix{A: 1, B: 0, C: 0, D: 1, E: 0, F: 0} if m != expected { t.Errorf("NewIdentityMatrix() = %v, want %v", m, expected) } } func TestTranslate(t *testing.T) { tests := []struct { x, y float64 expected Matrix }{ {10, 20, Matrix{A: 1, B: 0, C: 0, D: 1, E: 10, F: 20}}, {0, 0, Matrix{A: 1, B: 0, C: 0, D: 1, E: 0, F: 0}}, {-5, 15, Matrix{A: 1, B: 0, C: 0, D: 1, E: -5, F: 15}}, } for _, tt := range tests { result := Translate(tt.x, tt.y) if result != tt.expected { t.Errorf("Translate(%v, %v) = %v, want %v", tt.x, tt.y, result, tt.expected) } } } func TestRotate(t *testing.T) { tests := []struct { angle float64 expected Matrix }{ {0, Matrix{A: 1, B: 0, C: 0, D: 1, E: 0, F: 0}}, {math.Pi / 2, Matrix{A: 0, B: 1, C: -1, D: 0, E: 0, F: 0}}, {math.Pi, Matrix{A: -1, B: 0, C: 0, D: -1, E: 0, F: 0}}, {3 * math.Pi / 2, Matrix{A: 0, B: -1, C: 1, D: 0, E: 0, F: 0}}, } for _, tt := range tests { result := Rotate(tt.angle) // Use approximate equality for floating point comparison if !approximatelyEqual(result.A, tt.expected.A) || !approximatelyEqual(result.B, tt.expected.B) || !approximatelyEqual(result.C, tt.expected.C) || !approximatelyEqual(result.D, tt.expected.D) || !approximatelyEqual(result.E, tt.expected.E) || !approximatelyEqual(result.F, tt.expected.F) { t.Errorf("Rotate(%v) = %v, want %v", tt.angle, result, tt.expected) } } } func TestScale(t *testing.T) { tests := []struct { sx, sy float64 expected Matrix }{ {1, 1, Matrix{A: 1, B: 0, C: 0, D: 1, E: 0, F: 0}}, {2, 3, Matrix{A: 2, B: 0, C: 0, D: 3, E: 0, F: 0}}, {0.5, 2, Matrix{A: 0.5, B: 0, C: 0, D: 2, E: 0, F: 0}}, } for _, tt := range tests { result := Scale(tt.sx, tt.sy) if result != tt.expected { t.Errorf("Scale(%v, %v) = %v, want %v", tt.sx, tt.sy, result, tt.expected) } } } func TestMatrixMultiply(t *testing.T) { // Test identity multiplication identity := NewIdentityMatrix() translate := Translate(10, 20) result := identity.Multiply(translate) if result != translate { t.Errorf("Identity * Translate = %v, want %v", result, translate) } // Test translation composition t1 := Translate(10, 20) t2 := Translate(5, 10) result = t1.Multiply(t2) expected := Translate(15, 30) if result != expected { t.Errorf("Translate(10,20) * Translate(5,10) = %v, want %v", result, expected) } // Test scale composition s1 := Scale(2, 3) s2 := Scale(0.5, 0.5) result = s1.Multiply(s2) expected = Scale(1, 1.5) if result != expected { t.Errorf("Scale(2,3) * Scale(0.5,0.5) = %v, want %v", result, expected) } } func TestApplyTransform(t *testing.T) { tests := []struct { point Point matrix Matrix expected Point }{ {Point{X: 0, Y: 0}, NewIdentityMatrix(), Point{X: 0, Y: 0}}, {Point{X: 10, Y: 20}, Translate(5, 10), Point{X: 15, Y: 30}}, {Point{X: 1, Y: 0}, Scale(3, 2), Point{X: 3, Y: 0}}, {Point{X: 0, Y: 1}, Scale(3, 2), Point{X: 0, Y: 2}}, } for _, tt := range tests { result := ApplyTransform(tt.point, tt.matrix) if !approximatelyEqual(result.X, tt.expected.X) || !approximatelyEqual(result.Y, tt.expected.Y) { t.Errorf("ApplyTransform(%v, %v) = %v, want %v", tt.point, tt.matrix, result, tt.expected) } } } func TestNewTransform(t *testing.T) { transform := NewTransform(10, 20, 100, 1) if transform.x != 10 || transform.y != 20 || transform.size != 100 || transform.rotation != 1 { t.Errorf("NewTransform(10, 20, 100, 1) = %v, want {x:10, y:20, size:100, rotation:1}", transform) } } func TestTransformIconPoint(t *testing.T) { tests := []struct { transform Transform x, y, w, h float64 expected Point }{ // No rotation (0 degrees) {NewTransform(0, 0, 100, 0), 10, 20, 5, 5, Point{X: 10, Y: 20}}, {NewTransform(10, 20, 100, 0), 5, 10, 0, 0, Point{X: 15, Y: 30}}, // 90 degrees rotation {NewTransform(0, 0, 100, 1), 10, 20, 5, 5, Point{X: 75, Y: 10}}, // 180 degrees rotation {NewTransform(0, 0, 100, 2), 10, 20, 5, 5, Point{X: 85, Y: 75}}, // 270 degrees rotation {NewTransform(0, 0, 100, 3), 10, 20, 5, 5, Point{X: 20, Y: 85}}, // Test rotation normalization (rotation > 3) {NewTransform(0, 0, 100, 4), 10, 20, 0, 0, Point{X: 10, Y: 20}}, // Same as rotation 0 {NewTransform(0, 0, 100, 5), 10, 20, 5, 5, Point{X: 75, Y: 10}}, // Same as rotation 1 } for _, tt := range tests { result := tt.transform.TransformIconPoint(tt.x, tt.y, tt.w, tt.h) if !approximatelyEqual(result.X, tt.expected.X) || !approximatelyEqual(result.Y, tt.expected.Y) { t.Errorf("Transform(%v).TransformIconPoint(%v, %v, %v, %v) = %v, want %v", tt.transform, tt.x, tt.y, tt.w, tt.h, result, tt.expected) } } } func TestNoTransform(t *testing.T) { if NoTransform.x != 0 || NoTransform.y != 0 || NoTransform.size != 0 || NoTransform.rotation != 0 { t.Errorf("NoTransform should be {x:0, y:0, size:0, rotation:0}, got %v", NoTransform) } // Test that NoTransform doesn't change points point := Point{X: 10, Y: 20} result := NoTransform.TransformIconPoint(point.X, point.Y, 0, 0) if result != point { t.Errorf("NoTransform should not change point %v, got %v", point, result) } } // approximatelyEqual checks if two float64 values are approximately equal func approximatelyEqual(a, b float64) bool { const epsilon = 1e-9 return math.Abs(a-b) < epsilon }