// Minimal THREE.js mock for unit testing export class Vector2 { constructor(x = 0, y = 0) { this.x = x; this.y = y; } set(x, y) { this.x = x; this.y = y; return this; } copy(v) { this.x = v.x; this.y = v.y; return this; } clone() { return new Vector2(this.x, this.y); } } export class Vector3 { constructor(x = 0, y = 0, z = 0) { this.x = x; this.y = y; this.z = z; } set(x, y, z) { this.x = x; this.y = y; this.z = z; return this; } copy(v) { this.x = v.x; this.y = v.y; this.z = v.z; return this; } clone() { return new Vector3(this.x, this.y, this.z); } sub(v) { this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; } add(v) { this.x += v.x; this.y += v.y; this.z += v.z; return this; } multiplyScalar(s) { this.x *= s; this.y *= s; this.z *= s; return this; } equals(v) { return this.x === v.x && this.y === v.y && this.z === v.z; } } export class Euler { constructor(x = 0, y = 0, z = 0) { this.x = x; this.y = y; this.z = z; } copy(e) { this.x = e.x; this.y = e.y; this.z = e.z; return this; } } export class Color { constructor(c) { this._hex = typeof c === 'number' ? c : 0; } setHex(h) { this._hex = h; return this; } clone() { return new Color(this._hex); } } export class Object3D { constructor() { this.children = []; this.parent = null; this.position = new Vector3(); this.rotation = new Euler(); this.scale = new Vector3(1, 1, 1); this.userData = {}; } add(child) { this.children.push(child); child.parent = this; } remove(child) { const i = this.children.indexOf(child); if (i >= 0) this.children.splice(i, 1); child.parent = null; } traverse(fn) { fn(this); for (const child of this.children) child.traverse(fn); } } export class Group extends Object3D {} export class Scene extends Object3D { constructor() { super(); this.background = new Color(0); } } export class Mesh extends Object3D { constructor(geometry, material) { super(); this.geometry = geometry; this.material = material; this.isMesh = true; this.castShadow = false; this.receiveShadow = false; } } export class Sprite extends Object3D { constructor(material) { super(); this.material = material; } } export class LineSegments extends Object3D { constructor(geometry, material) { super(); this.geometry = geometry; this.material = material; } } export class BoxGeometry { constructor(w, h, d) { this.parameters = { width: w, height: h, depth: d }; } dispose() {} } export class PlaneGeometry { constructor(w, h) { this.parameters = { width: w, height: h }; } dispose() {} } export class CylinderGeometry { constructor(rT, rB, h, s) { this.parameters = { radiusTop: rT, radiusBottom: rB, height: h, radialSegments: s }; } dispose() {} } export class EdgesGeometry { constructor(geo) { this._source = geo; } dispose() {} } export class MeshStandardMaterial { constructor(opts = {}) { Object.assign(this, opts); this.emissive = new Color(0); } clone() { const m = new MeshStandardMaterial(); Object.assign(m, this); m.emissive = this.emissive.clone(); return m; } dispose() {} } export class MeshBasicMaterial { constructor(opts = {}) { Object.assign(this, opts); } dispose() {} } export class SpriteMaterial { constructor(opts = {}) { Object.assign(this, opts); } dispose() {} } export class LineBasicMaterial { constructor(opts = {}) { Object.assign(this, opts); } dispose() {} } export class CanvasTexture { constructor() {} dispose() {} } export class Plane { constructor(normal, constant) { this.normal = normal; this.constant = constant; } } export class Raycaster { constructor() { this.ray = { intersectPlane: () => new Vector3() }; } setFromCamera() {} intersectObject() { return []; } intersectObjects() { return []; } } export class PerspectiveCamera extends Object3D { constructor(fov, aspect, near, far) { super(); this.fov = fov; this.aspect = aspect; this.near = near; this.far = far; } lookAt() {} updateProjectionMatrix() {} } export class AmbientLight extends Object3D { constructor(color, intensity) { super(); this.color = new Color(color); this.intensity = intensity; this.isAmbientLight = true; } } export class DirectionalLight extends Object3D { constructor(color, intensity) { super(); this.color = new Color(color); this.intensity = intensity; this.isDirectionalLight = true; this.castShadow = false; this.shadow = { mapSize: { width: 0, height: 0 }, camera: { left: 0, right: 0, top: 0, bottom: 0, near: 0, far: 0 } }; } } export class GridHelper extends Object3D { constructor() { super(); } } export class WebGLRenderer { constructor() { this.domElement = { addEventListener: () => {}, removeEventListener: () => {}, getBoundingClientRect: () => ({ left: 0, top: 0, width: 800, height: 600 }), toDataURL: () => 'data:image/png;base64,fake' }; this.shadowMap = { enabled: false }; } setSize() {} setPixelRatio() {} getPixelRatio() { return 1; } getSize(v) { v.set(800, 600); } render() {} } export const DoubleSide = 2;