@jscad/modeling 2.9.4 → 2.10.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 +50 -0
- package/README.md +12 -2
- package/dist/jscad-modeling.min.js +148 -151
- package/package.json +2 -2
- package/src/colors/colorize.d.ts +6 -5
- package/src/geometries/geom2/type.d.ts +3 -2
- package/src/geometries/geom3/type.d.ts +3 -2
- package/src/geometries/path2/appendArc.js +6 -5
- package/src/geometries/path2/appendArc.test.js +3 -1
- package/src/geometries/path2/appendBezier.js +2 -1
- package/src/geometries/path2/appendPoints.js +3 -12
- package/src/geometries/path2/appendPoints.test.js +16 -0
- package/src/geometries/path2/concat.js +9 -8
- package/src/geometries/path2/concat.test.js +13 -7
- package/src/geometries/path2/transform.js +1 -1
- package/src/geometries/path2/type.d.ts +3 -2
- package/src/geometries/poly3/measureBoundingSphere.d.ts +2 -2
- package/src/geometries/poly3/measureBoundingSphere.js +46 -8
- package/src/geometries/poly3/measureBoundingSphere.test.js +16 -26
- package/src/geometries/poly3/type.d.ts +3 -2
- package/src/geometries/types.d.ts +4 -2
- package/src/index.d.ts +1 -0
- package/src/maths/constants.js +10 -0
- package/src/maths/mat4/fromRotation.js +10 -8
- package/src/maths/mat4/fromTaitBryanRotation.js +9 -7
- package/src/maths/mat4/fromXRotation.js +5 -3
- package/src/maths/mat4/fromYRotation.js +5 -3
- package/src/maths/mat4/fromZRotation.js +5 -3
- package/src/maths/mat4/invert.test.js +5 -2
- package/src/maths/mat4/isOnlyTransformScale.js +1 -1
- package/src/maths/mat4/isOnlyTransformScale.test.js +3 -1
- package/src/maths/mat4/rotate.js +9 -5
- package/src/maths/mat4/rotateX.js +4 -2
- package/src/maths/mat4/rotateY.js +4 -2
- package/src/maths/mat4/rotateZ.js +4 -2
- package/src/maths/mat4/translate.test.js +2 -3
- package/src/maths/rotation.test.js +5 -2
- package/src/maths/utils/index.d.ts +1 -0
- package/src/maths/utils/index.js +2 -0
- package/src/{utils → maths/utils}/trigonometry.d.ts +0 -0
- package/src/{utils → maths/utils}/trigonometry.js +7 -7
- package/src/{utils → maths/utils}/trigonometry.test.js +10 -8
- package/src/maths/vec2/distance.js +1 -1
- package/src/maths/vec2/fromAngleDegrees.js +1 -1
- package/src/maths/vec2/fromAngleRadians.js +4 -2
- package/src/maths/vec2/fromAngleRadians.test.js +4 -1
- package/src/maths/vec2/length.js +1 -1
- package/src/maths/vec2/length.test.js +0 -10
- package/src/maths/vec2/normal.js +3 -1
- package/src/maths/vec2/rotate.test.js +4 -1
- package/src/maths/vec3/angle.js +2 -2
- package/src/maths/vec3/angle.test.js +0 -12
- package/src/maths/vec3/distance.js +1 -1
- package/src/maths/vec3/length.js +1 -1
- package/src/maths/vec3/length.test.js +0 -10
- package/src/maths/vec3/rotateX.test.js +4 -1
- package/src/maths/vec3/rotateY.test.js +4 -1
- package/src/maths/vec3/rotateZ.test.js +4 -1
- package/src/operations/booleans/trees/PolygonTreeNode.js +2 -2
- package/src/operations/expansions/expand.test.js +3 -1
- package/src/operations/expansions/expandShell.js +4 -4
- package/src/operations/expansions/offsetFromPoints.js +2 -2
- package/src/operations/extrusions/extrudeFromSlices.test.js +3 -2
- package/src/operations/extrusions/extrudeLinear.test.js +5 -3
- package/src/operations/extrusions/extrudeRectangular.js +1 -1
- package/src/operations/extrusions/extrudeRectangular.test.js +5 -3
- package/src/operations/extrusions/extrudeRotate.js +14 -13
- package/src/operations/extrusions/extrudeRotate.test.js +49 -47
- package/src/operations/extrusions/project.test.js +2 -2
- package/src/operations/extrusions/slice/calculatePlane.test.js +3 -2
- package/src/operations/extrusions/slice/index.js +2 -0
- package/src/operations/extrusions/slice/repair.js +1 -1
- package/src/operations/modifiers/generalize.d.ts +12 -0
- package/src/operations/modifiers/generalize.test.js +3 -1
- package/src/operations/modifiers/index.d.ts +2 -0
- package/src/operations/modifiers/insertTjunctions.js +34 -35
- package/src/operations/modifiers/snap.d.ts +6 -0
- package/src/operations/modifiers/snap.test.js +5 -3
- package/src/operations/transforms/rotate.js +1 -1
- package/src/operations/transforms/rotate.test.js +13 -11
- package/src/operations/transforms/transform.js +1 -1
- package/src/primitives/arc.js +8 -8
- package/src/primitives/arc.test.js +9 -8
- package/src/primitives/circle.js +4 -2
- package/src/primitives/circle.test.js +12 -4
- package/src/primitives/cylinderElliptic.js +13 -11
- package/src/primitives/cylinderElliptic.test.js +9 -2
- package/src/primitives/ellipse.js +11 -11
- package/src/primitives/ellipse.test.js +12 -4
- package/src/primitives/ellipsoid.js +4 -3
- package/src/primitives/geodesicSphere.js +3 -2
- package/src/primitives/roundedCuboid.js +10 -8
- package/src/primitives/roundedCylinder.js +4 -4
- package/src/primitives/roundedRectangle.js +5 -5
- package/src/primitives/star.js +3 -2
- package/src/primitives/torus.js +4 -2
- package/src/primitives/torus.test.js +11 -5
- package/src/primitives/triangle.test.js +2 -1
- package/src/utils/degToRad.test.js +5 -5
- package/src/utils/index.d.ts +0 -1
- package/src/utils/index.js +1 -3
- package/src/utils/radToDeg.test.js +6 -6
- package/src/utils/radiusToSegments.js +6 -4
- package/src/utils/radiusToSegments.test.js +5 -3
- package/src/maths/mat4/constants.d.ts +0 -1
- package/src/maths/mat4/constants.js +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jscad/modeling",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
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": "13572067545460affd53b64b3ab834a39af8c7e5"
|
|
65
65
|
}
|
package/src/colors/colorize.d.ts
CHANGED
|
@@ -5,9 +5,10 @@ import { RGB, RGBA } from './types'
|
|
|
5
5
|
|
|
6
6
|
export default colorize
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
declare function colorize<T>(color: RGB | RGBA, object: T): T & Colored
|
|
8
|
+
// Single Geom3 returns Colored Geom3
|
|
9
|
+
declare function colorize<T extends Geometry>(color: RGB | RGBA, object: T): T & Colored
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
declare function colorize<T>(color: RGB | RGBA, ...objects: RecursiveArray<T>): Array<T & Colored>
|
|
13
|
-
|
|
11
|
+
// List of Geom3 returns list of Colored Geom3
|
|
12
|
+
declare function colorize<T extends Geometry>(color: RGB | RGBA, ...objects: RecursiveArray<T>): Array<T & Colored>
|
|
13
|
+
// List of mixed geometries returns list of colored geometries
|
|
14
|
+
declare function colorize(color: RGB | RGBA, ...objects: RecursiveArray<Geometry>): Array<Geometry & Colored>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import Vec2 from '../../maths/vec2/type'
|
|
2
2
|
import Mat4 from '../../maths/mat4/type'
|
|
3
|
-
import {
|
|
3
|
+
import { Color } from '../types'
|
|
4
4
|
|
|
5
5
|
export default Geom2
|
|
6
6
|
|
|
7
|
-
declare interface Geom2
|
|
7
|
+
declare interface Geom2 {
|
|
8
8
|
sides: Array<[Vec2, Vec2]>
|
|
9
9
|
transforms: Mat4
|
|
10
|
+
color?: Color
|
|
10
11
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import Poly3 from '../poly3/type'
|
|
2
2
|
import Mat4 from '../../maths/mat4/type'
|
|
3
|
-
import {
|
|
3
|
+
import { Color } from '../types'
|
|
4
4
|
|
|
5
5
|
export default Geom3
|
|
6
6
|
|
|
7
|
-
declare interface Geom3
|
|
7
|
+
declare interface Geom3 {
|
|
8
8
|
polygons: Array<Poly3>
|
|
9
9
|
transforms: Mat4
|
|
10
|
+
color?: Color
|
|
10
11
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { TAU } = require('../../maths/constants')
|
|
1
2
|
const vec2 = require('../../maths/vec2')
|
|
2
3
|
|
|
3
4
|
const fromPoints = require('./fromPoints')
|
|
@@ -12,7 +13,7 @@ const toPoints = require('./toPoints')
|
|
|
12
13
|
* @param {vec2} [options.radius=[0,0]] - radius of arc (X and Y)
|
|
13
14
|
* @param {Number} [options.xaxisrotation=0] - rotation (RADIANS) of the X axis of the arc with respect to the X axis of the coordinate system
|
|
14
15
|
* @param {Boolean} [options.clockwise=false] - draw an arc clockwise with respect to the center point
|
|
15
|
-
* @param {Boolean} [options.large=false] - draw an arc longer than
|
|
16
|
+
* @param {Boolean} [options.large=false] - draw an arc longer than TAU / 2 radians
|
|
16
17
|
* @param {Number} [options.segments=16] - number of segments per full rotation
|
|
17
18
|
* @param {path2} geometry - the path of which to append the arc
|
|
18
19
|
* @returns {path2} a new path with the appended points
|
|
@@ -111,15 +112,15 @@ const appendArc = (options, geometry) => {
|
|
|
111
112
|
const theta1 = vec2.angleRadians(vector1)
|
|
112
113
|
const theta2 = vec2.angleRadians(vector2)
|
|
113
114
|
let deltatheta = theta2 - theta1
|
|
114
|
-
deltatheta = deltatheta %
|
|
115
|
+
deltatheta = deltatheta % TAU
|
|
115
116
|
if ((!sweepFlag) && (deltatheta > 0)) {
|
|
116
|
-
deltatheta -=
|
|
117
|
+
deltatheta -= TAU
|
|
117
118
|
} else if ((sweepFlag) && (deltatheta < 0)) {
|
|
118
|
-
deltatheta +=
|
|
119
|
+
deltatheta += TAU
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
// Ok, we have the center point and angle range (from theta1, deltatheta radians) so we can create the ellipse
|
|
122
|
-
let numsteps = Math.ceil(Math.abs(deltatheta) /
|
|
123
|
+
let numsteps = Math.ceil(Math.abs(deltatheta) / TAU * segments) + 1
|
|
123
124
|
if (numsteps < 1) numsteps = 1
|
|
124
125
|
for (let step = 1; step < numsteps; step++) {
|
|
125
126
|
const theta = theta1 + step / numsteps * deltatheta
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const test = require('ava')
|
|
2
2
|
|
|
3
|
+
const { TAU } = require('../../maths/constants')
|
|
4
|
+
|
|
3
5
|
const { appendArc, fromPoints, toPoints } = require('./index')
|
|
4
6
|
|
|
5
7
|
const { comparePoints } = require('../../../test/helpers/')
|
|
@@ -48,7 +50,7 @@ test('appendArc: appending to a path produces a new path', (t) => {
|
|
|
48
50
|
t.is(pts.length, 16)
|
|
49
51
|
|
|
50
52
|
// test xaxisrotation
|
|
51
|
-
obs = appendArc({ endpoint: [12, -22], radius: [15, -20], xaxisrotation:
|
|
53
|
+
obs = appendArc({ endpoint: [12, -22], radius: [15, -20], xaxisrotation: TAU / 4 }, p2)
|
|
52
54
|
pts = toPoints(obs)
|
|
53
55
|
exp = [
|
|
54
56
|
[27, -22],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { TAU } = require('../../maths/constants')
|
|
1
2
|
const vec2 = require('../../maths/vec2')
|
|
2
3
|
const vec3 = require('../../maths/vec2')
|
|
3
4
|
|
|
@@ -117,7 +118,7 @@ const appendBezier = (options, geometry) => {
|
|
|
117
118
|
|
|
118
119
|
// subdivide each segment until the angle at each vertex becomes small enough:
|
|
119
120
|
let subdivideBase = 1
|
|
120
|
-
const maxangle =
|
|
121
|
+
const maxangle = TAU / segments
|
|
121
122
|
const maxsinangle = Math.sin(maxangle)
|
|
122
123
|
while (subdivideBase < newpoints.length - 1) {
|
|
123
124
|
const dir1 = vec2.subtract(v0, newpoints[subdivideBase], newpoints[subdivideBase - 1])
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
1
|
+
const concat = require('./concat')
|
|
2
|
+
const create = require('./create')
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Append the given list of points to the end of the given geometry.
|
|
@@ -10,15 +10,6 @@ const toPoints = require('./toPoints')
|
|
|
10
10
|
* @example
|
|
11
11
|
* let newpath = appendPoints([[3, 4], [4, 5]], oldpath)
|
|
12
12
|
*/
|
|
13
|
-
const appendPoints = (points, geometry) =>
|
|
14
|
-
if (geometry.isClosed) {
|
|
15
|
-
throw new Error('cannot append points to a closed path')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let newpoints = toPoints(geometry)
|
|
19
|
-
newpoints = newpoints.concat(points)
|
|
20
|
-
|
|
21
|
-
return fromPoints({}, newpoints)
|
|
22
|
-
}
|
|
13
|
+
const appendPoints = (points, geometry) => concat(geometry, create(points))
|
|
23
14
|
|
|
24
15
|
module.exports = appendPoints
|
|
@@ -17,3 +17,19 @@ test('appendPoints: appending to a path produces a new path with expected points
|
|
|
17
17
|
t.not(p1, obs)
|
|
18
18
|
t.is(pts.length, 4)
|
|
19
19
|
})
|
|
20
|
+
|
|
21
|
+
test('appendPoints: appending empty points to a path produces a new path with expected points', (t) => {
|
|
22
|
+
const p1 = fromPoints({}, [[1, 1], [2, 2]])
|
|
23
|
+
const obs = appendPoints([], p1)
|
|
24
|
+
const pts = toPoints(obs)
|
|
25
|
+
t.not(p1, obs)
|
|
26
|
+
t.is(pts.length, 2)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('appendPoints: appending same points to a path produces a new path with expected points', (t) => {
|
|
30
|
+
const p1 = fromPoints({}, [[1, 1], [2, 2]])
|
|
31
|
+
const obs = appendPoints([[2, 2], [3, 3]], p1)
|
|
32
|
+
const pts = toPoints(obs)
|
|
33
|
+
t.not(p1, obs)
|
|
34
|
+
t.is(pts.length, 3)
|
|
35
|
+
})
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
const fromPoints = require('./fromPoints')
|
|
2
2
|
const toPoints = require('./toPoints')
|
|
3
|
+
|
|
3
4
|
const { equals } = require('../../maths/vec2')
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Concatenate the given paths.
|
|
8
|
+
*
|
|
6
9
|
* If both contain the same point at the junction, merge it into one.
|
|
7
10
|
* A concatenation of zero paths is an empty, open path.
|
|
8
11
|
* A concatenation of one closed path to a series of open paths produces a closed path.
|
|
@@ -17,16 +20,14 @@ const { equals } = require('../../maths/vec2')
|
|
|
17
20
|
const concat = (...paths) => {
|
|
18
21
|
// Only the last path can be closed, producing a closed path.
|
|
19
22
|
let isClosed = false
|
|
20
|
-
for (const path of paths) {
|
|
21
|
-
if (isClosed) {
|
|
22
|
-
throw new Error('Cannot concatenate to a closed path')
|
|
23
|
-
}
|
|
24
|
-
isClosed = path.isClosed
|
|
25
|
-
}
|
|
26
23
|
let newpoints = []
|
|
27
|
-
paths.forEach((path) => {
|
|
28
|
-
const tmp = toPoints(path)
|
|
24
|
+
paths.forEach((path, i) => {
|
|
25
|
+
const tmp = toPoints(path).slice()
|
|
29
26
|
if (newpoints.length > 0 && tmp.length > 0 && equals(tmp[0], newpoints[newpoints.length - 1])) tmp.shift()
|
|
27
|
+
if (tmp.length > 0 && isClosed) {
|
|
28
|
+
throw new Error(`Cannot concatenate to a closed path; check the ${i}th path`)
|
|
29
|
+
}
|
|
30
|
+
isClosed = path.isClosed
|
|
30
31
|
newpoints = newpoints.concat(tmp)
|
|
31
32
|
})
|
|
32
33
|
return fromPoints({ closed: isClosed }, newpoints)
|
|
@@ -10,10 +10,16 @@ test('concat: empty paths produces an empty open path', (t) => {
|
|
|
10
10
|
t.true(equals(concat(fromPoints({}, []), fromPoints({}, [])), fromPoints({ closed: false }, [])))
|
|
11
11
|
})
|
|
12
12
|
|
|
13
|
-
test('concat:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
fromPoints({ closed: false }, [[
|
|
13
|
+
test('concat: many open paths produces a open path', (t) => {
|
|
14
|
+
const p1 = fromPoints({ closed: false }, [[0, 0]])
|
|
15
|
+
const p2 = fromPoints({ closed: false }, [[1, 1]])
|
|
16
|
+
const p3 = fromPoints({ closed: false }, [[1, 1], [3, 3]])
|
|
17
|
+
|
|
18
|
+
const result = concat(p1, p2, p3)
|
|
19
|
+
t.true(equals(result, fromPoints({}, [[0, 0], [1, 1], [3, 3]])))
|
|
20
|
+
t.is(p1.points.length, 1)
|
|
21
|
+
t.is(p2.points.length, 1)
|
|
22
|
+
t.is(p3.points.length, 2)
|
|
17
23
|
})
|
|
18
24
|
|
|
19
25
|
test('concat: An open path and a closed path produces a closed path', (t) => {
|
|
@@ -23,7 +29,7 @@ test('concat: An open path and a closed path produces a closed path', (t) => {
|
|
|
23
29
|
})
|
|
24
30
|
|
|
25
31
|
test('concat: A closed path and an open path throws an error', (t) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{ message: 'Cannot concatenate to a closed path' })
|
|
32
|
+
const p1 = fromPoints({ closed: true }, [[0, 0]])
|
|
33
|
+
const p2 = fromPoints({ closed: false }, [[1, 1]])
|
|
34
|
+
t.throws(() => concat(p1, p2), { message: 'Cannot concatenate to a closed path; check the 1th path' })
|
|
29
35
|
})
|
|
@@ -10,7 +10,7 @@ const mat4 = require('../../maths/mat4')
|
|
|
10
10
|
* @alias module:modeling/geometries/path2.transform
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
|
-
* let newpath = transform(fromZRotation(
|
|
13
|
+
* let newpath = transform(fromZRotation(TAU / 8), path)
|
|
14
14
|
*/
|
|
15
15
|
const transform = (matrix, geometry) => {
|
|
16
16
|
const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import Vec2 from '../../maths/vec2/type'
|
|
2
2
|
import Mat4 from '../../maths/mat4/type'
|
|
3
|
-
import {
|
|
3
|
+
import { Color } from '../types'
|
|
4
4
|
|
|
5
5
|
export default Path2
|
|
6
6
|
|
|
7
|
-
declare interface Path2
|
|
7
|
+
declare interface Path2 {
|
|
8
8
|
points: Array<Vec2>
|
|
9
9
|
isClosed: boolean
|
|
10
10
|
transforms: Mat4
|
|
11
|
+
color?: Color
|
|
11
12
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Poly3 from './type'
|
|
2
|
-
import
|
|
2
|
+
import Vec4 from '../../maths/vec4/type'
|
|
3
3
|
|
|
4
4
|
export default measureBoundingSphere
|
|
5
5
|
|
|
6
|
-
declare function measureBoundingSphere(polygon: Poly3):
|
|
6
|
+
declare function measureBoundingSphere(polygon: Poly3): Vec4
|
|
@@ -1,19 +1,57 @@
|
|
|
1
1
|
const vec3 = require('../../maths/vec3')
|
|
2
|
-
const
|
|
2
|
+
const vec4 = require('../../maths/vec4')
|
|
3
|
+
|
|
4
|
+
const cache = new WeakMap()
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Measure the bounding sphere of the given polygon.
|
|
6
8
|
* @param {poly3} polygon - the polygon to measure
|
|
7
|
-
* @returns {
|
|
9
|
+
* @returns {vec4} the computed bounding sphere; center point (3D) and radius
|
|
8
10
|
* @alias module:modeling/geometries/poly3.measureBoundingSphere
|
|
9
11
|
*/
|
|
10
12
|
const measureBoundingSphere = (polygon) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
13
|
+
let boundingSphere = cache.get(polygon)
|
|
14
|
+
if (boundingSphere) return boundingSphere
|
|
15
|
+
|
|
16
|
+
const vertices = polygon.vertices
|
|
17
|
+
const out = vec4.create()
|
|
18
|
+
|
|
19
|
+
if (vertices.length === 0) {
|
|
20
|
+
out[0] = 0
|
|
21
|
+
out[1] = 0
|
|
22
|
+
out[2] = 0
|
|
23
|
+
out[3] = 0
|
|
24
|
+
return out
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// keep a list of min/max vertices by axis
|
|
28
|
+
let minx = vertices[0]
|
|
29
|
+
let miny = minx
|
|
30
|
+
let minz = minx
|
|
31
|
+
let maxx = minx
|
|
32
|
+
let maxy = minx
|
|
33
|
+
let maxz = minx
|
|
34
|
+
|
|
35
|
+
vertices.forEach((v) => {
|
|
36
|
+
if (minx[0] > v[0]) minx = v
|
|
37
|
+
if (miny[1] > v[1]) miny = v
|
|
38
|
+
if (minz[2] > v[2]) minz = v
|
|
39
|
+
if (maxx[0] < v[0]) maxx = v
|
|
40
|
+
if (maxy[1] < v[1]) maxy = v
|
|
41
|
+
if (maxz[2] < v[2]) maxz = v
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
out[0] = (minx[0] + maxx[0]) * 0.5 // center of sphere
|
|
45
|
+
out[1] = (miny[1] + maxy[1]) * 0.5
|
|
46
|
+
out[2] = (minz[2] + maxz[2]) * 0.5
|
|
47
|
+
const x = out[0] - maxx[0]
|
|
48
|
+
const y = out[1] - maxy[1]
|
|
49
|
+
const z = out[2] - maxz[2]
|
|
50
|
+
out[3] = Math.sqrt(x * x + y * y + z * z) // radius of sphere
|
|
51
|
+
|
|
52
|
+
cache.set(polygon, out)
|
|
53
|
+
|
|
54
|
+
return out
|
|
17
55
|
}
|
|
18
56
|
|
|
19
57
|
module.exports = measureBoundingSphere
|
|
@@ -3,28 +3,23 @@ const { measureBoundingSphere, create, fromPoints, transform } = require('./inde
|
|
|
3
3
|
|
|
4
4
|
const mat4 = require('../../maths/mat4')
|
|
5
5
|
|
|
6
|
-
const { compareVectors, nearlyEqual } = require('../../../test/helpers/index')
|
|
7
|
-
|
|
8
6
|
test('poly3: measureBoundingSphere() should return correct values', (t) => {
|
|
9
7
|
let ply1 = create()
|
|
10
|
-
let exp1 = [
|
|
8
|
+
let exp1 = [0, 0, 0, 0]
|
|
11
9
|
let ret1 = measureBoundingSphere(ply1)
|
|
12
|
-
t.
|
|
13
|
-
nearlyEqual(t, ret1[1], exp1[1], Number.EPSILON)
|
|
10
|
+
t.deepEqual(ret1, exp1)
|
|
14
11
|
|
|
15
12
|
// simple triangle
|
|
16
13
|
let ply2 = fromPoints([[0, 0, 0], [0, 10, 0], [0, 10, 10]])
|
|
17
|
-
let exp2 = [
|
|
14
|
+
let exp2 = [0, 5, 5, 7.0710678118654755]
|
|
18
15
|
let ret2 = measureBoundingSphere(ply2)
|
|
19
|
-
t.
|
|
20
|
-
nearlyEqual(t, ret2[1], exp2[1], Number.EPSILON)
|
|
16
|
+
t.deepEqual(ret2, exp2)
|
|
21
17
|
|
|
22
18
|
// simple square
|
|
23
19
|
let ply3 = fromPoints([[0, 0, 0], [0, 10, 0], [0, 10, 10], [0, 0, 10]])
|
|
24
|
-
let exp3 = [
|
|
20
|
+
let exp3 = [0, 5, 5, 7.0710678118654755]
|
|
25
21
|
let ret3 = measureBoundingSphere(ply3)
|
|
26
|
-
t.
|
|
27
|
-
nearlyEqual(t, ret3[1], exp3[1], Number.EPSILON)
|
|
22
|
+
t.deepEqual(ret3, exp3)
|
|
28
23
|
|
|
29
24
|
// V-shape
|
|
30
25
|
const points = [
|
|
@@ -40,10 +35,9 @@ test('poly3: measureBoundingSphere() should return correct values', (t) => {
|
|
|
40
35
|
[0, 3, 3]
|
|
41
36
|
]
|
|
42
37
|
let ply4 = fromPoints(points)
|
|
43
|
-
let exp4 = [
|
|
38
|
+
let exp4 = [0, 4.5, 3, 4.6097722286464435]
|
|
44
39
|
let ret4 = measureBoundingSphere(ply4)
|
|
45
|
-
t.
|
|
46
|
-
nearlyEqual(t, ret4[1], exp4[1], Number.EPSILON)
|
|
40
|
+
t.deepEqual(ret4, exp4)
|
|
47
41
|
|
|
48
42
|
// rotated to various angles
|
|
49
43
|
const rotation = mat4.fromZRotation(mat4.create(), (45 * 0.017453292519943295))
|
|
@@ -55,16 +49,12 @@ test('poly3: measureBoundingSphere() should return correct values', (t) => {
|
|
|
55
49
|
ret2 = measureBoundingSphere(ply2)
|
|
56
50
|
ret3 = measureBoundingSphere(ply3)
|
|
57
51
|
ret4 = measureBoundingSphere(ply4)
|
|
58
|
-
exp1 = [
|
|
59
|
-
t.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
t.
|
|
66
|
-
nearlyEqual(t, ret3[1], exp3[1], Number.EPSILON)
|
|
67
|
-
exp4 = [[-3.181980515339464, 3.1819805153394642, 3], 4.6097722286464435]
|
|
68
|
-
t.true(compareVectors(ret4[0], exp4[0]))
|
|
69
|
-
nearlyEqual(t, ret4[1], exp4[1], Number.EPSILON)
|
|
52
|
+
exp1 = [0, 0, 0, 0]
|
|
53
|
+
t.deepEqual(ret1, exp1)
|
|
54
|
+
exp2 = [-3.5355339059327373, 3.5355339059327378, 5, 7.0710678118654755]
|
|
55
|
+
t.deepEqual(ret2, exp2)
|
|
56
|
+
exp3 = [-3.5355339059327373, 3.5355339059327378, 5, 7.0710678118654755]
|
|
57
|
+
t.deepEqual(ret3, exp3)
|
|
58
|
+
exp4 = [-3.181980515339464, 3.1819805153394642, 3, 4.6097722286464435]
|
|
59
|
+
t.deepEqual(ret4, exp4)
|
|
70
60
|
})
|
|
@@ -8,8 +8,10 @@ import { RGB, RGBA } from '../colors'
|
|
|
8
8
|
// see https://github.com/jscad/OpenJSCAD.org/pull/726#issuecomment-724575265
|
|
9
9
|
export type Geometry = Geom2 | Geom3 | Poly3 | Path2
|
|
10
10
|
|
|
11
|
-
export type
|
|
12
|
-
|
|
11
|
+
export type Color = RGB | RGBA
|
|
12
|
+
|
|
13
|
+
export interface Colored {
|
|
14
|
+
color: Color
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export { default as Geom2 } from './geom2/type'
|
package/src/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * as booleans from './operations/booleans'
|
|
|
11
11
|
export * as expansions from './operations/expansions'
|
|
12
12
|
export * as extrusions from './operations/extrusions'
|
|
13
13
|
export * as hulls from './operations/hulls'
|
|
14
|
+
export * as modifiers from './operations/modifiers'
|
|
14
15
|
export * as transforms from './operations/transforms'
|
|
15
16
|
|
|
16
17
|
export as namespace modeling
|
package/src/maths/constants.js
CHANGED
|
@@ -24,8 +24,18 @@ const NEPS = 1e-13
|
|
|
24
24
|
// for comparing coplanar polygons, as provided by the sphere primitive at high
|
|
25
25
|
// segmentation. NEPS is for 64 bit Number values.
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* The TAU property represents the ratio of the circumference of a circle to its radius.
|
|
29
|
+
* Approximately 6.28318530717958647692
|
|
30
|
+
* @default
|
|
31
|
+
* @example
|
|
32
|
+
* const { TAU } = require('@jscad/modeling').maths.constants
|
|
33
|
+
*/
|
|
34
|
+
const TAU = Math.PI * 2
|
|
35
|
+
|
|
27
36
|
module.exports = {
|
|
28
37
|
EPS,
|
|
29
38
|
NEPS,
|
|
39
|
+
TAU,
|
|
30
40
|
spatialResolution
|
|
31
41
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { EPS } = require('../constants')
|
|
2
|
+
|
|
3
|
+
const { sin, cos } = require('../utils/trigonometry')
|
|
2
4
|
|
|
3
|
-
const
|
|
5
|
+
const identity = require('./identity')
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Creates a matrix from a given angle around a given axis
|
|
@@ -15,24 +17,24 @@ const { EPSILON } = require('./constants')
|
|
|
15
17
|
* @returns {mat4} out
|
|
16
18
|
* @alias module:modeling/maths/mat4.fromRotation
|
|
17
19
|
* @example
|
|
18
|
-
* let matrix = fromRotation(create(),
|
|
20
|
+
* let matrix = fromRotation(create(), TAU / 4, [0, 0, 3])
|
|
19
21
|
*/
|
|
20
22
|
const fromRotation = (out, rad, axis) => {
|
|
21
23
|
let [x, y, z] = axis
|
|
22
|
-
|
|
24
|
+
const lengthSquared = x * x + y * y + z * z
|
|
23
25
|
|
|
24
|
-
if (Math.abs(
|
|
26
|
+
if (Math.abs(lengthSquared) < EPS) {
|
|
25
27
|
// axis is 0,0,0 or almost
|
|
26
28
|
return identity(out)
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
len = 1 /
|
|
31
|
+
const len = 1 / Math.sqrt(lengthSquared)
|
|
30
32
|
x *= len
|
|
31
33
|
y *= len
|
|
32
34
|
z *= len
|
|
33
35
|
|
|
34
|
-
const s =
|
|
35
|
-
const c =
|
|
36
|
+
const s = sin(rad)
|
|
37
|
+
const c = cos(rad)
|
|
36
38
|
const t = 1 - c
|
|
37
39
|
|
|
38
40
|
// Perform rotation-specific matrix multiplication
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { sin, cos } = require('../utils/trigonometry')
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Creates a matrix from the given Tait–Bryan angles.
|
|
3
5
|
*
|
|
@@ -11,16 +13,16 @@
|
|
|
11
13
|
* @returns {mat4} out
|
|
12
14
|
* @alias module:modeling/maths/mat4.fromTaitBryanRotation
|
|
13
15
|
* @example
|
|
14
|
-
* let matrix = fromTaitBryanRotation(create(),
|
|
16
|
+
* let matrix = fromTaitBryanRotation(create(), TAU / 4, 0, TAU / 2)
|
|
15
17
|
*/
|
|
16
18
|
const fromTaitBryanRotation = (out, yaw, pitch, roll) => {
|
|
17
19
|
// precompute sines and cosines of Euler angles
|
|
18
|
-
const sy =
|
|
19
|
-
const cy =
|
|
20
|
-
const sp =
|
|
21
|
-
const cp =
|
|
22
|
-
const sr =
|
|
23
|
-
const cr =
|
|
20
|
+
const sy = sin(yaw)
|
|
21
|
+
const cy = cos(yaw)
|
|
22
|
+
const sp = sin(pitch)
|
|
23
|
+
const cp = cos(pitch)
|
|
24
|
+
const sr = sin(roll)
|
|
25
|
+
const cr = cos(roll)
|
|
24
26
|
|
|
25
27
|
// create and populate rotation matrix
|
|
26
28
|
// left-hand-rule rotation
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { sin, cos } = require('../utils/trigonometry')
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Creates a matrix from the given angle around the X axis.
|
|
3
5
|
* This is equivalent to (but much faster than):
|
|
@@ -10,11 +12,11 @@
|
|
|
10
12
|
* @returns {mat4} out
|
|
11
13
|
* @alias module:modeling/maths/mat4.fromXRotation
|
|
12
14
|
* @example
|
|
13
|
-
* let matrix = fromXRotation(create(),
|
|
15
|
+
* let matrix = fromXRotation(create(), TAU / 4)
|
|
14
16
|
*/
|
|
15
17
|
const fromXRotation = (out, radians) => {
|
|
16
|
-
const s =
|
|
17
|
-
const c =
|
|
18
|
+
const s = sin(radians)
|
|
19
|
+
const c = cos(radians)
|
|
18
20
|
|
|
19
21
|
// Perform axis-specific matrix multiplication
|
|
20
22
|
out[0] = 1
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { sin, cos } = require('../utils/trigonometry')
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Creates a matrix from the given angle around the Y axis.
|
|
3
5
|
* This is equivalent to (but much faster than):
|
|
@@ -10,11 +12,11 @@
|
|
|
10
12
|
* @returns {mat4} out
|
|
11
13
|
* @alias module:modeling/maths/mat4.fromYRotation
|
|
12
14
|
* @example
|
|
13
|
-
* let matrix = fromYRotation(create(),
|
|
15
|
+
* let matrix = fromYRotation(create(), TAU / 4)
|
|
14
16
|
*/
|
|
15
17
|
const fromYRotation = (out, radians) => {
|
|
16
|
-
const s =
|
|
17
|
-
const c =
|
|
18
|
+
const s = sin(radians)
|
|
19
|
+
const c = cos(radians)
|
|
18
20
|
|
|
19
21
|
// Perform axis-specific matrix multiplication
|
|
20
22
|
out[0] = c
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { sin, cos } = require('../utils/trigonometry')
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Creates a matrix from the given angle around the Z axis.
|
|
3
5
|
* This is equivalent to (but much faster than):
|
|
@@ -10,11 +12,11 @@
|
|
|
10
12
|
* @returns {mat4} out
|
|
11
13
|
* @alias module:modeling/maths/mat4.fromZRotation
|
|
12
14
|
* @example
|
|
13
|
-
* let matrix = fromZRotation(create(),
|
|
15
|
+
* let matrix = fromZRotation(create(), TAU / 4)
|
|
14
16
|
*/
|
|
15
17
|
const fromZRotation = (out, radians) => {
|
|
16
|
-
const s =
|
|
17
|
-
const c =
|
|
18
|
+
const s = sin(radians)
|
|
19
|
+
const c = cos(radians)
|
|
18
20
|
|
|
19
21
|
// Perform axis-specific matrix multiplication
|
|
20
22
|
out[0] = c
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
const test = require('ava')
|
|
2
|
+
|
|
3
|
+
const { TAU } = require('../constants')
|
|
4
|
+
const vec3 = require('../vec3/index')
|
|
5
|
+
|
|
2
6
|
const { create, invert, fromTranslation, fromXRotation } = require('./index')
|
|
3
7
|
|
|
4
8
|
const { compareVectors } = require('../../../test/helpers/index')
|
|
5
|
-
const vec3 = require('../vec3/index')
|
|
6
9
|
|
|
7
10
|
test('mat4: invert() translate ', (t) => {
|
|
8
11
|
const matrix = fromTranslation(create(), [10, 10, 0])
|
|
@@ -17,7 +20,7 @@ test('mat4: invert() translate ', (t) => {
|
|
|
17
20
|
})
|
|
18
21
|
|
|
19
22
|
test('mat4: invert() rotate ', (t) => {
|
|
20
|
-
const matrix = fromXRotation(create(),
|
|
23
|
+
const matrix = fromXRotation(create(), TAU / 4)
|
|
21
24
|
const matrixInv = invert(create(), matrix)
|
|
22
25
|
|
|
23
26
|
const vec1 = [10, 10, 10]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/**
|
|
3
3
|
* Determine whether the given matrix is only translate and/or scale.
|
|
4
|
-
* This code returns true for
|
|
4
|
+
* This code returns true for TAU / 2 rotation as it can be interpreted as scale.
|
|
5
5
|
*
|
|
6
6
|
* @param {mat4} matrix - the matrix
|
|
7
7
|
* @returns {Boolean} true if matrix is for translate and/or scale
|