@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.
- package/CHANGELOG.md +46 -0
- package/dist/jscad-modeling.min.js +174 -144
- package/package.json +2 -2
- package/src/colors/hslToRgb.js +1 -1
- package/src/colors/hueToColorComponent.js +1 -0
- package/src/curves/bezier/tangentAt.js +2 -2
- package/src/curves/bezier/tangentAt.test.js +1 -1
- package/src/curves/bezier/valueAt.test.js +1 -1
- package/src/geometries/geom2/index.js +10 -0
- package/src/geometries/geom2/isA.js +2 -2
- package/src/geometries/geom2/toCompactBinary.js +4 -4
- package/src/geometries/geom2/toOutlines.js +6 -11
- package/src/geometries/geom2/toString.js +1 -1
- package/src/geometries/geom2/transform.test.js +1 -1
- package/src/geometries/geom3/fromCompactBinary.js +1 -1
- package/src/geometries/geom3/index.js +17 -0
- package/src/geometries/geom3/invert.js +2 -2
- package/src/geometries/geom3/isA.js +2 -2
- package/src/geometries/geom3/toCompactBinary.js +4 -4
- package/src/geometries/geom3/toPoints.js +1 -0
- package/src/geometries/geom3/toString.js +1 -1
- package/src/geometries/geom3/transform.test.js +1 -1
- package/src/geometries/index.js +8 -1
- package/src/geometries/path2/eachPoint.js +3 -3
- package/src/geometries/path2/index.js +11 -0
- package/src/geometries/path2/isA.js +2 -2
- package/src/geometries/path2/reverse.js +4 -4
- package/src/geometries/path2/toCompactBinary.js +6 -6
- package/src/geometries/path2/toString.js +1 -1
- package/src/geometries/path2/transform.test.js +1 -1
- package/src/geometries/poly2/arePointsInside.test.js +1 -1
- package/src/geometries/poly2/index.js +6 -0
- package/src/geometries/poly3/index.js +7 -1
- package/src/geometries/poly3/invert.js +7 -1
- package/src/geometries/poly3/isA.js +2 -2
- package/src/geometries/poly3/isConvex.js +2 -2
- package/src/geometries/poly3/measureArea.js +4 -4
- package/src/geometries/poly3/measureBoundingBox.js +2 -2
- package/src/geometries/poly3/measureBoundingSphere.js +2 -2
- package/src/geometries/poly3/measureSignedVolume.js +4 -4
- package/src/geometries/poly3/toPoints.js +2 -2
- package/src/geometries/poly3/toString.js +2 -2
- package/src/geometries/poly3/transform.js +2 -2
- package/src/maths/index.js +1 -1
- package/src/maths/line2/equals.js +2 -2
- package/src/maths/line2/fromValues.js +2 -2
- package/src/maths/line2/intersectPointOfLines.js +1 -1
- package/src/maths/line2/intersectPointOfLines.test.js +1 -1
- package/src/maths/line2/reverse.test.js +1 -1
- package/src/maths/line2/transform.test.js +1 -1
- package/src/maths/line3/equals.js +2 -2
- package/src/maths/line3/reverse.test.js +1 -1
- package/src/maths/line3/transform.test.js +1 -1
- package/src/maths/mat4/fromRotation.js +1 -1
- package/src/maths/mat4/fromVectorRotation.js +1 -1
- package/src/maths/mat4/fromVectorRotation.test.js +1 -1
- package/src/maths/mat4/identity.test.js +1 -1
- package/src/maths/mat4/invert.js +18 -18
- package/src/maths/mat4/isIdentity.js +1 -1
- package/src/maths/mat4/isIdentity.test.js +0 -2
- package/src/maths/mat4/isMirroring.js +4 -4
- package/src/maths/mat4/isMirroring.test.js +1 -1
- package/src/maths/mat4/leftMultiplyVec3.js +2 -2
- package/src/maths/mat4/rotate.js +1 -1
- package/src/maths/mat4/toString.js +2 -2
- package/src/maths/mat4/translate.test.js +1 -1
- package/src/maths/plane/flip.test.js +1 -1
- package/src/maths/plane/fromPoints.d.ts +1 -1
- package/src/maths/plane/fromPoints.js +1 -3
- package/src/maths/plane/signedDistanceToPoint.js +1 -1
- package/src/maths/plane/transform.test.js +1 -1
- package/src/maths/utils/aboutEqualNormals.js +2 -2
- package/src/maths/vec2/abs.d.ts +1 -1
- package/src/maths/vec2/add.test.js +1 -1
- package/src/maths/vec2/angleDegrees.d.ts +1 -1
- package/src/maths/vec2/angleRadians.d.ts +1 -1
- package/src/maths/vec2/create.js +1 -1
- package/src/maths/vec2/cross.test.js +1 -1
- package/src/maths/vec2/divide.test.js +1 -1
- package/src/maths/vec2/fromAngleDegrees.js +1 -1
- package/src/maths/vec2/fromScalar.js +1 -1
- package/src/maths/vec2/length.d.ts +1 -1
- package/src/maths/vec2/length.js +1 -1
- package/src/maths/vec2/length.test.js +10 -0
- package/src/maths/vec2/lerp.test.js +1 -1
- package/src/maths/vec2/multiply.test.js +1 -1
- package/src/maths/vec2/negate.test.js +1 -1
- package/src/maths/vec2/normal.js +1 -1
- package/src/maths/vec2/normalize.d.ts +1 -1
- package/src/maths/vec2/normalize.test.js +1 -1
- package/src/maths/vec2/rotate.test.js +1 -1
- package/src/maths/vec2/squaredLength.d.ts +1 -1
- package/src/maths/vec2/squaredLength.js +3 -3
- package/src/maths/vec2/subtract.test.js +1 -1
- package/src/maths/vec2/toString.js +1 -1
- package/src/maths/vec2/transform.test.js +1 -1
- package/src/maths/vec3/abs.d.ts +1 -1
- package/src/maths/vec3/add.test.js +1 -1
- package/src/maths/vec3/angle.js +2 -2
- package/src/maths/vec3/angle.test.js +17 -0
- package/src/maths/vec3/cross.test.js +1 -1
- package/src/maths/vec3/divide.test.js +1 -1
- package/src/maths/vec3/fromScalar.js +1 -1
- package/src/maths/vec3/fromVec2.d.ts +1 -1
- package/src/maths/vec3/fromVec2.js +3 -3
- package/src/maths/vec3/length.d.ts +1 -1
- package/src/maths/vec3/length.js +4 -4
- package/src/maths/vec3/length.test.js +10 -0
- package/src/maths/vec3/lerp.test.js +1 -1
- package/src/maths/vec3/multiply.test.js +1 -1
- package/src/maths/vec3/negate.d.ts +1 -1
- package/src/maths/vec3/negate.test.js +1 -1
- package/src/maths/vec3/normalize.d.ts +1 -1
- package/src/maths/vec3/normalize.test.js +1 -1
- package/src/maths/vec3/rotateX.test.js +1 -1
- package/src/maths/vec3/rotateY.test.js +1 -1
- package/src/maths/vec3/rotateZ.test.js +1 -1
- package/src/maths/vec3/scale.test.js +1 -1
- package/src/maths/vec3/squaredLength.d.ts +1 -1
- package/src/maths/vec3/squaredLength.js +4 -4
- package/src/maths/vec3/subtract.test.js +1 -1
- package/src/maths/vec3/toString.js +1 -1
- package/src/maths/vec3/transform.test.js +1 -1
- package/src/maths/vec4/toString.js +1 -1
- package/src/maths/vec4/transform.test.js +1 -1
- package/src/measurements/measureBoundingSphere.js +4 -4
- package/src/measurements/measureCenterOfMass.js +1 -1
- package/src/operations/booleans/mayOverlap.js +3 -3
- package/src/operations/booleans/retessellate.js +3 -5
- package/src/operations/booleans/scission.js +1 -1
- package/src/operations/booleans/subtract.js +1 -1
- package/src/operations/booleans/union.test.js +1 -1
- package/src/operations/booleans/unionGeom3Sub.js +1 -1
- package/src/operations/expansions/expand.js +2 -2
- package/src/operations/expansions/expand.test.js +3 -35
- package/src/operations/expansions/expandShell.js +24 -18
- package/src/operations/expansions/offset.js +1 -1
- package/src/operations/expansions/offset.test.js +25 -89
- package/src/operations/expansions/offsetFromPoints.js +11 -6
- package/src/operations/extrusions/earcut/assignHoles.js +87 -0
- package/src/operations/extrusions/earcut/assignHoles.test.js +28 -0
- package/src/operations/extrusions/earcut/eliminateHoles.js +131 -0
- package/src/operations/extrusions/earcut/index.js +252 -0
- package/src/operations/extrusions/earcut/linkedList.js +58 -0
- package/src/operations/extrusions/earcut/linkedListSort.js +54 -0
- package/src/operations/extrusions/earcut/linkedPolygon.js +197 -0
- package/src/operations/extrusions/earcut/polygonHierarchy.js +64 -0
- package/src/operations/extrusions/earcut/triangle.js +16 -0
- package/src/operations/extrusions/extrudeFromSlices.js +10 -3
- package/src/operations/extrusions/extrudeFromSlices.test.js +33 -23
- package/src/operations/extrusions/extrudeLinear.js +11 -6
- package/src/operations/extrusions/extrudeLinear.test.js +77 -27
- package/src/operations/extrusions/extrudeLinearGeom2.js +5 -2
- package/src/operations/extrusions/extrudeLinearPath2.js +24 -0
- package/src/operations/extrusions/extrudeRectangular.js +1 -1
- package/src/operations/extrusions/extrudeRectangular.test.js +7 -7
- package/src/operations/extrusions/extrudeRotate.test.js +19 -27
- package/src/operations/extrusions/project.js +1 -1
- package/src/operations/extrusions/slice/calculatePlane.js +7 -4
- package/src/operations/extrusions/slice/isA.js +2 -2
- package/src/operations/extrusions/slice/repairSlice.js +47 -0
- package/src/operations/extrusions/slice/toPolygons.js +24 -60
- package/src/operations/hulls/hull.test.js +1 -1
- package/src/operations/hulls/hullChain.js +1 -1
- package/src/operations/hulls/hullGeom2.js +1 -1
- package/src/operations/hulls/hullPath2.js +6 -4
- package/src/operations/hulls/hullPath2.test.js +16 -0
- package/src/operations/hulls/hullPoints2.test.js +1 -1
- package/src/operations/hulls/quickhull/QuickHull.js +2 -2
- package/src/operations/modifiers/edges.js +2 -4
- package/src/operations/modifiers/generalize.js +4 -7
- package/src/operations/modifiers/snap.test.js +3 -3
- package/src/operations/transforms/align.d.ts +1 -1
- package/src/operations/transforms/center.js +17 -17
- package/src/operations/transforms/mirror.js +11 -11
- package/src/operations/transforms/rotate.js +12 -12
- package/src/operations/transforms/scale.js +19 -19
- package/src/operations/transforms/transform.js +3 -3
- package/src/operations/transforms/translate.js +14 -14
- package/src/primitives/arc.js +1 -1
- package/src/primitives/cylinderElliptic.test.js +0 -2
- package/src/primitives/ellipsoid.js +1 -1
- package/src/primitives/ellipsoid.test.js +0 -2
- package/src/primitives/geodesicSphere.d.ts +0 -1
- package/src/primitives/geodesicSphere.js +2 -2
- package/src/primitives/polyhedron.js +1 -1
- package/src/primitives/roundedCylinder.js +1 -1
- package/src/primitives/torus.d.ts +0 -1
- package/src/primitives/torus.test.js +1 -1
- package/src/primitives/triangle.js +1 -1
- package/src/text/vectorText.js +2 -2
- package/src/utils/padArrayToLength.js +1 -1
- package/test/helpers/comparePolygons.js +1 -3
- 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
|
|
20
|
-
[[10
|
|
21
|
-
[[10
|
|
22
|
-
[[10
|
|
23
|
-
[[-10
|
|
24
|
-
[[-10
|
|
25
|
-
[[-10
|
|
26
|
-
[[-10
|
|
27
|
-
[[10,
|
|
28
|
-
[[10,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
[[
|
|
140
|
-
[[
|
|
141
|
-
[[10, 10, 1], [5,
|
|
142
|
-
[[5, 5, 1], [
|
|
143
|
-
[[-10, 10,
|
|
144
|
-
[[
|
|
145
|
-
[[10, -
|
|
146
|
-
[[5,
|
|
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,
|
|
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 {
|
|
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}
|
|
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
|
|
36
|
+
options = { offset: [0, 0, height], twistAngle, twistSteps, repair }
|
|
32
37
|
|
|
33
38
|
const results = objects.map((object) => {
|
|
34
|
-
|
|
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,
|
|
24
|
-
[[5,
|
|
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,
|
|
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,
|
|
45
|
-
[[5,
|
|
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,
|
|
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,
|
|
62
|
-
[[5, 5,
|
|
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,
|
|
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
|
-
[
|
|
83
|
-
[-7.
|
|
84
|
-
|
|
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,
|
|
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
|
-
[[
|
|
117
|
-
[[5,
|
|
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,
|
|
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,
|
|
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
|
-
[[
|
|
158
|
-
[[
|
|
159
|
-
[[5, 5, 15], [2,
|
|
160
|
-
[[2, 2, 15], [
|
|
161
|
-
[[-5, 5,
|
|
162
|
-
[[
|
|
163
|
-
[[5, -
|
|
164
|
-
[[2,
|
|
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,
|
|
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}
|
|
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,
|
|
15
|
+
t.is(pts.length, 44)
|
|
16
16
|
|
|
17
17
|
obs = extrudeRectangular({ }, geometry2)
|
|
18
18
|
pts = geom3.toPoints(obs)
|
|
19
|
-
t.is(pts.length,
|
|
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,
|
|
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
|
+
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,
|
|
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,
|
|
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,
|
|
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
|
-
[[
|
|
33
|
-
|
|
34
|
-
[[10, 4.
|
|
35
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
112
|
-
|
|
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,
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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,
|
|
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}
|
|
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
|
-
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
distance
|
|
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
|
-
*
|
|
3
|
-
* @param {slice} object - the object to
|
|
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
|