Skip to content

Commit 2c46fbc

Browse files
Copilottrusktr
andcommitted
Add comprehensive tests for PlaneGeometry and ShaderMaterial matching Three.js r125 API
Co-authored-by: trusktr <297678+trusktr@users.noreply.github.com>
1 parent e3759d0 commit 2c46fbc

2 files changed

Lines changed: 516 additions & 0 deletions

File tree

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/**
2+
* @author TristanVALCKE / https://github.com/Itee
3+
* @author Joe Pea / http://github.com/trusktr
4+
*/
5+
6+
import { runStdGeometryTests } from '../test-utils'
7+
import { PlaneGeometry } from './PlaneGeometry'
8+
import { BufferGeometry } from '../core/BufferGeometry'
9+
import { Float32BufferAttribute } from '../core/BufferAttribute'
10+
11+
let geometries: BufferGeometry[] = []
12+
13+
describe('Geometries', (): void => {
14+
describe('PlaneGeometry', (): void => {
15+
beforeEach((): void => {
16+
geometries = [
17+
new PlaneGeometry(),
18+
new PlaneGeometry(10, 20),
19+
new PlaneGeometry(10, 20, 2, 3),
20+
]
21+
})
22+
23+
// Standard geometry tests
24+
test('Standard geometry tests', (): void => {
25+
runStdGeometryTests(geometries)
26+
})
27+
28+
test('default parameters create 1x1 plane', (): void => {
29+
const geom = new PlaneGeometry()
30+
31+
expect(geom.parameters.width).toBe(1, 'default width should be 1')
32+
expect(geom.parameters.height).toBe(1, 'default height should be 1')
33+
expect(geom.parameters.widthSegments).toBe(1, 'default widthSegments should be 1')
34+
expect(geom.parameters.heightSegments).toBe(1, 'default heightSegments should be 1')
35+
})
36+
37+
test('custom parameters are stored correctly', (): void => {
38+
const geom = new PlaneGeometry(10, 20, 4, 5)
39+
40+
expect(geom.parameters.width).toBe(10, 'width should be 10')
41+
expect(geom.parameters.height).toBe(20, 'height should be 20')
42+
expect(geom.parameters.widthSegments).toBe(4, 'widthSegments should be 4')
43+
expect(geom.parameters.heightSegments).toBe(5, 'heightSegments should be 5')
44+
})
45+
46+
test('plane has required attributes', (): void => {
47+
const geom = new PlaneGeometry(4, 4)
48+
49+
expect(geom.attributes.has('position')).toBe(true, 'should have position attribute')
50+
expect(geom.attributes.has('normal')).toBe(true, 'should have normal attribute')
51+
expect(geom.attributes.has('uv')).toBe(true, 'should have uv attribute')
52+
})
53+
54+
test('plane with 1x1 segments has correct vertex count', (): void => {
55+
const geom = new PlaneGeometry(2, 2, 1, 1)
56+
const positionAttr = geom.attributes.get('position') as Float32BufferAttribute
57+
58+
// 1x1 segments = (1+1) * (1+1) = 4 vertices
59+
expect(positionAttr.count).toBe(4, '1x1 segments should have 4 vertices')
60+
})
61+
62+
test('plane with 2x3 segments has correct vertex count', (): void => {
63+
const geom = new PlaneGeometry(4, 6, 2, 3)
64+
const positionAttr = geom.attributes.get('position') as Float32BufferAttribute
65+
66+
// 2x3 segments = (2+1) * (3+1) = 3 * 4 = 12 vertices
67+
expect(positionAttr.count).toBe(12, '2x3 segments should have 12 vertices')
68+
})
69+
70+
test('plane vertices are within expected bounds', (): void => {
71+
const width: f32 = 10.0
72+
const height: f32 = 20.0
73+
const geom = new PlaneGeometry(width, height, 2, 2)
74+
const positionAttr = geom.attributes.get('position') as Float32BufferAttribute
75+
76+
for (let i = 0; i < positionAttr.count; i++) {
77+
const x = positionAttr.getX(i)
78+
const y = positionAttr.getY(i)
79+
const z = positionAttr.getZ(i)
80+
81+
// X coordinates should be within [-width/2, width/2]
82+
expect(Math.abs(x)).toBeLessThanOrEqual(width / 2 + 0.01, 'x should be within bounds')
83+
84+
// Y coordinates should be within [-height/2, height/2]
85+
expect(Math.abs(y)).toBeLessThanOrEqual(height / 2 + 0.01, 'y should be within bounds')
86+
87+
// Z coordinates should be 0 for a plane
88+
expect(z).toBe(0.0, 'z should be 0 for plane')
89+
}
90+
})
91+
92+
test('plane normals point in +Z direction', (): void => {
93+
const geom = new PlaneGeometry(4, 4, 2, 2)
94+
const normalAttr = geom.attributes.get('normal') as Float32BufferAttribute
95+
96+
for (let i = 0; i < normalAttr.count; i++) {
97+
const nx = normalAttr.getX(i)
98+
const ny = normalAttr.getY(i)
99+
const nz = normalAttr.getZ(i)
100+
101+
expect(nx).toBe(0.0, 'normal x component should be 0')
102+
expect(ny).toBe(0.0, 'normal y component should be 0')
103+
expect(nz).toBe(1.0, 'normal z component should be 1')
104+
}
105+
})
106+
107+
test('plane UVs are in [0, 1] range', (): void => {
108+
const geom = new PlaneGeometry(4, 4, 2, 3)
109+
const uvAttr = geom.attributes.get('uv') as Float32BufferAttribute
110+
111+
for (let i = 0; i < uvAttr.count; i++) {
112+
const u = uvAttr.getX(i)
113+
const v = uvAttr.getY(i)
114+
115+
expect(u).toBeGreaterThanOrEqual(0.0, 'u should be >= 0')
116+
expect(u).toBeLessThanOrEqual(1.0, 'u should be <= 1')
117+
expect(v).toBeGreaterThanOrEqual(0.0, 'v should be >= 0')
118+
expect(v).toBeLessThanOrEqual(1.0, 'v should be <= 1')
119+
}
120+
})
121+
122+
test('plane has correct index count for 1x1 segments', (): void => {
123+
const geom = new PlaneGeometry(2, 2, 1, 1)
124+
125+
// 1x1 segments = 1 * 1 = 1 quad = 2 triangles = 6 indices
126+
expect(geom.index!.count).toBe(6, '1x1 segments should have 6 indices')
127+
})
128+
129+
test('plane has correct index count for 2x3 segments', (): void => {
130+
const geom = new PlaneGeometry(4, 6, 2, 3)
131+
132+
// 2x3 segments = 2 * 3 = 6 quads = 12 triangles = 36 indices
133+
expect(geom.index!.count).toBe(36, '2x3 segments should have 36 indices')
134+
})
135+
136+
test('plane indices reference valid vertices', (): void => {
137+
const geom = new PlaneGeometry(4, 4, 2, 2)
138+
const positionAttr = geom.attributes.get('position') as Float32BufferAttribute
139+
const vertexCount = positionAttr.count
140+
141+
for (let i = 0; i < geom.index!.count; i++) {
142+
const index = geom.index!.getX(i)
143+
expect(index).toBeGreaterThanOrEqual(0, 'index should be >= 0')
144+
expect(index).toBeLessThan(vertexCount, 'index should be < vertex count')
145+
}
146+
})
147+
148+
test('plane with 0 segments defaults to 1 segment', (): void => {
149+
// Test edge case - segments should be at least 1
150+
const geom = new PlaneGeometry(2, 2, 0, 0)
151+
const positionAttr = geom.attributes.get('position') as Float32BufferAttribute
152+
153+
// Should behave like 1x1 segments (though the implementation may vary)
154+
// At minimum, should have 4 vertices
155+
expect(positionAttr.count).toBeGreaterThanOrEqual(4, 'should have at least 4 vertices')
156+
})
157+
158+
test('plane corners are at expected positions', (): void => {
159+
const width: f32 = 10.0
160+
const height: f32 = 20.0
161+
const geom = new PlaneGeometry(width, height, 1, 1)
162+
const positionAttr = geom.attributes.get('position') as Float32BufferAttribute
163+
164+
// For a 1x1 segment plane, we have 4 corners
165+
const corners: f32[][] = []
166+
for (let i = 0; i < 4; i++) {
167+
corners.push([
168+
positionAttr.getX(i),
169+
positionAttr.getY(i),
170+
positionAttr.getZ(i)
171+
])
172+
}
173+
174+
// Check that we have corners at the extremes
175+
let hasTopLeft = false
176+
let hasTopRight = false
177+
let hasBottomLeft = false
178+
let hasBottomRight = false
179+
180+
for (let i = 0; i < 4; i++) {
181+
const x = corners[i][0]
182+
const y = corners[i][1]
183+
184+
if (Math.abs(x - (-width / 2)) < 0.01 && Math.abs(y - (height / 2)) < 0.01) hasTopLeft = true
185+
if (Math.abs(x - (width / 2)) < 0.01 && Math.abs(y - (height / 2)) < 0.01) hasTopRight = true
186+
if (Math.abs(x - (-width / 2)) < 0.01 && Math.abs(y - (-height / 2)) < 0.01) hasBottomLeft = true
187+
if (Math.abs(x - (width / 2)) < 0.01 && Math.abs(y - (-height / 2)) < 0.01) hasBottomRight = true
188+
}
189+
190+
expect(hasTopLeft || hasTopRight || hasBottomLeft || hasBottomRight).toBe(
191+
true,
192+
'should have at least one corner at expected position'
193+
)
194+
})
195+
196+
test('plane is centered on origin', (): void => {
197+
const geom = new PlaneGeometry(10, 20, 3, 3)
198+
const positionAttr = geom.attributes.get('position') as Float32BufferAttribute
199+
200+
let sumX: f32 = 0.0
201+
let sumY: f32 = 0.0
202+
let sumZ: f32 = 0.0
203+
204+
for (let i = 0; i < positionAttr.count; i++) {
205+
sumX += positionAttr.getX(i)
206+
sumY += positionAttr.getY(i)
207+
sumZ += positionAttr.getZ(i)
208+
}
209+
210+
const avgX = sumX / positionAttr.count
211+
const avgY = sumY / positionAttr.count
212+
const avgZ = sumZ / positionAttr.count
213+
214+
// Average position should be close to origin
215+
expect(Math.abs(avgX)).toBeLessThan(0.01, 'average x should be near 0')
216+
expect(Math.abs(avgY)).toBeLessThan(0.01, 'average y should be near 0')
217+
expect(Math.abs(avgZ)).toBeLessThan(0.01, 'average z should be near 0')
218+
})
219+
})
220+
})

0 commit comments

Comments
 (0)