@jscad/modeling 2.7.2 → 2.9.1
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 +48 -0
- package/dist/jscad-modeling.min.js +443 -398
- package/package.json +2 -2
- package/src/curves/bezier/tangentAt.test.js +1 -1
- package/src/curves/bezier/valueAt.test.js +1 -1
- package/src/geometries/geom2/index.d.ts +1 -0
- package/src/geometries/geom2/index.js +12 -1
- package/src/geometries/geom2/isA.js +2 -2
- package/src/geometries/geom2/toCompactBinary.js +4 -4
- package/src/geometries/geom2/toString.js +1 -1
- package/src/geometries/geom2/transform.test.js +1 -1
- package/src/geometries/geom2/validate.d.ts +3 -0
- package/src/geometries/geom2/validate.js +36 -0
- package/src/geometries/geom3/fromCompactBinary.js +1 -1
- package/src/geometries/geom3/index.d.ts +1 -0
- package/src/geometries/geom3/index.js +19 -1
- package/src/geometries/geom3/isA.js +2 -2
- package/src/geometries/geom3/toCompactBinary.js +4 -4
- package/src/geometries/geom3/toString.js +1 -1
- package/src/geometries/geom3/transform.test.js +1 -1
- package/src/geometries/geom3/validate.d.ts +3 -0
- package/src/geometries/geom3/validate.js +62 -0
- package/src/geometries/index.js +8 -1
- package/src/geometries/path2/eachPoint.js +3 -3
- package/src/geometries/path2/index.d.ts +1 -0
- package/src/geometries/path2/index.js +13 -1
- package/src/geometries/path2/isA.js +2 -2
- package/src/geometries/path2/reverse.js +4 -4
- package/src/geometries/path2/toCompactBinary.js +6 -6
- package/src/geometries/path2/toString.js +1 -1
- package/src/geometries/path2/transform.test.js +1 -1
- package/src/geometries/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/poly2/arePointsInside.test.js +1 -1
- package/src/geometries/poly2/index.js +6 -0
- package/src/geometries/poly3/index.d.ts +1 -0
- package/src/geometries/poly3/index.js +9 -2
- package/src/geometries/poly3/invert.js +7 -1
- package/src/geometries/poly3/isA.js +2 -2
- package/src/geometries/poly3/isConvex.js +2 -2
- package/src/geometries/poly3/measureArea.js +4 -4
- package/src/geometries/poly3/measureArea.test.js +16 -16
- package/src/geometries/poly3/measureBoundingBox.js +2 -2
- package/src/geometries/poly3/measureBoundingSphere.js +2 -2
- package/src/geometries/poly3/measureBoundingSphere.test.js +8 -8
- package/src/geometries/poly3/measureSignedVolume.js +4 -4
- package/src/geometries/poly3/toPoints.js +2 -2
- package/src/geometries/poly3/toString.js +2 -2
- package/src/geometries/poly3/transform.js +2 -2
- package/src/geometries/poly3/validate.d.ts +4 -0
- package/src/geometries/poly3/validate.js +50 -0
- package/src/maths/index.js +1 -1
- package/src/maths/line2/equals.js +2 -2
- package/src/maths/line2/fromValues.js +2 -2
- package/src/maths/line2/intersectPointOfLines.js +1 -1
- package/src/maths/line2/intersectPointOfLines.test.js +1 -1
- package/src/maths/line2/reverse.test.js +1 -1
- package/src/maths/line2/transform.test.js +1 -1
- package/src/maths/line3/equals.js +2 -2
- package/src/maths/line3/reverse.test.js +1 -1
- package/src/maths/line3/transform.test.js +1 -1
- package/src/maths/mat4/fromVectorRotation.js +1 -1
- package/src/maths/mat4/fromVectorRotation.test.js +1 -1
- package/src/maths/mat4/identity.test.js +1 -1
- package/src/maths/mat4/invert.js +18 -18
- package/src/maths/mat4/isIdentity.js +1 -1
- package/src/maths/mat4/isMirroring.js +4 -4
- package/src/maths/mat4/isMirroring.test.js +1 -1
- package/src/maths/mat4/leftMultiplyVec3.js +2 -2
- package/src/maths/mat4/toString.js +2 -2
- package/src/maths/mat4/translate.test.js +1 -1
- package/src/maths/plane/flip.test.js +1 -1
- package/src/maths/plane/fromPoints.d.ts +1 -1
- package/src/maths/plane/fromPoints.js +1 -3
- package/src/maths/plane/signedDistanceToPoint.js +1 -1
- package/src/maths/plane/transform.test.js +1 -1
- package/src/maths/utils/aboutEqualNormals.js +2 -2
- package/src/maths/vec2/abs.d.ts +1 -1
- package/src/maths/vec2/add.test.js +1 -1
- package/src/maths/vec2/angleDegrees.d.ts +1 -1
- package/src/maths/vec2/angleRadians.d.ts +1 -1
- package/src/maths/vec2/create.js +1 -1
- package/src/maths/vec2/cross.test.js +1 -1
- package/src/maths/vec2/divide.test.js +1 -1
- package/src/maths/vec2/fromAngleDegrees.js +1 -1
- package/src/maths/vec2/fromScalar.js +1 -1
- package/src/maths/vec2/length.d.ts +1 -1
- package/src/maths/vec2/length.js +1 -1
- package/src/maths/vec2/lerp.test.js +1 -1
- package/src/maths/vec2/multiply.test.js +1 -1
- package/src/maths/vec2/negate.test.js +1 -1
- package/src/maths/vec2/normal.js +1 -1
- package/src/maths/vec2/normalize.d.ts +1 -1
- package/src/maths/vec2/normalize.test.js +1 -1
- package/src/maths/vec2/rotate.test.js +1 -1
- package/src/maths/vec2/squaredLength.d.ts +1 -1
- package/src/maths/vec2/squaredLength.js +3 -3
- package/src/maths/vec2/subtract.test.js +1 -1
- package/src/maths/vec2/toString.js +1 -1
- package/src/maths/vec2/transform.test.js +1 -1
- package/src/maths/vec3/abs.d.ts +1 -1
- package/src/maths/vec3/add.test.js +1 -1
- package/src/maths/vec3/cross.test.js +1 -1
- package/src/maths/vec3/divide.test.js +1 -1
- package/src/maths/vec3/fromScalar.js +1 -1
- package/src/maths/vec3/fromVec2.d.ts +1 -1
- package/src/maths/vec3/fromVec2.js +3 -3
- package/src/maths/vec3/length.d.ts +1 -1
- package/src/maths/vec3/length.js +4 -4
- package/src/maths/vec3/lerp.test.js +1 -1
- package/src/maths/vec3/multiply.test.js +1 -1
- package/src/maths/vec3/negate.d.ts +1 -1
- package/src/maths/vec3/negate.test.js +1 -1
- package/src/maths/vec3/normalize.d.ts +1 -1
- package/src/maths/vec3/normalize.test.js +1 -1
- package/src/maths/vec3/rotateX.test.js +1 -1
- package/src/maths/vec3/rotateY.test.js +1 -1
- package/src/maths/vec3/rotateZ.test.js +1 -1
- package/src/maths/vec3/scale.test.js +1 -1
- package/src/maths/vec3/squaredLength.d.ts +1 -1
- package/src/maths/vec3/squaredLength.js +4 -4
- package/src/maths/vec3/subtract.test.js +1 -1
- package/src/maths/vec3/toString.js +1 -1
- package/src/maths/vec3/transform.test.js +1 -1
- package/src/maths/vec4/toString.js +1 -1
- package/src/maths/vec4/transform.test.js +1 -1
- package/src/measurements/measureBoundingSphere.js +4 -4
- package/src/measurements/measureCenterOfMass.js +1 -1
- package/src/measurements/measureCenterOfMass.test.js +2 -2
- package/src/operations/booleans/intersect.test.js +8 -0
- package/src/operations/booleans/mayOverlap.js +3 -3
- package/src/operations/booleans/retessellate.js +2 -2
- package/src/operations/booleans/scission.js +1 -1
- package/src/operations/booleans/scission.test.js +4 -4
- package/src/operations/booleans/subtract.js +1 -1
- package/src/operations/booleans/subtract.test.js +8 -0
- 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 +28 -1
- package/src/operations/booleans/unionGeom3Sub.js +1 -1
- package/src/operations/expansions/expand.js +2 -2
- package/src/operations/expansions/expand.test.js +32 -55
- package/src/operations/expansions/expandShell.js +24 -18
- package/src/operations/expansions/offset.js +1 -1
- package/src/operations/expansions/offset.test.js +50 -89
- package/src/operations/expansions/offsetFromPoints.js +11 -6
- package/src/operations/extrusions/earcut/assignHoles.js +91 -0
- package/src/operations/extrusions/earcut/assignHoles.test.js +74 -0
- package/src/operations/extrusions/earcut/eliminateHoles.js +131 -0
- package/src/operations/extrusions/earcut/index.js +252 -0
- package/src/operations/extrusions/earcut/linkedList.js +58 -0
- package/src/operations/extrusions/earcut/linkedListSort.js +54 -0
- package/src/operations/extrusions/earcut/linkedPolygon.js +197 -0
- package/src/operations/extrusions/earcut/polygonHierarchy.js +64 -0
- package/src/operations/extrusions/earcut/triangle.js +16 -0
- package/src/operations/extrusions/extrudeFromSlices.js +10 -3
- package/src/operations/extrusions/extrudeFromSlices.test.js +47 -31
- package/src/operations/extrusions/extrudeLinear.js +10 -5
- package/src/operations/extrusions/extrudeLinear.test.js +91 -35
- package/src/operations/extrusions/extrudeLinearGeom2.js +5 -2
- package/src/operations/extrusions/extrudeLinearPath2.js +24 -0
- package/src/operations/extrusions/extrudeRectangular.js +1 -1
- package/src/operations/extrusions/extrudeRectangular.test.js +22 -15
- package/src/operations/extrusions/extrudeRotate.test.js +31 -27
- package/src/operations/extrusions/project.js +1 -1
- package/src/operations/extrusions/project.test.js +5 -5
- package/src/operations/extrusions/slice/calculatePlane.js +7 -4
- package/src/operations/extrusions/slice/isA.js +2 -2
- package/src/operations/extrusions/slice/repairSlice.js +47 -0
- package/src/operations/extrusions/slice/toPolygons.js +24 -60
- package/src/operations/hulls/hull.test.js +25 -2
- package/src/operations/hulls/hullChain.js +1 -1
- package/src/operations/hulls/hullChain.test.js +6 -4
- package/src/operations/hulls/hullGeom2.js +1 -1
- package/src/operations/hulls/hullPath2.js +6 -4
- package/src/operations/hulls/hullPath2.test.js +16 -0
- package/src/operations/hulls/hullPoints2.test.js +1 -1
- package/src/operations/modifiers/edges.js +1 -1
- package/src/operations/modifiers/generalize.js +1 -1
- package/src/operations/modifiers/generalize.test.js +6 -0
- package/src/operations/modifiers/snap.test.js +3 -3
- package/src/operations/transforms/align.d.ts +1 -1
- package/src/operations/transforms/align.test.js +12 -0
- package/src/operations/transforms/center.js +17 -17
- package/src/operations/transforms/center.test.js +12 -0
- package/src/operations/transforms/mirror.js +12 -12
- package/src/operations/transforms/mirror.test.js +16 -0
- package/src/operations/transforms/rotate.js +12 -12
- package/src/operations/transforms/rotate.test.js +10 -0
- package/src/operations/transforms/scale.js +19 -19
- package/src/operations/transforms/scale.test.js +15 -0
- package/src/operations/transforms/transform.js +3 -3
- package/src/operations/transforms/transform.test.js +5 -0
- package/src/operations/transforms/translate.js +14 -14
- package/src/operations/transforms/translate.test.js +16 -0
- package/src/primitives/arc.js +1 -1
- 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.test.js +9 -24
- package/src/primitives/cylinder.test.js +7 -4
- package/src/primitives/cylinderElliptic.js +13 -6
- package/src/primitives/cylinderElliptic.test.js +72 -52
- package/src/primitives/ellipse.js +3 -1
- package/src/primitives/ellipse.test.js +14 -8
- package/src/primitives/ellipsoid.js +7 -5
- package/src/primitives/ellipsoid.test.js +84 -82
- package/src/primitives/geodesicSphere.d.ts +0 -1
- 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.test.js +5 -0
- package/src/primitives/roundedCylinder.js +6 -4
- 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.d.ts +0 -1
- package/src/primitives/torus.test.js +8 -1
- package/src/primitives/triangle.js +1 -1
- package/src/primitives/triangle.test.js +7 -0
- package/src/text/vectorText.js +2 -2
- 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/padArrayToLength.js +1 -1
- package/src/utils/trigonometry.d.ts +2 -0
- package/src/utils/trigonometry.js +35 -0
- package/src/utils/trigonometry.test.js +25 -0
- package/test/helpers/nearlyEqual.js +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jscad/modeling",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "Constructive Solid Geometry (CSG) Library for JSCAD",
|
|
5
5
|
"repository": "https://github.com/jscad/OpenJSCAD.org",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"nyc": "15.1.0",
|
|
61
61
|
"uglifyify": "5.0.2"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "a90b9bad95a417661c619dc733e62c587dc71a4a"
|
|
64
64
|
}
|
|
@@ -20,7 +20,7 @@ test('quadratic bezier (3 control points)', (t) => {
|
|
|
20
20
|
t.is(bezier.tangentAt(1, OneDCurve), 20)
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
-
test('quadratic bezier (3 control points, non
|
|
23
|
+
test('quadratic bezier (3 control points, non symmetric)', (t) => {
|
|
24
24
|
const OneDCurve = bezier.create([0, 0, 20])
|
|
25
25
|
t.is(bezier.tangentAt(0, OneDCurve), 0)
|
|
26
26
|
t.is(bezier.tangentAt(0.5, OneDCurve), 20)
|
|
@@ -20,7 +20,7 @@ test('quadratic bezier (3 control points)', (t) => {
|
|
|
20
20
|
t.is(bezier.valueAt(1, OneDCurve), 20)
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
-
test('quadratic bezier (3 control points, non
|
|
23
|
+
test('quadratic bezier (3 control points, non symmetric)', (t) => {
|
|
24
24
|
const OneDCurve = bezier.create([0, 0, 20])
|
|
25
25
|
t.is(bezier.valueAt(0, OneDCurve), 0)
|
|
26
26
|
t.is(bezier.valueAt(0.5, OneDCurve), 5)
|
|
@@ -10,6 +10,7 @@ export { default as toSides } from './toSides'
|
|
|
10
10
|
export { default as toString } from './toString'
|
|
11
11
|
export { default as toCompactBinary } from './toCompactBinary'
|
|
12
12
|
export { default as transform } from './transform'
|
|
13
|
+
export { default as validate } from './validate'
|
|
13
14
|
|
|
14
15
|
export { default as Geom2 } from './type'
|
|
15
16
|
export as namespace geom2
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
* Represents a 2D geometry consisting of a list of sides.
|
|
3
3
|
* @see {@link geom2} for data structure information.
|
|
4
4
|
* @module modeling/geometries/geom2
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* colorize([0.5,0,1,1], square()) // purple square
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* {
|
|
11
|
+
* "sides": [[[-1,1],[-1,-1]],[[-1,-1],[1,-1]],[[1,-1],[1,1]],[[1,1],[-1,1]]],
|
|
12
|
+
* "transforms": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
|
|
13
|
+
* "color": [0.5,0,1,1]
|
|
14
|
+
* }
|
|
5
15
|
*/
|
|
6
16
|
module.exports = {
|
|
7
17
|
clone: require('./clone'),
|
|
@@ -15,5 +25,6 @@ module.exports = {
|
|
|
15
25
|
toSides: require('./toSides'),
|
|
16
26
|
toString: require('./toString'),
|
|
17
27
|
toCompactBinary: require('./toCompactBinary'),
|
|
18
|
-
transform: require('./transform')
|
|
28
|
+
transform: require('./transform'),
|
|
29
|
+
validate: require('./validate')
|
|
19
30
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param {Object} object - the object to
|
|
2
|
+
* Determine if the given object is a 2D geometry.
|
|
3
|
+
* @param {Object} object - the object to interrogate
|
|
4
4
|
* @returns {Boolean} true, if the object matches a geom2 based object
|
|
5
5
|
* @alias module:modeling/geometries/geom2.isA
|
|
6
6
|
*/
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* @returns {TypedArray} compact binary representation
|
|
5
5
|
* @alias module:modeling/geometries/geom2.toCompactBinary
|
|
6
6
|
*/
|
|
7
|
-
const toCompactBinary = (
|
|
8
|
-
const sides =
|
|
9
|
-
const transforms =
|
|
7
|
+
const toCompactBinary = (geometry) => {
|
|
8
|
+
const sides = geometry.sides
|
|
9
|
+
const transforms = geometry.transforms
|
|
10
10
|
let color = [-1, -1, -1, -1]
|
|
11
|
-
if (
|
|
11
|
+
if (geometry.color) color = geometry.color
|
|
12
12
|
|
|
13
13
|
// FIXME why Float32Array?
|
|
14
14
|
const compacted = new Float32Array(1 + 16 + 4 + (sides.length * 4)) // type + transforms + color + sides data
|
|
@@ -5,7 +5,7 @@ const toSides = require('./toSides')
|
|
|
5
5
|
/**
|
|
6
6
|
* Create a string representing the contents of the given geometry.
|
|
7
7
|
* @param {geom2} geometry - the geometry
|
|
8
|
-
* @returns {String} a
|
|
8
|
+
* @returns {String} a representative string
|
|
9
9
|
* @alias module:modeling/geometries/geom2.toString
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
@@ -11,7 +11,7 @@ test('transform: adjusts the transforms of geom2', (t) => {
|
|
|
11
11
|
const rotation = 90 * 0.017453292519943295
|
|
12
12
|
const rotate90 = mat4.fromZRotation(mat4.create(), rotation)
|
|
13
13
|
|
|
14
|
-
// continue with typical user scenario, several
|
|
14
|
+
// continue with typical user scenario, several iterations of transforms and access
|
|
15
15
|
|
|
16
16
|
// expect lazy transform, i.e. only the transforms change
|
|
17
17
|
const expected = {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const vec2 = require('../../maths/vec2')
|
|
2
|
+
const isA = require('./isA')
|
|
3
|
+
const toOutlines = require('./toOutlines')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Determine if the given object is a valid geom2.
|
|
7
|
+
* Checks for closedness, self-edges, and valid data points.
|
|
8
|
+
*
|
|
9
|
+
* **If the geometry is not valid, an exception will be thrown with details of the geometry error.**
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} object - the object to interrogate
|
|
12
|
+
* @throws {Error} error if the geometry is not valid
|
|
13
|
+
* @alias module:modeling/geometries/geom2.validate
|
|
14
|
+
*/
|
|
15
|
+
const validate = (object) => {
|
|
16
|
+
if (!isA(object)) {
|
|
17
|
+
throw new Error('invalid geom2 structure')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// check for closedness
|
|
21
|
+
toOutlines(object)
|
|
22
|
+
|
|
23
|
+
// check for self-edges
|
|
24
|
+
object.sides.forEach((side) => {
|
|
25
|
+
if (vec2.equals(side[0], side[1])) {
|
|
26
|
+
throw new Error(`geom2 self-edge ${side[0]}`)
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
// check transforms
|
|
31
|
+
if (!object.transforms.every(Number.isFinite)) {
|
|
32
|
+
throw new Error(`geom2 invalid transforms ${object.transforms}`)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = validate
|
|
@@ -33,7 +33,7 @@ const fromCompactBinary = (data) => {
|
|
|
33
33
|
created.polygons.push(poly3.create(vertices))
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// transfer known
|
|
36
|
+
// transfer known properties, i.e. color
|
|
37
37
|
if (data[17] >= 0) {
|
|
38
38
|
created.color = [data[17], data[18], data[19], data[20]]
|
|
39
39
|
}
|
|
@@ -9,6 +9,7 @@ export { default as toPolygons } from './toPolygons'
|
|
|
9
9
|
export { default as toString } from './toString'
|
|
10
10
|
export { default as toCompactBinary } from './toCompactBinary'
|
|
11
11
|
export { default as transform } from './transform'
|
|
12
|
+
export { default as validate } from './validate'
|
|
12
13
|
|
|
13
14
|
export { default as Geom3 } from './type'
|
|
14
15
|
export as namespace geom3
|
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
* Represents a 3D geometry consisting of a list of polygons.
|
|
3
3
|
* @see {@link geom3} for data structure information.
|
|
4
4
|
* @module modeling/geometries/geom3
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* colorize([0,0.5,1,0.6], cube()) // transparent ice cube
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* {
|
|
11
|
+
* "polygons": [
|
|
12
|
+
* {"vertices": [[-1,-1,-1], [-1,-1,1], [-1,1,1], [-1,1,-1]]},
|
|
13
|
+
* {"vertices": [[1,-1,-1], [1,1,-1], [1,1,1], [1,-1,1]]},
|
|
14
|
+
* {"vertices": [[-1,-1,-1], [1,-1,-1], [1,-1,1], [-1,-1,1]]},
|
|
15
|
+
* {"vertices": [[-1,1,-1], [-1,1,1], [1,1,1], [1,1,-1]]},
|
|
16
|
+
* {"vertices": [[-1,-1,-1], [-1,1,-1], [1,1,-1], [1,-1,-1]]},
|
|
17
|
+
* {"vertices": [[-1,-1,1], [1,-1,1], [1,1,1], [-1,1,1]]}
|
|
18
|
+
* ],
|
|
19
|
+
* "transforms": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
|
|
20
|
+
* "color": [0,0.5,1,0.6]
|
|
21
|
+
* }
|
|
5
22
|
*/
|
|
6
23
|
module.exports = {
|
|
7
24
|
clone: require('./clone'),
|
|
@@ -14,5 +31,6 @@ module.exports = {
|
|
|
14
31
|
toPolygons: require('./toPolygons'),
|
|
15
32
|
toString: require('./toString'),
|
|
16
33
|
toCompactBinary: require('./toCompactBinary'),
|
|
17
|
-
transform: require('./transform')
|
|
34
|
+
transform: require('./transform'),
|
|
35
|
+
validate: require('./validate')
|
|
18
36
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param {
|
|
2
|
+
* Determine if the given object is a 3D geometry.
|
|
3
|
+
* @param {Object} object - the object to interrogate
|
|
4
4
|
* @returns {Boolean} true if the object matches a geom3
|
|
5
5
|
* @alias module:modeling/geometries/geom3.isA
|
|
6
6
|
*/
|
|
@@ -6,14 +6,14 @@ const poly3 = require('../poly3')
|
|
|
6
6
|
* @return {TypedArray} compact binary representation
|
|
7
7
|
* @alias module:modeling/geometries/geom3.toCompactBinary
|
|
8
8
|
*/
|
|
9
|
-
const toCompactBinary = (
|
|
10
|
-
const polygons =
|
|
11
|
-
const transforms =
|
|
9
|
+
const toCompactBinary = (geometry) => {
|
|
10
|
+
const polygons = geometry.polygons
|
|
11
|
+
const transforms = geometry.transforms
|
|
12
12
|
|
|
13
13
|
const numberOfPolygons = polygons.length
|
|
14
14
|
const numberOfVertices = polygons.reduce((count, polygon) => count + polygon.vertices.length, 0)
|
|
15
15
|
let color = [-1, -1, -1, -1]
|
|
16
|
-
if (
|
|
16
|
+
if (geometry.color) color = geometry.color
|
|
17
17
|
|
|
18
18
|
// FIXME why Float32Array?
|
|
19
19
|
const compacted = new Float32Array(1 + 16 + 4 + 1 + numberOfPolygons + (numberOfVertices * 3))
|
|
@@ -5,7 +5,7 @@ const toPolygons = require('./toPolygons')
|
|
|
5
5
|
/**
|
|
6
6
|
* Create a string representing the contents of the given geometry.
|
|
7
7
|
* @param {geom3} geometry - the geometry
|
|
8
|
-
* @returns {String} a
|
|
8
|
+
* @returns {String} a representative string
|
|
9
9
|
* @alias module:modeling/geometries/geom3.toString
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
@@ -11,7 +11,7 @@ test('transform: Adjusts the transforms of a populated geom3', (t) => {
|
|
|
11
11
|
const rotation = 90 * 0.017453292519943295
|
|
12
12
|
const rotate90 = mat4.fromZRotation(mat4.create(), rotation)
|
|
13
13
|
|
|
14
|
-
// continue with typical user scenario, several
|
|
14
|
+
// continue with typical user scenario, several iterations of transforms and access
|
|
15
15
|
|
|
16
16
|
// expect lazy transform, i.e. only the transforms change
|
|
17
17
|
const expected = {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const poly3 = require('../poly3')
|
|
2
|
+
const isA = require('./isA')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Determine if the given object is a valid 3D geometry.
|
|
6
|
+
* Checks for valid data structure, convex polygon faces, and manifold edges.
|
|
7
|
+
*
|
|
8
|
+
* **If the geometry is not valid, an exception will be thrown with details of the geometry error.**
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} object - the object to interrogate
|
|
11
|
+
* @throws {Error} error if the geometry is not valid
|
|
12
|
+
* @alias module:modeling/geometries/geom3.validate
|
|
13
|
+
*/
|
|
14
|
+
const validate = (object) => {
|
|
15
|
+
if (!isA(object)) {
|
|
16
|
+
throw new Error('invalid geom3 structure')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// check polygons
|
|
20
|
+
object.polygons.forEach(poly3.validate)
|
|
21
|
+
validateManifold(object)
|
|
22
|
+
|
|
23
|
+
// check transforms
|
|
24
|
+
if (!object.transforms.every(Number.isFinite)) {
|
|
25
|
+
throw new Error(`geom3 invalid transforms ${object.transforms}`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// TODO: check for self-intersecting
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* Check manifold edge condition: Every edge is in exactly 2 faces
|
|
33
|
+
*/
|
|
34
|
+
const validateManifold = (object) => {
|
|
35
|
+
// count of each edge
|
|
36
|
+
const edgeCount = new Map()
|
|
37
|
+
object.polygons.forEach(({ vertices }) => {
|
|
38
|
+
vertices.forEach((v, i) => {
|
|
39
|
+
const v1 = `${v}`
|
|
40
|
+
const v2 = `${vertices[(i + 1) % vertices.length]}`
|
|
41
|
+
// sort for undirected edge
|
|
42
|
+
const edge = `${v1}/${v2}`
|
|
43
|
+
const count = edgeCount.has(edge) ? edgeCount.get(edge) : 0
|
|
44
|
+
edgeCount.set(edge, count + 1)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// check that edges are always matched
|
|
49
|
+
const nonManifold = []
|
|
50
|
+
edgeCount.forEach((count, edge) => {
|
|
51
|
+
const complementEdge = edge.split('/').reverse().join('/')
|
|
52
|
+
const complementCount = edgeCount.get(complementEdge)
|
|
53
|
+
if (count !== complementCount) {
|
|
54
|
+
nonManifold.push(edge.replace('/', ' -> '))
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
if (nonManifold.length > 0) {
|
|
58
|
+
throw new Error(`non-manifold edges ${nonManifold.length}\n${nonManifold.join('\n')}`)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = validate
|
package/src/geometries/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Geometries are objects that represent the contents of primitives or the results of operations.
|
|
3
|
-
* Note: Geometries are
|
|
3
|
+
* Note: Geometries are considered immutable, so never change the contents directly.
|
|
4
|
+
*
|
|
5
|
+
* @see {@link geom2} - 2D geometry consisting of sides
|
|
6
|
+
* @see {@link geom3} - 3D geometry consisting of polygons
|
|
7
|
+
* @see {@link path2} - 2D geometry consisting of ordered points
|
|
8
|
+
* @see {@link poly2} - 2D polygon consisting of ordered vertices
|
|
9
|
+
* @see {@link poly3} - 3D polygon consisting of ordered vertices
|
|
10
|
+
*
|
|
4
11
|
* @module modeling/geometries
|
|
5
12
|
* @example
|
|
6
13
|
* const { geom2, geom3, path2, poly2, poly3 } = require('@jscad/modeling').geometries
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
const toPoints = require('./toPoints')
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Calls a function for each point in the
|
|
4
|
+
* Calls a function for each point in the path.
|
|
5
5
|
* @param {Object} options - options
|
|
6
6
|
* @param {Function} thunk - the function to call
|
|
7
|
-
* @param {path2}
|
|
7
|
+
* @param {path2} path - the path to traverse
|
|
8
8
|
* @alias module:modeling/geometries/path2.eachPoint
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
|
-
* eachPoint({}, accumulate,
|
|
11
|
+
* eachPoint({}, accumulate, path)
|
|
12
12
|
*/
|
|
13
13
|
const eachPoint = (options, thunk, path) => {
|
|
14
14
|
toPoints(path).forEach(thunk)
|
|
@@ -15,6 +15,7 @@ export { default as toPoints } from './toPoints'
|
|
|
15
15
|
export { default as toString } from './toString'
|
|
16
16
|
export { default as toCompactBinary } from './toCompactBinary'
|
|
17
17
|
export { default as transform } from './transform'
|
|
18
|
+
export { default as validate } from './validate'
|
|
18
19
|
|
|
19
20
|
export { default as Path2 } from './type'
|
|
20
21
|
export as namespace path2
|
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
* Represents a 2D geometry consisting of a list of ordered points.
|
|
3
3
|
* @see {@link path2} for data structure information.
|
|
4
4
|
* @module modeling/geometries/path2
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* colorize([0,0,0,1], path2.fromPoints({ closed: true }, [[0,0], [4,0], [4,3]]))
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* {
|
|
11
|
+
* "points": [[0,0], [4,0], [4,3]],
|
|
12
|
+
* "isClosed": true,
|
|
13
|
+
* "transforms": [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
|
|
14
|
+
* "color": [0,0,0,1]
|
|
15
|
+
* }
|
|
5
16
|
*/
|
|
6
17
|
module.exports = {
|
|
7
18
|
appendArc: require('./appendArc'),
|
|
@@ -20,5 +31,6 @@ module.exports = {
|
|
|
20
31
|
toPoints: require('./toPoints'),
|
|
21
32
|
toString: require('./toString'),
|
|
22
33
|
toCompactBinary: require('./toCompactBinary'),
|
|
23
|
-
transform: require('./transform')
|
|
34
|
+
transform: require('./transform'),
|
|
35
|
+
validate: require('./validate')
|
|
24
36
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param {Object} object - the object to
|
|
2
|
+
* Determine if the given object is a path2 geometry.
|
|
3
|
+
* @param {Object} object - the object to interrogate
|
|
4
4
|
* @returns {Boolean} true if the object matches a path2
|
|
5
5
|
* @alias module:modeling/geometries/path2.isA
|
|
6
6
|
*/
|
|
@@ -3,17 +3,17 @@ const clone = require('./clone')
|
|
|
3
3
|
/**
|
|
4
4
|
* Reverses the path so that the points are in the opposite order.
|
|
5
5
|
* This swaps the left (interior) and right (exterior) edges.
|
|
6
|
-
* @param {path2} geometry - the
|
|
6
|
+
* @param {path2} geometry - the path to reverse
|
|
7
7
|
* @returns {path2} a new path
|
|
8
8
|
* @alias module:modeling/geometries/path2.reverse
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* let newpath = reverse(mypath)
|
|
12
12
|
*/
|
|
13
|
-
const reverse = (
|
|
13
|
+
const reverse = (geometry) => {
|
|
14
14
|
// NOTE: this only updates the order of the points
|
|
15
|
-
const cloned = clone(
|
|
16
|
-
cloned.points =
|
|
15
|
+
const cloned = clone(geometry)
|
|
16
|
+
cloned.points = geometry.points.slice().reverse()
|
|
17
17
|
return cloned
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Produce a compact binary representation from the given path.
|
|
3
|
-
* @param {path2} geometry - the path
|
|
3
|
+
* @param {path2} geometry - the path geometry
|
|
4
4
|
* @returns {TypedArray} compact binary representation
|
|
5
5
|
* @alias module:modeling/geometries/path2.toCompactBinary
|
|
6
6
|
*/
|
|
7
|
-
const toCompactBinary = (
|
|
8
|
-
const points =
|
|
9
|
-
const transforms =
|
|
7
|
+
const toCompactBinary = (geometry) => {
|
|
8
|
+
const points = geometry.points
|
|
9
|
+
const transforms = geometry.transforms
|
|
10
10
|
let color = [-1, -1, -1, -1]
|
|
11
|
-
if (
|
|
11
|
+
if (geometry.color) color = geometry.color
|
|
12
12
|
|
|
13
13
|
// FIXME why Float32Array?
|
|
14
14
|
const compacted = new Float32Array(1 + 16 + 1 + 4 + (points.length * 2)) // type + transforms + isClosed + color + points data
|
|
@@ -32,7 +32,7 @@ const toCompactBinary = (geom) => {
|
|
|
32
32
|
compacted[15] = transforms[14]
|
|
33
33
|
compacted[16] = transforms[15]
|
|
34
34
|
|
|
35
|
-
compacted[17] =
|
|
35
|
+
compacted[17] = geometry.isClosed ? 1 : 0
|
|
36
36
|
|
|
37
37
|
compacted[18] = color[0]
|
|
38
38
|
compacted[19] = color[1]
|
|
@@ -5,7 +5,7 @@ const toPoints = require('./toPoints')
|
|
|
5
5
|
/**
|
|
6
6
|
* Create a string representing the contents of the given path.
|
|
7
7
|
* @param {path2} geometry - the path
|
|
8
|
-
* @returns {String} a
|
|
8
|
+
* @returns {String} a representative string
|
|
9
9
|
* @alias module:modeling/geometries/path2.toString
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
@@ -11,7 +11,7 @@ test('transform: adjusts the transforms of path', (t) => {
|
|
|
11
11
|
const rotation = 90 * 0.017453292519943295
|
|
12
12
|
const rotate90 = mat4.fromZRotation(mat4.create(), rotation)
|
|
13
13
|
|
|
14
|
-
// continue with typical user scenario, several
|
|
14
|
+
// continue with typical user scenario, several iterations of transforms and access
|
|
15
15
|
|
|
16
16
|
// expect lazy transform, i.e. only the transforms change
|
|
17
17
|
const expected = {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const vec2 = require('../../maths/vec2')
|
|
2
|
+
const isA = require('./isA')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Determine if the given object is a valid path2.
|
|
6
|
+
* Checks for valid data points, and duplicate points.
|
|
7
|
+
*
|
|
8
|
+
* **If the geometry is not valid, an exception will be thrown with details of the geometry error.**
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} object - the object to interrogate
|
|
11
|
+
* @throws {Error} error if the geometry is not valid
|
|
12
|
+
* @alias module:modeling/geometries/path2.validate
|
|
13
|
+
*/
|
|
14
|
+
const validate = (object) => {
|
|
15
|
+
if (!isA(object)) {
|
|
16
|
+
throw new Error('invalid path2 structure')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// check for duplicate points
|
|
20
|
+
if (object.points.length > 1) {
|
|
21
|
+
for (let i = 0; i < object.points.length; i++) {
|
|
22
|
+
if (vec2.equals(object.points[i], object.points[(i + 1) % object.points.length])) {
|
|
23
|
+
throw new Error(`path2 duplicate points ${object.points[i]}`)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// check for infinity, nan
|
|
29
|
+
object.points.forEach((point) => {
|
|
30
|
+
if (!point.every(Number.isFinite)) {
|
|
31
|
+
throw new Error(`path2 invalid point ${point}`)
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// check transforms
|
|
36
|
+
if (!object.transforms.every(Number.isFinite)) {
|
|
37
|
+
throw new Error(`path2 invalid transforms ${object.transforms}`)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = validate
|
|
@@ -23,41 +23,6 @@ const arePointsInside = (points, polygon) => {
|
|
|
23
23
|
return sum === points.length ? 1 : 0
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/*
|
|
27
|
-
* Determine if the given point is inside the polygon.
|
|
28
|
-
*
|
|
29
|
-
* @see http://geomalgorithms.com/a03-_inclusion.html
|
|
30
|
-
* @param {Array} point - an array with X and Y values
|
|
31
|
-
* @param {Array} polygon - a list of points, where each point is an array with X and Y values
|
|
32
|
-
* @return {Integer} 1 if the point is inside, 0 if outside
|
|
33
|
-
*/
|
|
34
|
-
const isPointInsideOld = (point, polygon) => {
|
|
35
|
-
let wn = 0
|
|
36
|
-
const n = polygon.length
|
|
37
|
-
const x = point[0]
|
|
38
|
-
const y = point[1]
|
|
39
|
-
for (let i = 0; i < polygon.length; i++) {
|
|
40
|
-
const p1 = polygon[i]
|
|
41
|
-
const p2 = polygon[(i + 1) % n]
|
|
42
|
-
if (x !== p1[0] && y !== p1[1] && x !== p2[0] && y !== p2[1]) { // no overlap of points
|
|
43
|
-
if (p1[1] <= y) {
|
|
44
|
-
if (p2[1] > y) { // upward crossing
|
|
45
|
-
if (isLeft(p1, p2, point) > 0) { // point left of edge
|
|
46
|
-
wn++
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
} else {
|
|
50
|
-
if (p2[1] <= y) { // downward crossing
|
|
51
|
-
if (isLeft(p1, p2, point) < 0) { // point right of edge
|
|
52
|
-
wn--
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return wn === 0 ? 1 : 0
|
|
59
|
-
}
|
|
60
|
-
|
|
61
26
|
/*
|
|
62
27
|
* Determine if the given point is inside the polygon.
|
|
63
28
|
*
|
|
@@ -12,7 +12,7 @@ test('poly2: arePointsInside() should return proper values', (t) => {
|
|
|
12
12
|
t.is(obs, 0)
|
|
13
13
|
|
|
14
14
|
polygon = create([[0, 0], [5, 0], [5, 5], [0, 5]])
|
|
15
|
-
// points
|
|
15
|
+
// points overlapping points
|
|
16
16
|
obs = arePointsInside([[0, 0]], polygon)
|
|
17
17
|
t.is(obs, 0)
|
|
18
18
|
obs = arePointsInside([[5, 0]], polygon)
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
* Represents a 2D polygon consisting of a list of ordered vertices.
|
|
3
3
|
* @see {@link poly2} for data structure information.
|
|
4
4
|
* @module modeling/geometries/poly2
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* poly2.create([[0,0], [4,0], [4,3]])
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* {"vertices": [[0,0], [4,0], [4,3]]}
|
|
5
11
|
*/
|
|
6
12
|
module.exports = {
|
|
7
13
|
arePointsInside: require('./arePointsInside'),
|
|
@@ -13,6 +13,7 @@ export { default as plane } from './plane'
|
|
|
13
13
|
export { default as toPoints } from './toPoints'
|
|
14
14
|
export { default as toString } from './toString'
|
|
15
15
|
export { default as transform } from './transform'
|
|
16
|
+
export { default as validate } from './validate'
|
|
16
17
|
|
|
17
18
|
export { default as Poly3 } from './type'
|
|
18
19
|
export as namespace poly3
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Represents a convex 3D polygon consisting of a list of vertices.
|
|
2
|
+
* Represents a convex 3D polygon consisting of a list of ordered vertices.
|
|
3
3
|
* @see {@link poly3} for data structure information.
|
|
4
4
|
* @module modeling/geometries/poly3
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* poly3.create([[0,0,0], [4,0,0], [4,3,12]])
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* {"vertices": [[0,0,0], [4,0,0], [4,3,12]]}
|
|
5
11
|
*/
|
|
6
12
|
module.exports = {
|
|
7
13
|
clone: require('./clone'),
|
|
@@ -18,5 +24,6 @@ module.exports = {
|
|
|
18
24
|
plane: require('./plane'),
|
|
19
25
|
toPoints: require('./toPoints'),
|
|
20
26
|
toString: require('./toString'),
|
|
21
|
-
transform: require('./transform')
|
|
27
|
+
transform: require('./transform'),
|
|
28
|
+
validate: require('./validate')
|
|
22
29
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const plane = require('../../maths/plane')
|
|
1
2
|
const create = require('./create')
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -9,7 +10,12 @@ const create = require('./create')
|
|
|
9
10
|
*/
|
|
10
11
|
const invert = (polygon) => {
|
|
11
12
|
const vertices = polygon.vertices.slice().reverse()
|
|
12
|
-
|
|
13
|
+
const inverted = create(vertices)
|
|
14
|
+
if (polygon.plane) {
|
|
15
|
+
// Flip existing plane to save recompute
|
|
16
|
+
inverted.plane = plane.flip(plane.create(), polygon.plane)
|
|
17
|
+
}
|
|
18
|
+
return inverted
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
module.exports = invert
|