@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
|
@@ -6,20 +6,20 @@ const expandGeom3 = require('./expandGeom3')
|
|
|
6
6
|
test('expandGeom3: expand completes properly, issue 876', async (t) => {
|
|
7
7
|
setTimeout(() => t.fail(), 1000)
|
|
8
8
|
const polies = [
|
|
9
|
-
poly3.
|
|
10
|
-
poly3.
|
|
11
|
-
poly3.
|
|
12
|
-
poly3.
|
|
13
|
-
poly3.
|
|
14
|
-
poly3.
|
|
15
|
-
poly3.
|
|
16
|
-
poly3.
|
|
17
|
-
poly3.
|
|
18
|
-
poly3.
|
|
19
|
-
poly3.
|
|
20
|
-
poly3.
|
|
21
|
-
poly3.
|
|
22
|
-
poly3.
|
|
9
|
+
poly3.create([[-19.61, -0.7999999999999986, 11.855], [-19.61, -0.8000000000000015, -11.855], [-19.61, -2.7500000000000018, -11.855], [-19.61, -2.7499999999999982, 11.855]]),
|
|
10
|
+
poly3.create([[-17.32, -2.75, 10], [-17.32, -2.7500000000000013, -10], [-17.32, -0.8000000000000014, -10], [-17.32, -0.7999999999999987, 10]]),
|
|
11
|
+
poly3.create([[-16.863040644206997, -0.8000000000000015, -10.28], [-16.863040644206997, -2.7500000000000018, -10.28], [-14.292644267871385, -2.7500000000000018, -11.855000000000016], [-14.292644267871383, -0.8000000000000015, -11.855000000000018]]),
|
|
12
|
+
poly3.create([[-17.319999999999993, -0.8000000000000015, -9.999999999999996], [-17.319999999999993, -2.7500000000000018, -9.999999999999996], [-16.87560702649131, -2.7500000000000018, -10.272299999999998], [-16.866696319053347, -0.8000000000000015, -10.277759999999997]]),
|
|
13
|
+
poly3.create([[-16.863040644207004, -2.7500000000000013, -10.280000000000001], [-16.863040644207004, -0.8000000000000014, -10.280000000000001], [-16.86669631905335, -0.8000000000000012, -10.27776], [-16.875607026491313, -2.75, -10.272300000000001]]),
|
|
14
|
+
poly3.create([[-14.107140000000015, -0.7999999999999987, 11.85500000000003], [-14.107140000000015, -2.7499999999999982, 11.85500000000003], [-17.319999999999975, -2.7499999999999982, 9.999999999999956], [-17.319999999999975, -0.7999999999999987, 9.999999999999956]]),
|
|
15
|
+
poly3.create([[-17.32, -0.7999999999999988, 9.999999999999993], [-17.32, -0.7999999999999986, 11.855], [-14.107139999999994, -0.7999999999999986, 11.855]]),
|
|
16
|
+
poly3.create([[-17.32, -0.800000000000001, -11.855], [-17.32, -0.8000000000000008, -10.000000000000078], [-14.292644267871482, -0.800000000000001, -11.855]]),
|
|
17
|
+
poly3.create([[-17.32, -0.800000000000001, -11.855], [-19.61, -0.800000000000001, -11.855], [-19.61, -0.7999999999999986, 11.855], [-17.32, -0.7999999999999986, 11.855]]),
|
|
18
|
+
poly3.create([[-17.32, -2.7500000000000013, -10.000000000000076], [-17.32, -2.7500000000000018, -11.855], [-14.292644267871482, -2.7500000000000018, -11.855]]),
|
|
19
|
+
poly3.create([[-17.32, -2.7499999999999982, 11.855], [-17.32, -2.7499999999999987, 9.999999999999996], [-14.107139999999996, -2.7499999999999982, 11.855]]),
|
|
20
|
+
poly3.create([[-17.32, -2.7499999999999982, 11.855], [-19.61, -2.7499999999999982, 11.855], [-19.61, -2.7500000000000018, -11.855], [-17.32, -2.7500000000000018, -11.855]]),
|
|
21
|
+
poly3.create([[-14.107139999999996, -0.7999999999999986, 11.855], [-19.61, -0.7999999999999986, 11.855], [-19.61, -2.7499999999999982, 11.855], [-14.107139999999994, -2.7499999999999982, 11.855]]),
|
|
22
|
+
poly3.create([[-19.61, -0.8000000000000015, -11.855], [-14.292644267871486, -0.8000000000000015, -11.855], [-14.292644267871482, -2.7500000000000018, -11.855], [-19.61, -2.7500000000000018, -11.855]])
|
|
23
23
|
]
|
|
24
24
|
|
|
25
25
|
const sub = geom3.create(polies)
|
|
@@ -10,7 +10,8 @@ const poly3 = require('../../geometries/poly3')
|
|
|
10
10
|
|
|
11
11
|
const sphere = require('../../primitives/sphere')
|
|
12
12
|
|
|
13
|
-
const retessellate = require('../
|
|
13
|
+
const retessellate = require('../modifiers/retessellate')
|
|
14
|
+
|
|
14
15
|
const unionGeom3Sub = require('../booleans/unionGeom3Sub')
|
|
15
16
|
|
|
16
17
|
const extrudePolygon = require('./extrudePolygon')
|
|
@@ -166,7 +167,7 @@ const expandShell = (options, geometry) => {
|
|
|
166
167
|
startfacevertices.push(p1)
|
|
167
168
|
endfacevertices.push(p2)
|
|
168
169
|
const points = [prevp2, p2, p1, prevp1]
|
|
169
|
-
const polygon = poly3.
|
|
170
|
+
const polygon = poly3.create(points)
|
|
170
171
|
polygons.push(polygon)
|
|
171
172
|
}
|
|
172
173
|
prevp1 = p1
|
|
@@ -174,8 +175,8 @@ const expandShell = (options, geometry) => {
|
|
|
174
175
|
}
|
|
175
176
|
}
|
|
176
177
|
endfacevertices.reverse()
|
|
177
|
-
polygons.push(poly3.
|
|
178
|
-
polygons.push(poly3.
|
|
178
|
+
polygons.push(poly3.create(startfacevertices))
|
|
179
|
+
polygons.push(poly3.create(endfacevertices))
|
|
179
180
|
|
|
180
181
|
const cylinder = geom3.create(polygons)
|
|
181
182
|
result = unionGeom3Sub(result, cylinder)
|
|
@@ -17,14 +17,14 @@ const extrudePolygon = (offsetvector, polygon1) => {
|
|
|
17
17
|
const polygon2 = poly3.transform(mat4.fromTranslation(mat4.create(), offsetvector), polygon1)
|
|
18
18
|
const numvertices = polygon1.vertices.length
|
|
19
19
|
for (let i = 0; i < numvertices; i++) {
|
|
20
|
-
const sidefacepoints = []
|
|
21
20
|
const nexti = (i < (numvertices - 1)) ? i + 1 : 0
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
const sideFacePolygon = poly3.create([
|
|
22
|
+
polygon1.vertices[i],
|
|
23
|
+
polygon2.vertices[i],
|
|
24
|
+
polygon2.vertices[nexti],
|
|
25
|
+
polygon1.vertices[nexti]
|
|
26
|
+
])
|
|
27
|
+
newpolygons.push(sideFacePolygon)
|
|
28
28
|
}
|
|
29
29
|
newpolygons.push(poly3.invert(polygon2))
|
|
30
30
|
|
|
@@ -12,6 +12,7 @@ test('offset: offsetting a straight line produces expected geometry', (t) => {
|
|
|
12
12
|
// offset it by 2.
|
|
13
13
|
let offsetLinePath2 = offset({ delta: 2, corners: 'edge', segments: 8 }, linePath2)
|
|
14
14
|
let offsetPoints = path2.toPoints(offsetLinePath2)
|
|
15
|
+
t.notThrows(() => path2.validate(offsetLinePath2))
|
|
15
16
|
t.is(offsetPoints.length, 2)
|
|
16
17
|
let boundingBox = measureBoundingBox(offsetLinePath2)
|
|
17
18
|
t.true(comparePoints(boundingBox, [[2, 0, 0], [2, 10, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
@@ -19,6 +20,7 @@ test('offset: offsetting a straight line produces expected geometry', (t) => {
|
|
|
19
20
|
// offset it by -2.
|
|
20
21
|
offsetLinePath2 = offset({ delta: -2, corners: 'edge', segments: 8 }, linePath2)
|
|
21
22
|
offsetPoints = path2.toPoints(offsetLinePath2)
|
|
23
|
+
t.notThrows(() => path2.validate(offsetLinePath2))
|
|
22
24
|
t.is(offsetPoints.length, 2)
|
|
23
25
|
boundingBox = measureBoundingBox(offsetLinePath2)
|
|
24
26
|
t.true(comparePoints(boundingBox, [[-2, 0, 0], [-2, 10, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
@@ -27,6 +29,7 @@ test('offset: offsetting a straight line produces expected geometry', (t) => {
|
|
|
27
29
|
linePath2 = path2.fromPoints({ closed: false }, points.reverse())
|
|
28
30
|
offsetLinePath2 = offset({ delta: 2, corners: 'edge', segments: 8 }, linePath2)
|
|
29
31
|
offsetPoints = path2.toPoints(offsetLinePath2)
|
|
32
|
+
t.notThrows(() => path2.validate(offsetLinePath2))
|
|
30
33
|
t.is(offsetPoints.length, 2)
|
|
31
34
|
boundingBox = measureBoundingBox(offsetLinePath2)
|
|
32
35
|
t.true(comparePoints(boundingBox, [[-2, 0, 0], [-2, 10, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
@@ -39,6 +42,7 @@ test('offset: offsetting a bent line produces expected geometry', (t) => {
|
|
|
39
42
|
// offset it by 2.
|
|
40
43
|
let offsetLinePath2 = offset({ delta: 2, corners: 'edge', segments: 8 }, linePath2)
|
|
41
44
|
let offsetPoints = path2.toPoints(offsetLinePath2)
|
|
45
|
+
t.notThrows(() => path2.validate(offsetLinePath2))
|
|
42
46
|
t.is(offsetPoints.length, 5)
|
|
43
47
|
let boundingBox = measureBoundingBox(offsetLinePath2)
|
|
44
48
|
t.true(comparePoints(boundingBox, [[2, 0, 0], [10, 8, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
@@ -46,6 +50,7 @@ test('offset: offsetting a bent line produces expected geometry', (t) => {
|
|
|
46
50
|
// offset it by -2.
|
|
47
51
|
offsetLinePath2 = offset({ delta: -2, corners: 'edge', segments: 8 }, linePath2)
|
|
48
52
|
offsetPoints = path2.toPoints(offsetLinePath2)
|
|
53
|
+
t.notThrows(() => path2.validate(offsetLinePath2))
|
|
49
54
|
t.is(offsetPoints.length, 5)
|
|
50
55
|
boundingBox = measureBoundingBox(offsetLinePath2)
|
|
51
56
|
t.true(comparePoints(boundingBox, [[-2, 0, 0], [10, 12, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
@@ -56,6 +61,7 @@ test('offset: offsetting a 2 segment straight line produces expected geometry',
|
|
|
56
61
|
const linePath2 = path2.fromPoints({ closed: false }, points)
|
|
57
62
|
const offsetLinePath2 = offset({ delta: 2, corners: 'edge', segments: 8 }, linePath2)
|
|
58
63
|
const offsetPoints = path2.toPoints(offsetLinePath2)
|
|
64
|
+
t.notThrows(() => path2.validate(offsetLinePath2))
|
|
59
65
|
t.is(offsetPoints.length, 3)
|
|
60
66
|
const boundingBox = measureBoundingBox(offsetLinePath2)
|
|
61
67
|
t.true(comparePoints(boundingBox, [[2, 0, 0], [2, 10, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
@@ -71,6 +77,7 @@ test('offset (corners: chamfer): offset of a path2 produces expected offset path
|
|
|
71
77
|
let pts = path2.toPoints(obs)
|
|
72
78
|
let exp = [
|
|
73
79
|
]
|
|
80
|
+
t.notThrows(() => path2.validate(obs))
|
|
74
81
|
t.true(comparePoints(pts, exp))
|
|
75
82
|
|
|
76
83
|
// expand +
|
|
@@ -82,6 +89,7 @@ test('offset (corners: chamfer): offset of a path2 produces expected offset path
|
|
|
82
89
|
[5.707106781186548, 0.7071067811865475],
|
|
83
90
|
[0.7071067811865475, 5.707106781186548]
|
|
84
91
|
]
|
|
92
|
+
t.notThrows(() => path2.validate(obs))
|
|
85
93
|
t.true(comparePoints(pts, exp))
|
|
86
94
|
|
|
87
95
|
obs = offset({ delta: 1, corners: 'chamfer' }, closeline)
|
|
@@ -94,6 +102,7 @@ test('offset (corners: chamfer): offset of a path2 produces expected offset path
|
|
|
94
102
|
[-1, 5],
|
|
95
103
|
[-1, 6.123233995736766e-17]
|
|
96
104
|
]
|
|
105
|
+
t.notThrows(() => path2.validate(obs))
|
|
97
106
|
t.true(comparePoints(pts, exp))
|
|
98
107
|
|
|
99
108
|
// contract -
|
|
@@ -104,6 +113,7 @@ test('offset (corners: chamfer): offset of a path2 produces expected offset path
|
|
|
104
113
|
[2.5857864376269046, 1],
|
|
105
114
|
[-0.7071067811865475, 4.292893218813452]
|
|
106
115
|
]
|
|
116
|
+
t.notThrows(() => path2.validate(obs))
|
|
107
117
|
t.true(comparePoints(pts, exp))
|
|
108
118
|
|
|
109
119
|
obs = offset({ delta: -1, corners: 'chamfer' }, closeline)
|
|
@@ -113,6 +123,7 @@ test('offset (corners: chamfer): offset of a path2 produces expected offset path
|
|
|
113
123
|
[2.5857864376269046, 1],
|
|
114
124
|
[0.9999999999999996, 2.585786437626905]
|
|
115
125
|
]
|
|
126
|
+
t.notThrows(() => path2.validate(obs))
|
|
116
127
|
t.true(comparePoints(pts, exp))
|
|
117
128
|
})
|
|
118
129
|
|
|
@@ -132,6 +143,7 @@ test('offset (corners: edge): offset of a path2 produces expected offset path2',
|
|
|
132
143
|
[-1.9999999999999996, 6],
|
|
133
144
|
[-5, 6]
|
|
134
145
|
]
|
|
146
|
+
t.notThrows(() => path2.validate(obs))
|
|
135
147
|
t.true(comparePoints(pts, exp))
|
|
136
148
|
|
|
137
149
|
obs = offset({ delta: 1, corners: 'edge' }, closeline)
|
|
@@ -146,6 +158,7 @@ test('offset (corners: edge): offset of a path2 produces expected offset path2',
|
|
|
146
158
|
[-6, 6],
|
|
147
159
|
[-6, -6]
|
|
148
160
|
]
|
|
161
|
+
t.notThrows(() => path2.validate(obs))
|
|
149
162
|
t.true(comparePoints(pts, exp))
|
|
150
163
|
|
|
151
164
|
obs = offset({ delta: -0.5, corners: 'edge' }, openline)
|
|
@@ -160,6 +173,7 @@ test('offset (corners: edge): offset of a path2 produces expected offset path2',
|
|
|
160
173
|
[-3.5, 4.5],
|
|
161
174
|
[-5, 4.5]
|
|
162
175
|
]
|
|
176
|
+
t.notThrows(() => path2.validate(obs))
|
|
163
177
|
t.true(comparePoints(pts, exp))
|
|
164
178
|
|
|
165
179
|
obs = offset({ delta: -0.5, corners: 'edge' }, closeline)
|
|
@@ -174,6 +188,7 @@ test('offset (corners: edge): offset of a path2 produces expected offset path2',
|
|
|
174
188
|
[-3.5, 4.5],
|
|
175
189
|
[-4.5, 4.5]
|
|
176
190
|
]
|
|
191
|
+
t.notThrows(() => path2.validate(obs))
|
|
177
192
|
t.true(comparePoints(pts, exp))
|
|
178
193
|
})
|
|
179
194
|
|
|
@@ -209,6 +224,7 @@ test('offset (corners: round): offset of a path2 produces expected offset path2'
|
|
|
209
224
|
[-3, 6],
|
|
210
225
|
[-5, 6]
|
|
211
226
|
]
|
|
227
|
+
t.notThrows(() => path2.validate(obs))
|
|
212
228
|
t.true(comparePoints(pts, exp))
|
|
213
229
|
|
|
214
230
|
obs = offset({ delta: 1, corners: 'round', segments: 16 }, closeline)
|
|
@@ -247,6 +263,7 @@ test('offset (corners: round): offset of a path2 produces expected offset path2'
|
|
|
247
263
|
[-6, 5],
|
|
248
264
|
[-6, -5]
|
|
249
265
|
]
|
|
266
|
+
t.notThrows(() => path2.validate(obs))
|
|
250
267
|
t.true(comparePoints(pts, exp))
|
|
251
268
|
})
|
|
252
269
|
|
|
@@ -289,6 +306,7 @@ test('offset (corners: round): offset of a CW path2 produces expected offset pat
|
|
|
289
306
|
[5, -6],
|
|
290
307
|
[-5, -6]
|
|
291
308
|
]
|
|
309
|
+
t.notThrows(() => path2.validate(obs))
|
|
292
310
|
t.true(comparePoints(pts, exp))
|
|
293
311
|
})
|
|
294
312
|
|
|
@@ -301,6 +319,7 @@ test('offset (options): offsetting of a simple geom2 produces expected offset ge
|
|
|
301
319
|
let pts = geom2.toPoints(obs)
|
|
302
320
|
let exp = [
|
|
303
321
|
]
|
|
322
|
+
t.notThrows(() => geom2.validate(obs))
|
|
304
323
|
t.true(comparePoints(pts, exp))
|
|
305
324
|
|
|
306
325
|
// expand +
|
|
@@ -322,6 +341,7 @@ test('offset (options): offsetting of a simple geom2 produces expected offset ge
|
|
|
322
341
|
[-6, 5],
|
|
323
342
|
[-6, -5]
|
|
324
343
|
]
|
|
344
|
+
t.notThrows(() => geom2.validate(obs))
|
|
325
345
|
t.true(comparePoints(pts, exp))
|
|
326
346
|
|
|
327
347
|
// contract -
|
|
@@ -339,6 +359,7 @@ test('offset (options): offsetting of a simple geom2 produces expected offset ge
|
|
|
339
359
|
[-3.5, 4.5],
|
|
340
360
|
[-4.5, 4.5]
|
|
341
361
|
]
|
|
362
|
+
t.notThrows(() => geom2.validate(obs))
|
|
342
363
|
t.true(comparePoints(pts, exp))
|
|
343
364
|
|
|
344
365
|
// segments 1 - sharp points at corner
|
|
@@ -354,6 +375,7 @@ test('offset (options): offsetting of a simple geom2 produces expected offset ge
|
|
|
354
375
|
[-6, 6],
|
|
355
376
|
[-6, -6]
|
|
356
377
|
]
|
|
378
|
+
t.notThrows(() => geom2.validate(obs))
|
|
357
379
|
t.true(comparePoints(pts, exp))
|
|
358
380
|
|
|
359
381
|
// segments 16 - rounded corners
|
|
@@ -377,6 +399,7 @@ test('offset (options): offsetting of a simple geom2 produces expected offset ge
|
|
|
377
399
|
[-3.5, 4.5],
|
|
378
400
|
[-4.5, 4.5]
|
|
379
401
|
]
|
|
402
|
+
t.notThrows(() => geom2.validate(obs))
|
|
380
403
|
t.true(comparePoints(pts, exp))
|
|
381
404
|
})
|
|
382
405
|
|
|
@@ -429,6 +452,7 @@ test('offset (options): offsetting of a complex geom2 produces expected offset g
|
|
|
429
452
|
[-4, -13],
|
|
430
453
|
[-77, -77]
|
|
431
454
|
]
|
|
455
|
+
t.notThrows(() => geom2.validate(obs))
|
|
432
456
|
t.is(pts.length, 20)
|
|
433
457
|
t.true(comparePoints(pts, exp))
|
|
434
458
|
})
|
|
@@ -473,6 +497,7 @@ test('offset (options): offsetting of round geom2 produces expected offset geom2
|
|
|
473
497
|
[6.7105900605102855, -6.710590060510285],
|
|
474
498
|
[8.767810140100096, -3.6317399864658024]
|
|
475
499
|
]
|
|
500
|
+
t.notThrows(() => geom2.validate(obs))
|
|
476
501
|
t.is(pts.length, 16)
|
|
477
502
|
t.true(comparePoints(pts, exp))
|
|
478
503
|
})
|
|
@@ -31,6 +31,7 @@ const assignHoles = (geometry) => {
|
|
|
31
31
|
solids.push(i)
|
|
32
32
|
}
|
|
33
33
|
})
|
|
34
|
+
|
|
34
35
|
// for each hole, determine what solids it is inside of
|
|
35
36
|
const children = [] // child holes of solid[i]
|
|
36
37
|
const parents = [] // parent solids of hole[i]
|
|
@@ -47,19 +48,22 @@ const assignHoles = (geometry) => {
|
|
|
47
48
|
}
|
|
48
49
|
})
|
|
49
50
|
})
|
|
51
|
+
|
|
50
52
|
// check if holes have multiple parents and choose one with fewest children
|
|
51
53
|
holes.forEach((h, j) => {
|
|
52
54
|
// ensure at least one parent exists
|
|
53
55
|
if (parents[j] && parents[j].length > 1) {
|
|
54
|
-
|
|
56
|
+
// the solid directly containing this hole
|
|
57
|
+
const directParent = minIndex(parents[j], (p) => children[p].length)
|
|
55
58
|
parents[j].forEach((p, i) => {
|
|
56
|
-
if (i !==
|
|
59
|
+
if (i !== directParent) {
|
|
57
60
|
// Remove hole from skip level parents
|
|
58
|
-
children[p] = children[p].filter((c) => c !==
|
|
61
|
+
children[p] = children[p].filter((c) => c !== h)
|
|
59
62
|
}
|
|
60
63
|
})
|
|
61
64
|
}
|
|
62
65
|
})
|
|
66
|
+
|
|
63
67
|
// map indices back to points
|
|
64
68
|
return children.map((holes, i) => ({
|
|
65
69
|
solid: outlines[solids[i]],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const test = require('ava')
|
|
2
2
|
|
|
3
|
-
const { subtract } = require('../../../operations/booleans')
|
|
4
|
-
const
|
|
3
|
+
const { subtract, union } = require('../../../operations/booleans')
|
|
4
|
+
const square = require('../../../primitives/square')
|
|
5
5
|
const assignHoles = require('./assignHoles')
|
|
6
6
|
|
|
7
7
|
test('slice: assignHoles() should return a polygon hierarchy', (t) => {
|
|
@@ -20,9 +20,55 @@ test('slice: assignHoles() should return a polygon hierarchy', (t) => {
|
|
|
20
20
|
]]
|
|
21
21
|
}]
|
|
22
22
|
const geometry = subtract(
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
square({ size: 6 }),
|
|
24
|
+
square({ size: 4 })
|
|
25
25
|
)
|
|
26
26
|
const obs1 = assignHoles(geometry)
|
|
27
27
|
t.deepEqual(obs1, exp1)
|
|
28
28
|
})
|
|
29
|
+
|
|
30
|
+
test('slice: assignHoles() should handle nested holes', (t) => {
|
|
31
|
+
const geometry = union(
|
|
32
|
+
subtract(
|
|
33
|
+
square({ size: 6 }),
|
|
34
|
+
square({ size: 4 })
|
|
35
|
+
),
|
|
36
|
+
subtract(
|
|
37
|
+
square({ size: 10 }),
|
|
38
|
+
square({ size: 8 })
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
const obs1 = assignHoles(geometry)
|
|
42
|
+
|
|
43
|
+
const exp1 = [
|
|
44
|
+
{
|
|
45
|
+
solid: [
|
|
46
|
+
[-3.0000006060444444, -3.0000006060444444],
|
|
47
|
+
[3.0000006060444444, -3.0000006060444444],
|
|
48
|
+
[3.0000006060444444, 3.0000006060444444],
|
|
49
|
+
[-3.0000006060444444, 3.0000006060444444]
|
|
50
|
+
],
|
|
51
|
+
holes: [[
|
|
52
|
+
[-2.0000248485333336, 2.0000248485333336],
|
|
53
|
+
[2.0000248485333336, 2.0000248485333336],
|
|
54
|
+
[2.0000248485333336, -2.0000248485333336],
|
|
55
|
+
[-2.0000248485333336, -2.0000248485333336]
|
|
56
|
+
]]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
solid: [
|
|
60
|
+
[-5.000025454577778, -5.000025454577778],
|
|
61
|
+
[5.000025454577778, -5.000025454577778],
|
|
62
|
+
[5.000025454577778, 5.000025454577778],
|
|
63
|
+
[-5.000025454577778, 5.000025454577778]
|
|
64
|
+
],
|
|
65
|
+
holes: [[
|
|
66
|
+
[-3.9999763635555556, 3.9999763635555556],
|
|
67
|
+
[3.9999763635555556, 3.9999763635555556],
|
|
68
|
+
[3.9999763635555556, -3.9999763635555556],
|
|
69
|
+
[-3.9999763635555556, -3.9999763635555556]
|
|
70
|
+
]]
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
t.deepEqual(obs1, exp1)
|
|
74
|
+
})
|
|
@@ -32,10 +32,11 @@ test('extrudeFromSlices (defaults)', (t) => {
|
|
|
32
32
|
t.is(pts.length, 12)
|
|
33
33
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
34
34
|
|
|
35
|
-
const poly2 = poly3.
|
|
35
|
+
const poly2 = poly3.create([[10, 10, 0], [-10, 10, 0], [-10, -10, 0], [10, -10, 0]])
|
|
36
36
|
geometry3 = extrudeFromSlices({ }, poly2)
|
|
37
37
|
pts = geom3.toPoints(geometry3)
|
|
38
38
|
|
|
39
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
39
40
|
t.is(pts.length, 12)
|
|
40
41
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
41
42
|
})
|
|
@@ -44,7 +45,7 @@ test('extrudeFromSlices (torus)', (t) => {
|
|
|
44
45
|
const sqrt3 = Math.sqrt(3) / 2
|
|
45
46
|
const radius = 10
|
|
46
47
|
|
|
47
|
-
let hex = poly3.
|
|
48
|
+
let hex = poly3.create([
|
|
48
49
|
[radius, 0, 0],
|
|
49
50
|
[radius / 2, radius * sqrt3, 0],
|
|
50
51
|
[-radius / 2, radius * sqrt3, 0],
|
|
@@ -68,6 +69,7 @@ test('extrudeFromSlices (torus)', (t) => {
|
|
|
68
69
|
}, hex
|
|
69
70
|
)
|
|
70
71
|
const pts = geom3.toPoints(geometry3)
|
|
72
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
71
73
|
t.is(pts.length, 96)
|
|
72
74
|
})
|
|
73
75
|
|
|
@@ -86,6 +88,8 @@ test('extrudeFromSlices (same shape, changing dimensions)', (t) => {
|
|
|
86
88
|
}, base
|
|
87
89
|
)
|
|
88
90
|
const pts = geom3.toPoints(geometry3)
|
|
91
|
+
// expected to throw because capEnd is false (non-closed geometry)
|
|
92
|
+
t.throws(() => geom3.validate(geometry3))
|
|
89
93
|
t.is(pts.length, 26)
|
|
90
94
|
})
|
|
91
95
|
|
|
@@ -103,20 +107,21 @@ test('extrudeFromSlices (changing shape, changing dimensions)', (t) => {
|
|
|
103
107
|
}, base
|
|
104
108
|
)
|
|
105
109
|
const pts = geom3.toPoints(geometry3)
|
|
110
|
+
t.notThrows.skip(() => geom3.validate(geometry3))
|
|
106
111
|
t.is(pts.length, 304)
|
|
107
112
|
})
|
|
108
113
|
|
|
109
114
|
test('extrudeFromSlices (holes)', (t) => {
|
|
110
115
|
const geometry2 = geom2.create(
|
|
111
116
|
[
|
|
112
|
-
[[-10
|
|
113
|
-
[[-10
|
|
114
|
-
[[10
|
|
115
|
-
[[10
|
|
116
|
-
[[-5
|
|
117
|
-
[[5
|
|
118
|
-
[[5
|
|
119
|
-
[[-5
|
|
117
|
+
[[-10, 10], [-10, -10]],
|
|
118
|
+
[[-10, -10], [10, -10]],
|
|
119
|
+
[[10, -10], [10, 10]],
|
|
120
|
+
[[10, 10], [-10, 10]],
|
|
121
|
+
[[-5, -5], [-5, 5]],
|
|
122
|
+
[[5, -5], [-5, -5]],
|
|
123
|
+
[[5, 5], [5, -5]],
|
|
124
|
+
[[-5, 5], [5, 5]]
|
|
120
125
|
]
|
|
121
126
|
)
|
|
122
127
|
const geometry3 = extrudeFromSlices({ }, geometry2)
|
|
@@ -155,6 +160,7 @@ test('extrudeFromSlices (holes)', (t) => {
|
|
|
155
160
|
[[-5, -5, 0], [5, -5, 0], [-10, -10, 0]],
|
|
156
161
|
[[-10, -10, 0], [-10, 10, 0], [-5, -5, 0]]
|
|
157
162
|
]
|
|
163
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
158
164
|
t.is(pts.length, 32)
|
|
159
165
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
160
166
|
})
|
|
@@ -25,6 +25,7 @@ test('extrudeLinear (defaults)', (t) => {
|
|
|
25
25
|
[[5, 5, 0], [5, -5, 0], [-5, -5, 0]],
|
|
26
26
|
[[-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
|
|
27
27
|
]
|
|
28
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
28
29
|
t.is(pts.length, 12)
|
|
29
30
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
30
31
|
})
|
|
@@ -48,6 +49,7 @@ test('extrudeLinear (no twist)', (t) => {
|
|
|
48
49
|
[[5, 5, 0], [5, -5, 0], [-5, -5, 0]],
|
|
49
50
|
[[-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
|
|
50
51
|
]
|
|
52
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
51
53
|
t.is(pts.length, 12)
|
|
52
54
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
53
55
|
|
|
@@ -67,6 +69,7 @@ test('extrudeLinear (no twist)', (t) => {
|
|
|
67
69
|
[[5, -5, 0], [5, 5, 0], [-5, 5, 0]],
|
|
68
70
|
[[-5, 5, 0], [-5, -5, 0], [5, -5, 0]]
|
|
69
71
|
]
|
|
72
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
70
73
|
t.is(pts.length, 12)
|
|
71
74
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
72
75
|
})
|
|
@@ -98,6 +101,7 @@ test('extrudeLinear (twist)', (t) => {
|
|
|
98
101
|
[[5, 5, 0], [5, -5, 0], [-5, -5, 0]],
|
|
99
102
|
[[-5, -5, 0], [-5, 5, 0], [5, 5, 0]]
|
|
100
103
|
]
|
|
104
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
101
105
|
t.is(pts.length, 12)
|
|
102
106
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
103
107
|
|
|
@@ -138,19 +142,20 @@ test('extrudeLinear (twist)', (t) => {
|
|
|
138
142
|
|
|
139
143
|
geometry3 = extrudeLinear({ height: 15, twistAngle: Math.PI / 2, twistSteps: 30 }, geometry2)
|
|
140
144
|
pts = geom3.toPoints(geometry3)
|
|
145
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
141
146
|
t.is(pts.length, 244)
|
|
142
147
|
})
|
|
143
148
|
|
|
144
149
|
test('extrudeLinear (holes)', (t) => {
|
|
145
150
|
const geometry2 = geom2.create([
|
|
146
|
-
[[-5
|
|
147
|
-
[[-5
|
|
148
|
-
[[5
|
|
149
|
-
[[5
|
|
150
|
-
[[-2
|
|
151
|
-
[[2
|
|
152
|
-
[[2
|
|
153
|
-
[[-2
|
|
151
|
+
[[-5, 5], [-5, -5]],
|
|
152
|
+
[[-5, -5], [5, -5]],
|
|
153
|
+
[[5, -5], [5, 5]],
|
|
154
|
+
[[5, 5], [-5, 5]],
|
|
155
|
+
[[-2, -2], [-2, 2]],
|
|
156
|
+
[[2, -2], [-2, -2]],
|
|
157
|
+
[[2, 2], [2, -2]],
|
|
158
|
+
[[-2, 2], [2, 2]]
|
|
154
159
|
])
|
|
155
160
|
const geometry3 = extrudeLinear({ height: 15 }, geometry2)
|
|
156
161
|
const pts = geom3.toPoints(geometry3)
|
|
@@ -188,6 +193,7 @@ test('extrudeLinear (holes)', (t) => {
|
|
|
188
193
|
[[-2, -2, 0], [2, -2, 0], [-5, -5, 0]],
|
|
189
194
|
[[-5, -5, 0], [-5, 5, 0], [-2, -2, 0]]
|
|
190
195
|
]
|
|
196
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
191
197
|
t.is(pts.length, 32)
|
|
192
198
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
193
199
|
})
|
|
@@ -195,7 +201,7 @@ test('extrudeLinear (holes)', (t) => {
|
|
|
195
201
|
test('extrudeLinear (path2)', (t) => {
|
|
196
202
|
const geometry2 = path2.fromPoints({ closed: true }, [[0, 0], [12, 0], [6, 10]])
|
|
197
203
|
const geometry3 = extrudeLinear({ height: 15 }, geometry2)
|
|
198
|
-
t.
|
|
204
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
199
205
|
const pts = geom3.toPoints(geometry3)
|
|
200
206
|
const exp = [
|
|
201
207
|
[[6, 10, 0], [0, 0, 0], [0, 0, 15]],
|
|
@@ -12,10 +12,12 @@ test('extrudeRectangular (defaults)', (t) => {
|
|
|
12
12
|
|
|
13
13
|
let obs = extrudeRectangular({ }, geometry1)
|
|
14
14
|
let pts = geom3.toPoints(obs)
|
|
15
|
+
t.notThrows(() => geom3.validate(obs))
|
|
15
16
|
t.is(pts.length, 44)
|
|
16
17
|
|
|
17
18
|
obs = extrudeRectangular({ }, geometry2)
|
|
18
19
|
pts = geom3.toPoints(obs)
|
|
20
|
+
t.notThrows(() => geom3.validate(obs))
|
|
19
21
|
t.is(pts.length, 32)
|
|
20
22
|
})
|
|
21
23
|
|
|
@@ -25,10 +27,12 @@ test('extrudeRectangular (chamfer)', (t) => {
|
|
|
25
27
|
|
|
26
28
|
let obs = extrudeRectangular({ corners: 'chamfer' }, geometry1)
|
|
27
29
|
let pts = geom3.toPoints(obs)
|
|
30
|
+
t.notThrows(() => geom3.validate(obs))
|
|
28
31
|
t.is(pts.length, 60)
|
|
29
32
|
|
|
30
33
|
obs = extrudeRectangular({ corners: 'chamfer' }, geometry2)
|
|
31
34
|
pts = geom3.toPoints(obs)
|
|
35
|
+
t.notThrows(() => geom3.validate(obs))
|
|
32
36
|
t.is(pts.length, 48)
|
|
33
37
|
})
|
|
34
38
|
|
|
@@ -38,26 +42,29 @@ test('extrudeRectangular (segments = 8, round)', (t) => {
|
|
|
38
42
|
|
|
39
43
|
let obs = extrudeRectangular({ segments: 8, corners: 'round' }, geometry1)
|
|
40
44
|
let pts = geom3.toPoints(obs)
|
|
45
|
+
t.notThrows(() => geom3.validate(obs))
|
|
41
46
|
t.is(pts.length, 84)
|
|
42
47
|
|
|
43
48
|
obs = extrudeRectangular({ segments: 8, corners: 'round' }, geometry2)
|
|
44
49
|
pts = geom3.toPoints(obs)
|
|
50
|
+
t.notThrows(() => geom3.validate(obs))
|
|
45
51
|
t.is(pts.length, 64)
|
|
46
52
|
})
|
|
47
53
|
|
|
48
54
|
test('extrudeRectangular (holes)', (t) => {
|
|
49
55
|
const geometry2 = geom2.create([
|
|
50
|
-
[[15
|
|
51
|
-
[[-15
|
|
52
|
-
[[-15
|
|
53
|
-
[[15
|
|
54
|
-
[[-5
|
|
55
|
-
[[5
|
|
56
|
-
[[5
|
|
57
|
-
[[-5
|
|
56
|
+
[[15, 15], [-15, 15]],
|
|
57
|
+
[[-15, 15], [-15, -15]],
|
|
58
|
+
[[-15, -15], [15, -15]],
|
|
59
|
+
[[15, -15], [15, 15]],
|
|
60
|
+
[[-5, 5], [5, 5]],
|
|
61
|
+
[[5, 5], [5, -5]],
|
|
62
|
+
[[5, -5], [-5, -5]],
|
|
63
|
+
[[-5, -5], [-5, 5]]
|
|
58
64
|
])
|
|
59
65
|
|
|
60
66
|
const obs = extrudeRectangular({ size: 2, height: 15, segments: 16, corners: 'round' }, geometry2)
|
|
61
67
|
const pts = geom3.toPoints(obs)
|
|
68
|
+
t.notThrows(() => geom3.validate(obs))
|
|
62
69
|
t.is(pts.length, 192)
|
|
63
70
|
})
|
|
@@ -114,7 +114,11 @@ const extrudeRotate = (options, geometry) => {
|
|
|
114
114
|
|
|
115
115
|
const matrix = mat4.create()
|
|
116
116
|
const createSlice = (progress, index, base) => {
|
|
117
|
-
|
|
117
|
+
let Zrotation = rotationPerSlice * index + startAngle
|
|
118
|
+
// fix rounding error when rotating 2 * PI radians
|
|
119
|
+
if (totalRotation === Math.PI * 2 && index === segments) {
|
|
120
|
+
Zrotation = startAngle
|
|
121
|
+
}
|
|
118
122
|
mat4.multiply(matrix, mat4.fromZRotation(matrix, Zrotation), mat4.fromXRotation(mat4.create(), Math.PI / 2))
|
|
119
123
|
|
|
120
124
|
return slice.transform(matrix, base)
|