UNPKG

web-ifc-viewer

Version:
142 lines 6.17 kB
import * as THREE from 'three'; import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils'; import { MeshBVH } from 'three-mesh-bvh'; import { compressEdgeOverlaps, edgesToGeometry, generateEdges, getProjectedOverlaps, isLineAbovePlane, isLineTriangleEdge, isYProjectedLineDegenerate, isYProjectedTriangleDegenerate, overlapsToLines, trimToBeneathTriPlane } from './edgeUtils'; // Source: https://github.com/gkjohnson/three-mesh-bvh/blob/master/example/edgeProjection.js export class EdgeProjector { constructor(context) { this.context = context; this.params = { displayModel: 'color', displayEdges: false, displayProjection: true, useBVH: true, sortEdges: true, amount: 50, color: 0x030303 }; this.projectedEdges = []; } dispose() { this.projectedEdges.forEach((edge) => { edge.geometry.dispose(); if (Array.isArray(edge.material)) edge.material.forEach((mat) => mat.dispose()); else edge.material.dispose(); }); this.projectedEdges = []; } async projectEdges(model) { const scene = this.context.getScene(); // create projection display mesh const projection = new THREE.LineSegments(new THREE.BufferGeometry(), new THREE.LineBasicMaterial({ color: this.params.color })); let task = this.updateEdges(scene, this.params, model, projection); while (task) { const res = task.next(); if (res.done) { task = null; } } this.projectedEdges.push(projection); return projection; } *updateEdges(scene, params, model, projection) { scene.remove(projection); // transform and merge geometries to project into a single model const geometries = []; model.updateWorldMatrix(true, true); const clone = model.geometry.clone(); clone.applyMatrix4(model.matrixWorld); for (const key in clone.attributes) { if (key !== 'position') { clone.deleteAttribute(key); } } geometries.push(clone); const mergedGeometry = mergeBufferGeometries(geometries, false); geometries.length = 0; clone.dispose(); yield; // generate the bvh for acceleration const bvh = new MeshBVH(mergedGeometry); yield; // generate the candidate edges const edges = generateEdges(mergedGeometry, new THREE.Vector3(0, 1, 0), 50); if (params.sortEdges) { edges.sort((a, b) => { return Math.min(a.start.y, a.end.y) - Math.min(b.start.y, b.end.y); }); } yield; scene.add(projection); // trim the candidate edges const finalEdges = []; const tempLine = new THREE.Line3(); const tempRay = new THREE.Ray(); const tempVec = new THREE.Vector3(); for (let i = 0, l = edges.length; i < l; i++) { const line = edges[i]; if (isYProjectedLineDegenerate(line)) { continue; } const lowestLineY = Math.min(line.start.y, line.end.y); const overlaps = []; bvh.shapecast({ intersectsBounds: (box) => { if (!params.useBVH) { return true; } // check if the box bounds are above the lowest line point box.min.y = Math.min(lowestLineY, box.min.y); tempRay.origin.copy(line.start); line.delta(tempRay.direction).normalize(); if (box.containsPoint(tempRay.origin)) { return true; } if (tempRay.intersectBox(box, tempVec)) { return tempRay.origin.distanceToSquared(tempVec) < line.distanceSq(); } return false; }, intersectsTriangle: (tri) => { // skip the triangle if it is completely below the line const highestTriangleY = Math.max(tri.a.y, tri.b.y, tri.c.y); if (highestTriangleY < lowestLineY) { return false; } // if the projected triangle is just a line then don't check it if (isYProjectedTriangleDegenerate(tri)) { return false; } // if this line lies on a triangle edge then don't check it if (isLineTriangleEdge(tri, line)) { return false; } trimToBeneathTriPlane(tri, line, tempLine); if (isLineAbovePlane(tri.plane, tempLine)) { return false; } if (tempLine.distance() < 1e-10) { return false; } // compress the edge overlaps so we can easily tell if the whole edge is hidden already // and exit early if (getProjectedOverlaps(tri, line, overlaps)) { compressEdgeOverlaps(overlaps); } // if we're hiding the edge entirely now then skip further checks if (overlaps.length !== 0) { const [d0, d1] = overlaps[overlaps.length - 1]; return d0 === 0.0 && d1 === 1.0; } return false; } }); overlapsToLines(line, overlaps, finalEdges); } projection.geometry.dispose(); projection.geometry = edgesToGeometry(finalEdges, 0); } } //# sourceMappingURL=edge-projection.js.map