@jscad/modeling 2.7.1 → 2.9.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 (194) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/jscad-modeling.min.js +174 -144
  3. package/package.json +2 -2
  4. package/src/colors/hslToRgb.js +1 -1
  5. package/src/colors/hueToColorComponent.js +1 -0
  6. package/src/curves/bezier/tangentAt.js +2 -2
  7. package/src/curves/bezier/tangentAt.test.js +1 -1
  8. package/src/curves/bezier/valueAt.test.js +1 -1
  9. package/src/geometries/geom2/index.js +10 -0
  10. package/src/geometries/geom2/isA.js +2 -2
  11. package/src/geometries/geom2/toCompactBinary.js +4 -4
  12. package/src/geometries/geom2/toOutlines.js +6 -11
  13. package/src/geometries/geom2/toString.js +1 -1
  14. package/src/geometries/geom2/transform.test.js +1 -1
  15. package/src/geometries/geom3/fromCompactBinary.js +1 -1
  16. package/src/geometries/geom3/index.js +17 -0
  17. package/src/geometries/geom3/invert.js +2 -2
  18. package/src/geometries/geom3/isA.js +2 -2
  19. package/src/geometries/geom3/toCompactBinary.js +4 -4
  20. package/src/geometries/geom3/toPoints.js +1 -0
  21. package/src/geometries/geom3/toString.js +1 -1
  22. package/src/geometries/geom3/transform.test.js +1 -1
  23. package/src/geometries/index.js +8 -1
  24. package/src/geometries/path2/eachPoint.js +3 -3
  25. package/src/geometries/path2/index.js +11 -0
  26. package/src/geometries/path2/isA.js +2 -2
  27. package/src/geometries/path2/reverse.js +4 -4
  28. package/src/geometries/path2/toCompactBinary.js +6 -6
  29. package/src/geometries/path2/toString.js +1 -1
  30. package/src/geometries/path2/transform.test.js +1 -1
  31. package/src/geometries/poly2/arePointsInside.test.js +1 -1
  32. package/src/geometries/poly2/index.js +6 -0
  33. package/src/geometries/poly3/index.js +7 -1
  34. package/src/geometries/poly3/invert.js +7 -1
  35. package/src/geometries/poly3/isA.js +2 -2
  36. package/src/geometries/poly3/isConvex.js +2 -2
  37. package/src/geometries/poly3/measureArea.js +4 -4
  38. package/src/geometries/poly3/measureBoundingBox.js +2 -2
  39. package/src/geometries/poly3/measureBoundingSphere.js +2 -2
  40. package/src/geometries/poly3/measureSignedVolume.js +4 -4
  41. package/src/geometries/poly3/toPoints.js +2 -2
  42. package/src/geometries/poly3/toString.js +2 -2
  43. package/src/geometries/poly3/transform.js +2 -2
  44. package/src/maths/index.js +1 -1
  45. package/src/maths/line2/equals.js +2 -2
  46. package/src/maths/line2/fromValues.js +2 -2
  47. package/src/maths/line2/intersectPointOfLines.js +1 -1
  48. package/src/maths/line2/intersectPointOfLines.test.js +1 -1
  49. package/src/maths/line2/reverse.test.js +1 -1
  50. package/src/maths/line2/transform.test.js +1 -1
  51. package/src/maths/line3/equals.js +2 -2
  52. package/src/maths/line3/reverse.test.js +1 -1
  53. package/src/maths/line3/transform.test.js +1 -1
  54. package/src/maths/mat4/fromRotation.js +1 -1
  55. package/src/maths/mat4/fromVectorRotation.js +1 -1
  56. package/src/maths/mat4/fromVectorRotation.test.js +1 -1
  57. package/src/maths/mat4/identity.test.js +1 -1
  58. package/src/maths/mat4/invert.js +18 -18
  59. package/src/maths/mat4/isIdentity.js +1 -1
  60. package/src/maths/mat4/isIdentity.test.js +0 -2
  61. package/src/maths/mat4/isMirroring.js +4 -4
  62. package/src/maths/mat4/isMirroring.test.js +1 -1
  63. package/src/maths/mat4/leftMultiplyVec3.js +2 -2
  64. package/src/maths/mat4/rotate.js +1 -1
  65. package/src/maths/mat4/toString.js +2 -2
  66. package/src/maths/mat4/translate.test.js +1 -1
  67. package/src/maths/plane/flip.test.js +1 -1
  68. package/src/maths/plane/fromPoints.d.ts +1 -1
  69. package/src/maths/plane/fromPoints.js +1 -3
  70. package/src/maths/plane/signedDistanceToPoint.js +1 -1
  71. package/src/maths/plane/transform.test.js +1 -1
  72. package/src/maths/utils/aboutEqualNormals.js +2 -2
  73. package/src/maths/vec2/abs.d.ts +1 -1
  74. package/src/maths/vec2/add.test.js +1 -1
  75. package/src/maths/vec2/angleDegrees.d.ts +1 -1
  76. package/src/maths/vec2/angleRadians.d.ts +1 -1
  77. package/src/maths/vec2/create.js +1 -1
  78. package/src/maths/vec2/cross.test.js +1 -1
  79. package/src/maths/vec2/divide.test.js +1 -1
  80. package/src/maths/vec2/fromAngleDegrees.js +1 -1
  81. package/src/maths/vec2/fromScalar.js +1 -1
  82. package/src/maths/vec2/length.d.ts +1 -1
  83. package/src/maths/vec2/length.js +1 -1
  84. package/src/maths/vec2/length.test.js +10 -0
  85. package/src/maths/vec2/lerp.test.js +1 -1
  86. package/src/maths/vec2/multiply.test.js +1 -1
  87. package/src/maths/vec2/negate.test.js +1 -1
  88. package/src/maths/vec2/normal.js +1 -1
  89. package/src/maths/vec2/normalize.d.ts +1 -1
  90. package/src/maths/vec2/normalize.test.js +1 -1
  91. package/src/maths/vec2/rotate.test.js +1 -1
  92. package/src/maths/vec2/squaredLength.d.ts +1 -1
  93. package/src/maths/vec2/squaredLength.js +3 -3
  94. package/src/maths/vec2/subtract.test.js +1 -1
  95. package/src/maths/vec2/toString.js +1 -1
  96. package/src/maths/vec2/transform.test.js +1 -1
  97. package/src/maths/vec3/abs.d.ts +1 -1
  98. package/src/maths/vec3/add.test.js +1 -1
  99. package/src/maths/vec3/angle.js +2 -2
  100. package/src/maths/vec3/angle.test.js +17 -0
  101. package/src/maths/vec3/cross.test.js +1 -1
  102. package/src/maths/vec3/divide.test.js +1 -1
  103. package/src/maths/vec3/fromScalar.js +1 -1
  104. package/src/maths/vec3/fromVec2.d.ts +1 -1
  105. package/src/maths/vec3/fromVec2.js +3 -3
  106. package/src/maths/vec3/length.d.ts +1 -1
  107. package/src/maths/vec3/length.js +4 -4
  108. package/src/maths/vec3/length.test.js +10 -0
  109. package/src/maths/vec3/lerp.test.js +1 -1
  110. package/src/maths/vec3/multiply.test.js +1 -1
  111. package/src/maths/vec3/negate.d.ts +1 -1
  112. package/src/maths/vec3/negate.test.js +1 -1
  113. package/src/maths/vec3/normalize.d.ts +1 -1
  114. package/src/maths/vec3/normalize.test.js +1 -1
  115. package/src/maths/vec3/rotateX.test.js +1 -1
  116. package/src/maths/vec3/rotateY.test.js +1 -1
  117. package/src/maths/vec3/rotateZ.test.js +1 -1
  118. package/src/maths/vec3/scale.test.js +1 -1
  119. package/src/maths/vec3/squaredLength.d.ts +1 -1
  120. package/src/maths/vec3/squaredLength.js +4 -4
  121. package/src/maths/vec3/subtract.test.js +1 -1
  122. package/src/maths/vec3/toString.js +1 -1
  123. package/src/maths/vec3/transform.test.js +1 -1
  124. package/src/maths/vec4/toString.js +1 -1
  125. package/src/maths/vec4/transform.test.js +1 -1
  126. package/src/measurements/measureBoundingSphere.js +4 -4
  127. package/src/measurements/measureCenterOfMass.js +1 -1
  128. package/src/operations/booleans/mayOverlap.js +3 -3
  129. package/src/operations/booleans/retessellate.js +3 -5
  130. package/src/operations/booleans/scission.js +1 -1
  131. package/src/operations/booleans/subtract.js +1 -1
  132. package/src/operations/booleans/union.test.js +1 -1
  133. package/src/operations/booleans/unionGeom3Sub.js +1 -1
  134. package/src/operations/expansions/expand.js +2 -2
  135. package/src/operations/expansions/expand.test.js +3 -35
  136. package/src/operations/expansions/expandShell.js +24 -18
  137. package/src/operations/expansions/offset.js +1 -1
  138. package/src/operations/expansions/offset.test.js +25 -89
  139. package/src/operations/expansions/offsetFromPoints.js +11 -6
  140. package/src/operations/extrusions/earcut/assignHoles.js +87 -0
  141. package/src/operations/extrusions/earcut/assignHoles.test.js +28 -0
  142. package/src/operations/extrusions/earcut/eliminateHoles.js +131 -0
  143. package/src/operations/extrusions/earcut/index.js +252 -0
  144. package/src/operations/extrusions/earcut/linkedList.js +58 -0
  145. package/src/operations/extrusions/earcut/linkedListSort.js +54 -0
  146. package/src/operations/extrusions/earcut/linkedPolygon.js +197 -0
  147. package/src/operations/extrusions/earcut/polygonHierarchy.js +64 -0
  148. package/src/operations/extrusions/earcut/triangle.js +16 -0
  149. package/src/operations/extrusions/extrudeFromSlices.js +10 -3
  150. package/src/operations/extrusions/extrudeFromSlices.test.js +33 -23
  151. package/src/operations/extrusions/extrudeLinear.js +11 -6
  152. package/src/operations/extrusions/extrudeLinear.test.js +77 -27
  153. package/src/operations/extrusions/extrudeLinearGeom2.js +5 -2
  154. package/src/operations/extrusions/extrudeLinearPath2.js +24 -0
  155. package/src/operations/extrusions/extrudeRectangular.js +1 -1
  156. package/src/operations/extrusions/extrudeRectangular.test.js +7 -7
  157. package/src/operations/extrusions/extrudeRotate.test.js +19 -27
  158. package/src/operations/extrusions/project.js +1 -1
  159. package/src/operations/extrusions/slice/calculatePlane.js +7 -4
  160. package/src/operations/extrusions/slice/isA.js +2 -2
  161. package/src/operations/extrusions/slice/repairSlice.js +47 -0
  162. package/src/operations/extrusions/slice/toPolygons.js +24 -60
  163. package/src/operations/hulls/hull.test.js +1 -1
  164. package/src/operations/hulls/hullChain.js +1 -1
  165. package/src/operations/hulls/hullGeom2.js +1 -1
  166. package/src/operations/hulls/hullPath2.js +6 -4
  167. package/src/operations/hulls/hullPath2.test.js +16 -0
  168. package/src/operations/hulls/hullPoints2.test.js +1 -1
  169. package/src/operations/hulls/quickhull/QuickHull.js +2 -2
  170. package/src/operations/modifiers/edges.js +2 -4
  171. package/src/operations/modifiers/generalize.js +4 -7
  172. package/src/operations/modifiers/snap.test.js +3 -3
  173. package/src/operations/transforms/align.d.ts +1 -1
  174. package/src/operations/transforms/center.js +17 -17
  175. package/src/operations/transforms/mirror.js +11 -11
  176. package/src/operations/transforms/rotate.js +12 -12
  177. package/src/operations/transforms/scale.js +19 -19
  178. package/src/operations/transforms/transform.js +3 -3
  179. package/src/operations/transforms/translate.js +14 -14
  180. package/src/primitives/arc.js +1 -1
  181. package/src/primitives/cylinderElliptic.test.js +0 -2
  182. package/src/primitives/ellipsoid.js +1 -1
  183. package/src/primitives/ellipsoid.test.js +0 -2
  184. package/src/primitives/geodesicSphere.d.ts +0 -1
  185. package/src/primitives/geodesicSphere.js +2 -2
  186. package/src/primitives/polyhedron.js +1 -1
  187. package/src/primitives/roundedCylinder.js +1 -1
  188. package/src/primitives/torus.d.ts +0 -1
  189. package/src/primitives/torus.test.js +1 -1
  190. package/src/primitives/triangle.js +1 -1
  191. package/src/text/vectorText.js +2 -2
  192. package/src/utils/padArrayToLength.js +1 -1
  193. package/test/helpers/comparePolygons.js +1 -3
  194. package/test/helpers/nearlyEqual.js +2 -6
@@ -16,25 +16,27 @@ test('extrudeFromSlices (defaults)', (t) => {
16
16
  let geometry3 = extrudeFromSlices({ }, geometry2)
17
17
  let pts = geom3.toPoints(geometry3)
18
18
  const exp = [
19
- [[10.0, -10.0, 0.0], [10.0, 10.0, 0.0], [10.0, 10.0, 1.0]],
20
- [[10.0, -10.0, 0.0], [10.0, 10.0, 1.0], [10.0, -10.0, 1.0]],
21
- [[10.0, 10.0, 0.0], [-10.0, 10.0, 0.0], [-10.0, 10.0, 1.0]],
22
- [[10.0, 10.0, 0.0], [-10.0, 10.0, 1.0], [10.0, 10.0, 1.0]],
23
- [[-10.0, 10.0, 0.0], [-10.0, -10.0, 0.0], [-10.0, -10.0, 1.0]],
24
- [[-10.0, 10.0, 0.0], [-10.0, -10.0, 1.0], [-10.0, 10.0, 1.0]],
25
- [[-10.0, -10.0, 0.0], [10.0, -10.0, 0.0], [10.0, -10.0, 1.0]],
26
- [[-10.0, -10.0, 0.0], [10.0, -10.0, 1.0], [-10.0, -10.0, 1.0]],
27
- [[10, 10, 1], [-10, 10, 1], [-10, -10, 1], [10, -10, 1]],
28
- [[10, -10, 0], [-10, -10, 0], [-10, 10, 0], [10, 10, 0]]
19
+ [[10, -10, 0], [10, 10, 0], [10, 10, 1]],
20
+ [[10, -10, 0], [10, 10, 1], [10, -10, 1]],
21
+ [[10, 10, 0], [-10, 10, 0], [-10, 10, 1]],
22
+ [[10, 10, 0], [-10, 10, 1], [10, 10, 1]],
23
+ [[-10, 10, 0], [-10, -10, 0], [-10, -10, 1]],
24
+ [[-10, 10, 0], [-10, -10, 1], [-10, 10, 1]],
25
+ [[-10, -10, 0], [10, -10, 0], [10, -10, 1]],
26
+ [[-10, -10, 0], [10, -10, 1], [-10, -10, 1]],
27
+ [[-10, -10, 1], [10, -10, 1], [10, 10, 1]],
28
+ [[10, 10, 1], [-10, 10, 1], [-10, -10, 1]],
29
+ [[10, 10, 0], [10, -10, 0], [-10, -10, 0]],
30
+ [[-10, -10, 0], [-10, 10, 0], [10, 10, 0]]
29
31
  ]
30
- t.is(pts.length, 10)
32
+ t.is(pts.length, 12)
31
33
  t.true(comparePolygonsAsPoints(pts, exp))
32
34
 
33
35
  const poly2 = poly3.fromPoints([[10, 10, 0], [-10, 10, 0], [-10, -10, 0], [10, -10, 0]])
34
36
  geometry3 = extrudeFromSlices({ }, poly2)
35
37
  pts = geom3.toPoints(geometry3)
36
38
 
37
- t.is(pts.length, 10)
39
+ t.is(pts.length, 12)
38
40
  t.true(comparePolygonsAsPoints(pts, exp))
39
41
  })
40
42
 
@@ -84,7 +86,7 @@ test('extrudeFromSlices (same shape, changing dimensions)', (t) => {
84
86
  }, base
85
87
  )
86
88
  const pts = geom3.toPoints(geometry3)
87
- t.is(pts.length, 25)
89
+ t.is(pts.length, 26)
88
90
  })
89
91
 
90
92
  test('extrudeFromSlices (changing shape, changing dimensions)', (t) => {
@@ -101,7 +103,7 @@ test('extrudeFromSlices (changing shape, changing dimensions)', (t) => {
101
103
  }, base
102
104
  )
103
105
  const pts = geom3.toPoints(geometry3)
104
- t.is(pts.length, 298)
106
+ t.is(pts.length, 304)
105
107
  })
106
108
 
107
109
  test('extrudeFromSlices (holes)', (t) => {
@@ -136,15 +138,23 @@ test('extrudeFromSlices (holes)', (t) => {
136
138
  [[5, 5, 0], [5, -5, 1], [5, 5, 1]],
137
139
  [[-5, 5, 0], [5, 5, 0], [5, 5, 1]],
138
140
  [[-5, 5, 0], [5, 5, 1], [-5, 5, 1]],
139
- [[-10, -10, 1], [-5, -10, 1], [-5, 10, 1], [-10, 10, 1]],
140
- [[10, -10, 1], [10, -5, 1], [-5, -5, 1], [-5, -10, 1]],
141
- [[10, 10, 1], [5, 10, 1], [5, -5, 1], [10, -5, 1]],
142
- [[5, 5, 1], [5, 10, 1], [-5, 10, 1], [-5, 5, 1]],
143
- [[-10, 10, 0], [-5, 10, 0], [-5, -10, 0], [-10, -10, 0]],
144
- [[10, -10, 0], [-5, -10, 0], [-5, -5, 0], [10, -5, 0]],
145
- [[10, -5, 0], [5, -5, 0], [5, 10, 0], [10, 10, 0]],
146
- [[5, 10, 0], [5, 5, 0], [-5, 5, 0], [-5, 10, 0]]
141
+ [[10, -10, 1], [10, 10, 1], [5, 5, 1]],
142
+ [[-5, 5, 1], [5, 5, 1], [10, 10, 1]],
143
+ [[10, -10, 1], [5, 5, 1], [5, -5, 1]],
144
+ [[-5, 5, 1], [10, 10, 1], [-10, 10, 1]],
145
+ [[-10, -10, 1], [10, -10, 1], [5, -5, 1]],
146
+ [[-5, -5, 1], [-5, 5, 1], [-10, 10, 1]],
147
+ [[-10, -10, 1], [5, -5, 1], [-5, -5, 1]],
148
+ [[-5, -5, 1], [-10, 10, 1], [-10, -10, 1]],
149
+ [[5, 5, 0], [10, 10, 0], [10, -10, 0]],
150
+ [[10, 10, 0], [5, 5, 0], [-5, 5, 0]],
151
+ [[5, -5, 0], [5, 5, 0], [10, -10, 0]],
152
+ [[-10, 10, 0], [10, 10, 0], [-5, 5, 0]],
153
+ [[5, -5, 0], [10, -10, 0], [-10, -10, 0]],
154
+ [[-10, 10, 0], [-5, 5, 0], [-5, -5, 0]],
155
+ [[-5, -5, 0], [5, -5, 0], [-10, -10, 0]],
156
+ [[-10, -10, 0], [-10, 10, 0], [-5, -5, 0]]
147
157
  ]
148
- t.is(pts.length, 24)
158
+ t.is(pts.length, 32)
149
159
  t.true(comparePolygonsAsPoints(pts, exp))
150
160
  })
@@ -1,16 +1,20 @@
1
1
  const flatten = require('../../utils/flatten')
2
2
 
3
3
  const geom2 = require('../../geometries/geom2')
4
+ const path2 = require('../../geometries/path2')
4
5
 
5
6
  const extrudeLinearGeom2 = require('./extrudeLinearGeom2')
7
+ const extrudeLinearPath2 = require('./extrudeLinearPath2')
6
8
 
7
9
  /**
8
10
  * Extrude the given geometry in an upward linear direction using the given options.
11
+ * Accepts path2 or geom2 objects as input. Paths must be closed.
12
+ *
9
13
  * @param {Object} options - options for extrude
10
- * @param {Array} [options.height=1] the height of the extrusion
14
+ * @param {Number} [options.height=1] the height of the extrusion
11
15
  * @param {Number} [options.twistAngle=0] the final rotation (RADIANS) about the origin of the shape (if any)
12
16
  * @param {Integer} [options.twistSteps=1] the resolution of the twist about the axis (if any)
13
- * @param {...Object} geometry - the list of geometry to extrude
17
+ * @param {...Object} objects - the geometries to extrude
14
18
  * @return {Object|Array} the extruded geometry, or a list of extruded geometry
15
19
  * @alias module:modeling/extrusions.extrudeLinear
16
20
  *
@@ -21,17 +25,18 @@ const extrudeLinear = (options, ...objects) => {
21
25
  const defaults = {
22
26
  height: 1,
23
27
  twistAngle: 0,
24
- twistSteps: 1
28
+ twistSteps: 1,
29
+ repair: true
25
30
  }
26
- const { height, twistAngle, twistSteps } = Object.assign({ }, defaults, options)
31
+ const { height, twistAngle, twistSteps, repair } = Object.assign({ }, defaults, options)
27
32
 
28
33
  objects = flatten(objects)
29
34
  if (objects.length === 0) throw new Error('wrong number of arguments')
30
35
 
31
- options = { offset: [0, 0, height], twistAngle: twistAngle, twistSteps: twistSteps }
36
+ options = { offset: [0, 0, height], twistAngle, twistSteps, repair }
32
37
 
33
38
  const results = objects.map((object) => {
34
- // if (path.isA(object)) return pathextrude(options, object)
39
+ if (path2.isA(object)) return extrudeLinearPath2(options, object)
35
40
  if (geom2.isA(object)) return extrudeLinearGeom2(options, object)
36
41
  // if (geom3.isA(object)) return geom3.extrude(options, object)
37
42
  return object
@@ -2,7 +2,7 @@ const test = require('ava')
2
2
 
3
3
  const comparePolygonsAsPoints = require('../../../test/helpers/comparePolygonsAsPoints')
4
4
 
5
- const { geom2, geom3 } = require('../../geometries')
5
+ const { geom2, geom3, path2 } = require('../../geometries')
6
6
 
7
7
  const { extrudeLinear } = require('./index')
8
8
 
@@ -20,10 +20,12 @@ test('extrudeLinear (defaults)', (t) => {
20
20
  [[-5, 5, 0], [-5, -5, 1], [-5, 5, 1]],
21
21
  [[-5, -5, 0], [5, -5, 0], [5, -5, 1]],
22
22
  [[-5, -5, 0], [5, -5, 1], [-5, -5, 1]],
23
- [[5, 5, 1], [-5, 5, 1], [-5, -5, 1], [5, -5, 1]],
24
- [[5, -5, 0], [-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
23
+ [[-5, -5, 1], [5, -5, 1], [5, 5, 1]],
24
+ [[5, 5, 1], [-5, 5, 1], [-5, -5, 1]],
25
+ [[5, 5, 0], [5, -5, 0], [-5, -5, 0]],
26
+ [[-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
25
27
  ]
26
- t.is(pts.length, 10)
28
+ t.is(pts.length, 12)
27
29
  t.true(comparePolygonsAsPoints(pts, exp))
28
30
  })
29
31
 
@@ -41,10 +43,12 @@ test('extrudeLinear (no twist)', (t) => {
41
43
  [[-5, 5, 0], [-5, -5, 15], [-5, 5, 15]],
42
44
  [[-5, -5, 0], [5, -5, 0], [5, -5, 15]],
43
45
  [[-5, -5, 0], [5, -5, 15], [-5, -5, 15]],
44
- [[5, 5, 15], [-5, 5, 15], [-5, -5, 15], [5, -5, 15]],
45
- [[5, -5, 0], [-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
46
+ [[-5, -5, 15], [5, -5, 15], [5, 5, 15]],
47
+ [[5, 5, 15], [-5, 5, 15], [-5, -5, 15]],
48
+ [[5, 5, 0], [5, -5, 0], [-5, -5, 0]],
49
+ [[-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
46
50
  ]
47
- t.is(pts.length, 10)
51
+ t.is(pts.length, 12)
48
52
  t.true(comparePolygonsAsPoints(pts, exp))
49
53
 
50
54
  geometry3 = extrudeLinear({ height: -15 }, geometry2)
@@ -58,10 +62,12 @@ test('extrudeLinear (no twist)', (t) => {
58
62
  [[-5, -5, 0], [-5, 5, -15], [-5, -5, -15]],
59
63
  [[5, -5, 0], [-5, -5, 0], [-5, -5, -15]],
60
64
  [[5, -5, 0], [-5, -5, -15], [5, -5, -15]],
61
- [[5, -5, -15], [-5, -5, -15], [-5, 5, -15], [5, 5, -15]],
62
- [[5, 5, 0], [-5, 5, 0], [-5, -5, 0], [5, -5, 0]]
65
+ [[-5, 5, -15], [5, 5, -15], [5, -5, -15]],
66
+ [[5, -5, -15], [-5, -5, -15], [-5, 5, -15]],
67
+ [[5, -5, 0], [5, 5, 0], [-5, 5, 0]],
68
+ [[-5, 5, 0], [-5, -5, 0], [5, -5, 0]]
63
69
  ]
64
- t.is(pts.length, 10)
70
+ t.is(pts.length, 12)
65
71
  t.true(comparePolygonsAsPoints(pts, exp))
66
72
  })
67
73
 
@@ -79,11 +85,20 @@ test('extrudeLinear (twist)', (t) => {
79
85
  [[-5, 5, 0], [-7.0710678118654755, -4.440892098500626e-16, 15], [-4.440892098500626e-16, 7.0710678118654755, 15]],
80
86
  [[-5, -5, 0], [5, -5, 0], [4.440892098500626e-16, -7.0710678118654755, 15]],
81
87
  [[-5, -5, 0], [4.440892098500626e-16, -7.0710678118654755, 15], [-7.0710678118654755, -4.440892098500626e-16, 15]],
82
- [[7.071067811865477, 8.881784197001252e-16, 15], [1.7763568394002505e-15, 7.071067811865477, 15],
83
- [-7.071067811865475, 0, 15], [0, -7.0710678118654755, 15]],
84
- [[5, -5, 0], [-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
88
+ [
89
+ [-7.0710678118654755, -4.440892098500626e-16, 15],
90
+ [4.440892098500626e-16, -7.0710678118654755, 15],
91
+ [7.0710678118654755, 4.440892098500626e-16, 15]
92
+ ],
93
+ [
94
+ [7.0710678118654755, 4.440892098500626e-16, 15],
95
+ [-4.440892098500626e-16, 7.0710678118654755, 15],
96
+ [-7.0710678118654755, -4.440892098500626e-16, 15]
97
+ ],
98
+ [[5, 5, 0], [5, -5, 0], [-5, -5, 0]],
99
+ [[-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
85
100
  ]
86
- t.is(pts.length, 10)
101
+ t.is(pts.length, 12)
87
102
  t.true(comparePolygonsAsPoints(pts, exp))
88
103
 
89
104
  geometry3 = extrudeLinear({ height: 15, twistAngle: Math.PI / 2, twistSteps: 3 }, geometry2)
@@ -113,15 +128,17 @@ test('extrudeLinear (twist)', (t) => {
113
128
  [[-6.830127018922193, -1.8301270189221923, 10], [5, -5, 15], [-5, -5, 15]],
114
129
  [[1.8301270189221923, -6.830127018922193, 10], [6.830127018922193, 1.8301270189221923, 10], [5, 5, 15]],
115
130
  [[1.8301270189221923, -6.830127018922193, 10], [5, 5, 15], [5, -5, 15]],
116
- [[-5, 5, 15], [-5, -5, 15], [5, -5, 15], [5, 5, 15]],
117
- [[5, -5, 0], [-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
131
+ [[5, -5, 15], [5, 5, 15], [-5, 5, 15]],
132
+ [[-5, 5, 15], [-5, -5, 15], [5, -5, 15]],
133
+ [[5, 5, 0], [5, -5, 0], [-5, -5, 0]],
134
+ [[-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
118
135
  ]
119
- t.is(pts.length, 26)
136
+ t.is(pts.length, 28)
120
137
  t.true(comparePolygonsAsPoints(pts, exp))
121
138
 
122
139
  geometry3 = extrudeLinear({ height: 15, twistAngle: Math.PI / 2, twistSteps: 30 }, geometry2)
123
140
  pts = geom3.toPoints(geometry3)
124
- t.is(pts.length, 242)
141
+ t.is(pts.length, 244)
125
142
  })
126
143
 
127
144
  test('extrudeLinear (holes)', (t) => {
@@ -154,15 +171,48 @@ test('extrudeLinear (holes)', (t) => {
154
171
  [[2, 2, 0], [2, -2, 15], [2, 2, 15]],
155
172
  [[-2, 2, 0], [2, 2, 0], [2, 2, 15]],
156
173
  [[-2, 2, 0], [2, 2, 15], [-2, 2, 15]],
157
- [[-5, -5, 15], [-2, -5, 15], [-2, 5, 15], [-5, 5, 15]],
158
- [[5, -5, 15], [5, -2, 15], [-2, -2, 15], [-2, -5, 15]],
159
- [[5, 5, 15], [2, 5, 15], [2, -2, 15], [5, -2, 15]],
160
- [[2, 2, 15], [2, 5, 15], [-2, 5, 15], [-2, 2, 15]],
161
- [[-5, 5, 0], [-2, 5, 0], [-2, -5, 0], [-5, -5, 0]],
162
- [[5, -5, 0], [-2, -5, 0], [-2, -2, 0], [5, -2, 0]],
163
- [[5, -2, 0], [2, -2, 0], [2, 5, 0], [5, 5, 0]],
164
- [[2, 5, 0], [2, 2, 0], [-2, 2, 0], [-2, 5, 0]]
174
+ [[5, -5, 15], [5, 5, 15], [2, 2, 15]],
175
+ [[-2, 2, 15], [2, 2, 15], [5, 5, 15]],
176
+ [[5, -5, 15], [2, 2, 15], [2, -2, 15]],
177
+ [[-2, 2, 15], [5, 5, 15], [-5, 5, 15]],
178
+ [[-5, -5, 15], [5, -5, 15], [2, -2, 15]],
179
+ [[-2, -2, 15], [-2, 2, 15], [-5, 5, 15]],
180
+ [[-5, -5, 15], [2, -2, 15], [-2, -2, 15]],
181
+ [[-2, -2, 15], [-5, 5, 15], [-5, -5, 15]],
182
+ [[2, 2, 0], [5, 5, 0], [5, -5, 0]],
183
+ [[5, 5, 0], [2, 2, 0], [-2, 2, 0]],
184
+ [[2, -2, 0], [2, 2, 0], [5, -5, 0]],
185
+ [[-5, 5, 0], [5, 5, 0], [-2, 2, 0]],
186
+ [[2, -2, 0], [5, -5, 0], [-5, -5, 0]],
187
+ [[-5, 5, 0], [-2, 2, 0], [-2, -2, 0]],
188
+ [[-2, -2, 0], [2, -2, 0], [-5, -5, 0]],
189
+ [[-5, -5, 0], [-5, 5, 0], [-2, -2, 0]]
165
190
  ]
166
- t.is(pts.length, 24)
191
+ t.is(pts.length, 32)
167
192
  t.true(comparePolygonsAsPoints(pts, exp))
168
193
  })
194
+
195
+ test('extrudeLinear (path2)', (t) => {
196
+ const geometry2 = path2.fromPoints({ closed: true }, [[0, 0], [12, 0], [6, 10]])
197
+ const geometry3 = extrudeLinear({ height: 15 }, geometry2)
198
+ t.true(geom3.isA(geometry3))
199
+ const pts = geom3.toPoints(geometry3)
200
+ const exp = [
201
+ [[6, 10, 0], [0, 0, 0], [0, 0, 15]],
202
+ [[6, 10, 0], [0, 0, 15], [6, 10, 15]],
203
+ [[0, 0, 0], [12, 0, 0], [12, 0, 15]],
204
+ [[0, 0, 0], [12, 0, 15], [0, 0, 15]],
205
+ [[12, 0, 0], [6, 10, 0], [6, 10, 15]],
206
+ [[12, 0, 0], [6, 10, 15], [12, 0, 15]],
207
+ [[12, 0, 15], [6, 10, 15], [0, 0, 15]],
208
+ [[0, 0, 0], [6, 10, 0], [12, 0, 0]]
209
+ ]
210
+
211
+ t.is(pts.length, 8)
212
+ t.true(comparePolygonsAsPoints(pts, exp))
213
+ })
214
+
215
+ test('extrudeLinear (unclosed path throws error)', (t) => {
216
+ const geometry2 = path2.fromPoints({ closed: false }, [[0, 0], [12, 0], [6, 10]])
217
+ t.throws(() => extrudeLinear({}, geometry2))
218
+ })
@@ -14,6 +14,7 @@ const extrudeFromSlices = require('./extrudeFromSlices')
14
14
  * @param {Array} [options.offset] - the direction of the extrusion as a 3D vector
15
15
  * @param {Number} [options.twistAngle] - the final rotation (RADIANS) about the origin
16
16
  * @param {Integer} [options.twistSteps] - the number of steps created to produce the twist (if any)
17
+ * @param {Boolean} [options.repair] - repair gaps in the geometry
17
18
  * @param {geom2} geometry - the geometry to extrude
18
19
  * @returns {geom3} the extruded 3D geometry
19
20
  */
@@ -21,9 +22,10 @@ const extrudeGeom2 = (options, geometry) => {
21
22
  const defaults = {
22
23
  offset: [0, 0, 1],
23
24
  twistAngle: 0,
24
- twistSteps: 12
25
+ twistSteps: 12,
26
+ repair: true
25
27
  }
26
- let { offset, twistAngle, twistSteps } = Object.assign({ }, defaults, options)
28
+ let { offset, twistAngle, twistSteps, repair } = Object.assign({ }, defaults, options)
27
29
 
28
30
  if (twistSteps < 1) throw new Error('twistSteps must be 1 or more')
29
31
 
@@ -53,6 +55,7 @@ const extrudeGeom2 = (options, geometry) => {
53
55
  numberOfSlices: twistSteps + 1,
54
56
  capStart: true,
55
57
  capEnd: true,
58
+ repair,
56
59
  callback: createTwist
57
60
  }
58
61
  return extrudeFromSlices(options, baseSlice)
@@ -0,0 +1,24 @@
1
+ const geom2 = require('../../geometries/geom2')
2
+ const path2 = require('../../geometries/path2')
3
+
4
+ const extrudeLinearGeom2 = require('./extrudeLinearGeom2')
5
+
6
+ /*
7
+ * Extrude the given geometry using the given options.
8
+ *
9
+ * @param {Object} [options] - options for extrude
10
+ * @param {Array} [options.offset] - the direction of the extrusion as a 3D vector
11
+ * @param {Number} [options.twistAngle] - the final rotation (RADIANS) about the origin
12
+ * @param {Integer} [options.twistSteps] - the number of steps created to produce the twist (if any)
13
+ * @param {path2} geometry - the geometry to extrude
14
+ * @returns {geom3} the extruded 3D geometry
15
+ */
16
+ const extrudePath2 = (options, geometry) => {
17
+ if (!geometry.isClosed) throw new Error('extruded path must be closed')
18
+ // Convert path2 to geom2
19
+ const points = path2.toPoints(geometry)
20
+ const geometry2 = geom2.fromPoints(points)
21
+ return extrudeLinearGeom2(options, geometry2)
22
+ }
23
+
24
+ module.exports = extrudePath2
@@ -12,7 +12,7 @@ const extrudeRectangularGeom2 = require('./extrudeRectangularGeom2')
12
12
  * @param {Object} options - options for extrusion, if any
13
13
  * @param {Number} [options.size=1] - size of the rectangle
14
14
  * @param {Number} [options.height=1] - height of the extrusion
15
- * @param {...Object} geometry - the list of geometry to extrude
15
+ * @param {...Object} objects - the geometries to extrude
16
16
  * @return {Object|Array} the extruded object, or a list of extruded objects
17
17
  * @alias module:modeling/extrusions.extrudeRectangular
18
18
  *
@@ -12,11 +12,11 @@ test('extrudeRectangular (defaults)', (t) => {
12
12
 
13
13
  let obs = extrudeRectangular({ }, geometry1)
14
14
  let pts = geom3.toPoints(obs)
15
- t.is(pts.length, 50)
15
+ t.is(pts.length, 44)
16
16
 
17
17
  obs = extrudeRectangular({ }, geometry2)
18
18
  pts = geom3.toPoints(obs)
19
- t.is(pts.length, 40)
19
+ t.is(pts.length, 32)
20
20
  })
21
21
 
22
22
  test('extrudeRectangular (chamfer)', (t) => {
@@ -25,11 +25,11 @@ test('extrudeRectangular (chamfer)', (t) => {
25
25
 
26
26
  let obs = extrudeRectangular({ corners: 'chamfer' }, geometry1)
27
27
  let pts = geom3.toPoints(obs)
28
- t.is(pts.length, 42)
28
+ t.is(pts.length, 60)
29
29
 
30
30
  obs = extrudeRectangular({ corners: 'chamfer' }, geometry2)
31
31
  pts = geom3.toPoints(obs)
32
- t.is(pts.length, 32)
32
+ t.is(pts.length, 48)
33
33
  })
34
34
 
35
35
  test('extrudeRectangular (segments = 8, round)', (t) => {
@@ -38,11 +38,11 @@ test('extrudeRectangular (segments = 8, round)', (t) => {
38
38
 
39
39
  let obs = extrudeRectangular({ segments: 8, corners: 'round' }, geometry1)
40
40
  let pts = geom3.toPoints(obs)
41
- t.is(pts.length, 56)
41
+ t.is(pts.length, 84)
42
42
 
43
43
  obs = extrudeRectangular({ segments: 8, corners: 'round' }, geometry2)
44
44
  pts = geom3.toPoints(obs)
45
- t.is(pts.length, 40)
45
+ t.is(pts.length, 64)
46
46
  })
47
47
 
48
48
  test('extrudeRectangular (holes)', (t) => {
@@ -59,5 +59,5 @@ test('extrudeRectangular (holes)', (t) => {
59
59
 
60
60
  const obs = extrudeRectangular({ size: 2, height: 15, segments: 16, corners: 'round' }, geometry2)
61
61
  const pts = geom3.toPoints(obs)
62
- t.is(pts.length, 122)
62
+ t.is(pts.length, 192)
63
63
  })
@@ -29,21 +29,21 @@ test('extrudeRotate: (angle) extruding of a geom2 produces an expected geom3', (
29
29
  [[26, -4.898587196589413e-16, -8], [7.0710678118654755, 7.071067811865475, -8], [18.38477631085024, 18.384776310850235, -8]],
30
30
  [[26, 4.898587196589413e-16, 8], [26, -4.898587196589413e-16, -8], [18.38477631085024, 18.384776310850235, -8]],
31
31
  [[26, 4.898587196589413e-16, 8], [18.38477631085024, 18.384776310850235, -8], [18.38477631085024, 18.384776310850235, 8]],
32
- [[18.38477631085024, 18.384776310850235, 7.999999999999998], [18.38477631085024, 18.384776310850235, -8],
33
- [7.071067811865478, 7.071067811865474, -8], [7.071067811865476, 7.071067811865475, 8]],
34
- [[10, 4.898587196589411e-16, 8], [10, -4.898587196589413e-16, -8],
35
- [26, -4.898587196589412e-16, -8], [26, 4.898587196589414e-16, 8]]
32
+ [[7.071067811865476, 7.0710678118654755, -8], [7.071067811865476, 7.0710678118654755, 8], [18.384776310850242, 18.384776310850235, 8]],
33
+ [[18.384776310850242, 18.384776310850235, 8], [18.384776310850242, 18.384776310850235, -8], [7.071067811865476, 7.0710678118654755, -8]],
34
+ [[26, 4.898587196589413e-16, 8], [10, 4.898587196589413e-16, 8], [10, -4.898587196589413e-16, -8]],
35
+ [[10, -4.898587196589413e-16, -8], [26, -4.898587196589413e-16, -8], [26, 4.898587196589413e-16, 8]]
36
36
  ]
37
- t.is(pts.length, 10)
37
+ t.is(pts.length, 12)
38
38
  t.true(comparePolygonsAsPoints(pts, exp))
39
39
 
40
40
  geometry3 = extrudeRotate({ segments: 4, angle: -250 * 0.017453292519943295 }, geometry2)
41
41
  pts = geom3.toPoints(geometry3)
42
- t.is(pts.length, 26)
42
+ t.is(pts.length, 28)
43
43
 
44
44
  geometry3 = extrudeRotate({ segments: 4, angle: 250 * 0.017453292519943295 }, geometry2)
45
45
  pts = geom3.toPoints(geometry3)
46
- t.is(pts.length, 26)
46
+ t.is(pts.length, 28)
47
47
  })
48
48
 
49
49
  test('extrudeRotate: (startAngle) extruding of a geom2 produces an expected geom3', (t) => {
@@ -107,16 +107,12 @@ test('extrudeRotate: (overlap +/-) extruding of a geom2 produces an expected geo
107
107
  [[7, -4.898587196589413e-16, -8], [4.898587196589413e-16, -2.999519565323715e-32, -8], [9.184850993605148e-16, 7, -8]],
108
108
  [[7, 4.898587196589413e-16, 8], [7, -4.898587196589413e-16, -8], [9.184850993605148e-16, 7, -8]],
109
109
  [[7, 4.898587196589413e-16, 8], [9.184850993605148e-16, 7, -8], [-6.123233995736767e-17, 7, 8]],
110
- [
111
- [-4.898587196589414e-16, 0, 8], [-6.123233995736777e-17, 6.999999999999999, 8],
112
- [9.18485099360515e-16, 7.000000000000001, -8], [4.898587196589413e-16, 0, -8]
113
- ],
114
- [
115
- [7, 4.898587196589413e-16, 8], [0, 4.898587196589413e-16, 8],
116
- [0, -4.898587196589413e-16, -8], [7, -4.898587196589413e-16, -8]
117
- ]
110
+ [[4.898587196589413e-16, -2.999519565323715e-32, -8], [-4.898587196589413e-16, 2.999519565323715e-32, 8], [-6.123233995736767e-17, 7, 8]],
111
+ [[-6.123233995736767e-17, 7, 8], [9.184850993605148e-16, 7, -8], [4.898587196589413e-16, -2.999519565323715e-32, -8]],
112
+ [[7, 4.898587196589413e-16, 8], [0, 4.898587196589413e-16, 8], [0, -4.898587196589413e-16, -8]],
113
+ [[0, -4.898587196589413e-16, -8], [7, -4.898587196589413e-16, -8], [7, 4.898587196589413e-16, 8]]
118
114
  ]
119
- t.is(pts.length, 6)
115
+ t.is(pts.length, 8)
120
116
  t.true(comparePolygonsAsPoints(pts, exp))
121
117
 
122
118
  // overlap of Y axis; larger number of - points
@@ -137,18 +133,14 @@ test('extrudeRotate: (overlap +/-) extruding of a geom2 produces an expected geo
137
133
  [[0.7071067811865472, 0.7071067811865478, 8], [1.414213562373095, 1.4142135623730951, 4], [-1.2246467991473532e-16, 2, 4]],
138
134
  [[0.7071067811865472, 0.7071067811865478, 8], [-1.2246467991473532e-16, 2, 4], [-4.286263797015736e-16, 1, 8]],
139
135
  [[-3.4638242249419727e-16, 3.4638242249419736e-16, 8], [0.7071067811865472, 0.7071067811865478, 8], [-4.286263797015736e-16, 1, 8]],
140
- [
141
- [-4.898587196589412e-16, 0, 8], [-4.2862637970157346e-16, 0.9999999999999998, 8],
142
- [-1.2246467991473475e-16, 2.0000000000000004, 3.9999999999999964], [5.510910596163092e-16, 1.0000000000000004, -8],
143
- [4.898587196589414e-16, 0, -8]
144
- ],
145
- [
146
- [0, -4.898587196589413e-16, -8.000000000000002], [1.0000000000000027, -4.898587196589413e-16, -8.000000000000002],
147
- [2.000000000000001, 2.449293598294702e-16, 3.9999999999999964], [1.0000000000000004, 4.898587196589411e-16, 8],
148
- [0, 4.898587196589411e-16, 8]
149
- ]
136
+ [[5.51091059616309e-16, 1, -8], [4.898587196589413e-16, -2.999519565323715e-32, -8], [-4.898587196589415e-16, 2.9995195653237163e-32, 8]],
137
+ [[-4.898587196589415e-16, 2.9995195653237163e-32, 8], [-4.286263797015738e-16, 1, 8], [-1.2246467991473544e-16, 2, 4]],
138
+ [[-1.2246467991473544e-16, 2, 4], [5.51091059616309e-16, 1, -8], [-4.898587196589415e-16, 2.9995195653237163e-32, 8]],
139
+ [[0, 4.898587196589413e-16, 8], [0, -4.898587196589413e-16, -8], [1, -4.898587196589413e-16, -8]],
140
+ [[2, 2.4492935982947064e-16, 4], [1, 4.898587196589413e-16, 8], [0, 4.898587196589413e-16, 8]],
141
+ [[0, 4.898587196589413e-16, 8], [1, -4.898587196589413e-16, -8], [2, 2.4492935982947064e-16, 4]]
150
142
  ]
151
- t.is(pts.length, 14)
143
+ t.is(pts.length, 18)
152
144
  t.true(comparePolygonsAsPoints(pts, exp))
153
145
  })
154
146
 
@@ -60,7 +60,7 @@ const projectGeom3 = (options, geometry) => {
60
60
  * @param {Object} options - options for project
61
61
  * @param {Array} [options.axis=[0,0,1]] the axis of the plane (default is Z axis)
62
62
  * @param {Array} [options.origin=[0,0,0]] the origin of the plane
63
- * @param {...Object} geometry - the list of 3D geometry to project
63
+ * @param {...Object} objects - the list of 3D geometry to project
64
64
  * @return {geom2|Array} the projected 2D geometry, or a list of 2D projected geometry
65
65
  * @alias module:modeling/extrusions.project
66
66
  *
@@ -23,10 +23,13 @@ const calculatePlane = (slice) => {
23
23
  let farthestEdge
24
24
  let distance = 0
25
25
  edges.forEach((edge) => {
26
- const d = vec3.squaredDistance(midpoint, edge[0])
27
- if (d > distance) {
28
- farthestEdge = edge
29
- distance = d
26
+ // Make sure that the farthest edge is not a self-edge
27
+ if (!vec3.equals(edge[0], edge[1])) {
28
+ const d = vec3.squaredDistance(midpoint, edge[0])
29
+ if (d > distance) {
30
+ farthestEdge = edge
31
+ distance = d
32
+ }
30
33
  }
31
34
  })
32
35
  // find the before edge
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Determin if the given object is a slice.
3
- * @param {slice} object - the object to interogate
2
+ * Determine if the given object is a slice.
3
+ * @param {slice} object - the object to interrogate
4
4
  * @returns {Boolean} true if the object matches a slice
5
5
  * @alias module:modeling/extrusions/slice.isA
6
6
  */
@@ -0,0 +1,47 @@
1
+ const vec3 = require('../../../maths/vec3')
2
+
3
+ /*
4
+ * Mend gaps in a 2D slice to make it a closed polygon
5
+ */
6
+ const repairSlice = (slice) => {
7
+ if (!slice.edges) return slice
8
+ const vertexMap = {} // string key to vertex map
9
+ const edgeCount = {} // count of (in - out) edges
10
+ slice.edges.forEach((edge) => {
11
+ const inKey = edge[0].toString()
12
+ const outKey = edge[1].toString()
13
+ vertexMap[inKey] = edge[0]
14
+ vertexMap[outKey] = edge[1]
15
+ edgeCount[inKey] = (edgeCount[inKey] || 0) + 1 // in
16
+ edgeCount[outKey] = (edgeCount[outKey] || 0) - 1 // out
17
+ })
18
+ // find vertices which are missing in or out edges
19
+ const missingIn = Object.keys(edgeCount).filter((e) => edgeCount[e] < 0)
20
+ const missingOut = Object.keys(edgeCount).filter((e) => edgeCount[e] > 0)
21
+ // pairwise distance of bad vertices
22
+ missingIn.forEach((key1) => {
23
+ const v1 = vertexMap[key1]
24
+ // find the closest vertex that is missing an out edge
25
+ let bestDistance = Infinity
26
+ let bestReplacement
27
+ missingOut.forEach((key2) => {
28
+ const v2 = vertexMap[key2]
29
+ const distance = Math.hypot(v1[0] - v2[0], v1[1] - v2[1])
30
+ if (distance < bestDistance) {
31
+ bestDistance = distance
32
+ bestReplacement = v2
33
+ }
34
+ })
35
+ console.warn(`repairSlice: repairing vertex gap ${v1} to ${bestReplacement} distance ${bestDistance}`)
36
+ // merge broken vertices
37
+ slice.edges.forEach((edge) => {
38
+ if (edge[0].toString() === key1) edge[0] = bestReplacement
39
+ if (edge[1].toString() === key1) edge[1] = bestReplacement
40
+ })
41
+ })
42
+ // Remove self-edges
43
+ slice.edges = slice.edges.filter((e) => !vec3.equals(e[0], e[1]))
44
+ return slice
45
+ }
46
+
47
+ module.exports = repairSlice