import { BufferGeometry } from '../core/BufferGeometry.js'; import { Float32BufferAttribute } from '../core/BufferAttribute.js'; import * as MathUtils from '../math/MathUtils.js'; import { Triangle } from '../math/Triangle.js'; import { Vector3 } from '../math/Vector3.js'; const _v0 = new Vector3(); const _v1 = new Vector3(); const _normal = new Vector3(); const _triangle = new Triangle(); class EdgesGeometry extends BufferGeometry { constructor(geometry = null, thresholdAngle = 1) { super(); this.type = 'EdgesGeometry'; this.parameters = { geometry: geometry, thresholdAngle: thresholdAngle, }; if (geometry !== null) { const precisionPoints = 4; const precision = Math.pow(10, precisionPoints); const thresholdDot = Math.cos(MathUtils.DEG2RAD * thresholdAngle); const indexAttr = geometry.getIndex(); const positionAttr = geometry.getAttribute('position'); const indexCount = indexAttr ? indexAttr.count : positionAttr.count; const indexArr = [0, 0, 0]; const vertKeys = ['a', 'b', 'c']; const hashes = new Array(3); const edgeData = {}; const vertices = []; for (let i = 0; i < indexCount; i += 3) { if (indexAttr) { indexArr[0] = indexAttr.getX(i); indexArr[1] = indexAttr.getX(i + 1); indexArr[2] = indexAttr.getX(i + 2); } else { indexArr[0] = i; indexArr[1] = i + 1; indexArr[2] = i + 2; } const { a, b, c } = _triangle; a.fromBufferAttribute(positionAttr, indexArr[0]); b.fromBufferAttribute(positionAttr, indexArr[1]); c.fromBufferAttribute(positionAttr, indexArr[2]); _triangle.getNormal(_normal); // create hashes for the edge from the vertices hashes[0] = `${Math.round(a.x * precision)},${Math.round(a.y * precision)},${Math.round(a.z * precision)}`; hashes[1] = `${Math.round(b.x * precision)},${Math.round(b.y * precision)},${Math.round(b.z * precision)}`; hashes[2] = `${Math.round(c.x * precision)},${Math.round(c.y * precision)},${Math.round(c.z * precision)}`; // skip degenerate triangles if (hashes[0] === hashes[1] || hashes[1] === hashes[2] || hashes[2] === hashes[0]) { continue; } // iterate over every edge for (let j = 0; j < 3; j++) { // get the first and next vertex making up the edge const jNext = (j + 1) % 3; const vecHash0 = hashes[j]; const vecHash1 = hashes[jNext]; const v0 = _triangle[vertKeys[j]]; const v1 = _triangle[vertKeys[jNext]]; const hash = `${vecHash0}_${vecHash1}`; const reverseHash = `${vecHash1}_${vecHash0}`; if (reverseHash in edgeData && edgeData[reverseHash]) { // if we found a sibling edge add it into the vertex array if // it meets the angle threshold and delete the edge from the map. if (_normal.dot(edgeData[reverseHash].normal) <= thresholdDot) { vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); } edgeData[reverseHash] = null; } else if (!(hash in edgeData)) { // if we've already got an edge here then skip adding a new one edgeData[hash] = { index0: indexArr[j], index1: indexArr[jNext], normal: _normal.clone(), }; } } } // iterate over all remaining, unmatched edges and add them to the vertex array for (const key in edgeData) { if (edgeData[key]) { const { index0, index1 } = edgeData[key]; _v0.fromBufferAttribute(positionAttr, index0); _v1.fromBufferAttribute(positionAttr, index1); vertices.push(_v0.x, _v0.y, _v0.z); vertices.push(_v1.x, _v1.y, _v1.z); } } this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); } } } export { EdgesGeometry };