@jscad/modeling 2.9.0 → 2.9.3
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 +42 -0
- package/README.md +4 -4
- package/dist/jscad-modeling.min.js +437 -428
- package/package.json +3 -2
- package/src/colors/colorize.test.js +1 -1
- package/src/geometries/geom2/index.d.ts +1 -0
- package/src/geometries/geom2/index.js +2 -1
- package/src/geometries/geom2/toOutlines.js +66 -52
- package/src/geometries/geom2/validate.d.ts +3 -0
- package/src/geometries/geom2/validate.js +36 -0
- package/src/geometries/geom3/create.js +1 -1
- package/src/geometries/geom3/create.test.js +1 -1
- package/src/geometries/geom3/fromPoints.js +1 -1
- package/src/geometries/geom3/index.d.ts +1 -0
- package/src/geometries/geom3/index.js +2 -1
- package/src/geometries/geom3/isA.js +1 -1
- package/src/geometries/geom3/validate.d.ts +3 -0
- package/src/geometries/geom3/validate.js +62 -0
- package/src/geometries/path2/index.d.ts +1 -1
- package/src/geometries/path2/index.js +2 -2
- package/src/geometries/path2/validate.d.ts +3 -0
- package/src/geometries/path2/validate.js +41 -0
- package/src/geometries/poly2/arePointsInside.js +0 -35
- package/src/geometries/poly3/create.js +1 -1
- package/src/geometries/poly3/index.d.ts +1 -0
- package/src/geometries/poly3/index.js +2 -1
- package/src/geometries/poly3/measureArea.test.js +16 -16
- package/src/geometries/poly3/measureBoundingSphere.test.js +8 -8
- package/src/geometries/poly3/validate.d.ts +4 -0
- package/src/geometries/poly3/validate.js +64 -0
- package/src/maths/constants.d.ts +1 -0
- package/src/maths/constants.js +11 -0
- package/src/maths/utils/aboutEqualNormals.js +1 -5
- package/src/measurements/measureCenterOfMass.test.js +2 -2
- package/src/operations/booleans/intersect.test.js +8 -0
- package/src/operations/booleans/intersectGeom3.js +2 -1
- package/src/operations/booleans/scission.test.js +4 -4
- package/src/operations/booleans/subtract.test.js +8 -0
- package/src/operations/booleans/subtractGeom3.js +2 -1
- package/src/operations/booleans/to3DWalls.js +1 -1
- package/src/operations/booleans/trees/Node.js +10 -16
- package/src/operations/booleans/trees/PolygonTreeNode.js +13 -14
- package/src/operations/booleans/trees/Tree.js +1 -2
- package/src/operations/booleans/trees/splitPolygonByPlane.js +2 -3
- package/src/operations/booleans/union.test.js +27 -0
- package/src/operations/booleans/unionGeom3.js +2 -1
- package/src/operations/expansions/expand.test.js +30 -21
- package/src/operations/expansions/expandGeom3.test.js +14 -14
- package/src/operations/expansions/expandShell.js +5 -4
- package/src/operations/expansions/extrudePolygon.js +7 -7
- package/src/operations/expansions/offset.test.js +25 -0
- package/src/operations/extrusions/earcut/assignHoles.js +7 -3
- package/src/operations/extrusions/earcut/assignHoles.test.js +50 -4
- package/src/operations/extrusions/earcut/linkedList.js +1 -1
- package/src/operations/extrusions/extrudeFromSlices.test.js +16 -10
- package/src/operations/extrusions/extrudeLinear.test.js +15 -9
- package/src/operations/extrusions/extrudeRectangular.test.js +15 -8
- package/src/operations/extrusions/extrudeRotate.js +5 -1
- package/src/operations/extrusions/extrudeRotate.test.js +12 -0
- package/src/operations/extrusions/extrudeWalls.js +2 -2
- package/src/operations/extrusions/project.js +11 -14
- package/src/operations/extrusions/project.test.js +55 -55
- package/src/operations/hulls/hull.test.js +24 -1
- package/src/operations/hulls/hullChain.test.js +6 -4
- package/src/operations/hulls/hullGeom2.js +6 -18
- package/src/operations/hulls/hullGeom3.js +5 -18
- package/src/operations/hulls/hullPath2.js +4 -14
- package/src/operations/hulls/hullPath2.test.js +1 -1
- package/src/operations/hulls/hullPoints2.js +43 -92
- package/src/operations/hulls/toUniquePoints.js +34 -0
- package/src/operations/modifiers/generalize.js +2 -13
- package/src/operations/modifiers/generalize.test.js +5 -31
- package/src/operations/modifiers/insertTjunctions.js +1 -1
- package/src/operations/modifiers/insertTjunctions.test.js +21 -21
- package/src/operations/modifiers/mergePolygons.js +11 -14
- package/src/operations/{booleans → modifiers}/reTesselateCoplanarPolygons.js +1 -1
- package/src/operations/{booleans → modifiers}/reTesselateCoplanarPolygons.test.js +5 -5
- package/src/operations/{booleans → modifiers}/retessellate.js +2 -9
- package/src/operations/{booleans → modifiers}/retessellate.test.js +0 -0
- package/src/operations/modifiers/snapPolygons.test.js +12 -12
- package/src/operations/modifiers/triangulatePolygons.js +3 -3
- package/src/operations/transforms/align.test.js +12 -0
- package/src/operations/transforms/center.js +1 -1
- package/src/operations/transforms/center.test.js +12 -0
- package/src/operations/transforms/mirror.test.js +16 -0
- package/src/operations/transforms/rotate.test.js +10 -0
- package/src/operations/transforms/scale.test.js +15 -0
- package/src/operations/transforms/transform.test.js +5 -0
- package/src/operations/transforms/translate.test.js +16 -0
- package/src/primitives/arc.test.js +11 -0
- package/src/primitives/circle.test.js +15 -9
- package/src/primitives/cube.test.js +3 -0
- package/src/primitives/cuboid.js +1 -1
- package/src/primitives/cuboid.test.js +9 -24
- package/src/primitives/cylinder.test.js +7 -4
- package/src/primitives/cylinderElliptic.js +14 -7
- package/src/primitives/cylinderElliptic.test.js +72 -50
- package/src/primitives/ellipse.js +3 -1
- package/src/primitives/ellipse.test.js +14 -8
- package/src/primitives/ellipsoid.js +8 -6
- package/src/primitives/ellipsoid.test.js +84 -80
- package/src/primitives/geodesicSphere.test.js +3 -0
- package/src/primitives/line.test.js +1 -0
- package/src/primitives/polygon.test.js +15 -10
- package/src/primitives/polyhedron.js +1 -1
- package/src/primitives/polyhedron.test.js +14 -42
- package/src/primitives/rectangle.test.js +3 -0
- package/src/primitives/roundedCuboid.js +6 -6
- package/src/primitives/roundedCuboid.test.js +5 -0
- package/src/primitives/roundedCylinder.js +7 -5
- package/src/primitives/roundedCylinder.test.js +40 -36
- package/src/primitives/roundedRectangle.test.js +5 -0
- package/src/primitives/sphere.test.js +52 -73
- package/src/primitives/square.test.js +3 -0
- package/src/primitives/star.test.js +6 -0
- package/src/primitives/torus.test.js +8 -1
- package/src/primitives/triangle.js +1 -2
- package/src/primitives/triangle.test.js +7 -0
- package/src/utils/areAllShapesTheSameType.js +2 -2
- package/src/utils/areAllShapesTheSameType.test.js +17 -0
- package/src/utils/index.d.ts +1 -0
- package/src/utils/index.js +3 -1
- package/src/utils/trigonometry.d.ts +2 -0
- package/src/utils/trigonometry.js +34 -0
- package/src/utils/trigonometry.test.js +25 -0
- package/test/helpers/nearlyEqual.js +4 -1
- package/src/geometries/path2/eachPoint.d.ts +0 -9
- package/src/geometries/path2/eachPoint.js +0 -17
- package/src/geometries/path2/eachPoint.test.js +0 -11
- package/src/operations/modifiers/edges.js +0 -195
- package/src/operations/modifiers/repairTjunctions.js +0 -44
|
@@ -11,6 +11,7 @@ test('extrudeRotate: (defaults) extruding of a geom2 produces an expected geom3'
|
|
|
11
11
|
|
|
12
12
|
const geometry3 = extrudeRotate({ }, geometry2)
|
|
13
13
|
const pts = geom3.toPoints(geometry3)
|
|
14
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
14
15
|
t.is(pts.length, 96)
|
|
15
16
|
})
|
|
16
17
|
|
|
@@ -34,15 +35,18 @@ test('extrudeRotate: (angle) extruding of a geom2 produces an expected geom3', (
|
|
|
34
35
|
[[26, 4.898587196589413e-16, 8], [10, 4.898587196589413e-16, 8], [10, -4.898587196589413e-16, -8]],
|
|
35
36
|
[[10, -4.898587196589413e-16, -8], [26, -4.898587196589413e-16, -8], [26, 4.898587196589413e-16, 8]]
|
|
36
37
|
]
|
|
38
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
37
39
|
t.is(pts.length, 12)
|
|
38
40
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
39
41
|
|
|
40
42
|
geometry3 = extrudeRotate({ segments: 4, angle: -250 * 0.017453292519943295 }, geometry2)
|
|
41
43
|
pts = geom3.toPoints(geometry3)
|
|
44
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
42
45
|
t.is(pts.length, 28)
|
|
43
46
|
|
|
44
47
|
geometry3 = extrudeRotate({ segments: 4, angle: 250 * 0.017453292519943295 }, geometry2)
|
|
45
48
|
pts = geom3.toPoints(geometry3)
|
|
49
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
46
50
|
t.is(pts.length, 28)
|
|
47
51
|
})
|
|
48
52
|
|
|
@@ -57,6 +61,7 @@ test('extrudeRotate: (startAngle) extruding of a geom2 produces an expected geom
|
|
|
57
61
|
[18.38477631085024, 18.384776310850235, 8],
|
|
58
62
|
[-11.803752993228215, 23.166169628897567, 8]
|
|
59
63
|
]
|
|
64
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
60
65
|
t.is(pts.length, 40)
|
|
61
66
|
t.true(comparePoints(pts[0], exp))
|
|
62
67
|
|
|
@@ -67,6 +72,7 @@ test('extrudeRotate: (startAngle) extruding of a geom2 produces an expected geom
|
|
|
67
72
|
[18.38477631085024, -18.384776310850235, 8],
|
|
68
73
|
[23.166169628897567, 11.803752993228215, 8]
|
|
69
74
|
]
|
|
75
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
70
76
|
t.is(pts.length, 40)
|
|
71
77
|
t.true(comparePoints(pts[0], exp))
|
|
72
78
|
})
|
|
@@ -77,22 +83,26 @@ test('extrudeRotate: (segments) extruding of a geom2 produces an expected geom3'
|
|
|
77
83
|
// test segments
|
|
78
84
|
let geometry3 = extrudeRotate({ segments: 4 }, geometry2)
|
|
79
85
|
let pts = geom3.toPoints(geometry3)
|
|
86
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
80
87
|
t.is(pts.length, 32)
|
|
81
88
|
|
|
82
89
|
geometry3 = extrudeRotate({ segments: 64 }, geometry2)
|
|
83
90
|
pts = geom3.toPoints(geometry3)
|
|
91
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
84
92
|
t.is(pts.length, 512)
|
|
85
93
|
|
|
86
94
|
// test overlapping edges
|
|
87
95
|
geometry2 = geom2.fromPoints([[0, 0], [2, 1], [1, 2], [1, 3], [3, 4], [0, 5]])
|
|
88
96
|
geometry3 = extrudeRotate({ segments: 8 }, geometry2)
|
|
89
97
|
pts = geom3.toPoints(geometry3)
|
|
98
|
+
t.notThrows.skip(() => geom3.validate(geometry3))
|
|
90
99
|
t.is(pts.length, 64)
|
|
91
100
|
|
|
92
101
|
// test overlapping edges that produce hollow shape
|
|
93
102
|
geometry2 = geom2.fromPoints([[30, 0], [30, 60], [0, 60], [0, 50], [10, 40], [10, 30], [0, 20], [0, 10], [10, 0]])
|
|
94
103
|
geometry3 = extrudeRotate({ segments: 8 }, geometry2)
|
|
95
104
|
pts = geom3.toPoints(geometry3)
|
|
105
|
+
t.notThrows.skip(() => geom3.validate(geometry3))
|
|
96
106
|
t.is(pts.length, 80)
|
|
97
107
|
})
|
|
98
108
|
|
|
@@ -112,6 +122,7 @@ test('extrudeRotate: (overlap +/-) extruding of a geom2 produces an expected geo
|
|
|
112
122
|
[[7, 4.898587196589413e-16, 8], [0, 4.898587196589413e-16, 8], [0, -4.898587196589413e-16, -8]],
|
|
113
123
|
[[0, -4.898587196589413e-16, -8], [7, -4.898587196589413e-16, -8], [7, 4.898587196589413e-16, 8]]
|
|
114
124
|
]
|
|
125
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
115
126
|
t.is(pts.length, 8)
|
|
116
127
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
117
128
|
|
|
@@ -140,6 +151,7 @@ test('extrudeRotate: (overlap +/-) extruding of a geom2 produces an expected geo
|
|
|
140
151
|
[[2, 2.4492935982947064e-16, 4], [1, 4.898587196589413e-16, 8], [0, 4.898587196589413e-16, 8]],
|
|
141
152
|
[[0, 4.898587196589413e-16, 8], [1, -4.898587196589413e-16, -8], [2, 2.4492935982947064e-16, 4]]
|
|
142
153
|
]
|
|
154
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
143
155
|
t.is(pts.length, 18)
|
|
144
156
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
145
157
|
})
|
|
@@ -64,11 +64,11 @@ const extrudeWalls = (slice0, slice1) => {
|
|
|
64
64
|
edges0.forEach((edge0, i) => {
|
|
65
65
|
const edge1 = edges1[i]
|
|
66
66
|
|
|
67
|
-
const poly0 = poly3.
|
|
67
|
+
const poly0 = poly3.create([edge0[0], edge0[1], edge1[1]])
|
|
68
68
|
const poly0area = poly3.measureArea(poly0)
|
|
69
69
|
if (Number.isFinite(poly0area) && poly0area > EPSAREA) walls.push(poly0)
|
|
70
70
|
|
|
71
|
-
const poly1 = poly3.
|
|
71
|
+
const poly1 = poly3.create([edge0[0], edge1[1], edge1[0]])
|
|
72
72
|
const poly1area = poly3.measureArea(poly1)
|
|
73
73
|
if (Number.isFinite(poly1area) && poly1area > EPSAREA) walls.push(poly1)
|
|
74
74
|
})
|
|
@@ -11,7 +11,6 @@ const poly3 = require('../../geometries/poly3')
|
|
|
11
11
|
const measureEpsilon = require('../../measurements/measureEpsilon')
|
|
12
12
|
|
|
13
13
|
const unionGeom2 = require('../booleans/unionGeom2')
|
|
14
|
-
const unionGeom3 = require('../booleans/unionGeom3')
|
|
15
14
|
|
|
16
15
|
const projectGeom3 = (options, geometry) => {
|
|
17
16
|
// create a plane from the options, and verify
|
|
@@ -27,32 +26,30 @@ const projectGeom3 = (options, geometry) => {
|
|
|
27
26
|
|
|
28
27
|
// project the polygons to the plane
|
|
29
28
|
const polygons = geom3.toPolygons(geometry)
|
|
30
|
-
|
|
29
|
+
let projpolys = []
|
|
31
30
|
for (let i = 0; i < polygons.length; i++) {
|
|
32
31
|
const newpoints = polygons[i].vertices.map((v) => plane.projectionOfPoint(projplane, v))
|
|
33
32
|
const newpoly = poly3.create(newpoints)
|
|
34
|
-
// only keep projections that have a measurable area
|
|
35
|
-
if (poly3.measureArea(newpoly) < epsilonArea) continue
|
|
36
33
|
// only keep projections that face the same direction as the plane
|
|
37
34
|
const newplane = poly3.plane(newpoly)
|
|
38
35
|
if (!aboutEqualNormals(projplane, newplane)) continue
|
|
36
|
+
// only keep projections that have a measurable area
|
|
37
|
+
if (poly3.measureArea(newpoly) < epsilonArea) continue
|
|
39
38
|
projpolys.push(newpoly)
|
|
40
39
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
projection = unionGeom3(projection, projection)
|
|
44
|
-
// rotate the projection to lay on X/Y axes if necessary
|
|
40
|
+
|
|
41
|
+
// rotate the polygons to lay on X/Y axes if necessary
|
|
45
42
|
if (!aboutEqualNormals(projplane, [0, 0, 1])) {
|
|
46
43
|
const rotation = mat4.fromVectorRotation(mat4.create(), projplane, [0, 0, 1])
|
|
47
|
-
|
|
44
|
+
projpolys = projpolys.map((p) => poly3.transform(rotation, p))
|
|
48
45
|
}
|
|
49
46
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
// union the 2D geometries to obtain the outline of the projection
|
|
53
|
-
projection = unionGeom2(projections2D)
|
|
47
|
+
// sort the polygons to allow the union to ignore small pieces efficiently
|
|
48
|
+
projpolys = projpolys.sort((a, b) => poly3.measureArea(b) - poly3.measureArea(a))
|
|
54
49
|
|
|
55
|
-
|
|
50
|
+
// convert polygons to geometry, and union all pieces into a single geometry
|
|
51
|
+
const projgeoms = projpolys.map((p) => geom2.fromPoints(p.vertices))
|
|
52
|
+
return unionGeom2(projgeoms)
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
/**
|
|
@@ -16,108 +16,108 @@ test('project (defaults)', (t) => {
|
|
|
16
16
|
|
|
17
17
|
const results = project({ }, geometry0, geometry1, geometry2, geometry3, geometry4)
|
|
18
18
|
t.is(results.length, 5)
|
|
19
|
-
t.
|
|
20
|
-
t.
|
|
19
|
+
t.notThrows(() => geom2.validate(results[0]))
|
|
20
|
+
t.notThrows(() => geom2.validate(results[1]))
|
|
21
21
|
t.is(results[2], geometry2)
|
|
22
22
|
t.is(results[3], geometry3)
|
|
23
23
|
t.is(results[4], geometry4)
|
|
24
24
|
|
|
25
25
|
const result = project({ }, torus({ outerSegments: 4 }))
|
|
26
|
-
t.
|
|
26
|
+
t.notThrows(() => geom2.validate(result))
|
|
27
27
|
const pts = geom2.toPoints(result)
|
|
28
28
|
const exp = [
|
|
29
|
-
[0, -2.9999933333333333],
|
|
30
29
|
[0, -5.000013333333333],
|
|
31
|
-
[0, 5.000013333333333],
|
|
32
30
|
[5.000013333333333, 0],
|
|
33
|
-
[
|
|
31
|
+
[-5.000013333333333, 0],
|
|
34
32
|
[-2.9999933333333333, 0],
|
|
35
33
|
[2.9999933333333333, 0],
|
|
36
|
-
[
|
|
34
|
+
[0, 2.9999933333333333],
|
|
35
|
+
[0, -2.9999933333333333],
|
|
36
|
+
[0, 5.000013333333333]
|
|
37
37
|
]
|
|
38
38
|
t.true(comparePoints(pts, exp))
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
test('project (X and Y axis)', (t) => {
|
|
42
42
|
let result = project({ axis: [1, 0, 0], origin: [1, 0, 0] }, torus({ outerSegments: 4 }))
|
|
43
|
-
t.
|
|
43
|
+
t.notThrows(() => geom2.validate(result))
|
|
44
44
|
let pts = geom2.toPoints(result)
|
|
45
45
|
let exp = [
|
|
46
|
-
[-1.0000200000000001, -3.999986666666667],
|
|
47
|
-
[-0.8314600000000001, 4.555553333333334],
|
|
48
|
-
[-0.9807933333333334, -4.1951],
|
|
49
|
-
[-0.7070933333333335, 4.707126666666667],
|
|
50
|
-
[-0.9238600000000001, -4.382700000000001],
|
|
51
|
-
[-0.5555666666666668, 4.831446666666667],
|
|
52
|
-
[-0.8314600000000001, -4.555553333333334],
|
|
53
|
-
[-0.3826666666666667, 4.923893333333334],
|
|
54
|
-
[-0.7070933333333335, -4.707126666666667],
|
|
55
|
-
[-0.19511333333333336, 4.98078],
|
|
56
|
-
[-0.5555666666666668, -4.831446666666667],
|
|
57
|
-
[0, 5.000006666666668],
|
|
58
|
-
[-0.3826666666666667, -4.923893333333334],
|
|
59
|
-
[0.19511333333333336, 4.98078],
|
|
60
46
|
[-0.19511333333333336, -4.98078],
|
|
61
|
-
[0.3826666666666667, 4.923893333333334],
|
|
62
47
|
[0, -5.000006666666668],
|
|
63
|
-
[0
|
|
48
|
+
[0, 5.000006666666668],
|
|
49
|
+
[0.3826666666666667, 4.923893333333334],
|
|
50
|
+
[-0.3826666666666667, -4.923893333333334],
|
|
64
51
|
[0.19511333333333336, -4.98078],
|
|
65
|
-
[0.
|
|
52
|
+
[-0.19511333333333336, 4.98078],
|
|
53
|
+
[-0.5555666666666668, -4.831446666666667],
|
|
54
|
+
[0.5555666666666668, 4.831446666666667],
|
|
55
|
+
[-0.3826666666666667, 4.923893333333334],
|
|
66
56
|
[0.3826666666666667, -4.923893333333334],
|
|
67
|
-
[0.
|
|
57
|
+
[0.7070933333333335, 4.707126666666667],
|
|
58
|
+
[-0.7070933333333335, -4.707126666666667],
|
|
68
59
|
[0.5555666666666668, -4.831446666666667],
|
|
69
|
-
[0.
|
|
60
|
+
[-0.5555666666666668, 4.831446666666667],
|
|
61
|
+
[0.8314600000000001, 4.555553333333334],
|
|
62
|
+
[-0.8314600000000001, -4.555553333333334],
|
|
70
63
|
[0.7070933333333335, -4.707126666666667],
|
|
71
|
-
[0.
|
|
64
|
+
[-0.7070933333333335, 4.707126666666667],
|
|
65
|
+
[-0.9238600000000001, -4.382700000000001],
|
|
66
|
+
[0.9238600000000001, 4.382700000000001],
|
|
67
|
+
[-0.8314600000000001, 4.555553333333334],
|
|
72
68
|
[0.8314600000000001, -4.555553333333334],
|
|
69
|
+
[0.9807933333333334, 4.1951],
|
|
70
|
+
[-0.9807933333333334, -4.1951],
|
|
71
|
+
[0.9238600000000001, -4.382700000000001],
|
|
72
|
+
[-0.9238600000000001, 4.382700000000001],
|
|
73
73
|
[1.0000200000000001, 3.999986666666667],
|
|
74
|
+
[-1.0000200000000001, -3.999986666666667],
|
|
74
75
|
[1.0000200000000001, -3.999986666666667],
|
|
75
|
-
[0.9238600000000001, -4.382700000000001],
|
|
76
|
-
[0.9807933333333334, -4.1951],
|
|
77
76
|
[-1.0000200000000001, 3.999986666666667],
|
|
78
77
|
[-0.9807933333333334, 4.1951],
|
|
79
|
-
[
|
|
78
|
+
[0.9807933333333334, -4.1951],
|
|
79
|
+
[0.19511333333333336, 4.98078]
|
|
80
80
|
]
|
|
81
81
|
t.true(comparePoints(pts, exp))
|
|
82
82
|
|
|
83
83
|
result = project({ axis: [0, 1, 0], origin: [0, -1, 0] }, torus({ outerSegments: 4 }))
|
|
84
|
-
t.
|
|
84
|
+
t.notThrows(() => geom2.validate(result))
|
|
85
85
|
pts = geom2.toPoints(result)
|
|
86
86
|
exp = [
|
|
87
|
-
[3.999986666666667, -1.0000200000000001],
|
|
88
|
-
[-4.555553333333334, -0.8314600000000001],
|
|
89
|
-
[4.1951, -0.9807933333333334],
|
|
90
|
-
[-4.707126666666667, -0.7070933333333335],
|
|
91
|
-
[4.382700000000001, -0.9238600000000001],
|
|
92
|
-
[-4.831446666666667, -0.5555666666666668],
|
|
93
|
-
[4.555553333333334, -0.8314600000000001],
|
|
94
|
-
[-4.923893333333334, -0.3826666666666667],
|
|
95
|
-
[4.707126666666667, -0.7070933333333335],
|
|
96
|
-
[-4.98078, -0.19511333333333336],
|
|
97
|
-
[4.831446666666667, -0.5555666666666668],
|
|
98
|
-
[-5.000006666666668, 0],
|
|
99
|
-
[4.923893333333334, -0.3826666666666667],
|
|
100
|
-
[-4.98078, 0.19511333333333336],
|
|
101
87
|
[4.98078, -0.19511333333333336],
|
|
102
|
-
[-4.923893333333334, 0.3826666666666667],
|
|
103
88
|
[5.000006666666668, 0],
|
|
104
|
-
[-
|
|
89
|
+
[-5.000006666666668, 0],
|
|
90
|
+
[-4.923893333333334, 0.3826666666666667],
|
|
91
|
+
[4.923893333333334, -0.3826666666666667],
|
|
105
92
|
[4.98078, 0.19511333333333336],
|
|
106
|
-
[-4.
|
|
93
|
+
[-4.98078, -0.19511333333333336],
|
|
94
|
+
[4.831446666666667, -0.5555666666666668],
|
|
95
|
+
[-4.831446666666667, 0.5555666666666668],
|
|
96
|
+
[-4.923893333333334, -0.3826666666666667],
|
|
107
97
|
[4.923893333333334, 0.3826666666666667],
|
|
108
|
-
[-4.
|
|
98
|
+
[-4.707126666666667, 0.7070933333333335],
|
|
99
|
+
[4.707126666666667, -0.7070933333333335],
|
|
109
100
|
[4.831446666666667, 0.5555666666666668],
|
|
110
|
-
[-4.
|
|
101
|
+
[-4.831446666666667, -0.5555666666666668],
|
|
102
|
+
[4.555553333333334, -0.8314600000000001],
|
|
103
|
+
[-4.555553333333334, 0.8314600000000001],
|
|
111
104
|
[4.707126666666667, 0.7070933333333335],
|
|
112
|
-
[-4.
|
|
105
|
+
[-4.707126666666667, -0.7070933333333335],
|
|
106
|
+
[4.382700000000001, -0.9238600000000001],
|
|
107
|
+
[-4.382700000000001, 0.9238600000000001],
|
|
108
|
+
[-4.555553333333334, -0.8314600000000001],
|
|
113
109
|
[4.555553333333334, 0.8314600000000001],
|
|
110
|
+
[-4.1951, 0.9807933333333334],
|
|
111
|
+
[4.1951, -0.9807933333333334],
|
|
112
|
+
[4.382700000000001, 0.9238600000000001],
|
|
113
|
+
[-4.382700000000001, -0.9238600000000001],
|
|
114
|
+
[3.999986666666667, -1.0000200000000001],
|
|
114
115
|
[-3.999986666666667, 1.0000200000000001],
|
|
115
116
|
[3.999986666666667, 1.0000200000000001],
|
|
116
|
-
[4.382700000000001, 0.9238600000000001],
|
|
117
|
-
[4.1951, 0.9807933333333334],
|
|
118
117
|
[-3.999986666666667, -1.0000200000000001],
|
|
118
|
+
[4.1951, 0.9807933333333334],
|
|
119
119
|
[-4.1951, -0.9807933333333334],
|
|
120
|
-
[-4.
|
|
120
|
+
[-4.98078, 0.19511333333333336]
|
|
121
121
|
]
|
|
122
122
|
t.true(comparePoints(pts, exp))
|
|
123
123
|
})
|
|
@@ -16,12 +16,14 @@ test('hull (single, geom2)', (t) => {
|
|
|
16
16
|
let obs = hull(geometry)
|
|
17
17
|
let pts = geom2.toPoints(obs)
|
|
18
18
|
|
|
19
|
+
t.notThrows(() => geom2.validate(geometry))
|
|
19
20
|
t.is(pts.length, 0)
|
|
20
21
|
|
|
21
22
|
geometry = geom2.fromPoints([[5, 5], [-5, 5], [-5, -5], [5, -5]])
|
|
22
23
|
obs = hull(geometry)
|
|
23
24
|
pts = geom2.toPoints(obs)
|
|
24
25
|
|
|
26
|
+
t.notThrows(() => geom2.validate(geometry))
|
|
25
27
|
t.is(pts.length, 4)
|
|
26
28
|
|
|
27
29
|
// convex C shape
|
|
@@ -40,6 +42,7 @@ test('hull (single, geom2)', (t) => {
|
|
|
40
42
|
obs = hull(geometry)
|
|
41
43
|
pts = geom2.toPoints(obs)
|
|
42
44
|
|
|
45
|
+
t.notThrows(() => geom2.validate(geometry))
|
|
43
46
|
t.is(pts.length, 7)
|
|
44
47
|
})
|
|
45
48
|
|
|
@@ -66,23 +69,27 @@ test('hull (multiple, overlapping, geom2)', (t) => {
|
|
|
66
69
|
let obs = hull(geometry1, geometry1)
|
|
67
70
|
let pts = geom2.toPoints(obs)
|
|
68
71
|
|
|
72
|
+
t.notThrows(() => geom2.validate(obs))
|
|
69
73
|
t.is(pts.length, 4)
|
|
70
74
|
|
|
71
75
|
// one inside another
|
|
72
76
|
obs = hull(geometry1, geometry2)
|
|
73
77
|
pts = geom2.toPoints(obs)
|
|
74
78
|
|
|
79
|
+
t.notThrows(() => geom2.validate(obs))
|
|
75
80
|
t.is(pts.length, 4)
|
|
76
81
|
|
|
77
82
|
// one overlapping another
|
|
78
83
|
obs = hull(geometry1, geometry3)
|
|
79
84
|
pts = geom2.toPoints(obs)
|
|
80
85
|
|
|
86
|
+
t.notThrows(() => geom2.validate(obs))
|
|
81
87
|
t.is(pts.length, 8)
|
|
82
88
|
|
|
83
89
|
obs = hull(geometry2, geometry4)
|
|
84
90
|
pts = geom2.toPoints(obs)
|
|
85
91
|
|
|
92
|
+
t.notThrows(() => geom2.validate(obs))
|
|
86
93
|
t.is(pts.length, 7)
|
|
87
94
|
})
|
|
88
95
|
|
|
@@ -108,22 +115,27 @@ test('hull (multiple, various, geom2)', (t) => {
|
|
|
108
115
|
|
|
109
116
|
let obs = hull(geometry1, geometry2)
|
|
110
117
|
let pts = geom2.toPoints(obs)
|
|
118
|
+
t.notThrows(() => geom2.validate(obs))
|
|
111
119
|
t.is(pts.length, 5)
|
|
112
120
|
|
|
113
121
|
obs = hull(geometry1, geometry3)
|
|
114
122
|
pts = geom2.toPoints(obs)
|
|
123
|
+
t.notThrows(() => geom2.validate(obs))
|
|
115
124
|
t.is(pts.length, 5)
|
|
116
125
|
|
|
117
126
|
obs = hull(geometry2, geometry3)
|
|
118
127
|
pts = geom2.toPoints(obs)
|
|
128
|
+
t.notThrows(() => geom2.validate(obs))
|
|
119
129
|
t.is(pts.length, 5)
|
|
120
130
|
|
|
121
131
|
obs = hull(geometry1, geometry2, geometry3)
|
|
122
132
|
pts = geom2.toPoints(obs)
|
|
133
|
+
t.notThrows(() => geom2.validate(obs))
|
|
123
134
|
t.is(pts.length, 6)
|
|
124
135
|
|
|
125
136
|
obs = hull(geometry5, geometry4)
|
|
126
137
|
pts = geom2.toPoints(obs)
|
|
138
|
+
t.notThrows(() => geom2.validate(obs))
|
|
127
139
|
t.is(pts.length, 8)
|
|
128
140
|
})
|
|
129
141
|
|
|
@@ -133,6 +145,7 @@ test('hull (single, path2)', (t) => {
|
|
|
133
145
|
let obs = hull(geometry)
|
|
134
146
|
let pts = path2.toPoints(obs)
|
|
135
147
|
|
|
148
|
+
t.notThrows(() => path2.validate(obs))
|
|
136
149
|
t.is(pts.length, 0)
|
|
137
150
|
|
|
138
151
|
geometry = path2.fromPoints({}, [[0, 0], [5, 0], [5, 10], [4, 1]])
|
|
@@ -140,6 +153,7 @@ test('hull (single, path2)', (t) => {
|
|
|
140
153
|
obs = hull(geometry)
|
|
141
154
|
pts = path2.toPoints(obs)
|
|
142
155
|
|
|
156
|
+
t.notThrows(() => path2.validate(obs))
|
|
143
157
|
t.is(pts.length, 3)
|
|
144
158
|
})
|
|
145
159
|
|
|
@@ -165,22 +179,27 @@ test('hull (multiple, various, path2)', (t) => {
|
|
|
165
179
|
|
|
166
180
|
let obs = hull(geometry1, geometry2)
|
|
167
181
|
let pts = path2.toPoints(obs)
|
|
182
|
+
t.notThrows(() => path2.validate(obs))
|
|
168
183
|
t.is(pts.length, 5)
|
|
169
184
|
|
|
170
185
|
obs = hull(geometry1, geometry3)
|
|
171
186
|
pts = path2.toPoints(obs)
|
|
187
|
+
t.notThrows(() => path2.validate(obs))
|
|
172
188
|
t.is(pts.length, 5)
|
|
173
189
|
|
|
174
190
|
obs = hull(geometry2, geometry3)
|
|
175
191
|
pts = path2.toPoints(obs)
|
|
192
|
+
t.notThrows(() => path2.validate(obs))
|
|
176
193
|
t.is(pts.length, 5)
|
|
177
194
|
|
|
178
195
|
obs = hull(geometry1, geometry2, geometry3)
|
|
179
196
|
pts = path2.toPoints(obs)
|
|
197
|
+
t.notThrows(() => path2.validate(obs))
|
|
180
198
|
t.is(pts.length, 6)
|
|
181
199
|
|
|
182
200
|
obs = hull(geometry5, geometry4)
|
|
183
201
|
pts = path2.toPoints(obs)
|
|
202
|
+
t.notThrows(() => path2.validate(obs))
|
|
184
203
|
t.is(pts.length, 8)
|
|
185
204
|
})
|
|
186
205
|
|
|
@@ -190,6 +209,7 @@ test('hull (single, geom3)', (t) => {
|
|
|
190
209
|
let obs = hull(geometry)
|
|
191
210
|
let pts = geom3.toPoints(obs)
|
|
192
211
|
|
|
212
|
+
t.notThrows(() => geom3.validate(obs))
|
|
193
213
|
t.is(pts.length, 0)
|
|
194
214
|
|
|
195
215
|
geometry = sphere({ radius: 2, segments: 8 })
|
|
@@ -197,6 +217,7 @@ test('hull (single, geom3)', (t) => {
|
|
|
197
217
|
obs = hull(geometry)
|
|
198
218
|
pts = geom3.toPoints(obs)
|
|
199
219
|
|
|
220
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
200
221
|
t.is(pts.length, 32)
|
|
201
222
|
})
|
|
202
223
|
|
|
@@ -214,6 +235,7 @@ test('hull (multiple, geom3)', (t) => {
|
|
|
214
235
|
[[1, -1, 1], [-1, -1, 1], [-1, -1, -1], [1, -1, -1]]
|
|
215
236
|
]
|
|
216
237
|
|
|
238
|
+
t.notThrows(() => geom3.validate(obs))
|
|
217
239
|
t.is(pts.length, 6)
|
|
218
240
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
219
241
|
|
|
@@ -236,6 +258,7 @@ test('hull (multiple, geom3)', (t) => {
|
|
|
236
258
|
[[1, 1, -1], [6.5, 6.5, 3.5], [6.5, 3.5, 3.5], [1, -1, -1]]
|
|
237
259
|
]
|
|
238
260
|
|
|
261
|
+
t.notThrows(() => geom3.validate(obs))
|
|
239
262
|
t.is(pts.length, 12)
|
|
240
263
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
241
264
|
})
|
|
@@ -248,6 +271,6 @@ test('hull (multiple, overlapping, geom3)', (t) => {
|
|
|
248
271
|
const obs = hull(geometry1, geometry2, geometry3)
|
|
249
272
|
const pts = geom3.toPoints(obs)
|
|
250
273
|
|
|
251
|
-
|
|
274
|
+
t.notThrows(() => geom3.validate(obs))
|
|
252
275
|
t.is(pts.length, 92)
|
|
253
276
|
})
|
|
@@ -12,12 +12,14 @@ test('hullChain (two, geom2)', (t) => {
|
|
|
12
12
|
let obs = hullChain(geometry1, geometry1)
|
|
13
13
|
let pts = geom2.toPoints(obs)
|
|
14
14
|
|
|
15
|
+
t.notThrows(() => geom2.validate(obs))
|
|
15
16
|
t.is(pts.length, 4)
|
|
16
17
|
|
|
17
18
|
// different
|
|
18
19
|
obs = hullChain(geometry1, geometry2)
|
|
19
20
|
pts = geom2.toPoints(obs)
|
|
20
21
|
|
|
22
|
+
t.notThrows(() => geom2.validate(obs))
|
|
21
23
|
t.is(pts.length, 6)
|
|
22
24
|
})
|
|
23
25
|
|
|
@@ -31,16 +33,16 @@ test('hullChain (three, geom2)', (t) => {
|
|
|
31
33
|
let pts = geom2.toPoints(obs)
|
|
32
34
|
|
|
33
35
|
// the sides change based on the bestplane chosen in trees/Node.js
|
|
36
|
+
t.notThrows(() => geom2.validate(obs))
|
|
34
37
|
t.is(pts.length, 10)
|
|
35
|
-
// t.is(pts.length, 11)
|
|
36
38
|
|
|
37
39
|
// closed
|
|
38
40
|
obs = hullChain(geometry1, geometry2, geometry3, geometry1)
|
|
39
41
|
pts = geom2.toPoints(obs)
|
|
40
42
|
|
|
41
43
|
// the sides change based on the bestplane chosen in trees/Node.js
|
|
44
|
+
t.notThrows(() => geom2.validate(obs))
|
|
42
45
|
t.is(pts.length, 10)
|
|
43
|
-
// t.is(pts.length, 13)
|
|
44
46
|
})
|
|
45
47
|
|
|
46
48
|
test('hullChain (three, geom3)', (t) => {
|
|
@@ -73,13 +75,13 @@ test('hullChain (three, geom3)', (t) => {
|
|
|
73
75
|
let obs = hullChain(geometry1, geometry2, geometry3)
|
|
74
76
|
let pts = geom3.toPoints(obs)
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
77
79
|
t.is(pts.length, 23)
|
|
78
80
|
|
|
79
81
|
// closed
|
|
80
82
|
obs = hullChain(geometry1, geometry2, geometry3, geometry1)
|
|
81
83
|
pts = geom3.toPoints(obs)
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
t.notThrows.skip(() => geom3.validate(obs))
|
|
84
86
|
t.is(pts.length, 28)
|
|
85
87
|
})
|
|
@@ -3,6 +3,7 @@ const flatten = require('../../utils/flatten')
|
|
|
3
3
|
const geom2 = require('../../geometries/geom2')
|
|
4
4
|
|
|
5
5
|
const hullPoints2 = require('./hullPoints2')
|
|
6
|
+
const toUniquePoints = require('./toUniquePoints')
|
|
6
7
|
|
|
7
8
|
/*
|
|
8
9
|
* Create a convex hull of the given geom2 geometries.
|
|
@@ -13,28 +14,15 @@ const hullGeom2 = (...geometries) => {
|
|
|
13
14
|
geometries = flatten(geometries)
|
|
14
15
|
|
|
15
16
|
// extract the unique points from the geometries
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const sides = geom2.toSides(geometries[g])
|
|
20
|
-
for (let s = 0; s < sides.length; s++) {
|
|
21
|
-
const side = sides[s]
|
|
22
|
-
const point = side[0]
|
|
23
|
-
const id = `${point[0]},${point[1]}`
|
|
24
|
-
if (found.has(id)) continue
|
|
25
|
-
uniquepoints.push(point)
|
|
26
|
-
found.set(id, true)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
found.clear()
|
|
30
|
-
|
|
31
|
-
const hullpoints = hullPoints2(uniquepoints)
|
|
17
|
+
const unique = toUniquePoints(geometries)
|
|
18
|
+
|
|
19
|
+
const hullPoints = hullPoints2(unique)
|
|
32
20
|
|
|
33
21
|
// NOTE: more than three points are required to create a new geometry
|
|
34
|
-
if (
|
|
22
|
+
if (hullPoints.length < 3) return geom2.create()
|
|
35
23
|
|
|
36
24
|
// assemble a new geometry from the list of points
|
|
37
|
-
return geom2.fromPoints(
|
|
25
|
+
return geom2.fromPoints(hullPoints)
|
|
38
26
|
}
|
|
39
27
|
|
|
40
28
|
module.exports = hullGeom2
|
|
@@ -4,6 +4,7 @@ const geom3 = require('../../geometries/geom3')
|
|
|
4
4
|
const poly3 = require('../../geometries/poly3')
|
|
5
5
|
|
|
6
6
|
const quickhull = require('./quickhull')
|
|
7
|
+
const toUniquePoints = require('./toUniquePoints')
|
|
7
8
|
|
|
8
9
|
/*
|
|
9
10
|
* Create a convex hull of the given geometries (geom3).
|
|
@@ -16,26 +17,12 @@ const hullGeom3 = (...geometries) => {
|
|
|
16
17
|
if (geometries.length === 1) return geometries[0]
|
|
17
18
|
|
|
18
19
|
// extract the unique vertices from the geometries
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const polygons = geom3.toPolygons(geometries[g])
|
|
23
|
-
for (let p = 0; p < polygons.length; ++p) {
|
|
24
|
-
const vertices = polygons[p].vertices
|
|
25
|
-
for (let v = 0; v < vertices.length; ++v) {
|
|
26
|
-
const id = `${vertices[v]}`
|
|
27
|
-
if (found.has(id)) continue
|
|
28
|
-
uniquevertices.push(vertices[v])
|
|
29
|
-
found.set(id, true)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
found.clear()
|
|
34
|
-
|
|
35
|
-
const faces = quickhull(uniquevertices, { skipTriangulation: true })
|
|
20
|
+
const unique = toUniquePoints(geometries)
|
|
21
|
+
|
|
22
|
+
const faces = quickhull(unique, { skipTriangulation: true })
|
|
36
23
|
|
|
37
24
|
const polygons = faces.map((face) => {
|
|
38
|
-
const vertices = face.map((index) =>
|
|
25
|
+
const vertices = face.map((index) => unique[index])
|
|
39
26
|
return poly3.create(vertices)
|
|
40
27
|
})
|
|
41
28
|
|
|
@@ -3,6 +3,7 @@ const flatten = require('../../utils/flatten')
|
|
|
3
3
|
const path2 = require('../../geometries/path2')
|
|
4
4
|
|
|
5
5
|
const hullPoints2 = require('./hullPoints2')
|
|
6
|
+
const toUniquePoints = require('./toUniquePoints')
|
|
6
7
|
|
|
7
8
|
/*
|
|
8
9
|
* Create a convex hull of the given geometries (path2).
|
|
@@ -13,23 +14,12 @@ const hullPath2 = (...geometries) => {
|
|
|
13
14
|
geometries = flatten(geometries)
|
|
14
15
|
|
|
15
16
|
// extract the unique points from the geometries
|
|
16
|
-
const
|
|
17
|
-
const found = new Set()
|
|
18
|
-
geometries.forEach((geometry) => {
|
|
19
|
-
const points = path2.toPoints(geometry)
|
|
20
|
-
points.forEach((point) => {
|
|
21
|
-
const key = point.toString()
|
|
22
|
-
if (!found.has(key)) {
|
|
23
|
-
uniquepoints.push(point)
|
|
24
|
-
found.add(key)
|
|
25
|
-
}
|
|
26
|
-
})
|
|
27
|
-
})
|
|
17
|
+
const unique = toUniquePoints(geometries)
|
|
28
18
|
|
|
29
|
-
const
|
|
19
|
+
const hullPoints = hullPoints2(unique)
|
|
30
20
|
|
|
31
21
|
// assemble a new geometry from the list of points
|
|
32
|
-
return path2.fromPoints({ closed: true },
|
|
22
|
+
return path2.fromPoints({ closed: true }, hullPoints)
|
|
33
23
|
}
|
|
34
24
|
|
|
35
25
|
module.exports = hullPath2
|
|
@@ -10,7 +10,7 @@ test('hullPath2', (t) => {
|
|
|
10
10
|
const geometry2 = path2.fromPoints({ closed }, [[0, 0], [4, -4], [4, 4]])
|
|
11
11
|
|
|
12
12
|
const obs = hullPath2(geometry1, geometry2)
|
|
13
|
-
t.
|
|
13
|
+
t.notThrows(() => path2.validate(obs))
|
|
14
14
|
const pts = path2.toPoints(obs)
|
|
15
15
|
t.is(pts.length, 4)
|
|
16
16
|
})
|