@jscad/modeling 2.12.2 → 2.12.4
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 +22 -0
- package/dist/jscad-modeling.min.js +112 -109
- package/package.json +2 -2
- package/src/geometries/geom3/fromPointsConvex.test.js +20 -15
- package/src/geometries/poly3/type.d.ts +2 -0
- package/src/maths/OrthoNormalBasis.js +0 -148
- package/src/operations/booleans/scission.d.ts +1 -2
- package/src/operations/hulls/hull.test.js +9 -1
- package/src/operations/hulls/hullGeom2.js +3 -0
- package/src/operations/hulls/hullGeom3.js +7 -12
- package/src/operations/hulls/hullPath2.js +4 -1
- package/src/operations/hulls/hullPoints2.d.ts +5 -0
- package/src/operations/hulls/hullPoints2.js +4 -2
- package/src/operations/hulls/hullPoints3.d.ts +6 -0
- package/src/operations/hulls/hullPoints3.js +23 -0
- package/src/operations/hulls/index.d.ts +2 -0
- package/src/operations/hulls/index.js +3 -1
- package/src/primitives/polygon.js +2 -2
- package/src/primitives/polygon.test.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jscad/modeling",
|
|
3
|
-
"version": "2.12.
|
|
3
|
+
"version": "2.12.4",
|
|
4
4
|
"description": "Constructive Solid Geometry (CSG) Library for JSCAD",
|
|
5
5
|
"homepage": "https://openjscad.xyz/",
|
|
6
6
|
"repository": "https://github.com/jscad/OpenJSCAD.org",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"nyc": "15.1.0",
|
|
62
62
|
"uglifyify": "5.0.2"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "ce4978ee40c30803cba05d1c96ba8b6aaf3abc61"
|
|
65
65
|
}
|
|
@@ -3,24 +3,29 @@ const test = require('ava')
|
|
|
3
3
|
const { fromPointsConvex, validate } = require('./index')
|
|
4
4
|
|
|
5
5
|
test('fromPointsConvex (uniquePoints)', (t) => {
|
|
6
|
-
|
|
7
|
-
for(x
|
|
8
|
-
for(y
|
|
9
|
-
|
|
10
|
-
if (x*x+y*y+z*z <= 96)
|
|
11
|
-
|
|
6
|
+
const out = []
|
|
7
|
+
for (let x = -9; x <= 9; ++x) {
|
|
8
|
+
for (let y = -9; y <= 9; ++y) {
|
|
9
|
+
for (let z = -9; z <= 9; ++z) {
|
|
10
|
+
if (x * x + y * y + z * z <= 96) out.push([x, y, z])
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
const obs = fromPointsConvex(out)
|
|
14
16
|
validate(obs)
|
|
17
|
+
|
|
15
18
|
t.is(obs.polygons.length, 170)
|
|
16
|
-
t.true(obs.polygons.every((f) => ([3,4,8,9].indexOf(f.vertices.length) !== -1)))
|
|
17
|
-
|
|
19
|
+
t.true(obs.polygons.every((f) => ([3, 4, 8, 9].indexOf(f.vertices.length) !== -1)))
|
|
20
|
+
|
|
21
|
+
const c = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
18
22
|
obs.polygons.forEach((f) => c[f.vertices.length]++)
|
|
19
|
-
t.is(c[3], 120)
|
|
20
|
-
t.is(c[4], 24)
|
|
21
|
-
t.is(c[8], 18)
|
|
22
|
-
t.is(c[9], 8)
|
|
23
|
-
|
|
23
|
+
t.is(c[3], 120)
|
|
24
|
+
t.is(c[4], 24)
|
|
25
|
+
t.is(c[8], 18)
|
|
26
|
+
t.is(c[9], 8)
|
|
27
|
+
|
|
28
|
+
let edges2 = 336 * 2
|
|
24
29
|
obs.polygons.forEach((f) => edges2 -= f.vertices.length)
|
|
25
|
-
t.is(edges2, 0)
|
|
30
|
+
t.is(edges2, 0)
|
|
26
31
|
})
|
|
@@ -21,128 +21,6 @@ const OrthoNormalBasis = function (plane, rightvector) {
|
|
|
21
21
|
this.planeorigin = vec3.scale(vec3.create(), plane, plane[3])
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// Get an orthonormal basis for the standard XYZ planes.
|
|
25
|
-
// Parameters: the names of two 3D axes. The 2d x axis will map to the first given 3D axis, the 2d y
|
|
26
|
-
// axis will map to the second.
|
|
27
|
-
// Prepend the axis with a "-" to invert the direction of this axis.
|
|
28
|
-
// For example: OrthoNormalBasis.GetCartesian("-Y","Z")
|
|
29
|
-
// will return an orthonormal basis where the 2d X axis maps to the 3D inverted Y axis, and
|
|
30
|
-
// the 2d Y axis maps to the 3D Z axis.
|
|
31
|
-
OrthoNormalBasis.GetCartesian = function (xaxisid, yaxisid) {
|
|
32
|
-
const axisid = xaxisid + '/' + yaxisid
|
|
33
|
-
let planenormal, rightvector
|
|
34
|
-
if (axisid === 'X/Y') {
|
|
35
|
-
planenormal = [0, 0, 1]
|
|
36
|
-
rightvector = [1, 0, 0]
|
|
37
|
-
} else if (axisid === 'Y/-X') {
|
|
38
|
-
planenormal = [0, 0, 1]
|
|
39
|
-
rightvector = [0, 1, 0]
|
|
40
|
-
} else if (axisid === '-X/-Y') {
|
|
41
|
-
planenormal = [0, 0, 1]
|
|
42
|
-
rightvector = [-1, 0, 0]
|
|
43
|
-
} else if (axisid === '-Y/X') {
|
|
44
|
-
planenormal = [0, 0, 1]
|
|
45
|
-
rightvector = [0, -1, 0]
|
|
46
|
-
} else if (axisid === '-X/Y') {
|
|
47
|
-
planenormal = [0, 0, -1]
|
|
48
|
-
rightvector = [-1, 0, 0]
|
|
49
|
-
} else if (axisid === '-Y/-X') {
|
|
50
|
-
planenormal = [0, 0, -1]
|
|
51
|
-
rightvector = [0, -1, 0]
|
|
52
|
-
} else if (axisid === 'X/-Y') {
|
|
53
|
-
planenormal = [0, 0, -1]
|
|
54
|
-
rightvector = [1, 0, 0]
|
|
55
|
-
} else if (axisid === 'Y/X') {
|
|
56
|
-
planenormal = [0, 0, -1]
|
|
57
|
-
rightvector = [0, 1, 0]
|
|
58
|
-
} else if (axisid === 'X/Z') {
|
|
59
|
-
planenormal = [0, -1, 0]
|
|
60
|
-
rightvector = [1, 0, 0]
|
|
61
|
-
} else if (axisid === 'Z/-X') {
|
|
62
|
-
planenormal = [0, -1, 0]
|
|
63
|
-
rightvector = [0, 0, 1]
|
|
64
|
-
} else if (axisid === '-X/-Z') {
|
|
65
|
-
planenormal = [0, -1, 0]
|
|
66
|
-
rightvector = [-1, 0, 0]
|
|
67
|
-
} else if (axisid === '-Z/X') {
|
|
68
|
-
planenormal = [0, -1, 0]
|
|
69
|
-
rightvector = [0, 0, -1]
|
|
70
|
-
} else if (axisid === '-X/Z') {
|
|
71
|
-
planenormal = [0, 1, 0]
|
|
72
|
-
rightvector = [-1, 0, 0]
|
|
73
|
-
} else if (axisid === '-Z/-X') {
|
|
74
|
-
planenormal = [0, 1, 0]
|
|
75
|
-
rightvector = [0, 0, -1]
|
|
76
|
-
} else if (axisid === 'X/-Z') {
|
|
77
|
-
planenormal = [0, 1, 0]
|
|
78
|
-
rightvector = [1, 0, 0]
|
|
79
|
-
} else if (axisid === 'Z/X') {
|
|
80
|
-
planenormal = [0, 1, 0]
|
|
81
|
-
rightvector = [0, 0, 1]
|
|
82
|
-
} else if (axisid === 'Y/Z') {
|
|
83
|
-
planenormal = [1, 0, 0]
|
|
84
|
-
rightvector = [0, 1, 0]
|
|
85
|
-
} else if (axisid === 'Z/-Y') {
|
|
86
|
-
planenormal = [1, 0, 0]
|
|
87
|
-
rightvector = [0, 0, 1]
|
|
88
|
-
} else if (axisid === '-Y/-Z') {
|
|
89
|
-
planenormal = [1, 0, 0]
|
|
90
|
-
rightvector = [0, -1, 0]
|
|
91
|
-
} else if (axisid === '-Z/Y') {
|
|
92
|
-
planenormal = [1, 0, 0]
|
|
93
|
-
rightvector = [0, 0, -1]
|
|
94
|
-
} else if (axisid === '-Y/Z') {
|
|
95
|
-
planenormal = [-1, 0, 0]
|
|
96
|
-
rightvector = [0, -1, 0]
|
|
97
|
-
} else if (axisid === '-Z/-Y') {
|
|
98
|
-
planenormal = [-1, 0, 0]
|
|
99
|
-
rightvector = [0, 0, -1]
|
|
100
|
-
} else if (axisid === 'Y/-Z') {
|
|
101
|
-
planenormal = [-1, 0, 0]
|
|
102
|
-
rightvector = [0, 1, 0]
|
|
103
|
-
} else if (axisid === 'Z/Y') {
|
|
104
|
-
planenormal = [-1, 0, 0]
|
|
105
|
-
rightvector = [0, 0, 1]
|
|
106
|
-
} else {
|
|
107
|
-
throw new Error('OrthoNormalBasis.GetCartesian: invalid combination of axis identifiers. Should pass two string arguments from [X,Y,Z,-X,-Y,-Z], being two different axes.')
|
|
108
|
-
}
|
|
109
|
-
return new OrthoNormalBasis(new Plane(new Vector3D(planenormal), 0), new Vector3D(rightvector))
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/*
|
|
113
|
-
// test code for OrthoNormalBasis.GetCartesian()
|
|
114
|
-
OrthoNormalBasis.GetCartesian_Test=function() {
|
|
115
|
-
let axisnames=["X","Y","Z","-X","-Y","-Z"];
|
|
116
|
-
let axisvectors=[[1,0,0], [0,1,0], [0,0,1], [-1,0,0], [0,-1,0], [0,0,-1]];
|
|
117
|
-
for(let axis1=0; axis1 < 3; axis1++) {
|
|
118
|
-
for(let axis1inverted=0; axis1inverted < 2; axis1inverted++) {
|
|
119
|
-
let axis1name=axisnames[axis1+3*axis1inverted];
|
|
120
|
-
let axis1vector=axisvectors[axis1+3*axis1inverted];
|
|
121
|
-
for(let axis2=0; axis2 < 3; axis2++) {
|
|
122
|
-
if(axis2 != axis1) {
|
|
123
|
-
for(let axis2inverted=0; axis2inverted < 2; axis2inverted++) {
|
|
124
|
-
let axis2name=axisnames[axis2+3*axis2inverted];
|
|
125
|
-
let axis2vector=axisvectors[axis2+3*axis2inverted];
|
|
126
|
-
let orthobasis=OrthoNormalBasis.GetCartesian(axis1name, axis2name);
|
|
127
|
-
let test1=orthobasis.to3D(new Vector2D([1,0]));
|
|
128
|
-
let test2=orthobasis.to3D(new Vector2D([0,1]));
|
|
129
|
-
let expected1=new Vector3D(axis1vector);
|
|
130
|
-
let expected2=new Vector3D(axis2vector);
|
|
131
|
-
let d1=test1.distanceTo(expected1);
|
|
132
|
-
let d2=test2.distanceTo(expected2);
|
|
133
|
-
if( (d1 > 0.01) || (d2 > 0.01) ) {
|
|
134
|
-
throw new Error("Wrong!");
|
|
135
|
-
}}}}}}
|
|
136
|
-
throw new Error("OK");
|
|
137
|
-
};
|
|
138
|
-
*/
|
|
139
|
-
|
|
140
|
-
// The z=0 plane, with the 3D x and y vectors mapped to the 2D x and y vector
|
|
141
|
-
OrthoNormalBasis.Z0Plane = function () {
|
|
142
|
-
const plane = new Plane(new Vector3D([0, 0, 1]), 0)
|
|
143
|
-
return new OrthoNormalBasis(plane, new Vector3D([1, 0, 0]))
|
|
144
|
-
}
|
|
145
|
-
|
|
146
24
|
OrthoNormalBasis.prototype = {
|
|
147
25
|
|
|
148
26
|
getProjectionMatrix: function () {
|
|
@@ -175,32 +53,6 @@ OrthoNormalBasis.prototype = {
|
|
|
175
53
|
const v3 = vec3.add(v1, v1, this.planeorigin)
|
|
176
54
|
const v4 = vec3.add(v2, v2, v3)
|
|
177
55
|
return v4
|
|
178
|
-
},
|
|
179
|
-
|
|
180
|
-
line3Dto2D: function (line3d) {
|
|
181
|
-
const a = line3d.point
|
|
182
|
-
const b = line3d.direction.plus(a)
|
|
183
|
-
const a2d = this.to2D(a)
|
|
184
|
-
const b2d = this.to2D(b)
|
|
185
|
-
return Line2D.fromPoints(a2d, b2d)
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
line2Dto3D: function (line2d) {
|
|
189
|
-
const a = line2d.origin()
|
|
190
|
-
const b = line2d.direction().plus(a)
|
|
191
|
-
const a3d = this.to3D(a)
|
|
192
|
-
const b3d = this.to3D(b)
|
|
193
|
-
return Line3D.fromPoints(a3d, b3d)
|
|
194
|
-
},
|
|
195
|
-
|
|
196
|
-
transform: function (matrix4x4) {
|
|
197
|
-
// todo: this may not work properly in case of mirroring
|
|
198
|
-
const newplane = this.plane.transform(matrix4x4)
|
|
199
|
-
const rightpointTransformed = this.u.transform(matrix4x4)
|
|
200
|
-
const originTransformed = new Vector3D(0, 0, 0).transform(matrix4x4)
|
|
201
|
-
const newrighthandvector = rightpointTransformed.minus(originTransformed)
|
|
202
|
-
const newbasis = new OrthoNormalBasis(newplane, newrighthandvector)
|
|
203
|
-
return newbasis
|
|
204
56
|
}
|
|
205
57
|
}
|
|
206
58
|
|
|
@@ -3,5 +3,4 @@ import RecursiveArray from '../../utils/recursiveArray'
|
|
|
3
3
|
|
|
4
4
|
export default scission
|
|
5
5
|
|
|
6
|
-
declare function scission(...geometries: RecursiveArray<
|
|
7
|
-
declare function scission(...geometries: RecursiveArray<Geom3>): Geom3
|
|
6
|
+
declare function scission(...geometries: RecursiveArray<Geom3>): Geom3[]
|
|
@@ -2,7 +2,9 @@ const test = require('ava')
|
|
|
2
2
|
|
|
3
3
|
const { geom2, geom3, path2 } = require('../../geometries')
|
|
4
4
|
|
|
5
|
-
const {
|
|
5
|
+
const { measureVolume } = require('../../measurements')
|
|
6
|
+
|
|
7
|
+
const { sphere, cuboid, ellipsoid, torus } = require('../../primitives')
|
|
6
8
|
|
|
7
9
|
const { center } = require('../transforms/center')
|
|
8
10
|
|
|
@@ -274,3 +276,9 @@ test('hull (multiple, overlapping, geom3)', (t) => {
|
|
|
274
276
|
t.notThrows(() => geom3.validate(obs))
|
|
275
277
|
t.is(pts.length, 92)
|
|
276
278
|
})
|
|
279
|
+
|
|
280
|
+
test('hull (single, unconvex, geom3)', (t) => {
|
|
281
|
+
const geometry = torus()
|
|
282
|
+
const obs = hull(geometry)
|
|
283
|
+
t.assert(measureVolume(obs) > measureVolume(geometry))
|
|
284
|
+
})
|
|
@@ -7,6 +7,9 @@ const toUniquePoints = require('./toUniquePoints')
|
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
9
|
* Create a convex hull of the given geom2 geometries.
|
|
10
|
+
*
|
|
11
|
+
* NOTE: The given geometries must be valid geom2 geometries.
|
|
12
|
+
*
|
|
10
13
|
* @param {...geometries} geometries - list of geom2 geometries
|
|
11
14
|
* @returns {geom2} new geometry
|
|
12
15
|
*/
|
|
@@ -1,32 +1,27 @@
|
|
|
1
1
|
const flatten = require('../../utils/flatten')
|
|
2
2
|
|
|
3
3
|
const geom3 = require('../../geometries/geom3')
|
|
4
|
-
const poly3 = require('../../geometries/poly3')
|
|
5
4
|
|
|
6
|
-
const quickhull = require('./quickhull')
|
|
7
5
|
const toUniquePoints = require('./toUniquePoints')
|
|
6
|
+
const hullPoints3 = require('./hullPoints3')
|
|
8
7
|
|
|
9
8
|
/*
|
|
10
|
-
* Create a convex hull of the given geometries
|
|
9
|
+
* Create a convex hull of the given geom3 geometries.
|
|
10
|
+
*
|
|
11
|
+
* NOTE: The given geometries must be valid geom3 geometries.
|
|
12
|
+
*
|
|
11
13
|
* @param {...geometries} geometries - list of geom3 geometries
|
|
12
14
|
* @returns {geom3} new geometry
|
|
13
15
|
*/
|
|
14
16
|
const hullGeom3 = (...geometries) => {
|
|
15
17
|
geometries = flatten(geometries)
|
|
16
18
|
|
|
17
|
-
if (geometries.length === 1) return geometries[0]
|
|
18
|
-
|
|
19
19
|
// extract the unique vertices from the geometries
|
|
20
20
|
const unique = toUniquePoints(geometries)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const polygons = faces.map((face) => {
|
|
25
|
-
const vertices = face.map((index) => unique[index])
|
|
26
|
-
return poly3.create(vertices)
|
|
27
|
-
})
|
|
22
|
+
if (unique.length === 0) return geom3.create()
|
|
28
23
|
|
|
29
|
-
return geom3.create(
|
|
24
|
+
return geom3.create(hullPoints3(unique))
|
|
30
25
|
}
|
|
31
26
|
|
|
32
27
|
module.exports = hullGeom3
|
|
@@ -6,7 +6,10 @@ const hullPoints2 = require('./hullPoints2')
|
|
|
6
6
|
const toUniquePoints = require('./toUniquePoints')
|
|
7
7
|
|
|
8
8
|
/*
|
|
9
|
-
* Create a convex hull of the given geometries
|
|
9
|
+
* Create a convex hull of the given path2 geometries.
|
|
10
|
+
*
|
|
11
|
+
* NOTE: The given geometries must be valid path2 geometry.
|
|
12
|
+
*
|
|
10
13
|
* @param {...geometries} geometries - list of path2 geometries
|
|
11
14
|
* @returns {path2} new geometry
|
|
12
15
|
*/
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const vec2 = require('../../maths/vec2')
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
4
|
* Create a convex hull of the given set of points, where each point is an array of [x,y].
|
|
5
|
-
*
|
|
5
|
+
* @see https://en.wikipedia.org/wiki/Graham_scan
|
|
6
|
+
*
|
|
6
7
|
* @param {Array} uniquePoints - list of UNIQUE points from which to create a hull
|
|
7
8
|
* @returns {Array} a list of points that form the hull
|
|
9
|
+
* @alias module:modeling/hulls.hullPoints2
|
|
8
10
|
*/
|
|
9
11
|
const hullPoints2 = (uniquePoints) => {
|
|
10
12
|
// find min point
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const poly3 = require('../../geometries/poly3')
|
|
2
|
+
|
|
3
|
+
const quickhull = require('./quickhull')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Create a convex hull of the given set of points, where each point is an array of [x,y,z].
|
|
7
|
+
*
|
|
8
|
+
* @param {Array} uniquePoints - list of UNIQUE points from which to create a hull
|
|
9
|
+
* @returns {Array} a list of polygons (poly3)
|
|
10
|
+
* @alias module:modeling/hulls.hullPoints3
|
|
11
|
+
*/
|
|
12
|
+
const hullPoints3 = (uniquePoints) => {
|
|
13
|
+
const faces = quickhull(uniquePoints, { skipTriangulation: true })
|
|
14
|
+
|
|
15
|
+
const polygons = faces.map((face) => {
|
|
16
|
+
const vertices = face.map((index) => uniquePoints[index])
|
|
17
|
+
return poly3.create(vertices)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
return polygons
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = hullPoints3
|
|
@@ -71,8 +71,8 @@ const polygon = (options) => {
|
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
// convert the list of sides into a geometry
|
|
74
|
-
let geometry =
|
|
75
|
-
if (orientation
|
|
74
|
+
let geometry = geom2.create(sides)
|
|
75
|
+
if (orientation === 'clockwise') {
|
|
76
76
|
geometry = geom2.reverse(geometry)
|
|
77
77
|
}
|
|
78
78
|
return geometry
|
|
@@ -56,7 +56,7 @@ test('polygon: providing object.points (array) and object.path (array) creates e
|
|
|
56
56
|
test('polygon: clockwise points', (t) => {
|
|
57
57
|
const poly = polygon({
|
|
58
58
|
points: [[-10, -0], [-10, -10], [-15, -5]],
|
|
59
|
-
orientation:
|
|
59
|
+
orientation: 'clockwise'
|
|
60
60
|
})
|
|
61
61
|
t.is(poly.sides.length, 3)
|
|
62
62
|
t.is(measureArea(poly), 25)
|