/** Original low-poly demonstration meshes; no external assets/dependencies. */ import { mkdir, writeFile } from 'node:fs/promises' import { enuToGltf } from '../../src/modules/cockpit/services/pipeInstanceGltf.js' const out = new URL('../../public/cockpit/models/pipes/', import.meta.url) await mkdir(out, { recursive: true }) function geometry() { const positions = [], normals = [], indices = [] // Canonical ENU xyz -> standard glTF Y-up, Z-forward (Cesium corrects to ENU). const convert = enuToGltf function vertex(p, n) { const index = positions.length / 3 positions.push(...convert(p)); normals.push(...convert(n)) return index } function tube(path, radius, sides = 12) { const rings = [] for (let i = 0; i < path.length; i += 1) { const before = path[Math.max(0, i - 1)], after = path[Math.min(path.length - 1, i + 1)] const d = after.map((v, j) => v - before[j]), length = Math.hypot(...d) const tangent = d.map((v) => v / length) const axis = Math.abs(tangent[2]) < 0.9 ? [0, 0, 1] : [0, 1, 0] const cross = (a, b) => [a[1]*b[2]-a[2]*b[1], a[2]*b[0]-a[0]*b[2], a[0]*b[1]-a[1]*b[0]] const u0 = cross(tangent, axis), uLength = Math.hypot(...u0), u = u0.map((v) => v/uLength), v = cross(tangent, u) const ring = [] for (let j = 0; j < sides; j += 1) { const angle = j * Math.PI * 2 / sides const n = u.map((value, k) => value * Math.cos(angle) + v[k] * Math.sin(angle)) ring.push(vertex(path[i].map((value, k) => value + radius*n[k]), n)) } rings.push(ring) } for (let i = 1; i < rings.length; i += 1) for (let j = 0; j < sides; j += 1) { const k = (j+1)%sides, a = rings[i-1][j], b = rings[i-1][k], c = rings[i][j], d = rings[i][k] indices.push(a,b,c,b,d,c) } // Flat end caps use separate vertices for correct normals. for (const end of [0, path.length-1]) { const other = end === 0 ? 1 : end-1 const delta = path[end].map((value,k) => value-path[other][k]), len = Math.hypot(...delta), normal = delta.map((value)=>value/len) const center = vertex(path[end],normal), ring = [] for (const index of rings[end]) { // Reverse conversion of the already stored position. ring.push(vertex([positions[index*3+2],positions[index*3],positions[index*3+1]],normal)) } for (let j=0;j{ const a=-Math.PI/2+i*Math.PI/16; return [-.25+.5*Math.cos(a),.25+.5*Math.sin(a),0] }) g.tube(path,.12) } if (['branch','cross'].includes(kind)) { g.tube([[-.5,0,0],[.5,0,0]],.12) g.tube([[0,kind==='cross'?-.5:0,0],[0,.5,0]],.12) } if (kind === 'valve') { g.tube([[-.5,0,0],[.5,0,0]],.12) g.tube([[-.16,0,0],[.16,0,0]],.21) g.tube([[0,0,0],[0,0,.38]],.045) const circle = Array.from({length:17},(_,i)=>[.22*Math.cos(i*Math.PI/8),.22*Math.sin(i*Math.PI/8),.38]) g.tube(circle,.035,8) g.tube([[-.22,0,.38],[.22,0,.38]],.025,8) g.tube([[0,-.22,.38],[0,.22,.38]],.025,8) } const arrays = [new Float32Array(g.positions),new Float32Array(g.normals),new Uint16Array(g.indices)] const bytes = Buffer.concat(arrays.map((array)=>Buffer.from(array.buffer))) const min=[Infinity,Infinity,Infinity],max=[-Infinity,-Infinity,-Infinity] for(let i=0;i