1
+ using NUnit . Framework ;
2
+ using static Box2D . NET . B2MathFunction ;
3
+
4
+ namespace Box2D . NET . Test ;
5
+
6
+ /// <summary>
7
+ /// Tests for B2Transform, which represents a 2D rigid body transformation.
8
+ /// A transform combines a position (translation) and a rotation.
9
+ ///
10
+ /// Key concepts:
11
+ /// 1. A transform maps points from local space to world space
12
+ /// 2. The inverse transform maps points from world space back to local space
13
+ /// 3. Transforms can be combined (multiplied) to chain transformations
14
+ /// 4. The identity transform represents no transformation
15
+ /// </summary>
16
+ public class B2TransformTest
17
+ {
18
+ [ Test ]
19
+ public void Test_B2Transform_Identity ( )
20
+ {
21
+ // The identity transform represents no transformation
22
+ // - Position is at origin (0,0)
23
+ // - Rotation is zero (cos=1, sin=0)
24
+ var identity = b2Transform_identity ;
25
+
26
+ Assert . That ( identity . p . X , Is . EqualTo ( 0.0f ) . Within ( FLT_EPSILON ) , "Identity transform should be at origin" ) ;
27
+ Assert . That ( identity . p . Y , Is . EqualTo ( 0.0f ) . Within ( FLT_EPSILON ) , "Identity transform should be at origin" ) ;
28
+ Assert . That ( identity . q . c , Is . EqualTo ( 1.0f ) . Within ( FLT_EPSILON ) , "Identity rotation should be zero (cos=1)" ) ;
29
+ Assert . That ( identity . q . s , Is . EqualTo ( 0.0f ) . Within ( FLT_EPSILON ) , "Identity rotation should be zero (sin=0)" ) ;
30
+ }
31
+
32
+ [ Test ]
33
+ public void Test_B2Transform_LocalToWorld ( )
34
+ {
35
+ // Create a transform that:
36
+ // 1. Rotates 90 degrees counter-clockwise
37
+ // 2. Translates by (1,2)
38
+ var transform = new B2Transform (
39
+ new B2Vec2 ( 1.0f , 2.0f ) , // Translation
40
+ b2MakeRot ( B2_PI / 2 ) // 90 degree rotation
41
+ ) ;
42
+
43
+ // Test transforming a point from local to world space
44
+ var localPoint = new B2Vec2 ( 1.0f , 0.0f ) ; // Point at (1,0) in local space
45
+ var worldPoint = b2TransformPoint ( ref transform , localPoint ) ;
46
+
47
+ // Expected result:
48
+ // 1. First rotate (1,0) by 90 degrees -> (0,1)
49
+ // 2. Then translate by (1,2) -> (1,3)
50
+ Assert . That ( worldPoint . X , Is . EqualTo ( 1.0f ) . Within ( 0.0001f ) , "X coordinate should be translated by 1" ) ;
51
+ Assert . That ( worldPoint . Y , Is . EqualTo ( 3.0f ) . Within ( 0.0001f ) , "Y coordinate should be rotated and translated" ) ;
52
+ }
53
+
54
+ [ Test ]
55
+ public void Test_B2Transform_WorldToLocal ( )
56
+ {
57
+ // Create the same transform as above
58
+ var transform = new B2Transform (
59
+ new B2Vec2 ( 1.0f , 2.0f ) ,
60
+ b2MakeRot ( B2_PI / 2 )
61
+ ) ;
62
+
63
+ // Test transforming a point from world to local space
64
+ var worldPoint = new B2Vec2 ( 1.0f , 3.0f ) ; // Point at (1,3) in world space
65
+ var localPoint = b2InvTransformPoint ( transform , worldPoint ) ;
66
+
67
+ // Expected result:
68
+ // 1. First subtract translation (1,2) -> (0,1)
69
+ // 2. Then rotate back by -90 degrees -> (1,0)
70
+ Assert . That ( localPoint . X , Is . EqualTo ( 1.0f ) . Within ( 0.0001f ) , "X coordinate should be rotated back to 1" ) ;
71
+ Assert . That ( localPoint . Y , Is . EqualTo ( 0.0f ) . Within ( 0.0001f ) , "Y coordinate should be rotated back to 0" ) ;
72
+ }
73
+
74
+ [ Test ]
75
+ public void Test_B2Transform_Combine ( )
76
+ {
77
+ // Create two transforms:
78
+ // 1. First transform: rotate 45 degrees and translate by (1,0)
79
+ // 2. Second transform: rotate 45 degrees and translate by (0,1)
80
+ var transform1 = new B2Transform (
81
+ new B2Vec2 ( 1.0f , 0.0f ) ,
82
+ b2MakeRot ( B2_PI / 4 )
83
+ ) ;
84
+ var transform2 = new B2Transform (
85
+ new B2Vec2 ( 0.0f , 1.0f ) ,
86
+ b2MakeRot ( B2_PI / 4 )
87
+ ) ;
88
+
89
+ // Combine the transforms
90
+ var combined = b2MulTransforms ( transform1 , transform2 ) ;
91
+
92
+ // Test transforming a point through the combined transform
93
+ var localPoint = new B2Vec2 ( 1.0f , 0.0f ) ;
94
+ var worldPoint = b2TransformPoint ( ref combined , localPoint ) ;
95
+
96
+ // Expected result:
97
+ // 1. First apply transform2: rotate 45 degrees and translate by (0,1)
98
+ // - (1,0) rotated 45 degrees = (0.7071, 0.7071)
99
+ // - Then translate by (0,1) = (0.7071, 1.7071)
100
+ // 2. Then apply transform1: rotate 45 degrees and translate by (1,0)
101
+ // - (0.7071, 1.7071) rotated 45 degrees = (-0.7071, 1.7071)
102
+ // - Then translate by (1,0) = (0.2929, 1.7071)
103
+ Assert . That ( worldPoint . X , Is . EqualTo ( 0.2929f ) . Within ( 0.0001f ) , "Combined transform should apply both translations and rotations" ) ;
104
+ Assert . That ( worldPoint . Y , Is . EqualTo ( 1.7071f ) . Within ( 0.0001f ) , "Combined transform should apply both translations and rotations" ) ;
105
+ }
106
+
107
+ [ Test ]
108
+ public void Test_B2Transform_VectorRotation ( )
109
+ {
110
+ // Create a transform that rotates 90 degrees
111
+ var transform = new B2Transform (
112
+ new B2Vec2 ( 1.0f , 2.0f ) , // Translation doesn't affect vectors
113
+ b2MakeRot ( B2_PI / 2 )
114
+ ) ;
115
+
116
+ // Test rotating a vector (direction only, no position)
117
+ var localVector = new B2Vec2 ( 1.0f , 0.0f ) ; // Vector pointing right
118
+ var worldVector = b2RotateVector ( transform . q , localVector ) ;
119
+
120
+ // Expected result:
121
+ // Rotate (1,0) by 90 degrees -> (0,1)
122
+ Assert . That ( worldVector . X , Is . EqualTo ( 0.0f ) . Within ( 0.0001f ) , "Vector should be rotated 90 degrees" ) ;
123
+ Assert . That ( worldVector . Y , Is . EqualTo ( 1.0f ) . Within ( 0.0001f ) , "Vector should be rotated 90 degrees" ) ;
124
+ }
125
+
126
+ [ Test ]
127
+ public void Test_B2Transform_Validation ( )
128
+ {
129
+ // Test valid transform
130
+ var validTransform = new B2Transform (
131
+ new B2Vec2 ( 1.0f , 2.0f ) ,
132
+ b2MakeRot ( B2_PI / 4 )
133
+ ) ;
134
+ Assert . That ( b2IsValidVec2 ( validTransform . p ) && b2IsValidRotation ( validTransform . q ) ,
135
+ Is . True , "Transform with valid position and rotation should be valid" ) ;
136
+
137
+ // Test invalid position (NaN)
138
+ var invalidPosTransform = new B2Transform (
139
+ new B2Vec2 ( float . NaN , 2.0f ) ,
140
+ b2MakeRot ( B2_PI / 4 )
141
+ ) ;
142
+ Assert . That ( b2IsValidVec2 ( invalidPosTransform . p ) && b2IsValidRotation ( invalidPosTransform . q ) ,
143
+ Is . False , "Transform with NaN position should be invalid" ) ;
144
+
145
+ // Test invalid rotation (not normalized)
146
+ var invalidRotTransform = new B2Transform (
147
+ new B2Vec2 ( 1.0f , 2.0f ) ,
148
+ new B2Rot ( 2.0f , 0.0f ) // Not normalized
149
+ ) ;
150
+ Assert . That ( b2IsValidVec2 ( invalidRotTransform . p ) && b2IsValidRotation ( invalidRotTransform . q ) ,
151
+ Is . False , "Transform with non-normalized rotation should be invalid" ) ;
152
+ }
153
+ }
0 commit comments