File size: 2,700 Bytes
39508ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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);
  });
});