@math.gl/geometry 5.0.0-alpha.2 → 5.0.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +97 -49
- package/README.md +16 -1
- package/THIRD-PARTY-NOTICES.md +18 -0
- package/dist/culling/constants.d.ts +3 -0
- package/dist/culling/constants.d.ts.map +1 -0
- package/dist/culling/constants.js +5 -0
- package/dist/culling/constants.js.map +1 -0
- package/dist/culling/index.cjs +2014 -0
- package/dist/culling/index.cjs.map +6 -0
- package/dist/culling/index.d.ts +29 -0
- package/dist/culling/index.d.ts.map +1 -0
- package/dist/culling/index.js +23 -0
- package/dist/culling/index.js.map +1 -0
- package/dist/culling/lib/algorithms/bounding-box-from-points.d.ts +15 -0
- package/dist/culling/lib/algorithms/bounding-box-from-points.d.ts.map +1 -0
- package/dist/culling/lib/algorithms/bounding-box-from-points.js +137 -0
- package/dist/culling/lib/algorithms/bounding-box-from-points.js.map +1 -0
- package/dist/culling/lib/algorithms/bounding-sphere-from-points.d.ts +14 -0
- package/dist/culling/lib/algorithms/bounding-sphere-from-points.d.ts.map +1 -0
- package/dist/culling/lib/algorithms/bounding-sphere-from-points.js +146 -0
- package/dist/culling/lib/algorithms/bounding-sphere-from-points.js.map +1 -0
- package/dist/culling/lib/algorithms/compute-eigen-decomposition.d.ts +37 -0
- package/dist/culling/lib/algorithms/compute-eigen-decomposition.d.ts.map +1 -0
- package/dist/culling/lib/algorithms/compute-eigen-decomposition.js +137 -0
- package/dist/culling/lib/algorithms/compute-eigen-decomposition.js.map +1 -0
- package/dist/culling/lib/algorithms/oriented-bounding-box-quaternion.d.ts +7 -0
- package/dist/culling/lib/algorithms/oriented-bounding-box-quaternion.d.ts.map +1 -0
- package/dist/culling/lib/algorithms/oriented-bounding-box-quaternion.js +26 -0
- package/dist/culling/lib/algorithms/oriented-bounding-box-quaternion.js.map +1 -0
- package/dist/culling/lib/bounding-volumes/axis-aligned-bounding-box.d.ts +56 -0
- package/dist/culling/lib/bounding-volumes/axis-aligned-bounding-box.d.ts.map +1 -0
- package/dist/culling/lib/bounding-volumes/axis-aligned-bounding-box.js +116 -0
- package/dist/culling/lib/bounding-volumes/axis-aligned-bounding-box.js.map +1 -0
- package/dist/culling/lib/bounding-volumes/bounding-sphere.d.ts +40 -0
- package/dist/culling/lib/bounding-volumes/bounding-sphere.d.ts.map +1 -0
- package/dist/culling/lib/bounding-volumes/bounding-sphere.js +123 -0
- package/dist/culling/lib/bounding-volumes/bounding-sphere.js.map +1 -0
- package/dist/culling/lib/bounding-volumes/bounding-volume.d.ts +30 -0
- package/dist/culling/lib/bounding-volumes/bounding-volume.d.ts.map +1 -0
- package/dist/culling/lib/bounding-volumes/bounding-volume.js +7 -0
- package/dist/culling/lib/bounding-volumes/bounding-volume.js.map +1 -0
- package/dist/culling/lib/bounding-volumes/oriented-bounding-box.d.ts +62 -0
- package/dist/culling/lib/bounding-volumes/oriented-bounding-box.d.ts.map +1 -0
- package/dist/culling/lib/bounding-volumes/oriented-bounding-box.js +230 -0
- package/dist/culling/lib/bounding-volumes/oriented-bounding-box.js.map +1 -0
- package/dist/culling/lib/culling-volume.d.ts +46 -0
- package/dist/culling/lib/culling-volume.d.ts.map +1 -0
- package/dist/culling/lib/culling-volume.js +126 -0
- package/dist/culling/lib/culling-volume.js.map +1 -0
- package/dist/culling/lib/perspective-frustum.d.ts +153 -0
- package/dist/culling/lib/perspective-frustum.d.ts.map +1 -0
- package/dist/culling/lib/perspective-frustum.js +191 -0
- package/dist/culling/lib/perspective-frustum.js.map +1 -0
- package/dist/culling/lib/perspective-off-center-frustum.d.ts +158 -0
- package/dist/culling/lib/perspective-off-center-frustum.d.ts.map +1 -0
- package/dist/culling/lib/perspective-off-center-frustum.js +276 -0
- package/dist/culling/lib/perspective-off-center-frustum.js.map +1 -0
- package/dist/culling/lib/plane.d.ts +35 -0
- package/dist/culling/lib/plane.d.ts.map +1 -0
- package/dist/culling/lib/plane.js +90 -0
- package/dist/culling/lib/plane.js.map +1 -0
- package/dist/culling/lib/ray.d.ts +13 -0
- package/dist/culling/lib/ray.d.ts.map +1 -0
- package/dist/culling/lib/ray.js +30 -0
- package/dist/culling/lib/ray.js.map +1 -0
- package/dist/culling/lib/shapes/box-shape.d.ts +19 -0
- package/dist/culling/lib/shapes/box-shape.d.ts.map +1 -0
- package/dist/culling/lib/shapes/box-shape.js +69 -0
- package/dist/culling/lib/shapes/box-shape.js.map +1 -0
- package/dist/culling/lib/shapes/capsule-shape.d.ts +23 -0
- package/dist/culling/lib/shapes/capsule-shape.d.ts.map +1 -0
- package/dist/culling/lib/shapes/capsule-shape.js +151 -0
- package/dist/culling/lib/shapes/capsule-shape.js.map +1 -0
- package/dist/culling/lib/shapes/cylinder-shape.d.ts +22 -0
- package/dist/culling/lib/shapes/cylinder-shape.d.ts.map +1 -0
- package/dist/culling/lib/shapes/cylinder-shape.js +99 -0
- package/dist/culling/lib/shapes/cylinder-shape.js.map +1 -0
- package/dist/culling/lib/shapes/implicit-shape.d.ts +56 -0
- package/dist/culling/lib/shapes/implicit-shape.d.ts.map +1 -0
- package/dist/culling/lib/shapes/implicit-shape.js +142 -0
- package/dist/culling/lib/shapes/implicit-shape.js.map +1 -0
- package/dist/culling/lib/shapes/plane-shape.d.ts +27 -0
- package/dist/culling/lib/shapes/plane-shape.d.ts.map +1 -0
- package/dist/culling/lib/shapes/plane-shape.js +71 -0
- package/dist/culling/lib/shapes/plane-shape.js.map +1 -0
- package/dist/culling/lib/shapes/sphere-shape.d.ts +18 -0
- package/dist/culling/lib/shapes/sphere-shape.d.ts.map +1 -0
- package/dist/culling/lib/shapes/sphere-shape.js +43 -0
- package/dist/culling/lib/shapes/sphere-shape.js.map +1 -0
- package/dist/culling/oriented-box-2d.cjs +73 -0
- package/dist/culling/oriented-box-2d.cjs.map +6 -0
- package/dist/culling/oriented-box-2d.d.ts +22 -0
- package/dist/culling/oriented-box-2d.d.ts.map +1 -0
- package/dist/culling/oriented-box-2d.js +86 -0
- package/dist/culling/oriented-box-2d.js.map +1 -0
- package/dist/culling/queries.cjs +161 -0
- package/dist/culling/queries.cjs.map +6 -0
- package/dist/culling/queries.d.ts +20 -0
- package/dist/culling/queries.d.ts.map +1 -0
- package/dist/culling/queries.js +139 -0
- package/dist/culling/queries.js.map +1 -0
- package/dist/curves/bezier-curves.d.ts +18 -0
- package/dist/curves/bezier-curves.d.ts.map +1 -0
- package/dist/curves/bezier-curves.js +54 -0
- package/dist/curves/bezier-curves.js.map +1 -0
- package/dist/curves/catmull-rom-curve.d.ts +15 -0
- package/dist/curves/catmull-rom-curve.d.ts.map +1 -0
- package/dist/curves/catmull-rom-curve.js +77 -0
- package/dist/curves/catmull-rom-curve.js.map +1 -0
- package/dist/curves/curve-arc-length.d.ts +17 -0
- package/dist/curves/curve-arc-length.d.ts.map +1 -0
- package/dist/curves/curve-arc-length.js +58 -0
- package/dist/curves/curve-arc-length.js.map +1 -0
- package/dist/curves/curve.d.ts +23 -0
- package/dist/curves/curve.d.ts.map +1 -0
- package/dist/curves/curve.js +77 -0
- package/dist/curves/curve.js.map +1 -0
- package/dist/curves/hermite-curve.d.ts +9 -0
- package/dist/curves/hermite-curve.d.ts.map +1 -0
- package/dist/curves/hermite-curve.js +24 -0
- package/dist/curves/hermite-curve.js.map +1 -0
- package/dist/curves/index.cjs +293 -0
- package/dist/curves/index.cjs.map +6 -0
- package/dist/curves/index.d.ts +8 -0
- package/dist/curves/index.d.ts.map +1 -0
- package/dist/curves/index.js +9 -0
- package/dist/curves/index.js.map +1 -0
- package/dist/geometries/box-geometry.js +1 -1
- package/dist/geometries/box-geometry.js.map +1 -1
- package/dist/geometries/capsule-geometry.js +1 -1
- package/dist/geometries/capsule-geometry.js.map +1 -1
- package/dist/geometries/cylinder-geometry.js +1 -1
- package/dist/geometries/cylinder-geometry.js.map +1 -1
- package/dist/geometries/geometry-helpers.js +1 -1
- package/dist/geometries/geometry-helpers.js.map +1 -1
- package/dist/geometries/ico-sphere-geometry.js +1 -1
- package/dist/geometries/ico-sphere-geometry.js.map +1 -1
- package/dist/geometries/plane-geometry.js +1 -1
- package/dist/geometries/plane-geometry.js.map +1 -1
- package/dist/geometries/primitive-mesh-builder.d.ts +24 -0
- package/dist/geometries/primitive-mesh-builder.d.ts.map +1 -0
- package/dist/geometries/primitive-mesh-builder.js +74 -0
- package/dist/geometries/primitive-mesh-builder.js.map +1 -0
- package/dist/geometries/prism-geometry.d.ts +13 -0
- package/dist/geometries/prism-geometry.d.ts.map +1 -0
- package/dist/geometries/prism-geometry.js +34 -0
- package/dist/geometries/prism-geometry.js.map +1 -0
- package/dist/geometries/ring-geometry.d.ts +15 -0
- package/dist/geometries/ring-geometry.d.ts.map +1 -0
- package/dist/geometries/ring-geometry.js +64 -0
- package/dist/geometries/ring-geometry.js.map +1 -0
- package/dist/geometries/sphere-geometry.js +1 -1
- package/dist/geometries/sphere-geometry.js.map +1 -1
- package/dist/geometries/surface-of-revolution.js +1 -1
- package/dist/geometries/surface-of-revolution.js.map +1 -1
- package/dist/geometries/truncated-cone-geometry.js +1 -1
- package/dist/geometries/truncated-cone-geometry.js.map +1 -1
- package/dist/index.cjs +290 -9
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/geometry-utils.js +1 -1
- package/dist/lib/geometry-utils.js.map +1 -1
- package/dist/lib/geometry.d.ts +1 -1
- package/dist/lib/geometry.d.ts.map +1 -1
- package/dist/lib/geometry.js +1 -1
- package/dist/lib/geometry.js.map +1 -1
- package/dist/lib/hinge-tree.d.ts +33 -0
- package/dist/lib/hinge-tree.d.ts.map +1 -0
- package/dist/lib/hinge-tree.js +144 -0
- package/dist/lib/hinge-tree.js.map +1 -0
- package/dist/parametric/lathe-geometry.d.ts +14 -0
- package/dist/parametric/lathe-geometry.d.ts.map +1 -0
- package/dist/parametric/lathe-geometry.js +48 -0
- package/dist/parametric/lathe-geometry.js.map +1 -0
- package/dist/parametric/parametric-geometry.d.ts +20 -0
- package/dist/parametric/parametric-geometry.d.ts.map +1 -0
- package/dist/parametric/parametric-geometry.js +88 -0
- package/dist/parametric/parametric-geometry.js.map +1 -0
- package/dist/parametric/torus-geometry.d.ts +19 -0
- package/dist/parametric/torus-geometry.d.ts.map +1 -0
- package/dist/parametric/torus-geometry.js +89 -0
- package/dist/parametric/torus-geometry.js.map +1 -0
- package/dist/parametric.cjs +328 -0
- package/dist/parametric.cjs.map +6 -0
- package/dist/parametric.d.ts +7 -0
- package/dist/parametric.d.ts.map +1 -0
- package/dist/parametric.js +7 -0
- package/dist/parametric.js.map +1 -0
- package/dist/polygon/cut-by-grid.d.ts +24 -0
- package/dist/polygon/cut-by-grid.d.ts.map +1 -0
- package/dist/polygon/cut-by-grid.js +221 -0
- package/dist/polygon/cut-by-grid.js.map +1 -0
- package/dist/polygon/cut-by-mercator-bounds.d.ts +17 -0
- package/dist/polygon/cut-by-mercator-bounds.d.ts.map +1 -0
- package/dist/polygon/cut-by-mercator-bounds.js +149 -0
- package/dist/polygon/cut-by-mercator-bounds.js.map +1 -0
- package/dist/polygon/earcut.d.ts +13 -0
- package/dist/polygon/earcut.d.ts.map +1 -0
- package/dist/polygon/earcut.js +627 -0
- package/dist/polygon/earcut.js.map +1 -0
- package/dist/polygon/index.cjs +1437 -0
- package/dist/polygon/index.cjs.map +6 -0
- package/dist/polygon/index.d.ts +12 -0
- package/dist/polygon/index.d.ts.map +1 -0
- package/dist/polygon/index.js +12 -0
- package/dist/polygon/index.js.map +1 -0
- package/dist/polygon/lineclip.d.ts +31 -0
- package/dist/polygon/lineclip.d.ts.map +1 -0
- package/dist/polygon/lineclip.js +184 -0
- package/dist/polygon/lineclip.js.map +1 -0
- package/dist/polygon/polygon-utils.d.ts +103 -0
- package/dist/polygon/polygon-utils.d.ts.map +1 -0
- package/dist/polygon/polygon-utils.js +155 -0
- package/dist/polygon/polygon-utils.js.map +1 -0
- package/dist/polygon/polygon.d.ts +41 -0
- package/dist/polygon/polygon.d.ts.map +1 -0
- package/dist/polygon/polygon.js +76 -0
- package/dist/polygon/polygon.js.map +1 -0
- package/dist/polygon/subdivide-polyline.d.ts +56 -0
- package/dist/polygon/subdivide-polyline.d.ts.map +1 -0
- package/dist/polygon/subdivide-polyline.js +141 -0
- package/dist/polygon/subdivide-polyline.js.map +1 -0
- package/dist/polygon/subdivide-triangle-mesh.d.ts +68 -0
- package/dist/polygon/subdivide-triangle-mesh.d.ts.map +1 -0
- package/dist/polygon/subdivide-triangle-mesh.js +228 -0
- package/dist/polygon/subdivide-triangle-mesh.js.map +1 -0
- package/dist/polygon/utils.d.ts +5 -0
- package/dist/polygon/utils.d.ts.map +1 -0
- package/dist/polygon/utils.js +38 -0
- package/dist/polygon/utils.js.map +1 -0
- package/dist/spatial-index/box-index.cjs +328 -0
- package/dist/spatial-index/box-index.cjs.map +6 -0
- package/dist/spatial-index/box-index.d.ts +65 -0
- package/dist/spatial-index/box-index.d.ts.map +1 -0
- package/dist/spatial-index/box-index.js +263 -0
- package/dist/spatial-index/box-index.js.map +1 -0
- package/dist/spatial-index/index.cjs +494 -0
- package/dist/spatial-index/index.cjs.map +6 -0
- package/dist/spatial-index/index.d.ts +5 -0
- package/dist/spatial-index/index.d.ts.map +1 -0
- package/dist/spatial-index/index.js +6 -0
- package/dist/spatial-index/index.js.map +1 -0
- package/dist/spatial-index/triangle-bvh.d.ts +53 -0
- package/dist/spatial-index/triangle-bvh.d.ts.map +1 -0
- package/dist/spatial-index/triangle-bvh.js +102 -0
- package/dist/spatial-index/triangle-bvh.js.map +1 -0
- package/dist/utils/attribute-compression.d.ts +50 -0
- package/dist/utils/attribute-compression.d.ts.map +1 -0
- package/dist/utils/attribute-compression.js +186 -0
- package/dist/utils/attribute-compression.js.map +1 -0
- package/dist/utils/attribute-iterator.d.ts +8 -0
- package/dist/utils/attribute-iterator.d.ts.map +1 -0
- package/dist/utils/attribute-iterator.js +25 -0
- package/dist/utils/attribute-iterator.js.map +1 -0
- package/dist/utils/compute-vertex-normals.d.ts +4 -0
- package/dist/utils/compute-vertex-normals.d.ts.map +1 -0
- package/dist/utils/compute-vertex-normals.js +54 -0
- package/dist/utils/compute-vertex-normals.js.map +1 -0
- package/dist/utils/constants.d.ts +52 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +39 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/coordinates.d.ts +3 -0
- package/dist/utils/coordinates.d.ts.map +1 -0
- package/dist/utils/coordinates.js +8 -0
- package/dist/utils/coordinates.js.map +1 -0
- package/dist/utils/geometry.d.ts +20 -0
- package/dist/utils/geometry.d.ts.map +1 -0
- package/dist/utils/geometry.js +31 -0
- package/dist/utils/geometry.js.map +1 -0
- package/dist/utils/gl-type.d.ts +17 -0
- package/dist/utils/gl-type.d.ts.map +1 -0
- package/dist/utils/gl-type.js +69 -0
- package/dist/utils/gl-type.js.map +1 -0
- package/dist/utils/index.cjs +965 -0
- package/dist/utils/index.cjs.map +6 -0
- package/dist/utils/index.d.ts +18 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +16 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/interpolate-packed-attributes.cjs +176 -0
- package/dist/utils/interpolate-packed-attributes.cjs.map +6 -0
- package/dist/utils/interpolate-packed-attributes.d.ts +43 -0
- package/dist/utils/interpolate-packed-attributes.d.ts.map +1 -0
- package/dist/utils/interpolate-packed-attributes.js +192 -0
- package/dist/utils/interpolate-packed-attributes.js.map +1 -0
- package/dist/utils/mesh-processing.d.ts +18 -0
- package/dist/utils/mesh-processing.d.ts.map +1 -0
- package/dist/utils/mesh-processing.js +300 -0
- package/dist/utils/mesh-processing.js.map +1 -0
- package/dist/utils/primitive-iterator.d.ts +21 -0
- package/dist/utils/primitive-iterator.d.ts.map +1 -0
- package/dist/utils/primitive-iterator.js +117 -0
- package/dist/utils/primitive-iterator.js.map +1 -0
- package/dist/utils/rgb565.d.ts +5 -0
- package/dist/utils/rgb565.d.ts.map +1 -0
- package/dist/utils/rgb565.js +24 -0
- package/dist/utils/rgb565.js.map +1 -0
- package/dist/utils/typed-array-utils.d.ts +3 -0
- package/dist/utils/typed-array-utils.d.ts.map +1 -0
- package/dist/utils/typed-array-utils.js +16 -0
- package/dist/utils/typed-array-utils.js.map +1 -0
- package/package.json +57 -4
- package/src/culling/constants.ts +6 -0
- package/src/culling/index.ts +45 -0
- package/src/culling/lib/algorithms/bounding-box-from-points.ts +174 -0
- package/src/culling/lib/algorithms/bounding-sphere-from-points.ts +175 -0
- package/src/culling/lib/algorithms/compute-eigen-decomposition.ts +171 -0
- package/src/culling/lib/algorithms/oriented-bounding-box-quaternion.ts +32 -0
- package/src/culling/lib/bounding-volumes/axis-aligned-bounding-box.ts +153 -0
- package/src/culling/lib/bounding-volumes/bounding-sphere.ts +154 -0
- package/src/culling/lib/bounding-volumes/bounding-volume.ts +38 -0
- package/src/culling/lib/bounding-volumes/oriented-bounding-box.ts +320 -0
- package/src/culling/lib/culling-volume.ts +163 -0
- package/src/culling/lib/perspective-frustum.ts +288 -0
- package/src/culling/lib/perspective-off-center-frustum.ts +381 -0
- package/src/culling/lib/plane.ts +118 -0
- package/src/culling/lib/ray.ts +33 -0
- package/src/culling/lib/shapes/box-shape.ts +78 -0
- package/src/culling/lib/shapes/capsule-shape.ts +232 -0
- package/src/culling/lib/shapes/cylinder-shape.ts +133 -0
- package/src/culling/lib/shapes/implicit-shape.ts +209 -0
- package/src/culling/lib/shapes/plane-shape.ts +78 -0
- package/src/culling/lib/shapes/sphere-shape.ts +60 -0
- package/src/culling/oriented-box-2d.ts +165 -0
- package/src/culling/queries.ts +155 -0
- package/src/curves/bezier-curves.ts +62 -0
- package/src/curves/catmull-rom-curve.ts +89 -0
- package/src/curves/curve-arc-length.ts +70 -0
- package/src/curves/curve.ts +96 -0
- package/src/curves/hermite-curve.ts +34 -0
- package/src/curves/index.ts +15 -0
- package/src/geometries/box-geometry.ts +1 -1
- package/src/geometries/capsule-geometry.ts +1 -1
- package/src/geometries/cylinder-geometry.ts +1 -1
- package/src/geometries/geometry-helpers.ts +1 -1
- package/src/geometries/ico-sphere-geometry.ts +1 -1
- package/src/geometries/plane-geometry.ts +1 -1
- package/src/geometries/primitive-mesh-builder.ts +84 -0
- package/src/geometries/prism-geometry.ts +57 -0
- package/src/geometries/ring-geometry.ts +102 -0
- package/src/geometries/sphere-geometry.ts +1 -1
- package/src/geometries/surface-of-revolution.ts +1 -1
- package/src/geometries/truncated-cone-geometry.ts +1 -1
- package/src/index.ts +10 -1
- package/src/lib/geometry-utils.ts +2 -2
- package/src/lib/geometry.ts +2 -2
- package/src/lib/hinge-tree.ts +170 -0
- package/src/parametric/lathe-geometry.ts +70 -0
- package/src/parametric/parametric-geometry.ts +118 -0
- package/src/parametric/torus-geometry.ts +118 -0
- package/src/parametric.ts +9 -0
- package/src/polygon/cut-by-grid.ts +313 -0
- package/src/polygon/cut-by-mercator-bounds.ts +204 -0
- package/src/polygon/earcut.ts +798 -0
- package/src/polygon/index.ts +35 -0
- package/src/polygon/lineclip.ts +218 -0
- package/src/polygon/polygon-utils.ts +273 -0
- package/src/polygon/polygon.ts +105 -0
- package/src/polygon/subdivide-polyline.ts +223 -0
- package/src/polygon/subdivide-triangle-mesh.ts +322 -0
- package/src/polygon/utils.ts +50 -0
- package/src/spatial-index/box-index.ts +343 -0
- package/src/spatial-index/index.ts +18 -0
- package/src/spatial-index/triangle-bvh.ts +162 -0
- package/src/utils/attribute-compression.ts +234 -0
- package/src/utils/attribute-iterator.ts +28 -0
- package/src/utils/compute-vertex-normals.ts +63 -0
- package/src/utils/constants.ts +42 -0
- package/src/utils/coordinates.ts +8 -0
- package/src/utils/geometry.ts +49 -0
- package/src/utils/gl-type.ts +87 -0
- package/src/utils/index.ts +55 -0
- package/src/utils/interpolate-packed-attributes.ts +284 -0
- package/src/utils/mesh-processing.ts +377 -0
- package/src/utils/primitive-iterator.ts +145 -0
- package/src/utils/rgb565.ts +26 -0
- package/src/utils/typed-array-utils.ts +16 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
intersectRayBounds,
|
|
7
|
+
normalizeQueryDirection,
|
|
8
|
+
validateQueryDistance,
|
|
9
|
+
validateQueryPoint,
|
|
10
|
+
type QueryPoint
|
|
11
|
+
} from '../culling/queries';
|
|
12
|
+
|
|
13
|
+
export type BoxIndexOptions = {
|
|
14
|
+
/** Flat [minX, minY, (minZ), maxX, maxY, (maxZ)] records, copied at construction. */
|
|
15
|
+
bounds: ArrayLike<number>;
|
|
16
|
+
dimension: 2 | 3;
|
|
17
|
+
leafSize?: number;
|
|
18
|
+
};
|
|
19
|
+
export type NearestIndexResult = {index: number; distance: number};
|
|
20
|
+
export type NearestIndexOptions = {
|
|
21
|
+
maxDistance?: number;
|
|
22
|
+
filter?: (index: number) => boolean;
|
|
23
|
+
/** Exact geometry distance; must be at least the distance to its indexed box. null excludes it. */
|
|
24
|
+
distanceToItem?: (index: number) => number | null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Constraints for the nearest accepted ray hit against indexed boxes or caller-owned geometry. */
|
|
28
|
+
export type RayIndexOptions = {
|
|
29
|
+
/** Inclusive Euclidean distance limit; defaults to Infinity. */
|
|
30
|
+
maxDistance?: number;
|
|
31
|
+
/** Exclude an original row before exact refinement. */
|
|
32
|
+
filter?: (index: number) => boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Exact Euclidean hit distance, or null to reject the row.
|
|
35
|
+
* Must be at least its indexed-box entry distance, apart from floating-point roundoff;
|
|
36
|
+
* geometry must stay inside that box. Refined distances are never rounded or clamped.
|
|
37
|
+
* The second argument is the current accepted distance limit, initially maxDistance.
|
|
38
|
+
* Traversal order is unspecified; this is not an all-intersections callback.
|
|
39
|
+
*/
|
|
40
|
+
intersectItem?: (index: number, maxDistance: number) => number | null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** Static median-split BVH. Results refer to original input rows, not tree order. */
|
|
44
|
+
export class BoxIndex {
|
|
45
|
+
readonly dimension: 2 | 3;
|
|
46
|
+
readonly size: number;
|
|
47
|
+
private readonly bounds: Float64Array;
|
|
48
|
+
private readonly nodeBounds: Float64Array;
|
|
49
|
+
private readonly children: Int32Array;
|
|
50
|
+
private readonly starts: Uint32Array;
|
|
51
|
+
private readonly counts: Uint32Array;
|
|
52
|
+
private readonly order: Uint32Array;
|
|
53
|
+
|
|
54
|
+
constructor(options: BoxIndexOptions) {
|
|
55
|
+
const {dimension, bounds, leafSize = 8} = options;
|
|
56
|
+
if (dimension !== 2 && dimension !== 3) throw new RangeError('Index dimension must be 2 or 3');
|
|
57
|
+
if (!Number.isSafeInteger(leafSize) || leafSize < 1)
|
|
58
|
+
throw new RangeError('leafSize must be positive');
|
|
59
|
+
if (bounds.length % (dimension * 2)) throw new RangeError('Incomplete bounds record');
|
|
60
|
+
this.dimension = dimension;
|
|
61
|
+
this.bounds = Float64Array.from(bounds);
|
|
62
|
+
this.size = bounds.length / (dimension * 2);
|
|
63
|
+
if (this.size > 0x7fffffff) throw new RangeError('Index exceeds 32-bit capacity');
|
|
64
|
+
const stride = dimension * 2;
|
|
65
|
+
for (let i = 0; i < this.size; i++)
|
|
66
|
+
this.validateBounds(
|
|
67
|
+
this.bounds.subarray(i * stride, i * stride + dimension),
|
|
68
|
+
this.bounds.subarray(i * stride + dimension, (i + 1) * stride)
|
|
69
|
+
);
|
|
70
|
+
const records: {bounds: number[]; left: number; right: number; start: number; count: number}[] =
|
|
71
|
+
[];
|
|
72
|
+
const ids = Array.from({length: this.size}, (_, i) => i);
|
|
73
|
+
const build = (start: number, end: number): number => {
|
|
74
|
+
const node = records.length;
|
|
75
|
+
const bound = [
|
|
76
|
+
...new Array(dimension).fill(Infinity),
|
|
77
|
+
...new Array(dimension).fill(-Infinity)
|
|
78
|
+
];
|
|
79
|
+
for (let i = start; i < end; i++)
|
|
80
|
+
for (let axis = 0; axis < dimension; axis++) {
|
|
81
|
+
bound[axis] = Math.min(bound[axis], this.bounds[ids[i] * stride + axis]);
|
|
82
|
+
bound[axis + dimension] = Math.max(
|
|
83
|
+
bound[axis + dimension],
|
|
84
|
+
this.bounds[ids[i] * stride + axis + dimension]
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const record = {bounds: bound, left: -1, right: -1, start, count: end - start};
|
|
88
|
+
records.push(record);
|
|
89
|
+
if (end - start > leafSize) {
|
|
90
|
+
let axis = 0;
|
|
91
|
+
for (let i = 1; i < dimension; i++) {
|
|
92
|
+
if (bound[i + dimension] - bound[i] > bound[axis + dimension] - bound[axis]) axis = i;
|
|
93
|
+
}
|
|
94
|
+
const center = (i: number): number =>
|
|
95
|
+
this.bounds[i * stride + axis] / 2 + this.bounds[i * stride + axis + dimension] / 2;
|
|
96
|
+
const sorted = ids.slice(start, end).sort((a, b) => center(a) - center(b) || a - b);
|
|
97
|
+
for (let i = 0; i < sorted.length; i++) ids[start + i] = sorted[i];
|
|
98
|
+
const middle = Math.floor((start + end) / 2);
|
|
99
|
+
record.left = build(start, middle);
|
|
100
|
+
record.right = build(middle, end);
|
|
101
|
+
record.count = 0;
|
|
102
|
+
}
|
|
103
|
+
return node;
|
|
104
|
+
};
|
|
105
|
+
if (this.size) build(0, this.size);
|
|
106
|
+
this.nodeBounds = new Float64Array(records.length * stride);
|
|
107
|
+
this.children = new Int32Array(records.length * 2);
|
|
108
|
+
this.starts = new Uint32Array(records.length);
|
|
109
|
+
this.counts = new Uint32Array(records.length);
|
|
110
|
+
records.forEach((record, i) => {
|
|
111
|
+
this.nodeBounds.set(record.bounds, i * stride);
|
|
112
|
+
this.children.set([record.left, record.right], i * 2);
|
|
113
|
+
this.starts[i] = record.start;
|
|
114
|
+
this.counts[i] = record.count;
|
|
115
|
+
});
|
|
116
|
+
this.order = Uint32Array.from(ids);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Closed-box overlap, including boundary contact. Sorted by original row index. */
|
|
120
|
+
search(minimum: QueryPoint, maximum: QueryPoint): number[] {
|
|
121
|
+
this.validateBounds(minimum, maximum);
|
|
122
|
+
const result: number[] = [];
|
|
123
|
+
const overlaps = (bounds: Float64Array, index: number): boolean => {
|
|
124
|
+
const start = index * this.dimension * 2;
|
|
125
|
+
for (let i = 0; i < this.dimension; i++) {
|
|
126
|
+
if (bounds[start + i] > maximum[i] || bounds[start + i + this.dimension] < minimum[i])
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
};
|
|
131
|
+
const stack = this.size ? [0] : [];
|
|
132
|
+
while (stack.length) {
|
|
133
|
+
const node = stack.pop();
|
|
134
|
+
if (!overlaps(this.nodeBounds, node)) continue;
|
|
135
|
+
if (this.counts[node]) {
|
|
136
|
+
for (let i = this.starts[node]; i < this.starts[node] + this.counts[node]; i++) {
|
|
137
|
+
if (overlaps(this.bounds, this.order[i])) result.push(this.order[i]);
|
|
138
|
+
}
|
|
139
|
+
} else stack.push(this.children[node * 2], this.children[node * 2 + 1]);
|
|
140
|
+
}
|
|
141
|
+
return result.sort((a, b) => a - b);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Branch-and-bound nearest box, optionally refined against actual geometry. */
|
|
145
|
+
nearest(point: QueryPoint, options: NearestIndexOptions = {}): NearestIndexResult | null {
|
|
146
|
+
validateQueryPoint(point, this.dimension);
|
|
147
|
+
let best = options.maxDistance ?? Infinity;
|
|
148
|
+
validateQueryDistance(best);
|
|
149
|
+
let result: NearestIndexResult | null = null;
|
|
150
|
+
const distance = (bounds: Float64Array, index: number): number => {
|
|
151
|
+
const start = index * this.dimension * 2;
|
|
152
|
+
let x = 0,
|
|
153
|
+
y = 0,
|
|
154
|
+
z = 0;
|
|
155
|
+
x = Math.max(bounds[start] - point[0], 0, point[0] - bounds[start + this.dimension]);
|
|
156
|
+
y = Math.max(bounds[start + 1] - point[1], 0, point[1] - bounds[start + this.dimension + 1]);
|
|
157
|
+
if (this.dimension === 3)
|
|
158
|
+
z = Math.max(bounds[start + 2] - point[2], 0, point[2] - bounds[start + 5]);
|
|
159
|
+
return Math.hypot(x, y, z);
|
|
160
|
+
};
|
|
161
|
+
const stack = this.size ? [0] : [];
|
|
162
|
+
while (stack.length) {
|
|
163
|
+
const node = stack.pop();
|
|
164
|
+
if (distance(this.nodeBounds, node) > best) continue;
|
|
165
|
+
if (this.counts[node]) {
|
|
166
|
+
for (let i = this.starts[node]; i < this.starts[node] + this.counts[node]; i++) {
|
|
167
|
+
const index = this.order[i];
|
|
168
|
+
const lower = distance(this.bounds, index);
|
|
169
|
+
if (lower > best || (options.filter && !options.filter(index))) continue;
|
|
170
|
+
const exact = options.distanceToItem ? options.distanceToItem(index) : lower;
|
|
171
|
+
if (exact === null) continue;
|
|
172
|
+
validateQueryDistance(exact);
|
|
173
|
+
if (exact < lower)
|
|
174
|
+
throw new RangeError('Refinement distance cannot be below box distance');
|
|
175
|
+
if (
|
|
176
|
+
Number.isFinite(exact) &&
|
|
177
|
+
exact <= best &&
|
|
178
|
+
(!result || exact < best || index < result.index)
|
|
179
|
+
) {
|
|
180
|
+
best = exact;
|
|
181
|
+
result = {index, distance: exact};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
const a = this.children[node * 2],
|
|
186
|
+
b = this.children[node * 2 + 1];
|
|
187
|
+
if (distance(this.nodeBounds, a) < distance(this.nodeBounds, b)) stack.push(b, a);
|
|
188
|
+
else stack.push(a, b);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Nearest accepted box/geometry hit without materializing and sorting all ray candidates.
|
|
196
|
+
* Rejected refinements do not clip the ray. Equal distances select the lowest original row.
|
|
197
|
+
*/
|
|
198
|
+
intersectRay(
|
|
199
|
+
origin: QueryPoint,
|
|
200
|
+
direction: QueryPoint,
|
|
201
|
+
options: RayIndexOptions = {}
|
|
202
|
+
): NearestIndexResult | null {
|
|
203
|
+
validateQueryPoint(origin, this.dimension);
|
|
204
|
+
const unit = normalizeQueryDirection(direction, this.dimension);
|
|
205
|
+
let best = options.maxDistance ?? Infinity;
|
|
206
|
+
validateQueryDistance(best);
|
|
207
|
+
let result: NearestIndexResult | null = null;
|
|
208
|
+
const getEntry = (bounds: Float64Array, index: number): number | null => {
|
|
209
|
+
const offset = index * this.dimension * 2;
|
|
210
|
+
return intersectRayBounds(
|
|
211
|
+
origin,
|
|
212
|
+
unit,
|
|
213
|
+
bounds.subarray(offset, offset + this.dimension),
|
|
214
|
+
bounds.subarray(offset + this.dimension, offset + this.dimension * 2),
|
|
215
|
+
best + getRayDistanceTolerance(best, best)
|
|
216
|
+
);
|
|
217
|
+
};
|
|
218
|
+
const rootDistance = this.size ? getEntry(this.nodeBounds, 0) : null;
|
|
219
|
+
const stack = rootDistance === null ? [] : [{index: 0, distance: rootDistance}];
|
|
220
|
+
while (stack.length) {
|
|
221
|
+
const candidate = stack.pop();
|
|
222
|
+
if (
|
|
223
|
+
!candidate ||
|
|
224
|
+
candidate.distance > best + getRayDistanceTolerance(candidate.distance, best)
|
|
225
|
+
)
|
|
226
|
+
continue;
|
|
227
|
+
const node = candidate.index;
|
|
228
|
+
if (this.counts[node]) {
|
|
229
|
+
for (
|
|
230
|
+
let offset = this.starts[node];
|
|
231
|
+
offset < this.starts[node] + this.counts[node];
|
|
232
|
+
offset++
|
|
233
|
+
) {
|
|
234
|
+
const index = this.order[offset];
|
|
235
|
+
const lower = getEntry(this.bounds, index);
|
|
236
|
+
if (lower === null || (options.filter && !options.filter(index))) continue;
|
|
237
|
+
const exact = options.intersectItem ? options.intersectItem(index, best) : lower;
|
|
238
|
+
if (exact === null) continue;
|
|
239
|
+
validateQueryDistance(exact);
|
|
240
|
+
if (
|
|
241
|
+
exact < lower &&
|
|
242
|
+
(!Number.isFinite(lower) || lower - exact > getRayDistanceTolerance(lower, exact))
|
|
243
|
+
)
|
|
244
|
+
throw new RangeError('Refinement distance cannot be below box entry');
|
|
245
|
+
if (
|
|
246
|
+
Number.isFinite(exact) &&
|
|
247
|
+
exact <= best &&
|
|
248
|
+
(!result || exact < best || index < result.index)
|
|
249
|
+
) {
|
|
250
|
+
best = exact;
|
|
251
|
+
result = {index, distance: exact};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
const first = this.children[node * 2],
|
|
256
|
+
second = this.children[node * 2 + 1];
|
|
257
|
+
const firstDistance = getEntry(this.nodeBounds, first),
|
|
258
|
+
secondDistance = getEntry(this.nodeBounds, second);
|
|
259
|
+
if (firstDistance !== null && secondDistance !== null) {
|
|
260
|
+
if (firstDistance <= secondDistance)
|
|
261
|
+
stack.push(
|
|
262
|
+
{index: second, distance: secondDistance},
|
|
263
|
+
{index: first, distance: firstDistance}
|
|
264
|
+
);
|
|
265
|
+
else
|
|
266
|
+
stack.push(
|
|
267
|
+
{index: first, distance: firstDistance},
|
|
268
|
+
{index: second, distance: secondDistance}
|
|
269
|
+
);
|
|
270
|
+
} else if (firstDistance !== null) stack.push({index: first, distance: firstDistance});
|
|
271
|
+
else if (secondDistance !== null) stack.push({index: second, distance: secondDistance});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Broad-phase ray candidates, sorted by entry distance then original row index. */
|
|
278
|
+
searchRay(
|
|
279
|
+
origin: QueryPoint,
|
|
280
|
+
direction: QueryPoint,
|
|
281
|
+
maxDistance = Infinity
|
|
282
|
+
): NearestIndexResult[] {
|
|
283
|
+
validateQueryPoint(origin, this.dimension);
|
|
284
|
+
const unit = normalizeQueryDirection(direction, this.dimension);
|
|
285
|
+
validateQueryDistance(maxDistance);
|
|
286
|
+
const entry = (bounds: Float64Array, index: number): number | null => {
|
|
287
|
+
const start = index * this.dimension * 2;
|
|
288
|
+
return intersectRayBounds(
|
|
289
|
+
origin,
|
|
290
|
+
unit,
|
|
291
|
+
bounds.subarray(start, start + this.dimension),
|
|
292
|
+
bounds.subarray(start + this.dimension, start + this.dimension * 2),
|
|
293
|
+
maxDistance
|
|
294
|
+
);
|
|
295
|
+
};
|
|
296
|
+
const result: NearestIndexResult[] = [];
|
|
297
|
+
const stack = this.size ? [0] : [];
|
|
298
|
+
while (stack.length) {
|
|
299
|
+
const node = stack.pop();
|
|
300
|
+
if (entry(this.nodeBounds, node) === null) continue;
|
|
301
|
+
if (this.counts[node]) {
|
|
302
|
+
for (let i = this.starts[node]; i < this.starts[node] + this.counts[node]; i++) {
|
|
303
|
+
const index = this.order[i],
|
|
304
|
+
distance = entry(this.bounds, index);
|
|
305
|
+
if (distance !== null) result.push({index, distance});
|
|
306
|
+
}
|
|
307
|
+
} else stack.push(this.children[node * 2], this.children[node * 2 + 1]);
|
|
308
|
+
}
|
|
309
|
+
return result.sort((a, b) => a.distance - b.distance || a.index - b.index);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private validateBounds(minimum: QueryPoint, maximum: QueryPoint): void {
|
|
313
|
+
validateQueryPoint(minimum, this.dimension);
|
|
314
|
+
validateQueryPoint(maximum, this.dimension);
|
|
315
|
+
for (let i = 0; i < this.dimension; i++) {
|
|
316
|
+
if (minimum[i] > maximum[i]) throw new RangeError('Bounds must be ordered');
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** Relative, unit-independent allowance for independently rounded slab and geometry kernels. */
|
|
322
|
+
function getRayDistanceTolerance(first: number, second: number): number {
|
|
323
|
+
return (
|
|
324
|
+
8 * Math.max(Number.MIN_VALUE, Number.EPSILON * Math.max(Math.abs(first), Math.abs(second)))
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Points are zero-volume boxes: nearest gives exact Euclidean point distance. */
|
|
329
|
+
export class PointIndex extends BoxIndex {
|
|
330
|
+
constructor(options: {positions: ArrayLike<number>; dimension: 2 | 3; leafSize?: number}) {
|
|
331
|
+
const {positions, dimension, leafSize} = options;
|
|
332
|
+
if ((dimension !== 2 && dimension !== 3) || positions.length % dimension) {
|
|
333
|
+
throw new RangeError('Invalid point layout');
|
|
334
|
+
}
|
|
335
|
+
const bounds = new Float64Array(positions.length * 2);
|
|
336
|
+
for (let i = 0; i < positions.length / dimension; i++)
|
|
337
|
+
for (let axis = 0; axis < dimension; axis++) {
|
|
338
|
+
bounds[i * dimension * 2 + axis] = bounds[i * dimension * 2 + axis + dimension] =
|
|
339
|
+
positions[i * dimension + axis];
|
|
340
|
+
}
|
|
341
|
+
super({bounds, dimension, leafSize});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
export {BoxIndex, PointIndex} from './box-index';
|
|
6
|
+
export type {
|
|
7
|
+
BoxIndexOptions,
|
|
8
|
+
NearestIndexOptions,
|
|
9
|
+
NearestIndexResult,
|
|
10
|
+
RayIndexOptions
|
|
11
|
+
} from './box-index';
|
|
12
|
+
export {TriangleBVH} from './triangle-bvh';
|
|
13
|
+
export type {
|
|
14
|
+
TriangleBVHOptions,
|
|
15
|
+
TriangleQueryResult,
|
|
16
|
+
TriangleRayResult,
|
|
17
|
+
TriangleRayOptions
|
|
18
|
+
} from './triangle-bvh';
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// math.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
getClosestPointOnTriangle,
|
|
7
|
+
intersectRayTriangle,
|
|
8
|
+
normalizeQueryDirection,
|
|
9
|
+
validateQueryPoint,
|
|
10
|
+
type QueryPoint
|
|
11
|
+
} from '../culling/queries';
|
|
12
|
+
import {BoxIndex} from './box-index';
|
|
13
|
+
|
|
14
|
+
export type TriangleBVHOptions = {
|
|
15
|
+
/** Packed xyz vertices. Copied at construction. */
|
|
16
|
+
positions: ArrayLike<number>;
|
|
17
|
+
/** Triangle-list vertex indices. Omit for non-indexed triangles. */
|
|
18
|
+
indices?: ArrayLike<number>;
|
|
19
|
+
leafSize?: number;
|
|
20
|
+
};
|
|
21
|
+
export type TriangleQueryResult = {
|
|
22
|
+
index: number;
|
|
23
|
+
distance: number;
|
|
24
|
+
point: [number, number, number];
|
|
25
|
+
};
|
|
26
|
+
export type TriangleRayResult = TriangleQueryResult & {barycentric: [number, number, number]};
|
|
27
|
+
|
|
28
|
+
/** Optional application constraints for the nearest accepted triangle intersection. */
|
|
29
|
+
export type TriangleRayOptions = {
|
|
30
|
+
/** Inclusive Euclidean distance limit in input-coordinate units; defaults to Infinity. */
|
|
31
|
+
maxDistance?: number;
|
|
32
|
+
/** Ignore triangles facing away from the ray; defaults to false. */
|
|
33
|
+
backfaceCulling?: boolean;
|
|
34
|
+
/** Exclude an original triangle index before running its exact intersection kernel. */
|
|
35
|
+
filter?: (index: number) => boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Accept or reject an exact hit using its point and original-order barycentric weights.
|
|
38
|
+
* Rejected hits do not shorten the search: a valid surface behind a hole can still win.
|
|
39
|
+
* Called only for candidates that could improve the current accepted result, not all hits.
|
|
40
|
+
* Treat the detached candidate as immutable; traversal order is unspecified.
|
|
41
|
+
*/
|
|
42
|
+
acceptHit?: (
|
|
43
|
+
hit: Readonly<Omit<TriangleRayResult, 'point' | 'barycentric'>> & {
|
|
44
|
+
readonly point: readonly [number, number, number];
|
|
45
|
+
readonly barycentric: readonly [number, number, number];
|
|
46
|
+
}
|
|
47
|
+
) => boolean;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** Static triangle-list BVH. index is the original triangle number, not a vertex index. */
|
|
51
|
+
export class TriangleBVH {
|
|
52
|
+
readonly size: number;
|
|
53
|
+
private readonly positions: Float64Array;
|
|
54
|
+
private readonly indices: Uint32Array;
|
|
55
|
+
private readonly boxes: BoxIndex;
|
|
56
|
+
|
|
57
|
+
constructor(options: TriangleBVHOptions) {
|
|
58
|
+
if (options.positions.length % 3) throw new RangeError('Positions must be packed xyz');
|
|
59
|
+
this.positions = Float64Array.from(options.positions);
|
|
60
|
+
for (const value of this.positions) {
|
|
61
|
+
if (!Number.isFinite(value)) throw new RangeError('Positions must be finite');
|
|
62
|
+
}
|
|
63
|
+
const source = options.indices ?? Array.from({length: this.positions.length / 3}, (_, i) => i);
|
|
64
|
+
if (source.length % 3) throw new RangeError('Indices must form triangles');
|
|
65
|
+
this.indices = new Uint32Array(source.length);
|
|
66
|
+
for (let i = 0; i < source.length; i++) {
|
|
67
|
+
if (
|
|
68
|
+
!Number.isSafeInteger(source[i]) ||
|
|
69
|
+
source[i] < 0 ||
|
|
70
|
+
source[i] >= this.positions.length / 3 ||
|
|
71
|
+
source[i] > 0xffffffff
|
|
72
|
+
) {
|
|
73
|
+
throw new RangeError('Triangle index is out of bounds');
|
|
74
|
+
}
|
|
75
|
+
this.indices[i] = source[i];
|
|
76
|
+
}
|
|
77
|
+
this.size = this.indices.length / 3;
|
|
78
|
+
const bounds = new Float64Array(this.size * 6);
|
|
79
|
+
for (let i = 0; i < this.size; i++) {
|
|
80
|
+
const [a, b, c] = this.triangle(i);
|
|
81
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
82
|
+
bounds[i * 6 + axis] = Math.min(a[axis], b[axis], c[axis]);
|
|
83
|
+
bounds[i * 6 + axis + 3] = Math.max(a[axis], b[axis], c[axis]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.boxes = new BoxIndex({bounds, dimension: 3, leafSize: options.leafSize});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Bounds candidates only; does not imply exact triangle/box overlap. */
|
|
90
|
+
search(minimum: QueryPoint, maximum: QueryPoint): number[] {
|
|
91
|
+
return this.boxes.search(minimum, maximum);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Nearest accepted exact hit; application rejection never clips away later candidates. */
|
|
95
|
+
intersectRay(
|
|
96
|
+
origin: QueryPoint,
|
|
97
|
+
direction: QueryPoint,
|
|
98
|
+
options: TriangleRayOptions = {}
|
|
99
|
+
): TriangleRayResult | null {
|
|
100
|
+
validateQueryPoint(origin, 3);
|
|
101
|
+
const unit = normalizeQueryDirection(direction, 3);
|
|
102
|
+
let result: TriangleRayResult | null = null;
|
|
103
|
+
this.boxes.intersectRay(origin, direction, {
|
|
104
|
+
maxDistance: options.maxDistance,
|
|
105
|
+
filter: options.filter,
|
|
106
|
+
intersectItem: (index, maximum) => {
|
|
107
|
+
const [a, b, c] = this.triangle(index);
|
|
108
|
+
const hit = intersectRayTriangle(origin, unit, a, b, c, {
|
|
109
|
+
backfaceCulling: options.backfaceCulling,
|
|
110
|
+
maxT: maximum
|
|
111
|
+
});
|
|
112
|
+
if (
|
|
113
|
+
hit &&
|
|
114
|
+
(!result ||
|
|
115
|
+
hit.t < result.distance ||
|
|
116
|
+
(hit.t === result.distance && index < result.index))
|
|
117
|
+
) {
|
|
118
|
+
const candidateHit: TriangleRayResult = {
|
|
119
|
+
index,
|
|
120
|
+
distance: hit.t,
|
|
121
|
+
point: [
|
|
122
|
+
origin[0] + hit.t * unit[0],
|
|
123
|
+
origin[1] + hit.t * unit[1],
|
|
124
|
+
origin[2] + hit.t * unit[2]
|
|
125
|
+
],
|
|
126
|
+
barycentric: hit.barycentric
|
|
127
|
+
};
|
|
128
|
+
if (!options.acceptHit || options.acceptHit(candidateHit)) {
|
|
129
|
+
result = candidateHit;
|
|
130
|
+
return hit.t;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
nearest(
|
|
140
|
+
point: QueryPoint,
|
|
141
|
+
options: {maxDistance?: number; filter?: (index: number) => boolean} = {}
|
|
142
|
+
): TriangleQueryResult | null {
|
|
143
|
+
const result = this.boxes.nearest(point, {
|
|
144
|
+
...options,
|
|
145
|
+
distanceToItem: index => {
|
|
146
|
+
const closest = getClosestPointOnTriangle(point, ...this.triangle(index));
|
|
147
|
+
return Math.hypot(point[0] - closest[0], point[1] - closest[1], point[2] - closest[2]);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
return result
|
|
151
|
+
? {...result, point: getClosestPointOnTriangle(point, ...this.triangle(result.index))}
|
|
152
|
+
: null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private triangle(index: number): [Float64Array, Float64Array, Float64Array] {
|
|
156
|
+
const vertex = (i: number): Float64Array => {
|
|
157
|
+
const start = this.indices[index * 3 + i] * 3;
|
|
158
|
+
return this.positions.subarray(start, start + 3);
|
|
159
|
+
};
|
|
160
|
+
return [vertex(0), vertex(1), vertex(2)];
|
|
161
|
+
}
|
|
162
|
+
}
|