import { sourceTerrainToThreeMatrix } from '../sand-vortex/terrain-geometry.mjs'; import { Vector3 } from 'three'; const BOX_FACES = [ [0, 3, 2], [0, 2, 1], [4, 5, 6], [4, 6, 7], [0, 1, 5], [0, 5, 4], [1, 2, 6], [1, 6, 5], [2, 3, 7], [2, 7, 6], [3, 0, 4], [3, 4, 7], ]; /** Match the native ROS exporter: inverse primary TRS followed by reflected Z. */ export function sourceAnchorSupportGeometry(points, placement, { halfExtent = 60, thickness = 3, metersPerSourceUnit = 1, omitPointIndices = [], } = {}) { if (!Array.isArray(points) || !Number.isFinite(halfExtent) || halfExtent <= 0 || !Number.isFinite(thickness) || thickness <= 0) throw new Error('Invalid source-anchor support dimensions.'); const transform = sourceTerrainToThreeMatrix(placement, metersPerSourceUnit); if (!Array.isArray(omitPointIndices) || new Set(omitPointIndices).size !== omitPointIndices.length || omitPointIndices.some(i => !Number.isInteger(i) || i < 0 || i >= points.length)) throw new Error('Invalid source-anchor support omissions.'); const omitted = new Set(omitPointIndices); const faces = transform.determinant() < 0 ? BOX_FACES.map(([a, b, c]) => [a, c, b]) : BOX_FACES; const positions = [], indices = [], navigationPositions = []; for (const [pointIndex, point] of points.entries()) { if (omitted.has(pointIndex)) continue; if (!Array.isArray(point) || point.length !== 3 || !point.every(Number.isFinite)) throw new Error('Invalid source-anchor support point.'); const [x, y, z] = point, h = halfExtent; const vertices = [[x-h,y,z-h],[x+h,y,z-h],[x+h,y,z+h],[x-h,y,z+h], [x-h,y-thickness,z-h],[x+h,y-thickness,z-h],[x+h,y-thickness,z+h],[x-h,y-thickness,z+h]] .map(p => new Vector3(...p).applyMatrix4(transform).toArray()); const offset = positions.length / 3; positions.push(...vertices.flat()); for (const face of faces) indices.push(...face.map(i => offset + i)); for (const face of faces.slice(0, 2)) for (const i of face) navigationPositions.push(...vertices[i]); } return { positions, indices, navigationPositions }; }