UNPKG

three-bvh-csg

Version:

A fast, flexible, dynamic CSG implementation on top of three-mesh-bvh

213 lines (128 loc) 3.82 kB
import { Vector3 } from 'three'; const DEGENERATE_EPSILON = 1e-8; const _tempVec = new Vector3(); export function toTriIndex( v ) { return ~ ~ ( v / 3 ); } export function toEdgeIndex( v ) { return v % 3; } export function sortEdgeFunc( a, b ) { return a.start - b.start; } export function getProjectedDistance( ray, vec ) { return _tempVec.subVectors( vec, ray.origin ).dot( ray.direction ); } export function hasOverlaps( arr ) { arr = [ ...arr ].sort( sortEdgeFunc ); for ( let i = 0, l = arr.length; i < l - 1; i ++ ) { const info0 = arr[ i ]; const info1 = arr[ i + 1 ]; if ( info1.start < info0.end && Math.abs( info1.start - info0.end ) > 1e-5 ) { return true; } } return false; } export function getEdgeSetLength( arr ) { let tot = 0; arr.forEach( ( { start, end } ) => tot += end - start ); return tot; } export function matchEdges( forward, reverse, disjointConnectivityMap, eps = DEGENERATE_EPSILON ) { forward.sort( sortEdgeFunc ); reverse.sort( sortEdgeFunc ); for ( let i = 0; i < forward.length; i ++ ) { const e0 = forward[ i ]; for ( let o = 0; o < reverse.length; o ++ ) { const e1 = reverse[ o ]; if ( e1.start > e0.end ) { // e2 is completely after e1 // break; // NOTE: there are cases where there are overlaps due to precision issues or // thin / degenerate triangles. Assuming the sibling side has the same issues // we let the matching work here. Long term we should remove the degenerate // triangles before this. } else if ( e0.end < e1.start || e1.end < e0.start ) { // e1 is completely before e2 continue; } else if ( e0.start <= e1.start && e0.end >= e1.end ) { // e1 is larger than and e2 is completely within e1 if ( ! areDistancesDegenerate( e1.end, e0.end ) ) { forward.splice( i + 1, 0, { start: e1.end, end: e0.end, index: e0.index, } ); } e0.end = e1.start; e1.start = 0; e1.end = 0; } else if ( e0.start >= e1.start && e0.end <= e1.end ) { // e2 is larger than and e1 is completely within e2 if ( ! areDistancesDegenerate( e0.end, e1.end ) ) { reverse.splice( o + 1, 0, { start: e0.end, end: e1.end, index: e1.index, } ); } e1.end = e0.start; e0.start = 0; e0.end = 0; } else if ( e0.start <= e1.start && e0.end <= e1.end ) { // e1 overlaps e2 at the beginning const tmp = e0.end; e0.end = e1.start; e1.start = tmp; } else if ( e0.start >= e1.start && e0.end >= e1.end ) { // e1 overlaps e2 at the end const tmp = e1.end; e1.end = e0.start; e0.start = tmp; } else { throw new Error(); } // Add the connectivity information if ( ! disjointConnectivityMap.has( e0.index ) ) { disjointConnectivityMap.set( e0.index, [] ); } if ( ! disjointConnectivityMap.has( e1.index ) ) { disjointConnectivityMap.set( e1.index, [] ); } disjointConnectivityMap .get( e0.index ) .push( e1.index ); disjointConnectivityMap .get( e1.index ) .push( e0.index ); if ( isEdgeDegenerate( e1 ) ) { reverse.splice( o, 1 ); o --; } if ( isEdgeDegenerate( e0 ) ) { // and if we have to remove the current original edge then exit this loop // so we can work on the next one forward.splice( i, 1 ); i --; break; } } } cleanUpEdgeSet( forward ); cleanUpEdgeSet( reverse ); function cleanUpEdgeSet( arr ) { for ( let i = 0; i < arr.length; i ++ ) { if ( isEdgeDegenerate( arr[ i ] ) ) { arr.splice( i, 1 ); i --; } } } function areDistancesDegenerate( start, end ) { return Math.abs( end - start ) < eps; } function isEdgeDegenerate( e ) { return Math.abs( e.end - e.start ) < eps; } }