@constructorfleet/sketchforge-engine 1.26.0 → 1.28.0

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.
Files changed (200) hide show
  1. package/dist/cadAnalysis.d.ts +94 -0
  2. package/dist/cadAnalysis.d.ts.map +1 -0
  3. package/dist/cadAnalysis.js +157 -0
  4. package/dist/cadAnalysis.js.map +1 -0
  5. package/dist/cadBakeMetadata.d.ts +11 -2
  6. package/dist/cadBakeMetadata.d.ts.map +1 -1
  7. package/dist/cadBakeMetadata.js +9 -0
  8. package/dist/cadBakeMetadata.js.map +1 -1
  9. package/dist/cadProjection.d.ts.map +1 -1
  10. package/dist/cadProjection.js.map +1 -1
  11. package/dist/cadSection.d.ts.map +1 -1
  12. package/dist/cadShell.d.ts +143 -0
  13. package/dist/cadShell.d.ts.map +1 -0
  14. package/dist/cadShell.js +313 -0
  15. package/dist/cadShell.js.map +1 -0
  16. package/dist/cadSolids.d.ts.map +1 -1
  17. package/dist/cadSolids.js +18 -1
  18. package/dist/cadSolids.js.map +1 -1
  19. package/dist/cutBrepFrameMigration.d.ts +46 -0
  20. package/dist/cutBrepFrameMigration.d.ts.map +1 -0
  21. package/dist/cutBrepFrameMigration.js +105 -0
  22. package/dist/cutBrepFrameMigration.js.map +1 -0
  23. package/dist/cutterPadding.d.ts.map +1 -1
  24. package/dist/domain/cameraProjection.js.map +1 -1
  25. package/dist/domain/sketchforge.d.ts +31 -1
  26. package/dist/domain/sketchforge.d.ts.map +1 -1
  27. package/dist/editorHistory.d.ts.map +1 -1
  28. package/dist/editorHistory.js.map +1 -1
  29. package/dist/engine.d.ts +3 -3
  30. package/dist/engine.d.ts.map +1 -1
  31. package/dist/engine.js +156 -78
  32. package/dist/engine.js.map +1 -1
  33. package/dist/exactBooleanParts.d.ts +4 -2
  34. package/dist/exactBooleanParts.d.ts.map +1 -1
  35. package/dist/exactBooleanParts.js +7 -3
  36. package/dist/exactBooleanParts.js.map +1 -1
  37. package/dist/exactBooleanSolids.d.ts.map +1 -1
  38. package/dist/gearProfile.d.ts.map +1 -1
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/loftProfile.d.ts +1 -1
  44. package/dist/loftProfile.d.ts.map +1 -1
  45. package/dist/loftProfile.js.map +1 -1
  46. package/dist/meshEdgeProjection.d.ts +104 -0
  47. package/dist/meshEdgeProjection.d.ts.map +1 -0
  48. package/dist/meshEdgeProjection.js +439 -0
  49. package/dist/meshEdgeProjection.js.map +1 -0
  50. package/dist/meshImport.d.ts.map +1 -1
  51. package/dist/meshImport.js +2 -20
  52. package/dist/meshImport.js.map +1 -1
  53. package/dist/meshMassProperties.d.ts +92 -0
  54. package/dist/meshMassProperties.d.ts.map +1 -0
  55. package/dist/meshMassProperties.js +570 -0
  56. package/dist/meshMassProperties.js.map +1 -0
  57. package/dist/meshSection.d.ts +44 -0
  58. package/dist/meshSection.d.ts.map +1 -0
  59. package/dist/meshSection.js +154 -0
  60. package/dist/meshSection.js.map +1 -0
  61. package/dist/meshShellOverlap.d.ts +75 -0
  62. package/dist/meshShellOverlap.d.ts.map +1 -0
  63. package/dist/meshShellOverlap.js +797 -0
  64. package/dist/meshShellOverlap.js.map +1 -0
  65. package/dist/meshShellSolids.d.ts +44 -0
  66. package/dist/meshShellSolids.d.ts.map +1 -0
  67. package/dist/meshShellSolids.js +116 -0
  68. package/dist/meshShellSolids.js.map +1 -0
  69. package/dist/nodeSvgDomParser.d.ts +3 -0
  70. package/dist/nodeSvgDomParser.d.ts.map +1 -0
  71. package/dist/nodeSvgDomParser.js +53 -0
  72. package/dist/nodeSvgDomParser.js.map +1 -0
  73. package/dist/parametricRecompute.d.ts.map +1 -1
  74. package/dist/parametricRecompute.js +63 -10
  75. package/dist/parametricRecompute.js.map +1 -1
  76. package/dist/placementWorkplane.d.ts.map +1 -1
  77. package/dist/placementWorkplane.js.map +1 -1
  78. package/dist/projectAssets.d.ts +1 -1
  79. package/dist/projectStore.d.ts +1 -1
  80. package/dist/projectStore.d.ts.map +1 -1
  81. package/dist/projectStore.js +23 -2
  82. package/dist/projectStore.js.map +1 -1
  83. package/dist/revolveRequests.d.ts +57 -1
  84. package/dist/revolveRequests.d.ts.map +1 -1
  85. package/dist/revolveRequests.js +103 -4
  86. package/dist/revolveRequests.js.map +1 -1
  87. package/dist/sceneExport.d.ts +6 -0
  88. package/dist/sceneExport.d.ts.map +1 -1
  89. package/dist/sceneExport.js +6 -2
  90. package/dist/sceneExport.js.map +1 -1
  91. package/dist/serverCadInterference.d.ts +8 -45
  92. package/dist/serverCadInterference.d.ts.map +1 -1
  93. package/dist/serverCadInterference.js +21 -43
  94. package/dist/serverCadInterference.js.map +1 -1
  95. package/dist/serverCadOperations.d.ts +73 -4
  96. package/dist/serverCadOperations.d.ts.map +1 -1
  97. package/dist/serverCadOperations.js +356 -71
  98. package/dist/serverCadOperations.js.map +1 -1
  99. package/dist/serverCadProjection.d.ts +14 -1
  100. package/dist/serverCadProjection.d.ts.map +1 -1
  101. package/dist/serverCadProjection.js +135 -13
  102. package/dist/serverCadProjection.js.map +1 -1
  103. package/dist/serverCadProperties.d.ts +14 -7
  104. package/dist/serverCadProperties.d.ts.map +1 -1
  105. package/dist/serverCadProperties.js +60 -24
  106. package/dist/serverCadProperties.js.map +1 -1
  107. package/dist/serverCadSection.d.ts +21 -1
  108. package/dist/serverCadSection.d.ts.map +1 -1
  109. package/dist/serverCadSection.js +157 -43
  110. package/dist/serverCadSection.js.map +1 -1
  111. package/dist/serverCadShell.d.ts +7 -94
  112. package/dist/serverCadShell.d.ts.map +1 -1
  113. package/dist/serverCadShell.js +25 -272
  114. package/dist/serverCadShell.js.map +1 -1
  115. package/dist/serverCadThickness.d.ts +8 -8
  116. package/dist/serverCadThickness.d.ts.map +1 -1
  117. package/dist/serverCadThickness.js +15 -42
  118. package/dist/serverCadThickness.js.map +1 -1
  119. package/dist/serverShapeBooleans.d.ts +75 -0
  120. package/dist/serverShapeBooleans.d.ts.map +1 -1
  121. package/dist/serverShapeBooleans.js +251 -9
  122. package/dist/serverShapeBooleans.js.map +1 -1
  123. package/dist/serverShapeGroups.js.map +1 -1
  124. package/dist/serverStepExplode.d.ts +1 -2
  125. package/dist/serverStepExplode.d.ts.map +1 -1
  126. package/dist/serverStepExplode.js +15 -148
  127. package/dist/serverStepExplode.js.map +1 -1
  128. package/dist/shapeBounds.d.ts.map +1 -1
  129. package/dist/shapeCatalog.d.ts.map +1 -1
  130. package/dist/shapeCatalog.js +1 -0
  131. package/dist/shapeCatalog.js.map +1 -1
  132. package/dist/shapeRequests.d.ts +13 -0
  133. package/dist/shapeRequests.d.ts.map +1 -1
  134. package/dist/shapeRequests.js +14 -0
  135. package/dist/shapeRequests.js.map +1 -1
  136. package/dist/sketchCadProfile.d.ts +13 -0
  137. package/dist/sketchCadProfile.d.ts.map +1 -1
  138. package/dist/sketchCadProfile.js +108 -0
  139. package/dist/sketchCadProfile.js.map +1 -1
  140. package/dist/sketchCadSweep.js.map +1 -1
  141. package/dist/sketchEdgeSegmentLineage.d.ts +32 -0
  142. package/dist/sketchEdgeSegmentLineage.d.ts.map +1 -0
  143. package/dist/sketchEdgeSegmentLineage.js +49 -0
  144. package/dist/sketchEdgeSegmentLineage.js.map +1 -0
  145. package/dist/sketchEdgeSegmentLineageKernel.d.ts +83 -0
  146. package/dist/sketchEdgeSegmentLineageKernel.d.ts.map +1 -0
  147. package/dist/sketchEdgeSegmentLineageKernel.js +125 -0
  148. package/dist/sketchEdgeSegmentLineageKernel.js.map +1 -0
  149. package/dist/sketchFaceDraftKernel.d.ts +16 -9
  150. package/dist/sketchFaceDraftKernel.d.ts.map +1 -1
  151. package/dist/sketchFaceDraftKernel.js +19 -11
  152. package/dist/sketchFaceDraftKernel.js.map +1 -1
  153. package/dist/sketchRevolve.d.ts +13 -0
  154. package/dist/sketchRevolve.d.ts.map +1 -1
  155. package/dist/sketchRevolve.js +4 -1
  156. package/dist/sketchRevolve.js.map +1 -1
  157. package/dist/skfProject.d.ts +1 -1
  158. package/dist/skfProject.d.ts.map +1 -1
  159. package/dist/skfProject.js +63 -4
  160. package/dist/skfProject.js.map +1 -1
  161. package/dist/sphereTessellation.d.ts.map +1 -1
  162. package/dist/stepAssemblyExplode.d.ts +45 -0
  163. package/dist/stepAssemblyExplode.d.ts.map +1 -0
  164. package/dist/stepAssemblyExplode.js +195 -0
  165. package/dist/stepAssemblyExplode.js.map +1 -0
  166. package/dist/stepAssemblyHierarchy.js.map +1 -1
  167. package/dist/stepComponentMetadata.d.ts +1 -1
  168. package/dist/stepComponentMetadata.d.ts.map +1 -1
  169. package/dist/stepComponentMetadata.js +93 -39
  170. package/dist/stepComponentMetadata.js.map +1 -1
  171. package/dist/stepExport.d.ts +14 -1
  172. package/dist/stepExport.d.ts.map +1 -1
  173. package/dist/stepExport.js +81 -32
  174. package/dist/stepExport.js.map +1 -1
  175. package/dist/stepImportCommon.d.ts +4 -1
  176. package/dist/stepImportCommon.d.ts.map +1 -1
  177. package/dist/stepImportCommon.js +7 -6
  178. package/dist/stepImportCommon.js.map +1 -1
  179. package/dist/stlImport.js.map +1 -1
  180. package/dist/support/cadModifierTypes.d.ts +100 -1
  181. package/dist/support/cadModifierTypes.d.ts.map +1 -1
  182. package/dist/support/i18n/LocalizedError.d.ts.map +1 -1
  183. package/dist/support/i18n/locales/en/messages.json +42 -2
  184. package/dist/support/i18n/locales.d.ts.map +1 -1
  185. package/dist/support/lib/appTheme.d.ts.map +1 -1
  186. package/dist/support/lib/materialPresets.js.map +1 -1
  187. package/dist/support/lib/measurementUnits.d.ts.map +1 -1
  188. package/dist/support/lib/operator-configuration.json +13 -1
  189. package/dist/support/lib/operatorConfiguration.d.ts +5 -5
  190. package/dist/support/lib/workplaneGrid.js.map +1 -1
  191. package/dist/svgImport.d.ts +2 -0
  192. package/dist/svgImport.d.ts.map +1 -1
  193. package/dist/svgImport.js +7 -0
  194. package/dist/svgImport.js.map +1 -1
  195. package/dist/threadCatalog.js.map +1 -1
  196. package/dist/workplaneSettings.js.map +1 -1
  197. package/dist/workplaneShapes.d.ts +2 -2
  198. package/dist/workplaneShapes.d.ts.map +1 -1
  199. package/dist/workplaneShapes.js.map +1 -1
  200. package/package.json +7 -6
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Mass properties of a triangle mesh, straight from its triangles (#508).
3
+ *
4
+ * Sewing an imported mesh into an OCCT solid only to ask the kernel for its
5
+ * volume took over a minute on a 57k-triangle STL, blocked every other call
6
+ * while it ran, and reported the answer as tessellated anyway. The divergence
7
+ * theorem gives the same numbers from the triangles in one pass.
8
+ *
9
+ * The mesh may hold several shells, and some of them may be voids. Each shell's
10
+ * triangles are first wound consistently with one another; whether the shell
11
+ * then counts as material or as a cavity is decided by how many other shells
12
+ * enclose it (even: material, odd: void), not by the file's winding, which
13
+ * imported meshes get wrong often enough that OCCT's path had to repair it.
14
+ * That reading needs the shells to nest; two bodies overlapping without one
15
+ * enclosing the other would have the overlap counted twice, so a mesh holding
16
+ * them gets no answer here (#529, see meshShellOverlap.ts).
17
+ *
18
+ * Only a mesh whose every shell is closed gets an answer. The signed volume of
19
+ * an open fragment depends on where the origin is, so there is no honest number
20
+ * to give for one; `null` sends the caller back to the kernel's sewing, which
21
+ * can close gaps this does not and refuses what it cannot close.
22
+ *
23
+ * Pure arithmetic, no kernel: safe to call from anywhere.
24
+ */
25
+ import type { MeshData } from "./meshTransform.js";
26
+ export type MeshMassProperties = {
27
+ volume: number;
28
+ area: number;
29
+ centerOfMass: {
30
+ x: number;
31
+ y: number;
32
+ z: number;
33
+ };
34
+ bbox: {
35
+ xmin: number;
36
+ ymin: number;
37
+ zmin: number;
38
+ xmax: number;
39
+ ymax: number;
40
+ zmax: number;
41
+ };
42
+ };
43
+ /**
44
+ * The mesh's mass properties, or `null` when its triangles do not bound a
45
+ * solid at any weld tolerance.
46
+ *
47
+ * Throws `MeshShellsOverlapError` when two of its closed bodies overlap without
48
+ * one enclosing the other (#529): adding them would count the overlap twice,
49
+ * and sewing them only for the kernel to refuse them held it for minutes on a
50
+ * large mesh, so the refusal comes from here.
51
+ */
52
+ export declare function meshMassProperties(mesh: MeshData): MeshMassProperties | null;
53
+ /**
54
+ * Whether two of the mesh's closed shells overlap without one enclosing the
55
+ * other; `null` when the mesh does not close, so it has no shells to compare.
56
+ * Shells that only touch -- on a face, an edge or a corner -- do not overlap:
57
+ * what they share has no volume.
58
+ */
59
+ export declare function meshShellsOverlap(mesh: MeshData): boolean | null;
60
+ /** One closed shell of a mesh, with its triangles wound as the mesh wound them. */
61
+ export type MeshSolidShell = {
62
+ /** Flat triangle corners in the mesh's own coordinates. */
63
+ positions: number[];
64
+ /** How many other shells enclose it: even is material, odd is a void. */
65
+ depth: number;
66
+ /**
67
+ * The volume it contributes to the mesh: positive for a body, negative for a
68
+ * void. Their sum is the mesh's own volume, which is what a solid assembled
69
+ * from the shells must come to if the nesting was read correctly.
70
+ */
71
+ volume: number;
72
+ };
73
+ /**
74
+ * The closed shells a mesh is made of, each with its nesting depth (#501).
75
+ *
76
+ * OCCT sews one shell into one solid; a mesh with a sealed internal void, or
77
+ * with several separate bodies, has more than one, and sewing them together
78
+ * fails. A caller builds each shell into a solid and puts them back together:
79
+ * material at even depth, voids at odd depth taken out of what encloses them.
80
+ * The winding is left alone, since the depth -- not the winding -- says which
81
+ * side is material, and the sewing orients each solid outward by itself.
82
+ *
83
+ * The shells are those of the first weld tolerance that closes the mesh, so
84
+ * their corners are the welded ones. Null unless every shell is closed and
85
+ * consistently orientable, because only then is the nesting well defined. A
86
+ * shell enclosing no volume is left out. Throws `MeshShellsOverlapError` when
87
+ * two shells overlap without nesting, since no nesting assembles them.
88
+ */
89
+ export declare function meshSolidShells(mesh: MeshData): MeshSolidShell[] | null;
90
+ /** Flat triangle corners, as a stored mesh holds them, as `MeshData`. */
91
+ export declare function meshDataFromPositions(positions: readonly number[]): MeshData;
92
+ //# sourceMappingURL=meshMassProperties.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meshMassProperties.d.ts","sourceRoot":"","sources":["../src/meshMassProperties.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGnD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9F,CAAC;AAsBF;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAI5E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CAGhE;AAED,mFAAmF;AACnF,MAAM,MAAM,cAAc,GAAG;IAC3B,2DAA2D;IAC3D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,EAAE,GAAG,IAAI,CAgBvE;AAED,yEAAyE;AACzE,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,CAY5E"}
@@ -0,0 +1,570 @@
1
+ /**
2
+ * Mass properties of a triangle mesh, straight from its triangles (#508).
3
+ *
4
+ * Sewing an imported mesh into an OCCT solid only to ask the kernel for its
5
+ * volume took over a minute on a 57k-triangle STL, blocked every other call
6
+ * while it ran, and reported the answer as tessellated anyway. The divergence
7
+ * theorem gives the same numbers from the triangles in one pass.
8
+ *
9
+ * The mesh may hold several shells, and some of them may be voids. Each shell's
10
+ * triangles are first wound consistently with one another; whether the shell
11
+ * then counts as material or as a cavity is decided by how many other shells
12
+ * enclose it (even: material, odd: void), not by the file's winding, which
13
+ * imported meshes get wrong often enough that OCCT's path had to repair it.
14
+ * That reading needs the shells to nest; two bodies overlapping without one
15
+ * enclosing the other would have the overlap counted twice, so a mesh holding
16
+ * them gets no answer here (#529, see meshShellOverlap.ts).
17
+ *
18
+ * Only a mesh whose every shell is closed gets an answer. The signed volume of
19
+ * an open fragment depends on where the origin is, so there is no honest number
20
+ * to give for one; `null` sends the caller back to the kernel's sewing, which
21
+ * can close gaps this does not and refuses what it cannot close.
22
+ *
23
+ * Pure arithmetic, no kernel: safe to call from anywhere.
24
+ */
25
+ import { MeshShellsOverlapError, relateTouchingShells, shellPairKey } from "./meshShellOverlap.js";
26
+ /**
27
+ * Weld tolerances tried in turn, in mm, after a near-exact weld. They follow
28
+ * the sewing ladder `solidFromMeshPositions` uses, so a mesh whose corners miss
29
+ * by a micron -- as a mesh boolean's output can -- closes here as it did there.
30
+ */
31
+ const WELD_LADDER_MM = [1e-5, 1e-4, 1e-3, 1e-2];
32
+ /**
33
+ * Two shells closer than this fraction of the mesh's largest coordinate touch
34
+ * rather than overlap or stand apart: sixteen float32 units in the last place.
35
+ */
36
+ const FLOAT32_CONTACT = 16 * 2 ** -24;
37
+ /** A direction no axis-aligned or 45-degree feature lines up with, so a ray along it rarely grazes an edge. */
38
+ const RAY = (() => {
39
+ const d = [0.5773502691896258 + 0.1234567, 0.5773502691896258 + 0.0456789, 0.5773502691896258 - 0.0789123];
40
+ const length = Math.hypot(d[0], d[1], d[2]);
41
+ return [d[0] / length, d[1] / length, d[2] / length];
42
+ })();
43
+ /**
44
+ * The mesh's mass properties, or `null` when its triangles do not bound a
45
+ * solid at any weld tolerance.
46
+ *
47
+ * Throws `MeshShellsOverlapError` when two of its closed bodies overlap without
48
+ * one enclosing the other (#529): adding them would count the overlap twice,
49
+ * and sewing them only for the kernel to refuse them held it for minutes on a
50
+ * large mesh, so the refusal comes from here.
51
+ */
52
+ export function meshMassProperties(mesh) {
53
+ const analysis = analyseMesh(mesh);
54
+ if (analysis?.overlapping)
55
+ throw new MeshShellsOverlapError();
56
+ return analysis ? analysis.properties : null;
57
+ }
58
+ /**
59
+ * Whether two of the mesh's closed shells overlap without one enclosing the
60
+ * other; `null` when the mesh does not close, so it has no shells to compare.
61
+ * Shells that only touch -- on a face, an edge or a corner -- do not overlap:
62
+ * what they share has no volume.
63
+ */
64
+ export function meshShellsOverlap(mesh) {
65
+ const analysis = analyseMesh(mesh);
66
+ return analysis ? analysis.overlapping : null;
67
+ }
68
+ /**
69
+ * The closed shells a mesh is made of, each with its nesting depth (#501).
70
+ *
71
+ * OCCT sews one shell into one solid; a mesh with a sealed internal void, or
72
+ * with several separate bodies, has more than one, and sewing them together
73
+ * fails. A caller builds each shell into a solid and puts them back together:
74
+ * material at even depth, voids at odd depth taken out of what encloses them.
75
+ * The winding is left alone, since the depth -- not the winding -- says which
76
+ * side is material, and the sewing orients each solid outward by itself.
77
+ *
78
+ * The shells are those of the first weld tolerance that closes the mesh, so
79
+ * their corners are the welded ones. Null unless every shell is closed and
80
+ * consistently orientable, because only then is the nesting well defined. A
81
+ * shell enclosing no volume is left out. Throws `MeshShellsOverlapError` when
82
+ * two shells overlap without nesting, since no nesting assembles them.
83
+ */
84
+ export function meshSolidShells(mesh) {
85
+ const analysis = analyseMesh(mesh);
86
+ if (!analysis)
87
+ return null;
88
+ if (analysis.overlapping)
89
+ throw new MeshShellsOverlapError();
90
+ const { shells, depth, contribution, corners, points, origin } = analysis;
91
+ return shells.flatMap((members, shell) => {
92
+ if (depth[shell] < 0)
93
+ return [];
94
+ const positions = [];
95
+ for (const t of members) {
96
+ for (let k = 0; k < 3; k += 1) {
97
+ const index = 3 * corners[3 * t + k];
98
+ positions.push(points[index] + origin[0], points[index + 1] + origin[1], points[index + 2] + origin[2]);
99
+ }
100
+ }
101
+ return [{ positions, depth: depth[shell], volume: contribution[shell] }];
102
+ });
103
+ }
104
+ /** Flat triangle corners, as a stored mesh holds them, as `MeshData`. */
105
+ export function meshDataFromPositions(positions) {
106
+ const vertices = [];
107
+ const faces = [];
108
+ for (let index = 0; index + 8 < positions.length; index += 9) {
109
+ const first = vertices.length;
110
+ for (let corner = 0; corner < 3; corner += 1) {
111
+ const at = index + corner * 3;
112
+ vertices.push([positions[at], positions[at + 1], positions[at + 2]]);
113
+ }
114
+ faces.push([first, first + 1, first + 2]);
115
+ }
116
+ return { name: "mesh", vertices, faces };
117
+ }
118
+ /** The mesh welded at the first tolerance that closes it, measured, with the shells it found. */
119
+ function analyseMesh(mesh) {
120
+ if (!mesh.vertices.length || !mesh.faces.length)
121
+ return null;
122
+ // Bounds, from every vertex a triangle uses.
123
+ let xmin = Infinity, ymin = Infinity, zmin = Infinity;
124
+ let xmax = -Infinity, ymax = -Infinity, zmax = -Infinity;
125
+ for (const face of mesh.faces) {
126
+ for (const index of face) {
127
+ const vertex = mesh.vertices[index];
128
+ if (!vertex)
129
+ continue;
130
+ const [x, y, z] = vertex;
131
+ if (x < xmin)
132
+ xmin = x;
133
+ if (x > xmax)
134
+ xmax = x;
135
+ if (y < ymin)
136
+ ymin = y;
137
+ if (y > ymax)
138
+ ymax = y;
139
+ if (z < zmin)
140
+ zmin = z;
141
+ if (z > zmax)
142
+ zmax = z;
143
+ }
144
+ }
145
+ if (!Number.isFinite(xmin + ymin + zmin + xmax + ymax + zmax))
146
+ return null;
147
+ const bbox = { xmin, ymin, zmin, xmax, ymax, zmax };
148
+ // Corners measured from the bounds' centre to keep the products small.
149
+ const origin = [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2];
150
+ let welded = cornerPoints(mesh, origin);
151
+ const tight = Math.max(1, Math.hypot(xmax - xmin, ymax - ymin, zmax - zmin)) * 1e-9;
152
+ // How far apart two shells may be and still touch. An STL stores float32
153
+ // corners, rounded by up to half a unit in the last place of the coordinate's
154
+ // own size -- about 6e-8 of it, 1e-5 mm at 200 mm -- so a part resting on
155
+ // another, once rotated or moved, sits that far above or below it: far more
156
+ // than the weld's near-exact step, and far less than any real overlap.
157
+ const reach = Math.max(Math.abs(xmin), Math.abs(xmax), Math.abs(ymin), Math.abs(ymax), Math.abs(zmin), Math.abs(zmax));
158
+ const contact = FLOAT32_CONTACT * Math.max(1, reach, Math.hypot(xmax - xmin, ymax - ymin, zmax - zmin));
159
+ // Each pass welds what the last one left, so it only sees distinct points.
160
+ for (const tolerance of [tight, ...WELD_LADDER_MM.filter((step) => step > tight)]) {
161
+ welded = compact(welded, weld(welded.points, tolerance));
162
+ const measured = measureWelded(welded, tolerance, Math.max(contact, tolerance * 10));
163
+ if (!measured)
164
+ continue;
165
+ const { volume, area, moment } = measured;
166
+ const properties = {
167
+ volume,
168
+ area,
169
+ centerOfMass: { x: origin[0] + moment[0] / volume, y: origin[1] + moment[1] / volume, z: origin[2] + moment[2] / volume },
170
+ bbox,
171
+ };
172
+ return { ...measured, properties, points: welded.points, origin };
173
+ }
174
+ return null;
175
+ }
176
+ function cornerPoints(mesh, origin) {
177
+ const points = new Float64Array(mesh.faces.length * 9);
178
+ const corners = new Int32Array(mesh.faces.length * 3);
179
+ mesh.faces.forEach((face, f) => {
180
+ for (let k = 0; k < 3; k += 1) {
181
+ const c = 3 * f + k;
182
+ const vertex = mesh.vertices[face[k]];
183
+ corners[c] = vertex ? c : -1;
184
+ if (!vertex)
185
+ continue;
186
+ points[3 * c] = vertex[0] - origin[0];
187
+ points[3 * c + 1] = vertex[1] - origin[1];
188
+ points[3 * c + 2] = vertex[2] - origin[2];
189
+ }
190
+ });
191
+ return { points, corners };
192
+ }
193
+ /** Keep only the points `into` maps to themselves, and point every corner at its survivor. */
194
+ function compact({ points, corners }, into) {
195
+ const renumbered = new Int32Array(into.length).fill(-1);
196
+ let kept = 0;
197
+ for (let p = 0; p < into.length; p += 1)
198
+ if (into[p] === p)
199
+ renumbered[p] = kept++;
200
+ const next = new Float64Array(kept * 3);
201
+ for (let p = 0; p < into.length; p += 1) {
202
+ if (renumbered[p] < 0)
203
+ continue;
204
+ next.set(points.subarray(3 * p, 3 * p + 3), 3 * renumbered[p]);
205
+ }
206
+ const remapped = corners.map((c) => (c < 0 ? -1 : renumbered[into[c]]));
207
+ return { points: next, corners: remapped };
208
+ }
209
+ /**
210
+ * Merge points lying within `tolerance` of an earlier one on every axis.
211
+ *
212
+ * Points are bucketed into cells twice the tolerance wide, so the points within
213
+ * reach of one lie in the 2 x 2 x 2 cells on the side of its cell it sits
214
+ * nearest; checking those means two points straddling a cell boundary still
215
+ * merge, where rounding to a grid alone would keep them apart however close
216
+ * they are. Cells are keyed by a hash: a collision only puts two cells' points
217
+ * in one bucket, and the distance test still tells them apart.
218
+ */
219
+ function weld(points, tolerance) {
220
+ const count = points.length / 3;
221
+ const into = new Int32Array(count);
222
+ const cells = new Map();
223
+ const size = 2 * tolerance;
224
+ const hash = (i, j, k) => Math.imul(i, 73856093) ^ Math.imul(j, 19349663) ^ Math.imul(k, 83492791);
225
+ for (let p = 0; p < count; p += 1) {
226
+ const x = points[3 * p], y = points[3 * p + 1], z = points[3 * p + 2];
227
+ const fx = x / size, fy = y / size, fz = z / size;
228
+ const i = Math.floor(fx), j = Math.floor(fy), k = Math.floor(fz);
229
+ const si = fx - i < 0.5 ? -1 : 1, sj = fy - j < 0.5 ? -1 : 1, sk = fz - k < 0.5 ? -1 : 1;
230
+ let found = -1;
231
+ search: for (let n = 0; n < 8; n += 1) {
232
+ const bucket = cells.get(hash(n & 1 ? i + si : i, n & 2 ? j + sj : j, n & 4 ? k + sk : k));
233
+ if (!bucket)
234
+ continue;
235
+ for (const q of bucket) {
236
+ if (Math.abs(points[3 * q] - x) <= tolerance && Math.abs(points[3 * q + 1] - y) <= tolerance && Math.abs(points[3 * q + 2] - z) <= tolerance) {
237
+ found = q;
238
+ break search;
239
+ }
240
+ }
241
+ }
242
+ if (found >= 0) {
243
+ into[p] = into[found];
244
+ }
245
+ else {
246
+ into[p] = p;
247
+ const key = hash(i, j, k);
248
+ const bucket = cells.get(key);
249
+ if (bucket)
250
+ bucket.push(p);
251
+ else
252
+ cells.set(key, [p]);
253
+ }
254
+ }
255
+ return into;
256
+ }
257
+ /** Volume, area and first moment of the welded mesh, or `null` when some shell is open or cannot be wound consistently. */
258
+ function measureWelded({ points, corners: raw }, tolerance, contact) {
259
+ // Triangles on the welded corners. A triangle with a repeated corner encloses
260
+ // and bounds nothing. A triangle the file repeats counts once, and two that
261
+ // coincide facing opposite ways -- the shared face of two parts stacked on
262
+ // each other -- cancel, which leaves the two parts' union.
263
+ const vertexCount = points.length / 3;
264
+ // Keyed by the two lowest corners; each entry lists [highest corner, net winding] pairs.
265
+ const tally = new Map();
266
+ for (let t = 0; t < raw.length; t += 3) {
267
+ if (raw[t] < 0 || raw[t + 1] < 0 || raw[t + 2] < 0)
268
+ continue;
269
+ const a = raw[t], b = raw[t + 1], c = raw[t + 2];
270
+ if (a === b || b === c || a === c)
271
+ continue;
272
+ // Rotate the smallest corner first; the other two then read ascending or not.
273
+ const [p, q, r] = a < b && a < c ? [a, b, c] : b < c ? [b, c, a] : [c, a, b];
274
+ const ascending = q < r;
275
+ const key = p * vertexCount + (ascending ? q : r);
276
+ const high = ascending ? r : q;
277
+ const entry = tally.get(key);
278
+ if (!entry) {
279
+ tally.set(key, [high, ascending ? 1 : -1]);
280
+ continue;
281
+ }
282
+ let at = 0;
283
+ while (at < entry.length && entry[at] !== high)
284
+ at += 2;
285
+ if (at === entry.length)
286
+ entry.push(high, ascending ? 1 : -1);
287
+ else
288
+ entry[at + 1] += ascending ? 1 : -1;
289
+ }
290
+ const cornerList = [];
291
+ for (const [key, entry] of tally) {
292
+ const p = Math.floor(key / vertexCount), low = key - p * vertexCount;
293
+ for (let at = 0; at < entry.length; at += 2) {
294
+ if (entry[at + 1] > 0)
295
+ cornerList.push(p, low, entry[at]);
296
+ else if (entry[at + 1] < 0)
297
+ cornerList.push(p, entry[at], low);
298
+ }
299
+ }
300
+ const corners = Int32Array.from(cornerList);
301
+ const triangleCount = corners.length / 3;
302
+ if (!triangleCount)
303
+ return null;
304
+ const edgeKey = (a, b) => (a < b ? a * vertexCount + b : b * vertexCount + a);
305
+ // Which triangles meet along each edge.
306
+ const edgeTriangles = new Map();
307
+ for (let t = 0; t < triangleCount; t += 1) {
308
+ for (let k = 0; k < 3; k += 1) {
309
+ const key = edgeKey(corners[3 * t + k], corners[3 * t + ((k + 1) % 3)]);
310
+ const list = edgeTriangles.get(key);
311
+ if (list)
312
+ list.push(t);
313
+ else
314
+ edgeTriangles.set(key, [t]);
315
+ }
316
+ }
317
+ const runsForward = (t, a, b) => {
318
+ for (let k = 0; k < 3; k += 1) {
319
+ if (corners[3 * t + k] === a && corners[3 * t + ((k + 1) % 3)] === b)
320
+ return true;
321
+ }
322
+ return false;
323
+ };
324
+ // Shells: triangles joined across manifold edges, wound to agree with the
325
+ // first triangle of their shell. An edge shared by more than two triangles
326
+ // (two parts touching along it) joins nothing.
327
+ const shellOf = new Int32Array(triangleCount).fill(-1);
328
+ const flipped = new Uint8Array(triangleCount);
329
+ const shells = [];
330
+ for (let seed = 0; seed < triangleCount; seed += 1) {
331
+ if (shellOf[seed] >= 0)
332
+ continue;
333
+ const shell = shells.length;
334
+ const members = [seed];
335
+ shellOf[seed] = shell;
336
+ for (let cursor = 0; cursor < members.length; cursor += 1) {
337
+ const t = members[cursor];
338
+ for (let k = 0; k < 3; k += 1) {
339
+ let a = corners[3 * t + k], b = corners[3 * t + ((k + 1) % 3)];
340
+ if (flipped[t])
341
+ [a, b] = [b, a];
342
+ const sharing = edgeTriangles.get(edgeKey(a, b));
343
+ if (sharing.length !== 2)
344
+ continue;
345
+ const other = sharing[0] === t ? sharing[1] : sharing[0];
346
+ // Agreeing neighbours cross a shared edge in opposite directions.
347
+ const otherFlip = runsForward(other, a, b) ? 1 : 0;
348
+ if (shellOf[other] < 0) {
349
+ shellOf[other] = shell;
350
+ flipped[other] = otherFlip;
351
+ members.push(other);
352
+ }
353
+ else if (flipped[other] !== otherFlip) {
354
+ return null; // Not orientable.
355
+ }
356
+ }
357
+ }
358
+ shells.push(members);
359
+ }
360
+ // Every shell closed on its own: in its own winding, each edge it uses is
361
+ // crossed as often one way as the other. Checked before anything else, so an
362
+ // open mesh -- which can fall into one shell per triangle -- costs one pass.
363
+ for (const [key, sharing] of edgeTriangles) {
364
+ const a = Math.floor(key / vertexCount), b = key - a * vertexCount;
365
+ // [shell, net, shell, net, ...]: an edge rarely has more than one shell on it.
366
+ const nets = [];
367
+ for (const t of sharing) {
368
+ const direction = (runsForward(t, a, b) ? 1 : -1) * (flipped[t] ? -1 : 1);
369
+ let at = 0;
370
+ while (at < nets.length && nets[at] !== shellOf[t])
371
+ at += 2;
372
+ if (at === nets.length)
373
+ nets.push(shellOf[t], direction);
374
+ else
375
+ nets[at + 1] += direction;
376
+ }
377
+ for (let at = 1; at < nets.length; at += 2)
378
+ if (nets[at] !== 0)
379
+ return null;
380
+ }
381
+ // The triangle across each edge, where exactly two meet: the same joins the shells were made of.
382
+ const neighbours = new Int32Array(triangleCount * 3).fill(-1);
383
+ for (let t = 0; t < triangleCount; t += 1) {
384
+ for (let k = 0; k < 3; k += 1) {
385
+ const sharing = edgeTriangles.get(edgeKey(corners[3 * t + k], corners[3 * t + ((k + 1) % 3)]));
386
+ if (sharing.length === 2)
387
+ neighbours[3 * t + k] = sharing[0] === t ? sharing[1] : sharing[0];
388
+ }
389
+ }
390
+ // Per shell: signed volume, first moment, area and bounds, in its own winding.
391
+ const shellVolume = new Float64Array(shells.length);
392
+ const shellMoment = new Float64Array(shells.length * 3);
393
+ const shellBounds = new Float64Array(shells.length * 6);
394
+ let area = 0;
395
+ shells.forEach((members, shell) => {
396
+ let volume = 0, mx = 0, my = 0, mz = 0;
397
+ let bx0 = Infinity, by0 = Infinity, bz0 = Infinity, bx1 = -Infinity, by1 = -Infinity, bz1 = -Infinity;
398
+ for (const t of members) {
399
+ const ia = 3 * corners[3 * t];
400
+ let ib = 3 * corners[3 * t + 1];
401
+ let ic = 3 * corners[3 * t + 2];
402
+ if (flipped[t])
403
+ [ib, ic] = [ic, ib];
404
+ const ax = points[ia], ay = points[ia + 1], az = points[ia + 2];
405
+ const bx = points[ib], by = points[ib + 1], bz = points[ib + 2];
406
+ const cx = points[ic], cy = points[ic + 1], cz = points[ic + 2];
407
+ // Tetrahedron (origin, a, b, c): signed volume and centroid.
408
+ const v = (ax * (by * cz - bz * cy) - ay * (bx * cz - bz * cx) + az * (bx * cy - by * cx)) / 6;
409
+ volume += v;
410
+ mx += (v * (ax + bx + cx)) / 4;
411
+ my += (v * (ay + by + cy)) / 4;
412
+ mz += (v * (az + bz + cz)) / 4;
413
+ const ux = bx - ax, uy = by - ay, uz = bz - az;
414
+ const wx = cx - ax, wy = cy - ay, wz = cz - az;
415
+ area += Math.hypot(uy * wz - uz * wy, uz * wx - ux * wz, ux * wy - uy * wx) / 2;
416
+ bx0 = Math.min(bx0, ax, bx, cx);
417
+ bx1 = Math.max(bx1, ax, bx, cx);
418
+ by0 = Math.min(by0, ay, by, cy);
419
+ by1 = Math.max(by1, ay, by, cy);
420
+ bz0 = Math.min(bz0, az, bz, cz);
421
+ bz1 = Math.max(bz1, az, bz, cz);
422
+ }
423
+ shellVolume[shell] = volume;
424
+ shellMoment.set([mx, my, mz], shell * 3);
425
+ shellBounds.set([bx0, by0, bz0, bx1, by1, bz1], shell * 6);
426
+ });
427
+ // How many other shells enclose this one. Only a shell whose bounds hold all
428
+ // of this one's can, which rules out every pair that merely sits side by side
429
+ // or touches; for the rest, cast a ray from this shell's extreme corner along
430
+ // RAY, which leaves the shell itself at once, and count the crossings.
431
+ //
432
+ // Comparing every pair of shells' bounds would cost the square of the shell
433
+ // count, so shells are first entered in a coarse grid over every cell their
434
+ // bounds overlap. Any shell enclosing this one overlaps the cell this one's
435
+ // lowest corner is in, so only that cell's shells need comparing.
436
+ const slack = tolerance * 10;
437
+ // Shells whose surfaces meet: whether any overlap without nesting, and for
438
+ // the rest which encloses which, read from their surfaces -- a ray from a
439
+ // point where two shells touch cannot tell (#529).
440
+ const touching = shells.length > 1
441
+ ? relateTouchingShells({ points, corners, shells, shellOf, neighbours, bounds: shellBounds }, contact)
442
+ : { overlapping: null, relations: new Map() };
443
+ const perAxis = Math.max(1, Math.min(64, Math.ceil(Math.cbrt(shells.length))));
444
+ const lows = [Infinity, Infinity, Infinity], highs = [-Infinity, -Infinity, -Infinity];
445
+ for (let b = 0; b < shellBounds.length; b += 6) {
446
+ for (let axis = 0; axis < 3; axis += 1) {
447
+ lows[axis] = Math.min(lows[axis], shellBounds[b + axis]);
448
+ highs[axis] = Math.max(highs[axis], shellBounds[b + 3 + axis]);
449
+ }
450
+ }
451
+ const cellAlong = (axis, value) => {
452
+ const span = highs[axis] - lows[axis];
453
+ if (!(span > 0))
454
+ return 0;
455
+ return Math.max(0, Math.min(perAxis - 1, Math.floor(((value - lows[axis]) / span) * perAxis)));
456
+ };
457
+ const grid = new Map();
458
+ if (shells.length > 1) {
459
+ for (let shell = 0; shell < shells.length; shell += 1) {
460
+ const b = 6 * shell;
461
+ const i0 = cellAlong(0, shellBounds[b] - slack), i1 = cellAlong(0, shellBounds[b + 3] + slack);
462
+ const j0 = cellAlong(1, shellBounds[b + 1] - slack), j1 = cellAlong(1, shellBounds[b + 4] + slack);
463
+ const k0 = cellAlong(2, shellBounds[b + 2] - slack), k1 = cellAlong(2, shellBounds[b + 5] + slack);
464
+ for (let i = i0; i <= i1; i += 1) {
465
+ for (let j = j0; j <= j1; j += 1) {
466
+ for (let k = k0; k <= k1; k += 1) {
467
+ const key = i + perAxis * (j + perAxis * k);
468
+ const cell = grid.get(key);
469
+ if (cell)
470
+ cell.push(shell);
471
+ else
472
+ grid.set(key, [shell]);
473
+ }
474
+ }
475
+ }
476
+ }
477
+ }
478
+ const enclosingCount = (shell) => {
479
+ const s = 6 * shell;
480
+ let best = -Infinity, px = 0, py = 0, pz = 0;
481
+ let depth = 0;
482
+ const candidates = grid.get(cellAlong(0, shellBounds[s]) + perAxis * (cellAlong(1, shellBounds[s + 1]) + perAxis * cellAlong(2, shellBounds[s + 2]))) ?? [];
483
+ for (const other of candidates) {
484
+ if (other === shell)
485
+ continue;
486
+ const relation = touching.relations.get(shellPairKey(shell, other, shells.length));
487
+ if (relation) {
488
+ if (relation === (shell < other ? "first-inside" : "second-inside"))
489
+ depth += 1;
490
+ continue;
491
+ }
492
+ const b = 6 * other;
493
+ if (shellBounds[s] < shellBounds[b] - slack || shellBounds[s + 1] < shellBounds[b + 1] - slack || shellBounds[s + 2] < shellBounds[b + 2] - slack)
494
+ continue;
495
+ if (shellBounds[s + 3] > shellBounds[b + 3] + slack || shellBounds[s + 4] > shellBounds[b + 4] + slack || shellBounds[s + 5] > shellBounds[b + 5] + slack)
496
+ continue;
497
+ if (best === -Infinity) {
498
+ for (const t of shells[shell]) {
499
+ for (let k = 0; k < 3; k += 1) {
500
+ const i = 3 * corners[3 * t + k];
501
+ const reach = points[i] * RAY[0] + points[i + 1] * RAY[1] + points[i + 2] * RAY[2];
502
+ if (reach > best) {
503
+ best = reach;
504
+ px = points[i];
505
+ py = points[i + 1];
506
+ pz = points[i + 2];
507
+ }
508
+ }
509
+ }
510
+ }
511
+ let crossings = 0;
512
+ for (const t of shells[other]) {
513
+ if (rayHitsTriangle(px, py, pz, points, corners, t, slack))
514
+ crossings += 1;
515
+ }
516
+ if (crossings % 2 === 1)
517
+ depth += 1;
518
+ }
519
+ return depth;
520
+ };
521
+ let volume = 0;
522
+ const moment = [0, 0, 0];
523
+ // Nesting depth per shell, -1 for a shell that encloses nothing, and the
524
+ // signed volume each adds to the whole.
525
+ const depth = new Int32Array(shells.length).fill(-1);
526
+ const contribution = new Float64Array(shells.length);
527
+ for (let shell = 0; shell < shells.length; shell += 1) {
528
+ const own = shellVolume[shell];
529
+ if (own === 0)
530
+ continue;
531
+ depth[shell] = shells.length > 1 ? enclosingCount(shell) : 0;
532
+ const wanted = depth[shell] % 2 === 1 ? -1 : 1;
533
+ const sign = Math.sign(own) === wanted ? 1 : -1;
534
+ contribution[shell] = sign * own;
535
+ volume += sign * own;
536
+ moment[0] += sign * shellMoment[3 * shell];
537
+ moment[1] += sign * shellMoment[3 * shell + 1];
538
+ moment[2] += sign * shellMoment[3 * shell + 2];
539
+ }
540
+ if (!(volume > 0))
541
+ return null;
542
+ return { volume, area, moment, shells, depth, contribution, corners, overlapping: touching.overlapping !== null };
543
+ }
544
+ /** Möller-Trumbore: whether the ray from (px, py, pz) along RAY crosses triangle `t` beyond `minDistance`. */
545
+ function rayHitsTriangle(px, py, pz, points, corners, t, minDistance) {
546
+ const ia = 3 * corners[3 * t], ib = 3 * corners[3 * t + 1], ic = 3 * corners[3 * t + 2];
547
+ const ax = points[ia], ay = points[ia + 1], az = points[ia + 2];
548
+ const e1x = points[ib] - ax, e1y = points[ib + 1] - ay, e1z = points[ib + 2] - az;
549
+ const e2x = points[ic] - ax, e2y = points[ic + 1] - ay, e2z = points[ic + 2] - az;
550
+ const hx = RAY[1] * e2z - RAY[2] * e2y;
551
+ const hy = RAY[2] * e2x - RAY[0] * e2z;
552
+ const hz = RAY[0] * e2y - RAY[1] * e2x;
553
+ const det = e1x * hx + e1y * hy + e1z * hz;
554
+ if (Math.abs(det) < 1e-15)
555
+ return false;
556
+ const inv = 1 / det;
557
+ const sx = px - ax, sy = py - ay, sz = pz - az;
558
+ const u = (sx * hx + sy * hy + sz * hz) * inv;
559
+ if (u < 0 || u > 1)
560
+ return false;
561
+ const qx = sy * e1z - sz * e1y;
562
+ const qy = sz * e1x - sx * e1z;
563
+ const qz = sx * e1y - sy * e1x;
564
+ const v = (RAY[0] * qx + RAY[1] * qy + RAY[2] * qz) * inv;
565
+ if (v < 0 || u + v > 1)
566
+ return false;
567
+ const distance = (e2x * qx + e2y * qy + e2z * qz) * inv;
568
+ return distance > minDistance;
569
+ }
570
+ //# sourceMappingURL=meshMassProperties.js.map