Spaces:
Sleeping
Sleeping
| import { describe, it, expect } from 'vitest'; | |
| import * as THREE from 'three'; | |
| import { LayoutEngine } from '../src/visualizer/LayoutEngine.js'; | |
| import { PointShader } from '../src/engine/Shaders.js'; | |
| import { MeshFactory } from '../src/visualizer/MeshFactory.js'; | |
| import { Navigation } from '../src/engine/Navigation.js'; | |
| describe('WebGL 3D Engine & Spatial Math', () => { | |
| it('LayoutEngine maps 1D embedding vector into 3D space correctly', () => { | |
| const layout = new LayoutEngine({ scaleX: 1.0, scaleY: 100.0, scaleZ: 10.0 }); | |
| const rawVector = [0.1, 0.5, -0.2]; | |
| const points = layout.mapVectorTo3DPoints(rawVector, 1); | |
| expect(points.length).toBe(3); | |
| expect(points[0]).toBeInstanceOf(THREE.Vector3); | |
| expect(points[1].y).toBeCloseTo(50.0); | |
| expect(points[0].z).toBe(10.0); | |
| }); | |
| it('PointShader defines valid vertex and fragment shader GLSL sources', () => { | |
| expect(PointShader.vertexShader).toContain('attribute float aActivation'); | |
| expect(PointShader.fragmentShader).toContain('gl_FragColor'); | |
| expect(PointShader.fragmentShader).toContain('smoothstep'); | |
| }); | |
| it('MeshFactory enforces Invariant 3: frustumCulled = false on point cloud mesh', () => { | |
| const testPointsData = [ | |
| { position: new THREE.Vector3(0, 10, 0), activation: 0.8, size: 14.0 }, | |
| { position: new THREE.Vector3(5, 20, 5), activation: 0.3, size: 10.0 } | |
| ]; | |
| const pointsMesh = MeshFactory.createPointsMesh(testPointsData); | |
| expect(pointsMesh).toBeInstanceOf(THREE.Points); | |
| expect(pointsMesh.frustumCulled).toBe(false); | |
| }); | |
| it('Navigation bounds movement velocity smoothly without exponential buildup', () => { | |
| const camera = new THREE.PerspectiveCamera(); | |
| camera.position.set(0, 0, 0); | |
| const nav = new Navigation(camera); | |
| nav.keys.KeyW = true; | |
| // Simulate 60 frames holding W | |
| for (let i = 0; i < 60; i++) { | |
| nav.update(0.016); | |
| } | |
| // Velocity should be smoothly bounded around target speed per frame (~0.64), not 8x buildup | |
| expect(nav.velocity.length()).toBeLessThan(2.0); | |
| }); | |
| it('Navigation setNavigationView restores the captured default corridor pose', () => { | |
| const camera = new THREE.PerspectiveCamera(); | |
| const nav = new Navigation(camera); | |
| nav.setNavigationView(); | |
| expect(camera.position.x).toBeCloseTo(-178.3, 1); | |
| expect(camera.position.y).toBeCloseTo(13.5, 1); | |
| expect(camera.position.z).toBeCloseTo(52.2, 1); | |
| const deg2rad = Math.PI / 180; | |
| expect(nav.euler.x).toBeCloseTo(-5.4 * deg2rad, 3); | |
| expect(nav.euler.y).toBeCloseTo(-51.5 * deg2rad, 3); | |
| expect(nav.euler.z).toBeCloseTo(0, 3); | |
| expect(nav.velocity.length()).toBe(0); | |
| }); | |
| }); | |