UNPKG

three-to-cannon

Version:
1 lines 89.5 kB
{"version":3,"file":"three-to-cannon.cjs","sources":["../lib/ConvexHull.js","../src/utils.ts","../src/index.ts"],"sourcesContent":["import {\n\tLine3,\n\tPlane,\n\tTriangle,\n\tVector3\n} from 'three';\n/**\n * Ported from: https://github.com/maurizzzio/quickhull3d/ by Mauricio Poppe (https://github.com/maurizzzio)\n */\n\nvar ConvexHull = ( function () {\n\n\tvar Visible = 0;\n\tvar Deleted = 1;\n\n\tvar v1 = new Vector3();\n\n\tfunction ConvexHull() {\n\n\t\tthis.tolerance = - 1;\n\n\t\tthis.faces = []; // the generated faces of the convex hull\n\t\tthis.newFaces = []; // this array holds the faces that are generated within a single iteration\n\n\t\t// the vertex lists work as follows:\n\t\t//\n\t\t// let 'a' and 'b' be 'Face' instances\n\t\t// let 'v' be points wrapped as instance of 'Vertex'\n\t\t//\n\t\t// [v, v, ..., v, v, v, ...]\n\t\t// ^ ^\n\t\t// | |\n\t\t// a.outside b.outside\n\t\t//\n\t\tthis.assigned = new VertexList();\n\t\tthis.unassigned = new VertexList();\n\n\t\tthis.vertices = []; \t// vertices of the hull (internal representation of given geometry data)\n\n\t}\n\n\tObject.assign( ConvexHull.prototype, {\n\n\t\ttoJSON: function () {\n\t\t\t// Original ('src') indices do not include interior vertices,\n\t\t\t// but 'this.vertices' (the list they index) does. Output ('dst')\n\t\t\t// arrays have interior vertices omitted.\n\n\t\t\tconst srcIndices = this.faces.map((f) => f.toArray());\n\t\t\tconst uniqueSrcIndices = Array.from(new Set(srcIndices.flat())).sort();\n\n\t\t\t// Output vertex positions, omitting interior vertices.\n\t\t\tconst dstPositions = [];\n\t\t\tfor (let i = 0; i < uniqueSrcIndices.length; i++) {\n\t\t\t\tdstPositions.push(\n\t\t\t\t\tthis.vertices[uniqueSrcIndices[i]].point.x,\n\t\t\t\t\tthis.vertices[uniqueSrcIndices[i]].point.y,\n\t\t\t\t\tthis.vertices[uniqueSrcIndices[i]].point.z,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Mapping from 'src' (this.vertices) to 'dst' (dstPositions) indices.\n\t\t\tconst srcToDstIndexMap = new Map();\n\t\t\tfor (let i = 0; i < uniqueSrcIndices.length; i++) {\n\t\t\t\tsrcToDstIndexMap.set(uniqueSrcIndices[i], i);\n\t\t\t}\n\n\t\t\t// Output triangles, as indices on dstPositions.\n\t\t\tconst dstIndices = [];\n\t\t\tfor (let i = 0; i < srcIndices.length; i++) {\n\t\t\t\tdstIndices.push([\n\t\t\t\t\tsrcToDstIndexMap.get(srcIndices[i][0]),\n\t\t\t\t\tsrcToDstIndexMap.get(srcIndices[i][1]),\n\t\t\t\t\tsrcToDstIndexMap.get(srcIndices[i][2]),\n\t\t\t\t]);\n\t\t\t}\n\n\t\t\treturn [dstPositions, dstIndices];\n\t\t},\n\n\t\tsetFromPoints: function ( points ) {\n\n\t\t\tif ( Array.isArray( points ) !== true ) {\n\n\t\t\t\tconsole.error( 'THREE.ConvexHull: Points parameter is not an array.' );\n\n\t\t\t}\n\n\t\t\tif ( points.length < 4 ) {\n\n\t\t\t\tconsole.error( 'THREE.ConvexHull: The algorithm needs at least four points.' );\n\n\t\t\t}\n\n\t\t\tthis.makeEmpty();\n\n\t\t\tfor ( var i = 0, l = points.length; i < l; i ++ ) {\n\n\t\t\t\tthis.vertices.push( new VertexNode( points[ i ], i ) );\n\n\t\t\t}\n\n\t\t\tthis.compute();\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tsetFromObject: function ( object ) {\n\n\t\t\tvar points = [];\n\n\t\t\tobject.updateMatrixWorld( true );\n\n\t\t\tobject.traverse( function ( node ) {\n\n\t\t\t\tvar i, l, point;\n\n\t\t\t\tvar geometry = node.geometry;\n\n\t\t\t\tif ( geometry === undefined ) return;\n\n\t\t\t\tif ( geometry.isGeometry ) {\n\n\t\t\t\t\tgeometry = geometry.toBufferGeometry\n\t\t\t\t\t\t? geometry.toBufferGeometry()\n\t\t\t\t\t\t: new BufferGeometry().fromGeometry( geometry );\n\n\t\t\t\t}\n\n\t\t\t\tif ( geometry.isBufferGeometry ) {\n\n\t\t\t\t\tvar attribute = geometry.attributes.position;\n\n\t\t\t\t\tif ( attribute !== undefined ) {\n\n\t\t\t\t\t\tfor ( i = 0, l = attribute.count; i < l; i ++ ) {\n\n\t\t\t\t\t\t\tpoint = new Vector3();\n\n\t\t\t\t\t\t\tpoint.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld );\n\n\t\t\t\t\t\t\tpoints.push( point );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t} );\n\n\t\t\treturn this.setFromPoints( points );\n\n\t\t},\n\n\t\tcontainsPoint: function ( point ) {\n\n\t\t\tvar faces = this.faces;\n\n\t\t\tfor ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n\t\t\t\tvar face = faces[ i ];\n\n\t\t\t\t// compute signed distance and check on what half space the point lies\n\n\t\t\t\tif ( face.distanceToPoint( point ) > this.tolerance ) return false;\n\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t},\n\n\t\tintersectRay: function ( ray, target ) {\n\n\t\t\t// based on \"Fast Ray-Convex Polyhedron Intersection\" by Eric Haines, GRAPHICS GEMS II\n\n\t\t\tvar faces = this.faces;\n\n\t\t\tvar tNear = - Infinity;\n\t\t\tvar tFar = Infinity;\n\n\t\t\tfor ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n\t\t\t\tvar face = faces[ i ];\n\n\t\t\t\t// interpret faces as planes for the further computation\n\n\t\t\t\tvar vN = face.distanceToPoint( ray.origin );\n\t\t\t\tvar vD = face.normal.dot( ray.direction );\n\n\t\t\t\t// if the origin is on the positive side of a plane (so the plane can \"see\" the origin) and\n\t\t\t\t// the ray is turned away or parallel to the plane, there is no intersection\n\n\t\t\t\tif ( vN > 0 && vD >= 0 ) return null;\n\n\t\t\t\t// compute the distance from the ray’s origin to the intersection with the plane\n\n\t\t\t\tvar t = ( vD !== 0 ) ? ( - vN / vD ) : 0;\n\n\t\t\t\t// only proceed if the distance is positive. a negative distance means the intersection point\n\t\t\t\t// lies \"behind\" the origin\n\n\t\t\t\tif ( t <= 0 ) continue;\n\n\t\t\t\t// now categorized plane as front-facing or back-facing\n\n\t\t\t\tif ( vD > 0 ) {\n\n\t\t\t\t\t// plane faces away from the ray, so this plane is a back-face\n\n\t\t\t\t\ttFar = Math.min( t, tFar );\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// front-face\n\n\t\t\t\t\ttNear = Math.max( t, tNear );\n\n\t\t\t\t}\n\n\t\t\t\tif ( tNear > tFar ) {\n\n\t\t\t\t\t// if tNear ever is greater than tFar, the ray must miss the convex hull\n\n\t\t\t\t\treturn null;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// evaluate intersection point\n\n\t\t\t// always try tNear first since its the closer intersection point\n\n\t\t\tif ( tNear !== - Infinity ) {\n\n\t\t\t\tray.at( tNear, target );\n\n\t\t\t} else {\n\n\t\t\t\tray.at( tFar, target );\n\n\t\t\t}\n\n\t\t\treturn target;\n\n\t\t},\n\n\t\tintersectsRay: function ( ray ) {\n\n\t\t\treturn this.intersectRay( ray, v1 ) !== null;\n\n\t\t},\n\n\t\tmakeEmpty: function () {\n\n\t\t\tthis.faces = [];\n\t\t\tthis.vertices = [];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Adds a vertex to the 'assigned' list of vertices and assigns it to the given face\n\n\t\taddVertexToFace: function ( vertex, face ) {\n\n\t\t\tvertex.face = face;\n\n\t\t\tif ( face.outside === null ) {\n\n\t\t\t\tthis.assigned.append( vertex );\n\n\t\t\t} else {\n\n\t\t\t\tthis.assigned.insertBefore( face.outside, vertex );\n\n\t\t\t}\n\n\t\t\tface.outside = vertex;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Removes a vertex from the 'assigned' list of vertices and from the given face\n\n\t\tremoveVertexFromFace: function ( vertex, face ) {\n\n\t\t\tif ( vertex === face.outside ) {\n\n\t\t\t\t// fix face.outside link\n\n\t\t\t\tif ( vertex.next !== null && vertex.next.face === face ) {\n\n\t\t\t\t\t// face has at least 2 outside vertices, move the 'outside' reference\n\n\t\t\t\t\tface.outside = vertex.next;\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// vertex was the only outside vertex that face had\n\n\t\t\t\t\tface.outside = null;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.assigned.remove( vertex );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Removes all the visible vertices that a given face is able to see which are stored in the 'assigned' vertext list\n\n\t\tremoveAllVerticesFromFace: function ( face ) {\n\n\t\t\tif ( face.outside !== null ) {\n\n\t\t\t\t// reference to the first and last vertex of this face\n\n\t\t\t\tvar start = face.outside;\n\t\t\t\tvar end = face.outside;\n\n\t\t\t\twhile ( end.next !== null && end.next.face === face ) {\n\n\t\t\t\t\tend = end.next;\n\n\t\t\t\t}\n\n\t\t\t\tthis.assigned.removeSubList( start, end );\n\n\t\t\t\t// fix references\n\n\t\t\t\tstart.prev = end.next = null;\n\t\t\t\tface.outside = null;\n\n\t\t\t\treturn start;\n\n\t\t\t}\n\n\t\t},\n\n\t\t// Removes all the visible vertices that 'face' is able to see\n\n\t\tdeleteFaceVertices: function ( face, absorbingFace ) {\n\n\t\t\tvar faceVertices = this.removeAllVerticesFromFace( face );\n\n\t\t\tif ( faceVertices !== undefined ) {\n\n\t\t\t\tif ( absorbingFace === undefined ) {\n\n\t\t\t\t\t// mark the vertices to be reassigned to some other face\n\n\t\t\t\t\tthis.unassigned.appendChain( faceVertices );\n\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// if there's an absorbing face try to assign as many vertices as possible to it\n\n\t\t\t\t\tvar vertex = faceVertices;\n\n\t\t\t\t\tdo {\n\n\t\t\t\t\t\t// we need to buffer the subsequent vertex at this point because the 'vertex.next' reference\n\t\t\t\t\t\t// will be changed by upcoming method calls\n\n\t\t\t\t\t\tvar nextVertex = vertex.next;\n\n\t\t\t\t\t\tvar distance = absorbingFace.distanceToPoint( vertex.point );\n\n\t\t\t\t\t\t// check if 'vertex' is able to see 'absorbingFace'\n\n\t\t\t\t\t\tif ( distance > this.tolerance ) {\n\n\t\t\t\t\t\t\tthis.addVertexToFace( vertex, absorbingFace );\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\tthis.unassigned.append( vertex );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// now assign next vertex\n\n\t\t\t\t\t\tvertex = nextVertex;\n\n\t\t\t\t\t} while ( vertex !== null );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Reassigns as many vertices as possible from the unassigned list to the new faces\n\n\t\tresolveUnassignedPoints: function ( newFaces ) {\n\n\t\t\tif ( this.unassigned.isEmpty() === false ) {\n\n\t\t\t\tvar vertex = this.unassigned.first();\n\n\t\t\t\tdo {\n\n\t\t\t\t\t// buffer 'next' reference, see .deleteFaceVertices()\n\n\t\t\t\t\tvar nextVertex = vertex.next;\n\n\t\t\t\t\tvar maxDistance = this.tolerance;\n\n\t\t\t\t\tvar maxFace = null;\n\n\t\t\t\t\tfor ( var i = 0; i < newFaces.length; i ++ ) {\n\n\t\t\t\t\t\tvar face = newFaces[ i ];\n\n\t\t\t\t\t\tif ( face.mark === Visible ) {\n\n\t\t\t\t\t\t\tvar distance = face.distanceToPoint( vertex.point );\n\n\t\t\t\t\t\t\tif ( distance > maxDistance ) {\n\n\t\t\t\t\t\t\t\tmaxDistance = distance;\n\t\t\t\t\t\t\t\tmaxFace = face;\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ( maxDistance > 1000 * this.tolerance ) break;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t\t// 'maxFace' can be null e.g. if there are identical vertices\n\n\t\t\t\t\tif ( maxFace !== null ) {\n\n\t\t\t\t\t\tthis.addVertexToFace( vertex, maxFace );\n\n\t\t\t\t\t}\n\n\t\t\t\t\tvertex = nextVertex;\n\n\t\t\t\t} while ( vertex !== null );\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Computes the extremes of a simplex which will be the initial hull\n\n\t\tcomputeExtremes: function () {\n\n\t\t\tvar min = new Vector3();\n\t\t\tvar max = new Vector3();\n\n\t\t\tvar minVertices = [];\n\t\t\tvar maxVertices = [];\n\n\t\t\tvar i, l, j;\n\n\t\t\t// initially assume that the first vertex is the min/max\n\n\t\t\tfor ( i = 0; i < 3; i ++ ) {\n\n\t\t\t\tminVertices[ i ] = maxVertices[ i ] = this.vertices[ 0 ];\n\n\t\t\t}\n\n\t\t\tmin.copy( this.vertices[ 0 ].point );\n\t\t\tmax.copy( this.vertices[ 0 ].point );\n\n\t\t\t// compute the min/max vertex on all six directions\n\n\t\t\tfor ( i = 0, l = this.vertices.length; i < l; i ++ ) {\n\n\t\t\t\tvar vertex = this.vertices[ i ];\n\t\t\t\tvar point = vertex.point;\n\n\t\t\t\t// update the min coordinates\n\n\t\t\t\tfor ( j = 0; j < 3; j ++ ) {\n\n\t\t\t\t\tif ( point.getComponent( j ) < min.getComponent( j ) ) {\n\n\t\t\t\t\t\tmin.setComponent( j, point.getComponent( j ) );\n\t\t\t\t\t\tminVertices[ j ] = vertex;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// update the max coordinates\n\n\t\t\t\tfor ( j = 0; j < 3; j ++ ) {\n\n\t\t\t\t\tif ( point.getComponent( j ) > max.getComponent( j ) ) {\n\n\t\t\t\t\t\tmax.setComponent( j, point.getComponent( j ) );\n\t\t\t\t\t\tmaxVertices[ j ] = vertex;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// use min/max vectors to compute an optimal epsilon\n\n\t\t\tthis.tolerance = 3 * Number.EPSILON * (\n\t\t\t\tMath.max( Math.abs( min.x ), Math.abs( max.x ) ) +\n\t\t\t\tMath.max( Math.abs( min.y ), Math.abs( max.y ) ) +\n\t\t\t\tMath.max( Math.abs( min.z ), Math.abs( max.z ) )\n\t\t\t);\n\n\t\t\treturn { min: minVertices, max: maxVertices };\n\n\t\t},\n\n\t\t// Computes the initial simplex assigning to its faces all the points\n\t\t// that are candidates to form part of the hull\n\n\t\tcomputeInitialHull: function () {\n\n\t\t\tvar line3, plane, closestPoint;\n\n\t\t\treturn function computeInitialHull() {\n\n\t\t\t\tif ( line3 === undefined ) {\n\n\t\t\t\t\tline3 = new Line3();\n\t\t\t\t\tplane = new Plane();\n\t\t\t\t\tclosestPoint = new Vector3();\n\n\t\t\t\t}\n\n\t\t\t\tvar vertex, vertices = this.vertices;\n\t\t\t\tvar extremes = this.computeExtremes();\n\t\t\t\tvar min = extremes.min;\n\t\t\t\tvar max = extremes.max;\n\n\t\t\t\tvar v0, v1, v2, v3;\n\t\t\t\tvar i, l, j;\n\n\t\t\t\t// 1. Find the two vertices 'v0' and 'v1' with the greatest 1d separation\n\t\t\t\t// (max.x - min.x)\n\t\t\t\t// (max.y - min.y)\n\t\t\t\t// (max.z - min.z)\n\n\t\t\t\tvar distance, maxDistance = 0;\n\t\t\t\tvar index = 0;\n\n\t\t\t\tfor ( i = 0; i < 3; i ++ ) {\n\n\t\t\t\t\tdistance = max[ i ].point.getComponent( i ) - min[ i ].point.getComponent( i );\n\n\t\t\t\t\tif ( distance > maxDistance ) {\n\n\t\t\t\t\t\tmaxDistance = distance;\n\t\t\t\t\t\tindex = i;\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tv0 = min[ index ];\n\t\t\t\tv1 = max[ index ];\n\n\t\t\t\t// 2. The next vertex 'v2' is the one farthest to the line formed by 'v0' and 'v1'\n\n\t\t\t\tmaxDistance = 0;\n\t\t\t\tline3.set( v0.point, v1.point );\n\n\t\t\t\tfor ( i = 0, l = this.vertices.length; i < l; i ++ ) {\n\n\t\t\t\t\tvertex = vertices[ i ];\n\n\t\t\t\t\tif ( vertex !== v0 && vertex !== v1 ) {\n\n\t\t\t\t\t\tline3.closestPointToPoint( vertex.point, true, closestPoint );\n\n\t\t\t\t\t\tdistance = closestPoint.distanceToSquared( vertex.point );\n\n\t\t\t\t\t\tif ( distance > maxDistance ) {\n\n\t\t\t\t\t\t\tmaxDistance = distance;\n\t\t\t\t\t\t\tv2 = vertex;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// 3. The next vertex 'v3' is the one farthest to the plane 'v0', 'v1', 'v2'\n\n\t\t\t\tmaxDistance = - 1;\n\t\t\t\tplane.setFromCoplanarPoints( v0.point, v1.point, v2.point );\n\n\t\t\t\tfor ( i = 0, l = this.vertices.length; i < l; i ++ ) {\n\n\t\t\t\t\tvertex = vertices[ i ];\n\n\t\t\t\t\tif ( vertex !== v0 && vertex !== v1 && vertex !== v2 ) {\n\n\t\t\t\t\t\tdistance = Math.abs( plane.distanceToPoint( vertex.point ) );\n\n\t\t\t\t\t\tif ( distance > maxDistance ) {\n\n\t\t\t\t\t\t\tmaxDistance = distance;\n\t\t\t\t\t\t\tv3 = vertex;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tvar faces = [];\n\n\t\t\t\tif ( plane.distanceToPoint( v3.point ) < 0 ) {\n\n\t\t\t\t\t// the face is not able to see the point so 'plane.normal' is pointing outside the tetrahedron\n\n\t\t\t\t\tfaces.push(\n\t\t\t\t\t\tFace.create( v0, v1, v2 ),\n\t\t\t\t\t\tFace.create( v3, v1, v0 ),\n\t\t\t\t\t\tFace.create( v3, v2, v1 ),\n\t\t\t\t\t\tFace.create( v3, v0, v2 )\n\t\t\t\t\t);\n\n\t\t\t\t\t// set the twin edge\n\n\t\t\t\t\tfor ( i = 0; i < 3; i ++ ) {\n\n\t\t\t\t\t\tj = ( i + 1 ) % 3;\n\n\t\t\t\t\t\t// join face[ i ] i > 0, with the first face\n\n\t\t\t\t\t\tfaces[ i + 1 ].getEdge( 2 ).setTwin( faces[ 0 ].getEdge( j ) );\n\n\t\t\t\t\t\t// join face[ i ] with face[ i + 1 ], 1 <= i <= 3\n\n\t\t\t\t\t\tfaces[ i + 1 ].getEdge( 1 ).setTwin( faces[ j + 1 ].getEdge( 0 ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// the face is able to see the point so 'plane.normal' is pointing inside the tetrahedron\n\n\t\t\t\t\tfaces.push(\n\t\t\t\t\t\tFace.create( v0, v2, v1 ),\n\t\t\t\t\t\tFace.create( v3, v0, v1 ),\n\t\t\t\t\t\tFace.create( v3, v1, v2 ),\n\t\t\t\t\t\tFace.create( v3, v2, v0 )\n\t\t\t\t\t);\n\n\t\t\t\t\t// set the twin edge\n\n\t\t\t\t\tfor ( i = 0; i < 3; i ++ ) {\n\n\t\t\t\t\t\tj = ( i + 1 ) % 3;\n\n\t\t\t\t\t\t// join face[ i ] i > 0, with the first face\n\n\t\t\t\t\t\tfaces[ i + 1 ].getEdge( 2 ).setTwin( faces[ 0 ].getEdge( ( 3 - i ) % 3 ) );\n\n\t\t\t\t\t\t// join face[ i ] with face[ i + 1 ]\n\n\t\t\t\t\t\tfaces[ i + 1 ].getEdge( 0 ).setTwin( faces[ j + 1 ].getEdge( 1 ) );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// the initial hull is the tetrahedron\n\n\t\t\t\tfor ( i = 0; i < 4; i ++ ) {\n\n\t\t\t\t\tthis.faces.push( faces[ i ] );\n\n\t\t\t\t}\n\n\t\t\t\t// initial assignment of vertices to the faces of the tetrahedron\n\n\t\t\t\tfor ( i = 0, l = vertices.length; i < l; i ++ ) {\n\n\t\t\t\t\tvertex = vertices[ i ];\n\n\t\t\t\t\tif ( vertex !== v0 && vertex !== v1 && vertex !== v2 && vertex !== v3 ) {\n\n\t\t\t\t\t\tmaxDistance = this.tolerance;\n\t\t\t\t\t\tvar maxFace = null;\n\n\t\t\t\t\t\tfor ( j = 0; j < 4; j ++ ) {\n\n\t\t\t\t\t\t\tdistance = this.faces[ j ].distanceToPoint( vertex.point );\n\n\t\t\t\t\t\t\tif ( distance > maxDistance ) {\n\n\t\t\t\t\t\t\t\tmaxDistance = distance;\n\t\t\t\t\t\t\t\tmaxFace = this.faces[ j ];\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( maxFace !== null ) {\n\n\t\t\t\t\t\t\tthis.addVertexToFace( vertex, maxFace );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\t// Removes inactive faces\n\n\t\treindexFaces: function () {\n\n\t\t\tvar activeFaces = [];\n\n\t\t\tfor ( var i = 0; i < this.faces.length; i ++ ) {\n\n\t\t\t\tvar face = this.faces[ i ];\n\n\t\t\t\tif ( face.mark === Visible ) {\n\n\t\t\t\t\tactiveFaces.push( face );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tthis.faces = activeFaces;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Finds the next vertex to create faces with the current hull\n\n\t\tnextVertexToAdd: function () {\n\n\t\t\t// if the 'assigned' list of vertices is empty, no vertices are left. return with 'undefined'\n\n\t\t\tif ( this.assigned.isEmpty() === false ) {\n\n\t\t\t\tvar eyeVertex, maxDistance = 0;\n\n\t\t\t\t// grap the first available face and start with the first visible vertex of that face\n\n\t\t\t\tvar eyeFace = this.assigned.first().face;\n\t\t\t\tvar vertex = eyeFace.outside;\n\n\t\t\t\t// now calculate the farthest vertex that face can see\n\n\t\t\t\tdo {\n\n\t\t\t\t\tvar distance = eyeFace.distanceToPoint( vertex.point );\n\n\t\t\t\t\tif ( distance > maxDistance ) {\n\n\t\t\t\t\t\tmaxDistance = distance;\n\t\t\t\t\t\teyeVertex = vertex;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tvertex = vertex.next;\n\n\t\t\t\t} while ( vertex !== null && vertex.face === eyeFace );\n\n\t\t\t\treturn eyeVertex;\n\n\t\t\t}\n\n\t\t},\n\n\t\t// Computes a chain of half edges in CCW order called the 'horizon'.\n\t\t// For an edge to be part of the horizon it must join a face that can see\n\t\t// 'eyePoint' and a face that cannot see 'eyePoint'.\n\n\t\tcomputeHorizon: function ( eyePoint, crossEdge, face, horizon ) {\n\n\t\t\t// moves face's vertices to the 'unassigned' vertex list\n\n\t\t\tthis.deleteFaceVertices( face );\n\n\t\t\tface.mark = Deleted;\n\n\t\t\tvar edge;\n\n\t\t\tif ( crossEdge === null ) {\n\n\t\t\t\tedge = crossEdge = face.getEdge( 0 );\n\n\t\t\t} else {\n\n\t\t\t\t// start from the next edge since 'crossEdge' was already analyzed\n\t\t\t\t// (actually 'crossEdge.twin' was the edge who called this method recursively)\n\n\t\t\t\tedge = crossEdge.next;\n\n\t\t\t}\n\n\t\t\tdo {\n\n\t\t\t\tvar twinEdge = edge.twin;\n\t\t\t\tvar oppositeFace = twinEdge.face;\n\n\t\t\t\tif ( oppositeFace.mark === Visible ) {\n\n\t\t\t\t\tif ( oppositeFace.distanceToPoint( eyePoint ) > this.tolerance ) {\n\n\t\t\t\t\t\t// the opposite face can see the vertex, so proceed with next edge\n\n\t\t\t\t\t\tthis.computeHorizon( eyePoint, twinEdge, oppositeFace, horizon );\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// the opposite face can't see the vertex, so this edge is part of the horizon\n\n\t\t\t\t\t\thorizon.push( edge );\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tedge = edge.next;\n\n\t\t\t} while ( edge !== crossEdge );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Creates a face with the vertices 'eyeVertex.point', 'horizonEdge.tail' and 'horizonEdge.head' in CCW order\n\n\t\taddAdjoiningFace: function ( eyeVertex, horizonEdge ) {\n\n\t\t\t// all the half edges are created in ccw order thus the face is always pointing outside the hull\n\n\t\t\tvar face = Face.create( eyeVertex, horizonEdge.tail(), horizonEdge.head() );\n\n\t\t\tthis.faces.push( face );\n\n\t\t\t// join face.getEdge( - 1 ) with the horizon's opposite edge face.getEdge( - 1 ) = face.getEdge( 2 )\n\n\t\t\tface.getEdge( - 1 ).setTwin( horizonEdge.twin );\n\n\t\t\treturn face.getEdge( 0 ); // the half edge whose vertex is the eyeVertex\n\n\n\t\t},\n\n\t\t// Adds 'horizon.length' faces to the hull, each face will be linked with the\n\t\t// horizon opposite face and the face on the left/right\n\n\t\taddNewFaces: function ( eyeVertex, horizon ) {\n\n\t\t\tthis.newFaces = [];\n\n\t\t\tvar firstSideEdge = null;\n\t\t\tvar previousSideEdge = null;\n\n\t\t\tfor ( var i = 0; i < horizon.length; i ++ ) {\n\n\t\t\t\tvar horizonEdge = horizon[ i ];\n\n\t\t\t\t// returns the right side edge\n\n\t\t\t\tvar sideEdge = this.addAdjoiningFace( eyeVertex, horizonEdge );\n\n\t\t\t\tif ( firstSideEdge === null ) {\n\n\t\t\t\t\tfirstSideEdge = sideEdge;\n\n\t\t\t\t} else {\n\n\t\t\t\t\t// joins face.getEdge( 1 ) with previousFace.getEdge( 0 )\n\n\t\t\t\t\tsideEdge.next.setTwin( previousSideEdge );\n\n\t\t\t\t}\n\n\t\t\t\tthis.newFaces.push( sideEdge.face );\n\t\t\t\tpreviousSideEdge = sideEdge;\n\n\t\t\t}\n\n\t\t\t// perform final join of new faces\n\n\t\t\tfirstSideEdge.next.setTwin( previousSideEdge );\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Adds a vertex to the hull\n\n\t\taddVertexToHull: function ( eyeVertex ) {\n\n\t\t\tvar horizon = [];\n\n\t\t\tthis.unassigned.clear();\n\n\t\t\t// remove 'eyeVertex' from 'eyeVertex.face' so that it can't be added to the 'unassigned' vertex list\n\n\t\t\tthis.removeVertexFromFace( eyeVertex, eyeVertex.face );\n\n\t\t\tthis.computeHorizon( eyeVertex.point, null, eyeVertex.face, horizon );\n\n\t\t\tthis.addNewFaces( eyeVertex, horizon );\n\n\t\t\t// reassign 'unassigned' vertices to the new faces\n\n\t\t\tthis.resolveUnassignedPoints( this.newFaces );\n\n\t\t\treturn\tthis;\n\n\t\t},\n\n\t\tcleanup: function () {\n\n\t\t\tthis.assigned.clear();\n\t\t\tthis.unassigned.clear();\n\t\t\tthis.newFaces = [];\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tcompute: function () {\n\n\t\t\tvar vertex;\n\n\t\t\tthis.computeInitialHull();\n\n\t\t\t// add all available vertices gradually to the hull\n\n\t\t\twhile ( ( vertex = this.nextVertexToAdd() ) !== undefined ) {\n\n\t\t\t\tthis.addVertexToHull( vertex );\n\n\t\t\t}\n\n\t\t\tthis.reindexFaces();\n\n\t\t\tthis.cleanup();\n\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t//\n\n\tfunction Face() {\n\n\t\tthis.normal = new Vector3();\n\t\tthis.midpoint = new Vector3();\n\t\tthis.area = 0;\n\n\t\tthis.constant = 0; // signed distance from face to the origin\n\t\tthis.outside = null; // reference to a vertex in a vertex list this face can see\n\t\tthis.mark = Visible;\n\t\tthis.edge = null;\n\n\t}\n\n\tObject.assign( Face, {\n\n\t\tcreate: function ( a, b, c ) {\n\n\t\t\tvar face = new Face();\n\n\t\t\tvar e0 = new HalfEdge( a, face );\n\t\t\tvar e1 = new HalfEdge( b, face );\n\t\t\tvar e2 = new HalfEdge( c, face );\n\n\t\t\t// join edges\n\n\t\t\te0.next = e2.prev = e1;\n\t\t\te1.next = e0.prev = e2;\n\t\t\te2.next = e1.prev = e0;\n\n\t\t\t// main half edge reference\n\n\t\t\tface.edge = e0;\n\n\t\t\treturn face.compute();\n\n\t\t}\n\n\t} );\n\n\tObject.assign( Face.prototype, {\n\n\t\ttoArray: function () {\n\t\t\tconst indices = [];\n\t\t\tlet edge = this.edge;\n\t\t\tdo {\n\t\t\t\tindices.push(edge.head().index);\n\t\t\t\tedge = edge.next;\n\t\t\t} while (edge !== this.edge);\n\t\t\treturn indices;\n\t\t},\n\n\t\tgetEdge: function ( i ) {\n\n\t\t\tvar edge = this.edge;\n\n\t\t\twhile ( i > 0 ) {\n\n\t\t\t\tedge = edge.next;\n\t\t\t\ti --;\n\n\t\t\t}\n\n\t\t\twhile ( i < 0 ) {\n\n\t\t\t\tedge = edge.prev;\n\t\t\t\ti ++;\n\n\t\t\t}\n\n\t\t\treturn edge;\n\n\t\t},\n\n\t\tcompute: function () {\n\n\t\t\tvar triangle;\n\n\t\t\treturn function compute() {\n\n\t\t\t\tif ( triangle === undefined ) triangle = new Triangle();\n\n\t\t\t\tvar a = this.edge.tail();\n\t\t\t\tvar b = this.edge.head();\n\t\t\t\tvar c = this.edge.next.head();\n\n\t\t\t\ttriangle.set( a.point, b.point, c.point );\n\n\t\t\t\ttriangle.getNormal( this.normal );\n\t\t\t\ttriangle.getMidpoint( this.midpoint );\n\t\t\t\tthis.area = triangle.getArea();\n\n\t\t\t\tthis.constant = this.normal.dot( this.midpoint );\n\n\t\t\t\treturn this;\n\n\t\t\t};\n\n\t\t}(),\n\n\t\tdistanceToPoint: function ( point ) {\n\n\t\t\treturn this.normal.dot( point ) - this.constant;\n\n\t\t}\n\n\t} );\n\n\t// Entity for a Doubly-Connected Edge List (DCEL).\n\n\tfunction HalfEdge( vertex, face ) {\n\n\t\tthis.vertex = vertex;\n\t\tthis.prev = null;\n\t\tthis.next = null;\n\t\tthis.twin = null;\n\t\tthis.face = face;\n\n\t}\n\n\tObject.assign( HalfEdge.prototype, {\n\n\t\thead: function () {\n\n\t\t\treturn this.vertex;\n\n\t\t},\n\n\t\ttail: function () {\n\n\t\t\treturn this.prev ? this.prev.vertex : null;\n\n\t\t},\n\n\t\tlength: function () {\n\n\t\t\tvar head = this.head();\n\t\t\tvar tail = this.tail();\n\n\t\t\tif ( tail !== null ) {\n\n\t\t\t\treturn tail.point.distanceTo( head.point );\n\n\t\t\t}\n\n\t\t\treturn - 1;\n\n\t\t},\n\n\t\tlengthSquared: function () {\n\n\t\t\tvar head = this.head();\n\t\t\tvar tail = this.tail();\n\n\t\t\tif ( tail !== null ) {\n\n\t\t\t\treturn tail.point.distanceToSquared( head.point );\n\n\t\t\t}\n\n\t\t\treturn - 1;\n\n\t\t},\n\n\t\tsetTwin: function ( edge ) {\n\n\t\t\tthis.twin = edge;\n\t\t\tedge.twin = this;\n\n\t\t\treturn this;\n\n\t\t}\n\n\t} );\n\n\t// A vertex as a double linked list node.\n\n\tfunction VertexNode( point, index ) {\n\n\t\tthis.point = point;\n\t\t// index in the input array\n\t\tthis.index = index;\n\t\tthis.prev = null;\n\t\tthis.next = null;\n\t\t// the face that is able to see this vertex\n\t\tthis.face = null;\n\n\t}\n\n\t// A double linked list that contains vertex nodes.\n\n\tfunction VertexList() {\n\n\t\tthis.head = null;\n\t\tthis.tail = null;\n\n\t}\n\n\tObject.assign( VertexList.prototype, {\n\n\t\tfirst: function () {\n\n\t\t\treturn this.head;\n\n\t\t},\n\n\t\tlast: function () {\n\n\t\t\treturn this.tail;\n\n\t\t},\n\n\t\tclear: function () {\n\n\t\t\tthis.head = this.tail = null;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Inserts a vertex before the target vertex\n\n\t\tinsertBefore: function ( target, vertex ) {\n\n\t\t\tvertex.prev = target.prev;\n\t\t\tvertex.next = target;\n\n\t\t\tif ( vertex.prev === null ) {\n\n\t\t\t\tthis.head = vertex;\n\n\t\t\t} else {\n\n\t\t\t\tvertex.prev.next = vertex;\n\n\t\t\t}\n\n\t\t\ttarget.prev = vertex;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Inserts a vertex after the target vertex\n\n\t\tinsertAfter: function ( target, vertex ) {\n\n\t\t\tvertex.prev = target;\n\t\t\tvertex.next = target.next;\n\n\t\t\tif ( vertex.next === null ) {\n\n\t\t\t\tthis.tail = vertex;\n\n\t\t\t} else {\n\n\t\t\t\tvertex.next.prev = vertex;\n\n\t\t\t}\n\n\t\t\ttarget.next = vertex;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Appends a vertex to the end of the linked list\n\n\t\tappend: function ( vertex ) {\n\n\t\t\tif ( this.head === null ) {\n\n\t\t\t\tthis.head = vertex;\n\n\t\t\t} else {\n\n\t\t\t\tthis.tail.next = vertex;\n\n\t\t\t}\n\n\t\t\tvertex.prev = this.tail;\n\t\t\tvertex.next = null; // the tail has no subsequent vertex\n\n\t\t\tthis.tail = vertex;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Appends a chain of vertices where 'vertex' is the head.\n\n\t\tappendChain: function ( vertex ) {\n\n\t\t\tif ( this.head === null ) {\n\n\t\t\t\tthis.head = vertex;\n\n\t\t\t} else {\n\n\t\t\t\tthis.tail.next = vertex;\n\n\t\t\t}\n\n\t\t\tvertex.prev = this.tail;\n\n\t\t\t// ensure that the 'tail' reference points to the last vertex of the chain\n\n\t\t\twhile ( vertex.next !== null ) {\n\n\t\t\t\tvertex = vertex.next;\n\n\t\t\t}\n\n\t\t\tthis.tail = vertex;\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Removes a vertex from the linked list\n\n\t\tremove: function ( vertex ) {\n\n\t\t\tif ( vertex.prev === null ) {\n\n\t\t\t\tthis.head = vertex.next;\n\n\t\t\t} else {\n\n\t\t\t\tvertex.prev.next = vertex.next;\n\n\t\t\t}\n\n\t\t\tif ( vertex.next === null ) {\n\n\t\t\t\tthis.tail = vertex.prev;\n\n\t\t\t} else {\n\n\t\t\t\tvertex.next.prev = vertex.prev;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\t// Removes a list of vertices whose 'head' is 'a' and whose 'tail' is b\n\n\t\tremoveSubList: function ( a, b ) {\n\n\t\t\tif ( a.prev === null ) {\n\n\t\t\t\tthis.head = b.next;\n\n\t\t\t} else {\n\n\t\t\t\ta.prev.next = b.next;\n\n\t\t\t}\n\n\t\t\tif ( b.next === null ) {\n\n\t\t\t\tthis.tail = a.prev;\n\n\t\t\t} else {\n\n\t\t\t\tb.next.prev = a.prev;\n\n\t\t\t}\n\n\t\t\treturn this;\n\n\t\t},\n\n\t\tisEmpty: function () {\n\n\t\t\treturn this.head === null;\n\n\t\t}\n\n\t} );\n\n\treturn ConvexHull;\n\n} )();\n\nexport { ConvexHull };\n","import { BufferAttribute, BufferGeometry, Mesh, Object3D, Quaternion, Vector3 } from 'three';\n\nconst _v1 = new Vector3();\nconst _v2 = new Vector3();\nconst _q1 = new Quaternion();\n\n/**\n* Returns a single geometry for the given object. If the object is compound,\n* its geometries are automatically merged. Bake world scale into each\n* geometry, because we can't easily apply that to the cannonjs shapes later.\n*/\nexport function getGeometry (object: Object3D): BufferGeometry | null {\n\tconst meshes = getMeshes(object);\n\tif (meshes.length === 0) return null;\n\n\t// Single mesh. Return, preserving original type.\n\tif (meshes.length === 1) {\n\t\treturn normalizeGeometry(meshes[0]);\n\t}\n\n\t// Multiple meshes. Merge and return.\n\tlet mesh: Mesh | undefined;\n\tconst geometries: BufferGeometry[] = [];\n\twhile ((mesh = meshes.pop())) {\n\t\tgeometries.push(simplifyGeometry(normalizeGeometry(mesh)));\n\t}\n\n\treturn mergeBufferGeometries(geometries);\n}\n\nfunction normalizeGeometry (mesh: Mesh): BufferGeometry {\n\t// Preserve original type, e.g. CylinderBufferGeometry.\n\tconst geometry: BufferGeometry = mesh.geometry.clone();\n\n\tmesh.updateMatrixWorld();\n\tmesh.matrixWorld.decompose(_v1, _q1, _v2);\n\tgeometry.scale(_v2.x, _v2.y, _v2.z);\n\treturn geometry;\n}\n\n/**\n * Greatly simplified version of BufferGeometryUtils.mergeBufferGeometries.\n * Because we only care about the vertex positions, and not the indices or\n * other attributes, we throw everything else away.\n */\nfunction mergeBufferGeometries (geometries: BufferGeometry[]): BufferGeometry {\n\tlet vertexCount = 0;\n\tfor (let i = 0; i < geometries.length; i++) {\n\t\tconst position = geometries[i].attributes.position;\n\t\tif (position && position.itemSize === 3) {\n\t\t\tvertexCount += position.count;\n\t\t}\n\t}\n\n\tconst positionArray = new Float32Array(vertexCount * 3);\n\n\tlet positionOffset = 0;\n\tfor (let i = 0; i < geometries.length; i++) {\n\t\tconst position = geometries[i].attributes.position;\n\t\tif (position && position.itemSize === 3) {\n\t\t\tfor (let j = 0; j < position.count; j++) {\n\t\t\t\tpositionArray[positionOffset++] = position.getX(j);\n\t\t\t\tpositionArray[positionOffset++] = position.getY(j);\n\t\t\t\tpositionArray[positionOffset++] = position.getZ(j);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn new BufferGeometry().setAttribute('position', new BufferAttribute(positionArray, 3));\n}\n\nexport function getVertices (geometry: BufferGeometry): Float32Array {\n\tconst position = geometry.attributes.position;\n\tconst vertices = new Float32Array(position.count * 3);\n\tfor (let i = 0; i < position.count; i++) {\n\t\tvertices[i * 3] = position.getX(i);\n\t\tvertices[i * 3 + 1] = position.getY(i);\n\t\tvertices[i * 3 + 2] = position.getZ(i);\n\t}\n\treturn vertices;\n}\n\n/**\n* Returns a flat array of THREE.Mesh instances from the given object. If\n* nested transformations are found, they are applied to child meshes\n* as mesh.userData.matrix, so that each mesh has its position/rotation/scale\n* independently of all of its parents except the top-level object.\n*/\nfunction getMeshes (object: Object3D): Mesh[] {\n\tconst meshes: Mesh[] = [];\n\tobject.traverse(function (o) {\n\t\tif ((o as Mesh).isMesh) {\n\t\t\tmeshes.push(o as Mesh);\n\t\t}\n\t});\n\treturn meshes;\n}\n\nexport function getComponent(v: Vector3, component: string): number {\n\tswitch(component) {\n\t\tcase 'x': return v.x;\n\t\tcase 'y': return v.y;\n\t\tcase 'z': return v.z;\n\t}\n\tthrow new Error(`Unexpected component ${component}`);\n}\n\n/**\n* Modified version of BufferGeometryUtils.mergeVertices, ignoring vertex\n* attributes other than position.\n*\n* @param {THREE.BufferGeometry} geometry\n* @param {number} tolerance\n* @return {THREE.BufferGeometry>}\n*/\nfunction simplifyGeometry (geometry: BufferGeometry, tolerance = 1e-4): BufferGeometry {\n\n\ttolerance = Math.max( tolerance, Number.EPSILON );\n\n\t// Generate an index buffer if the geometry doesn't have one, or optimize it\n\t// if it's already available.\n\tconst hashToIndex: {[key: string]: number} = {};\n\tconst indices = geometry.getIndex();\n\tconst positions = geometry.getAttribute( 'position' );\n\tconst vertexCount = indices ? indices.count : positions.count;\n\n\t// Next value for triangle indices.\n\tlet nextIndex = 0;\n\n\tconst newIndices = [];\n\tconst newPositions = [];\n\n\t// Convert the error tolerance to an amount of decimal places to truncate to.\n\tconst decimalShift = Math.log10( 1 / tolerance );\n\tconst shiftMultiplier = Math.pow( 10, decimalShift );\n\n\tfor ( let i = 0; i < vertexCount; i ++ ) {\n\n\t\tconst index = indices ? indices.getX( i ) : i;\n\n\t\t// Generate a hash for the vertex attributes at the current index 'i'.\n\t\tlet hash = '';\n\n\t\t// Double tilde truncates the decimal value.\n\t\thash += `${ ~ ~ ( positions.getX( index ) * shiftMultiplier ) },`;\n\t\thash += `${ ~ ~ ( positions.getY( index ) * shiftMultiplier ) },`;\n\t\thash += `${ ~ ~ ( positions.getZ( index ) * shiftMultiplier ) },`;\n\n\t\t// Add another reference to the vertex if it's already\n\t\t// used by another index.\n\t\tif ( hash in hashToIndex ) {\n\n\t\t\tnewIndices.push( hashToIndex[ hash ] );\n\n\t\t} else {\n\n\t\t\tnewPositions.push( positions.getX( index ) );\n\t\t\tnewPositions.push( positions.getY( index ) );\n\t\t\tnewPositions.push( positions.getZ( index ) );\n\n\t\t\thashToIndex[ hash ] = nextIndex;\n\t\t\tnewIndices.push( nextIndex );\n\t\t\tnextIndex ++;\n\n\t\t}\n\n\t}\n\n\t// Construct merged BufferGeometry.\n\n\tconst positionAttribute = new BufferAttribute(\n\t\tnew Float32Array( newPositions ),\n\t\tpositions.itemSize,\n\t\tpositions.normalized\n\t);\n\n\tconst result = new BufferGeometry();\n\tresult.setAttribute( 'position', positionAttribute );\n\tresult.setIndex( newIndices );\n\n\treturn result;\n\n}\n","import { Box, ConvexPolyhedron, Cylinder, Quaternion as CQuaternion, Shape, Sphere, Trimesh, Vec3 } from 'cannon-es';\nimport { Box3, BufferGeometry, CylinderGeometry, MathUtils, Mesh, Object3D, SphereGeometry, Vector3 } from 'three';\nimport { ConvexHull } from '../lib/ConvexHull';\nimport { getComponent, getGeometry, getVertices } from './utils';\n\nconst PI_2 = Math.PI / 2;\n\nexport type BoxParameters = { x: number, y: number, z: number };\n\nexport type CylinderParameters = { radiusTop: number, radiusBottom: number, height: number, segments: number };\n\nexport type SphereParameters = { radius: number };\n\nexport type ConvexPolyhedronParameters = { vertices: Float32Array, faces: number[][] };\n\nexport type TrimeshParameters = { vertices: Float32Array, indices: Uint32Array };\n\ntype ShapeTypeToShapeParameters = {\n\tBox: BoxParameters,\n\tCylinder: CylinderParameters,\n\tSphere: SphereParameters,\n\tConvexPolyhedron: ConvexPolyhedronParameters,\n\tTrimesh: TrimeshParameters,\n};\n\nexport enum ShapeType {\n\tBOX = 'Box',\n\tCYLINDER = 'Cylinder',\n\tSPHERE = 'Sphere',\n\tHULL = 'ConvexPolyhedron',\n\tMESH = 'Trimesh',\n}\n\nexport interface ShapeOptions {\n\ttype?: ShapeType,\n\tcylinderAxis?: 'x' | 'y' | 'z',\n\tsphereRadius?: number,\n}\n\nexport interface ShapeParameters<T extends ShapeType = ShapeType> {\n\ttype: T,\n\tparams: ShapeTypeToShapeParameters[T],\n\toffset?: Vec3,\n\torientation?: CQuaternion,\n}\n\nexport interface ShapeResult<T extends Shape = Shape> {\n\tshape: T,\n\toffset?: Vec3,\n\torientation?: CQuaternion,\n}\n\n/**\n * Given a THREE.Object3D instance, creates parameters for a CANNON shape.\n */\nexport const getShapeParameters = function (object: Object3D, options: ShapeOptions = {}): ShapeParameters | null {\n\tlet geometry: BufferGeometry | null;\n\n\tif (options.type === ShapeType.BOX) {\n\t\treturn getBoundingBoxParameters(object);\n\t} else if (options.type === ShapeType.CYLINDER) {\n\t\treturn getBoundingCylinderParameters(object, options);\n\t} else if (options.type === ShapeType.SPHERE) {\n\t\treturn getBoundingSphereParameters(object, options);\n\t} else if (options.type === ShapeType.HULL) {\n\t\treturn getConvexPolyhedronParameters(object);\n\t} else if (options.type === ShapeType.MESH) {\n\t\tgeometry = getGeometry(object);\n\t\treturn geometry ? getTrimeshParameters(geometry) : null;\n\t} else if (options.type) {\n\t\tthrow new Error(`[CANNON.getShapeParameters] Invalid type \"${options.type}\".`);\n\t}\n\n\tgeometry = getGeometry(object);\n\tif (!geometry) return null;\n\n\tswitch (geometry.type) {\n\t\tcase 'BoxGeometry':\n\t\tcase 'BoxBufferGeometry':\n\t\t\treturn getBoxParameters(geometry);\n\t\tcase 'CylinderGeometry':\n\t\tcase 'CylinderBufferGeometry':\n\t\t\treturn getCylinderParameters(geometry as CylinderGeometry);\n\t\tcase 'PlaneGeometry':\n\t\tcase 'PlaneBufferGeometry':\n\t\t\treturn getPlaneParameters(geometry);\n\t\tcase 'SphereGeometry':\n\t\tcase 'SphereBufferGeometry':\n\t\t\treturn getSphereParameters(geometry as SphereGeometry);\n\t\tcase 'TubeGeometry':\n\t\tcase 'BufferGeometry':\n\t\t\treturn getBoundingBoxParameters(object);\n\t\tdefault:\n\t\t\tconsole.warn(\n\t\t\t\t'Unrecognized geometry: \"%s\". Using bounding box as shape.', geometry.type\n\t\t\t);\n\t\t\treturn getBoxParameters(geometry);\n\t}\n};\n\n/**\n * Given a THREE.Object3D instance, creates a corresponding CANNON shape.\n */\nexport const threeToCannon = function (object: Object3D, options: ShapeOptions = {}): ShapeResult | null {\n\tconst shapeParameters = getShapeParameters(object, options);\n\tif (!shapeParameters) {\n\t\treturn null;\n\t}\n\n\tconst { type, params, offset, orientation } = shapeParameters;\n\n\tlet shape: Shape;\n\tif (type === ShapeType.BOX) {\n\t\tshape = createBox(params as BoxParameters);\n\t} else if (type === ShapeType.CYLINDER) {\n\t\tshape = createCylinder(params as CylinderParameters);\n\t} else if (type === ShapeType.SPHERE) {\n\t\tshape = createSphere(params as SphereParameters);\n\t} else if (type === ShapeType.HULL) {\n\t\tshape = createConvexPolyhedron(params as ConvexPolyhedronParameters);\n\t} else {\n\t\tshape = createTrimesh(params as TrimeshParameters);\n\t}\n\n\treturn {\n\t\tshape,\n\t\toffset,\n\t\torientation,\n\t};\n};\n\n/******************************************************************************\n * Shape construction\n */\n\n function createBox (params: BoxParameters): Box {\n\tconst { x, y, z } = params;\n\tconst shape = new Box(new Vec3(x, y, z));\n\treturn shape;\n}\n\nfunction createCylinder (params: CylinderParameters): Cylinder {\n\tconst { radiusTop, radiusBottom, height, segments } = params;\n\n\tconst shape = new Cylinder(radiusTop, radiusBottom, height, segments);\n\n\t// Include metadata for serialization.\n\t// TODO(cleanup): Is this still necessary?\n\tshape.radiusTop = radiusBottom;\n\tshape.radiusBottom = radiusBottom;\n\tshape.height = height;\n\tshape.numSegments = segments;\n\n\treturn shape;\n}\n\nfunction createSphere (params: SphereParameters): Sphere {\n\tconst shape = new Sphere(params.radius);\n\n\treturn shape;\n}\n\nfunction createConvexPolyhedron (params: ConvexPolyhedronParameters): ConvexPolyhedron {\n\tconst { faces, vertices: verticesArray } = params;\n\n\tconst vertices: Vec3[] = [];\n\tfor (let i = 0; i < verticesArray.length; i += 3) {\n\t\tvertices.push(new Vec3(\n\t\t\tverticesArray[i],\n\t\t\tverticesArray[i + 1],\n\t\t\tverticesArray[i + 2]\n\t\t));\n\t}\n\n\tconst shape = new ConvexPolyhedron({\n\t\tfaces,\n\t\tvertices\n\t});\n\n\treturn shape;\n}\n\nfunction createTrimesh (params: TrimeshParameters): Trimesh {\n\tconst { vertices, indices } = params\n\tconst shape = new Trimesh(\n\t\tvertices as unknown as number[],\n\t\tindices as unknown as number[],\n\t);\n\n\treturn shape;\n}\n\n/******************************************************************************\n * Shape parameters\n */\n\nfunction getBoxParameters (geometry: BufferGeometry): ShapeParameters<ShapeType.BOX> | null {\n\tconst vertices = getVertices(geometry);\n\n\tif (!vertices.length) return null;\n\n\tgeometry.computeBoundingBox();\n\tconst box = geometry.boundingBox!;\n\n\treturn {\n\t\ttype: ShapeType.BOX,\n\t\tparams: {\n\t\t\tx: (box.max.x - box.min.x) / 2,\n\t\t\ty: (box.max.y - box.min.y) / 2,\n\t\t\tz: (box.max.z - box.min.z) / 2,\n\t\t},\n\t};\n}\n\n/** Bounding box needs to be computed with the entire subtree, not just geometry. */\nfunction getBoundingBoxParameters (object: Object3D): ShapeParameters<ShapeType.BOX> | null {\n\tconst clone = object.clone();\n\tclone.quaternion.set(0, 0, 0, 1);\n\tclone.updateMatrixWorld();\n\n\tconst box = new Box3().setFromObject(clone);\n\n\tif (!isFinite(box.min.lengthSq())) return null;\n\n\tconst localPosition = box.translate(clone.position.negate()).getCenter(new Vector3());\n\n\treturn {\n\t\ttype: ShapeType.BOX,\n\t\tparams: {\n\t\t\tx: (box.max.x - box.min.x) / 2,\n\t\t\ty: (box.max.y - box.min.y) / 2,\n\t\t\tz: (box.max.z - box.min.z) / 2,\n\t\t},\n\t\toffset: localPosition.lengthSq()\n\t\t\t? new Vec3(localPosition.x, localPosition.y, localPosition.z)\n\t\t\t: undefined,\n\t};\n}\n\n/** Computes 3D convex hull as a CANNON.ConvexPolyhedron. */\nfunction getConvexPolyhedronParameters (object: Object3D): ShapeParameters<ShapeType.HULL> | null {\n\tconst geometry = getGeometry(object);\n\n\tif (!geometry) return null;\n\n\t// Perturb.\n\tconst eps = 1e-4;\n\tfor (let i = 0; i < geometry.attributes.position.count; i++) {\n\t\tgeometry.attributes.position.setXYZ(\n\t\t\ti,\n\t\t\tgeometry.attributes.position.getX(i) + (Math.random() - 0.5) * eps,\n\t\t\tgeometry.attributes.position.getY(i) + (Math.random() - 0.5) * eps,\n\t\t\tgeometry.attributes.position.getZ(i) + (Math.random() - 0.5) * eps,\n\t\t);\n\t}\n\n\t// Compute the 3D convex hull and collect convex hull vertices and faces.\n\tconst [ positions, indices ] = new ConvexHull()\n\t\t.setFromObject(new Mesh(geometry))\n\t\t.toJSON();\n\n\treturn {\n\t\ttype: ShapeType.HULL,\n\t\tparams: {\n\t\t\tvertices: new Float32Array(positions),\n\t\t\tfaces: indices,\n\t\t},\n\t};\n}\n\nfunction getCylinderParameters (\n\tgeometry: CylinderGeometry\n): ShapeParameters<ShapeType.CYLINDER> | null {\n\tconst params = geometry.parameters;\n\n\treturn {\n\t\ttype: ShapeType.CYLINDER,\n\t\tparams: {\n\t\t\tradiusTop: params.radiusTop,\n\t\t\tradiusBottom: params.radiusBottom,\n\t\t\theight: params.height,\n\t\t\tsegments: params.radialSegments,\n\t\t},\n\t\torientation: new CQuaternion()\n\t\t\t.setFromEuler(MathUtils.degToRad(-90), 0, 0, 'XYZ')\n\t\t\t.normalize(),\n\t}\n}\n\nfunction getBoundingCylinderParameters (\n\tobject: Object3D,\n\toptions: ShapeOptions\n): ShapeParameters<ShapeType.CYLINDER> | null {\n\tconst axes = ['x', 'y', 'z'];\n\tconst majorAxis = options.cylinderAxis || 'y';\n\tconst minorAxes = axes.splice(axes.indexOf(majorAxis), 1) && axes;\n\tconst box = new Box3().setFromObject(object);\n\n\tif (!isFinite(box.min.lengthSq())) return null;\n\n\t// Compute cylinder dimensions.\n\tconst height = box.max[majorAxis] - box.min[majorAxis];\n\tconst radius = 0.5 * Math.max(\n\t\tgetComponent(box.max, minorAxes[0]) - getComponent(box.min, minorAxes[0]),\n\t\tgetComponent(box.max, minorAxes[1]) - getComponent(box.min, minorAxes[1]),\n\t);\n\n\tconst eulerX = majorAxis === 'y' ? PI_2 : 0;\n\tconst eulerY = majorAxis === 'z' ? PI_2 : 0;\n\n\treturn {\n\t\ttype: ShapeType.CYLINDER,\n\t\tparams: {\n\t\t\tradiusTop: radius,\n\t\t\tradiusBottom: radius,\n\t\t\theight,\n\t\t\tsegments: 12,\n\t\t},\n\t\torientation: new CQuaternion()\n\t\t\t.setFromEuler(eulerX, eulerY, 0, 'XYZ')\n\t\t\t.normalize(),\n\t};\n}\n\nfunction getPlaneParameters (geometry: BufferGeometry): ShapeParameters<ShapeType.BOX> | null {\n\tgeometry.computeBoundingBox();\n\tconst box = geometry.boundingBox!;\n\n\treturn {\n\t\ttype: ShapeType.BOX,\n\t\tparams: {\n\t\t\tx: (box.max.x - box.min.x) / 2 || 0.1,\n\t\t\ty: (box.max.y - box.min.y) / 2 || 0.1,\n\t\t\tz: (box.max.z - box.min.z) / 2 || 0.1,\n\t\t},\n\t};\n}\n\nfunction getSphereParameters (geometry: SphereGeometry): ShapeParameters<ShapeType.SPHERE> | null {\n\treturn {\n\t\ttype: ShapeType.SPHERE,\n\t\tparams: { radius: geometry.parameters.radius },\n\t};\n}\n\nfunction getBoundingSphereParameters (\n\tobject: Object3D,\n\toptions: ShapeOptions\n): ShapeParameters<ShapeType.SPHERE> | null {\n\tif (options.sphereRadius) {\n\t\treturn {\n\t\t\ttype: ShapeType.SPHERE,\n\t\t\tparams: { radius: options.sphereRadius },\n\t\t};\n\t}\n\tconst geometry = getGeometry(object);\n\tif (!geometry) return null;\n\tgeometry.computeBoundingSphere();\n\n\treturn {\n\t\ttype: ShapeType.SPHERE,\n\t\tparams: { radius: geometry.boundingSphere!.radius },\n\t};\n}\n\nfunction getTrimeshParameters (geometry: BufferGeometry): ShapeParameters<ShapeType.MESH> | null {\n\tconst vertices = getVertices(geometry);\n\n\tif (!vertices.length) return null;\n\n\tconst indices = new Uint32Array(vertices.length);\n\tfor (let i = 0; i < vertices.length; i++) {\n\t\tindices[i] = i;\n\t}\n\n\treturn {\n\t\ttype: ShapeType.MESH,\n\t\tparams: {\n\t\t\tvertices,\n\t\t\tindices,\n\t\t},\n\t};\n}\n"],"names":["ConvexHull","Visible","Deleted","v1","Vector3","tolerance","faces","newFaces","assigned","VertexList","unassigned","vertices","Object","assign","prototype","toJSON","srcIndices","map","f","toArray","uniqueSrcIndices","Array","from","Set","flat","sort","dstPositions","i","length","push","point","x","y","z","srcToDstIndexMap","Map","set","dstIndices","get","setFromPoints","points","isArray","console","error","makeEmpty","l","VertexNode","compute","setFromObject","object","updateMatrixWorld","traverse","node","geometry","undefined","isGeometry","toBufferGeometry","BufferGeometry","fromGeometry","isBufferGeometry","attribute","attributes","position","count","fromBufferAttribute","applyMatrix4","matrixWorld","containsPoint","face","distanceToPoint","intersectRay","ray","target","tNear","Infinity","tFar","vN","origin","vD","normal","dot","direction","t","Math","min","max","at","intersectsRay","addVertexToFace","vertex","outside","append","insertBefore","removeVertexFromFace","next","remove","removeAllVerticesFromFace","start","end","removeSubList","prev","deleteFaceVertices","absorbingFace","faceVertices","appendChain","nextVertex","distance","resolveUnassignedPoints","isEmpty","first","maxDistance","maxFace","mark","computeExtremes","minVertices","maxVertices","j","copy","getComponent","setComponent","Number","EPSILON","abs","computeInitialHull","line3","plane","closestPoint","Line3","Plane","extremes","v0","v2","v3","index","closestPointToPoint","distanceToSquared","setFromCoplanarPoints","Face","create","getEdge","setTwin","reindexFaces","activeFaces","nextVertexToAdd","eyeVertex","eyeFace","computeHorizon","eyePoint","crossEdge","horizon","edge","twinEdge","twin","oppositeFace","addAdjoiningFace","horizonEdge","tail","head","addNewFaces","firstSideEdge","previousSideEdge","sideEdge","addVertexToHull","clear","cleanup","midpoint","area","constant","a","b","c","e0","HalfEdge","e1","e2","indices","triangle","Triangle","getNormal","getMidpoint","getArea","distanceTo","lengthSquared","last","insertAfter","_v1","_v2","_q1","Quaternion","getGeometry","meshes","getMeshes","normalizeGeometry","mesh","geometries","pop","simplifyGeometry","mergeBufferGeometries","clone","decompose","scale","vertexCount","itemSize","positionArray","Float32Array","positionOffset","getX","getY","getZ","setAttribute","BufferAttribute","getVertices","o","isMesh","v","component","Error","hashToIndex","getIndex","positions","getAttribute","nextIndex","newIndices","newPositions","decimalShift","log10","shiftMultiplier","pow","hash","positionAttribute","normalized","result","setIndex","PI_2","PI","ShapeType","getShapeParameters","options","type","BOX","getBoundingBoxParameters","CYLINDER","getBoundingCylinderParameters","SPHERE","getBoundingSphereParameters","HULL","getConvexPolyhedronParameters","MESH","getTrimeshParameters","getBoxParameters","getCylinderParameters","getPlaneParameters","getSphereParameters","warn","threeToCannon","shapeParameters","params","offset","orientation","shape","createBox","createCylinder","createSphere","createConvexPolyhedron","createTrimesh","Box","Vec3","radiusTop","radiusBottom","height","segments","Cylinder","numSegments","Sphere","radius","verticesArray","ConvexPolyhedron","Trimesh","computeBoundingBox","box","boundingBox","quaternion","Box3","isFinite","lengthSq","localPosition","translate","negate","getCenter","eps","setXYZ","random","Mesh","parameters","radialSegments","CQuaternion","setFromEuler","MathUtils","degToRad","normalize","axes","majorAxis","cylinderAxis","minorAxes","splice","indexOf","eulerX","eulerY","sphereRadius","computeBoundingSphere","boundingSphere","Uint32Array"],"mappings":";;;AAMA;AACA;AACA;;AAEA,