@jscad/modeling 2.5.2 → 2.7.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 +57 -0
- package/dist/jscad-modeling.min.js +217 -202
- package/package.json +2 -2
- package/src/colors/colorize.js +4 -5
- package/src/colors/colorize.test.js +19 -12
- package/src/geometries/geom2/applyTransforms.js +1 -1
- package/src/geometries/geom2/clone.js +2 -12
- package/src/geometries/geom2/transform.js +2 -6
- package/src/geometries/geom3/applyTransforms.js +1 -1
- package/src/geometries/geom3/clone.js +2 -14
- package/src/geometries/geom3/clone.test.js +0 -2
- package/src/geometries/geom3/create.js +0 -2
- package/src/geometries/geom3/create.test.js +0 -2
- package/src/geometries/geom3/fromCompactBinary.js +4 -6
- package/src/geometries/geom3/fromToCompactBinary.test.js +0 -6
- package/src/geometries/geom3/invert.test.js +0 -2
- package/src/geometries/geom3/toCompactBinary.js +8 -10
- package/src/geometries/geom3/transform.js +2 -7
- package/src/geometries/geom3/transform.test.js +0 -1
- package/src/geometries/geom3/type.d.ts +0 -1
- package/src/geometries/path2/applyTransforms.js +1 -1
- package/src/geometries/path2/clone.js +2 -13
- package/src/geometries/path2/transform.js +2 -7
- package/src/geometries/poly3/isConvex.js +1 -1
- package/src/geometries/poly3/measureArea.js +12 -13
- package/src/geometries/poly3/measureArea.test.js +15 -0
- package/src/geometries/poly3/plane.js +1 -2
- package/src/maths/mat4/index.js +1 -0
- package/src/maths/mat4/isOnlyTransformScale.js +21 -0
- package/src/maths/mat4/isOnlyTransformScale.test.js +27 -0
- package/src/maths/plane/fromPoints.js +32 -10
- package/src/maths/plane/fromPoints.test.js +4 -0
- package/src/maths/utils/index.d.ts +0 -1
- package/src/maths/utils/index.js +0 -1
- package/src/maths/vec2/rotate.js +1 -1
- package/src/maths/vec2/rotate.test.js +3 -3
- package/src/measurements/index.js +4 -1
- package/src/measurements/measureBoundingBox.js +128 -38
- package/src/measurements/measureBoundingBox.test.js +8 -0
- package/src/measurements/measureBoundingSphere.js +146 -0
- package/src/measurements/measureBoundingSphere.test.js +59 -0
- package/src/measurements/measureCenter.js +28 -0
- package/src/measurements/measureCenter.test.js +58 -0
- package/src/measurements/measureCenterOfMass.js +106 -0
- package/src/measurements/measureCenterOfMass.test.js +58 -0
- package/src/measurements/measureDimensions.js +28 -0
- package/src/measurements/measureDimensions.test.js +58 -0
- package/src/measurements/measureEpsilon.js +3 -9
- package/src/operations/booleans/reTesselateCoplanarPolygons.js +1 -1
- package/src/operations/booleans/trees/PolygonTreeNode.js +0 -1
- package/src/operations/expansions/expand.test.js +1 -1
- package/src/operations/extrusions/extrudeRotate.test.js +18 -10
- package/src/operations/modifiers/generalize.js +0 -1
- package/src/operations/modifiers/snapPolygons.js +2 -2
- package/src/operations/modifiers/snapPolygons.test.js +13 -5
- package/src/primitives/index.d.ts +1 -0
- package/src/primitives/index.js +2 -1
- package/src/primitives/triangle.d.ts +10 -0
- package/src/primitives/triangle.js +164 -0
- package/src/primitives/triangle.test.js +95 -0
- package/src/maths/utils/clamp.d.ts +0 -3
- package/src/maths/utils/clamp.js +0 -4
- package/src/maths/utils/quantizeForSpace.d.ts +0 -3
- package/src/maths/utils/quantizeForSpace.js +0 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jscad/modeling",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
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": "fbc9b90f0ae02bea4e6e7b974c65e751c3858269"
|
|
64
64
|
}
|
package/src/colors/colorize.js
CHANGED
|
@@ -6,13 +6,13 @@ const path2 = require('../geometries/path2')
|
|
|
6
6
|
const poly3 = require('../geometries/poly3')
|
|
7
7
|
|
|
8
8
|
const colorGeom2 = (color, object) => {
|
|
9
|
-
const newgeom2 = geom2.
|
|
9
|
+
const newgeom2 = geom2.clone(object)
|
|
10
10
|
newgeom2.color = color
|
|
11
11
|
return newgeom2
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const colorGeom3 = (color, object) => {
|
|
15
|
-
const newgeom3 = geom3.
|
|
15
|
+
const newgeom3 = geom3.clone(object)
|
|
16
16
|
newgeom3.color = color
|
|
17
17
|
return newgeom3
|
|
18
18
|
}
|
|
@@ -31,10 +31,9 @@ const colorPoly3 = (color, object) => {
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Assign the given color to the given objects.
|
|
34
|
-
* Note: The color should only be assigned after performing all operations.
|
|
35
34
|
* @param {Array} color - RGBA color values, where each value is between 0 and 1.0
|
|
36
|
-
* @param {Object|Array} objects - the objects of which to color
|
|
37
|
-
* @return {Object|Array} new
|
|
35
|
+
* @param {Object|Array} objects - the objects of which to apply the given color
|
|
36
|
+
* @return {Object|Array} new object, or list of new objects with an additional attribute 'color'
|
|
38
37
|
* @alias module:modeling/colors.colorize
|
|
39
38
|
*
|
|
40
39
|
* @example
|
|
@@ -43,23 +43,30 @@ test('color (rgba on geometry)', (t) => {
|
|
|
43
43
|
const obs = colorize([1, 1, 0.5, 0.8], obj0, obj1, obj2, obj3)
|
|
44
44
|
t.is(obs.length, 4)
|
|
45
45
|
|
|
46
|
-
let exp = geom2.clone(obj0)
|
|
47
|
-
exp.color = [1, 1, 0.5, 0.8]
|
|
48
46
|
t.not(obj0, obs[0])
|
|
49
|
-
t.deepEqual(obs[0],
|
|
47
|
+
t.deepEqual(obs[0].color, [1, 1, 0.5, 0.8])
|
|
50
48
|
|
|
51
|
-
exp = geom3.clone(obj1)
|
|
52
|
-
exp.color = [1, 1, 0.5, 0.8]
|
|
53
49
|
t.not(obj1, obs[1])
|
|
54
|
-
t.deepEqual(obs[1],
|
|
50
|
+
t.deepEqual(obs[1].color, [1, 1, 0.5, 0.8])
|
|
55
51
|
|
|
56
|
-
exp = path2.clone(obj2)
|
|
57
|
-
exp.color = [1, 1, 0.5, 0.8]
|
|
58
52
|
t.not(obj2, obs[2])
|
|
59
|
-
t.deepEqual(obs[2],
|
|
53
|
+
t.deepEqual(obs[2].color, [1, 1, 0.5, 0.8])
|
|
60
54
|
|
|
61
|
-
exp = poly3.clone(obj3)
|
|
62
|
-
exp.color = [1, 1, 0.5, 0.8]
|
|
63
55
|
t.not(obj3, obs[3])
|
|
64
|
-
t.deepEqual(obs[3],
|
|
56
|
+
t.deepEqual(obs[3].color, [1, 1, 0.5, 0.8])
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('color (returns new object)', (t) => {
|
|
60
|
+
const obj0 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
|
|
61
|
+
// const obj1 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
|
|
62
|
+
// const obj2 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
|
|
63
|
+
const obj3 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
|
|
64
|
+
|
|
65
|
+
const obs = colorize([1, 1, 1, 0.8], obj0, obj3)
|
|
66
|
+
t.not(obj0, obs[0])
|
|
67
|
+
t.deepEqual(obs[0].color, [1, 1, 1, 0.8])
|
|
68
|
+
t.is(obj0.color, undefined)
|
|
69
|
+
t.not(obj3, obs[1])
|
|
70
|
+
t.deepEqual(obs[1].color, [1, 1, 1, 0.8])
|
|
71
|
+
t.is(obj3.color, undefined)
|
|
65
72
|
})
|
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
const mat4 = require('../../maths/mat4')
|
|
2
|
-
const vec2 = require('../../maths/vec2')
|
|
3
|
-
|
|
4
|
-
const create = require('./create')
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
|
-
* Performs a
|
|
2
|
+
* Performs a shallow clone of the given geometry.
|
|
8
3
|
* @param {geom2} geometry - the geometry to clone
|
|
9
4
|
* @returns {geom2} new geometry
|
|
10
5
|
* @alias module:modeling/geometries/geom2.clone
|
|
11
6
|
*/
|
|
12
|
-
const clone = (geometry) => {
|
|
13
|
-
const out = create()
|
|
14
|
-
out.sides = geometry.sides.map((side) => [vec2.clone(side[0]), vec2.clone(side[1])])
|
|
15
|
-
out.transforms = mat4.clone(geometry.transforms)
|
|
16
|
-
return out
|
|
17
|
-
}
|
|
7
|
+
const clone = (geometry) => Object.assign({}, geometry)
|
|
18
8
|
|
|
19
9
|
module.exports = clone
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const mat4 = require('../../maths/mat4')
|
|
2
2
|
|
|
3
|
-
const create = require('./create')
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* Transform the given geometry using the given matrix.
|
|
7
5
|
* This is a lazy transform of the sides, as this function only adjusts the transforms.
|
|
@@ -15,10 +13,8 @@ const create = require('./create')
|
|
|
15
13
|
* let newgeometry = transform(fromZRotation(degToRad(90)), geometry)
|
|
16
14
|
*/
|
|
17
15
|
const transform = (matrix, geometry) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
mat4.multiply(newgeometry.transforms, matrix, geometry.transforms)
|
|
21
|
-
return newgeometry
|
|
16
|
+
const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
|
|
17
|
+
return Object.assign({}, geometry, { transforms })
|
|
22
18
|
}
|
|
23
19
|
|
|
24
20
|
module.exports = transform
|
|
@@ -17,7 +17,7 @@ const applyTransforms = (geometry) => {
|
|
|
17
17
|
// const isMirror = mat4.isMirroring(geometry.transforms)
|
|
18
18
|
// TBD if (isMirror) newvertices.reverse()
|
|
19
19
|
geometry.polygons = geometry.polygons.map((polygon) => poly3.transform(geometry.transforms, polygon))
|
|
20
|
-
mat4.
|
|
20
|
+
geometry.transforms = mat4.create()
|
|
21
21
|
return geometry
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
const mat4 = require('../../maths/mat4')
|
|
2
|
-
|
|
3
|
-
const poly3 = require('../poly3')
|
|
4
|
-
|
|
5
|
-
const create = require('./create')
|
|
6
|
-
|
|
7
1
|
/**
|
|
8
|
-
* Performs a
|
|
2
|
+
* Performs a shallow clone of the given geometry.
|
|
9
3
|
* @param {geom3} geometry - the geometry to clone
|
|
10
4
|
* @returns {geom3} a new geometry
|
|
11
5
|
* @alias module:modeling/geometries/geom3.clone
|
|
12
6
|
*/
|
|
13
|
-
const clone = (geometry) => {
|
|
14
|
-
const out = create()
|
|
15
|
-
out.polygons = geometry.polygons.map((polygon) => poly3.clone(polygon))
|
|
16
|
-
out.isRetesselated = geometry.isRetesselated
|
|
17
|
-
out.transforms = mat4.clone(geometry.transforms)
|
|
18
|
-
return out
|
|
19
|
-
}
|
|
7
|
+
const clone = (geometry) => Object.assign({}, geometry)
|
|
20
8
|
|
|
21
9
|
module.exports = clone
|
|
@@ -7,7 +7,6 @@ const { comparePolygons, compareVectors } = require('../../../test/helpers/')
|
|
|
7
7
|
test('clone: Creates a clone on an empty geom3', (t) => {
|
|
8
8
|
const expected = {
|
|
9
9
|
polygons: [],
|
|
10
|
-
isRetesselated: false,
|
|
11
10
|
transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
|
|
12
11
|
}
|
|
13
12
|
const geometry = create()
|
|
@@ -22,7 +21,6 @@ test('clone: Creates a clone of a populated geom3', (t) => {
|
|
|
22
21
|
polygons: [
|
|
23
22
|
{ vertices: [[0, 0, 0], [1, 0, 0], [1, 0, 1]] }
|
|
24
23
|
],
|
|
25
|
-
isRetesselated: false,
|
|
26
24
|
transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
|
|
27
25
|
}
|
|
28
26
|
const geometry = fromPoints(points)
|
|
@@ -4,7 +4,6 @@ const mat4 = require('../../maths/mat4')
|
|
|
4
4
|
* Represents a 3D geometry consisting of a list of polygons.
|
|
5
5
|
* @typedef {Object} geom3
|
|
6
6
|
* @property {Array} polygons - list of polygons, each polygon containing three or more points
|
|
7
|
-
* @property {Boolean} isRetesselated - true if retesselation has been performed
|
|
8
7
|
* @property {mat4} transforms - transforms to apply to the sides, see transform()
|
|
9
8
|
*/
|
|
10
9
|
|
|
@@ -20,7 +19,6 @@ const create = (polygons) => {
|
|
|
20
19
|
}
|
|
21
20
|
return {
|
|
22
21
|
polygons: polygons,
|
|
23
|
-
isRetesselated: false,
|
|
24
22
|
transforms: mat4.create()
|
|
25
23
|
}
|
|
26
24
|
}
|
|
@@ -7,7 +7,6 @@ const { create } = require('./index')
|
|
|
7
7
|
test('create: Creates an empty geom3', (t) => {
|
|
8
8
|
const expected = {
|
|
9
9
|
polygons: [],
|
|
10
|
-
isRetesselated: false,
|
|
11
10
|
transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
|
|
12
11
|
}
|
|
13
12
|
t.deepEqual(create(), expected)
|
|
@@ -20,7 +19,6 @@ test('create: Creates a populated geom3', (t) => {
|
|
|
20
19
|
const polygons = [polygon]
|
|
21
20
|
const expected = {
|
|
22
21
|
polygons: polygons,
|
|
23
|
-
isRetesselated: false,
|
|
24
22
|
transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
|
|
25
23
|
}
|
|
26
24
|
t.deepEqual(create(polygons), expected)
|
|
@@ -18,10 +18,8 @@ const fromCompactBinary = (data) => {
|
|
|
18
18
|
|
|
19
19
|
created.transforms = mat4.clone(data.slice(1, 17))
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const numberOfVertices = data[22]
|
|
24
|
-
let ci = 23
|
|
21
|
+
const numberOfVertices = data[21]
|
|
22
|
+
let ci = 22
|
|
25
23
|
let vi = data.length - (numberOfVertices * 3)
|
|
26
24
|
while (vi < data.length) {
|
|
27
25
|
const verticesPerPolygon = data[ci]
|
|
@@ -36,8 +34,8 @@ const fromCompactBinary = (data) => {
|
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
// transfer known properities, i.e. color
|
|
39
|
-
if (data[
|
|
40
|
-
created.color = [data[
|
|
37
|
+
if (data[17] >= 0) {
|
|
38
|
+
created.color = [data[17], data[18], data[19], data[20]]
|
|
41
39
|
}
|
|
42
40
|
// TODO: how about custom properties or fields ?
|
|
43
41
|
return created
|
|
@@ -12,7 +12,6 @@ test('toCompactBinary: converts geom3 (default)', (t) => {
|
|
|
12
12
|
0, 1, 0, 0,
|
|
13
13
|
0, 0, 1, 0,
|
|
14
14
|
0, 0, 0, 1,
|
|
15
|
-
0, // isRetesselated flag
|
|
16
15
|
-1, -1, -1, -1, // color
|
|
17
16
|
0 // number of vertices
|
|
18
17
|
]
|
|
@@ -32,7 +31,6 @@ test('toCompactBinary: converts geom3 into a compact form', (t) => {
|
|
|
32
31
|
0, 1, 0, 0,
|
|
33
32
|
0, 0, 1, 0,
|
|
34
33
|
0, 0, 0, 1,
|
|
35
|
-
0, // isRetesselated flag
|
|
36
34
|
-1, -1, -1, -1, // color
|
|
37
35
|
7, // number of vertices
|
|
38
36
|
3, // number of vertices per polygon (2)
|
|
@@ -59,7 +57,6 @@ test('toCompactBinary: converts geom3 into a compact form', (t) => {
|
|
|
59
57
|
0, 1, 0, 0,
|
|
60
58
|
0, 0, 1, 0,
|
|
61
59
|
0, 0, 0, 1,
|
|
62
|
-
0, // isRetesselated flag
|
|
63
60
|
1, 2, 3, 4, // color
|
|
64
61
|
7, // number of vertices
|
|
65
62
|
3, // number of vertices per polygon (2)
|
|
@@ -84,7 +81,6 @@ test('fromCompactBinary: convert a compact form into a geom3', (t) => {
|
|
|
84
81
|
0, 1, 0, 0,
|
|
85
82
|
0, 0, 1, 0,
|
|
86
83
|
0, 0, 0, 1,
|
|
87
|
-
0, // isRetesselated flag
|
|
88
84
|
-1, -1, -1, -1, // color
|
|
89
85
|
0 // number of vertices
|
|
90
86
|
]
|
|
@@ -99,7 +95,6 @@ test('fromCompactBinary: convert a compact form into a geom3', (t) => {
|
|
|
99
95
|
0, 1, 0, 0,
|
|
100
96
|
0, 0, 1, 0,
|
|
101
97
|
0, 0, 0, 1,
|
|
102
|
-
0, // isRetesselated flag
|
|
103
98
|
-1, -1, -1, -1, // color
|
|
104
99
|
7, // number of vertices
|
|
105
100
|
3, // number of vertices per polygon (2)
|
|
@@ -125,7 +120,6 @@ test('fromCompactBinary: convert a compact form into a geom3', (t) => {
|
|
|
125
120
|
0, 1, 0, 0,
|
|
126
121
|
0, 0, 1, 0,
|
|
127
122
|
0, 0, 0, 1,
|
|
128
|
-
0, // isRetesselated flag
|
|
129
123
|
4, 5, 6, 7, // color
|
|
130
124
|
7, // number of vertices
|
|
131
125
|
3, // number of vertices per polygon (2)
|
|
@@ -7,7 +7,6 @@ const { comparePolygons, compareVectors } = require('../../../test/helpers/')
|
|
|
7
7
|
test('invert: Creates a invert on an empty geom3', (t) => {
|
|
8
8
|
const expected = {
|
|
9
9
|
polygons: [],
|
|
10
|
-
isRetesselated: false,
|
|
11
10
|
transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
|
|
12
11
|
}
|
|
13
12
|
const geometry = create()
|
|
@@ -22,7 +21,6 @@ test('invert: Creates a invert of a populated geom3', (t) => {
|
|
|
22
21
|
polygons: [
|
|
23
22
|
{ vertices: [[1, 0, 1], [1, 0, 0], [0, 0, 0]] }
|
|
24
23
|
],
|
|
25
|
-
isRetesselated: false,
|
|
26
24
|
transforms: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
|
|
27
25
|
}
|
|
28
26
|
const geometry = fromPoints(points)
|
|
@@ -16,8 +16,8 @@ const toCompactBinary = (geom) => {
|
|
|
16
16
|
if (geom.color) color = geom.color
|
|
17
17
|
|
|
18
18
|
// FIXME why Float32Array?
|
|
19
|
-
const compacted = new Float32Array(1 + 16 +
|
|
20
|
-
// type + transforms +
|
|
19
|
+
const compacted = new Float32Array(1 + 16 + 4 + 1 + numberOfPolygons + (numberOfVertices * 3))
|
|
20
|
+
// type + transforms + color + numberOfPolygons + numberOfVerticesPerPolygon[] + vertices data[]
|
|
21
21
|
|
|
22
22
|
compacted[0] = 1 // type code: 0 => geom2, 1 => geom3 , 2 => path2
|
|
23
23
|
|
|
@@ -38,16 +38,14 @@ const toCompactBinary = (geom) => {
|
|
|
38
38
|
compacted[15] = transforms[14]
|
|
39
39
|
compacted[16] = transforms[15]
|
|
40
40
|
|
|
41
|
-
compacted[17] =
|
|
41
|
+
compacted[17] = color[0]
|
|
42
|
+
compacted[18] = color[1]
|
|
43
|
+
compacted[19] = color[2]
|
|
44
|
+
compacted[20] = color[3]
|
|
42
45
|
|
|
43
|
-
compacted[
|
|
44
|
-
compacted[19] = color[1]
|
|
45
|
-
compacted[20] = color[2]
|
|
46
|
-
compacted[21] = color[3]
|
|
46
|
+
compacted[21] = numberOfVertices
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
let ci = 23
|
|
48
|
+
let ci = 22
|
|
51
49
|
let vi = ci + numberOfPolygons
|
|
52
50
|
polygons.forEach((polygon) => {
|
|
53
51
|
const points = poly3.toPoints(polygon)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const mat4 = require('../../maths/mat4')
|
|
2
2
|
|
|
3
|
-
const create = require('./create')
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* Transform the given geometry using the given matrix.
|
|
7
5
|
* This is a lazy transform of the polygons, as this function only adjusts the transforms.
|
|
@@ -15,11 +13,8 @@ const create = require('./create')
|
|
|
15
13
|
* let newgeometry = transform(fromXRotation(degToRad(90)), geometry)
|
|
16
14
|
*/
|
|
17
15
|
const transform = (matrix, geometry) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
mat4.multiply(newgeometry.transforms, matrix, geometry.transforms)
|
|
22
|
-
return newgeometry
|
|
16
|
+
const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
|
|
17
|
+
return Object.assign({}, geometry, { transforms })
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
module.exports = transform
|
|
@@ -18,7 +18,6 @@ test('transform: Adjusts the transforms of a populated geom3', (t) => {
|
|
|
18
18
|
polygons: [
|
|
19
19
|
{ vertices: [[0, 0, 0], [1, 0, 0], [1, 0, 1]] }
|
|
20
20
|
],
|
|
21
|
-
isRetesselated: false,
|
|
22
21
|
transforms: [0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
|
|
23
22
|
}
|
|
24
23
|
const geometry = fromPoints(points)
|
|
@@ -13,7 +13,7 @@ const applyTransforms = (geometry) => {
|
|
|
13
13
|
if (mat4.isIdentity(geometry.transforms)) return geometry
|
|
14
14
|
|
|
15
15
|
geometry.points = geometry.points.map((point) => vec2.transform(vec2.create(), point, geometry.transforms))
|
|
16
|
-
mat4.
|
|
16
|
+
geometry.transforms = mat4.create()
|
|
17
17
|
return geometry
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
const mat4 = require('../../maths/mat4')
|
|
2
|
-
const vec2 = require('../../maths/vec2')
|
|
3
|
-
|
|
4
|
-
const create = require('./create')
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
|
-
* Performs a
|
|
2
|
+
* Performs a shallow clone of the give geometry.
|
|
8
3
|
* @param {path2} geometry - the geometry to clone
|
|
9
4
|
* @returns {path2} a new path
|
|
10
5
|
* @alias module:modeling/geometries/path2.clone
|
|
11
6
|
*/
|
|
12
|
-
const clone = (geometry) => {
|
|
13
|
-
const out = create()
|
|
14
|
-
out.points = geometry.points.map((point) => vec2.clone(point))
|
|
15
|
-
out.isClosed = geometry.isClosed
|
|
16
|
-
out.transforms = mat4.clone(geometry.transforms)
|
|
17
|
-
return out
|
|
18
|
-
}
|
|
7
|
+
const clone = (geometry) => Object.assign({}, geometry)
|
|
19
8
|
|
|
20
9
|
module.exports = clone
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const mat4 = require('../../maths/mat4')
|
|
2
2
|
|
|
3
|
-
const create = require('./create')
|
|
4
|
-
|
|
5
3
|
/**
|
|
6
4
|
* Transform the given geometry using the given matrix.
|
|
7
5
|
* This is a lazy transform of the points, as this function only adjusts the transforms.
|
|
@@ -15,11 +13,8 @@ const create = require('./create')
|
|
|
15
13
|
* let newpath = transform(fromZRotation(Math.PI / 4), path)
|
|
16
14
|
*/
|
|
17
15
|
const transform = (matrix, geometry) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
mat4.multiply(newgeometry.transforms, matrix, geometry.transforms)
|
|
22
|
-
return newgeometry
|
|
16
|
+
const transforms = mat4.multiply(mat4.create(), matrix, geometry.transforms)
|
|
17
|
+
return Object.assign({}, geometry, { transforms })
|
|
23
18
|
}
|
|
24
19
|
|
|
25
20
|
module.exports = transform
|
|
@@ -13,7 +13,7 @@ const areVerticesConvex = (vertices) => {
|
|
|
13
13
|
const numvertices = vertices.length
|
|
14
14
|
if (numvertices > 2) {
|
|
15
15
|
// note: plane ~= normal point
|
|
16
|
-
const normal = plane.fromPoints(plane.create(), vertices
|
|
16
|
+
const normal = plane.fromPoints(plane.create(), ...vertices)
|
|
17
17
|
let prevprevpos = vertices[numvertices - 2]
|
|
18
18
|
let prevpos = vertices[numvertices - 1]
|
|
19
19
|
for (let i = 0; i < numvertices; i++) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const plane = require('./plane')
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Measure the area of the given polygon.
|
|
@@ -14,19 +14,18 @@ const measureArea = (poly3) => {
|
|
|
14
14
|
}
|
|
15
15
|
const vertices = poly3.vertices
|
|
16
16
|
|
|
17
|
-
// calculate a
|
|
18
|
-
const
|
|
19
|
-
const b = vertices[1]
|
|
20
|
-
const c = vertices[2]
|
|
21
|
-
const ba = vec3.subtract(vec3.create(), b, a)
|
|
22
|
-
const ca = vec3.subtract(vec3.create(), c, a)
|
|
23
|
-
const normal = vec3.cross(ba, ba, ca)
|
|
17
|
+
// calculate a normal vector
|
|
18
|
+
const normal = plane(poly3)
|
|
24
19
|
|
|
25
|
-
//
|
|
20
|
+
// determine direction of projection
|
|
26
21
|
const ax = Math.abs(normal[0])
|
|
27
22
|
const ay = Math.abs(normal[1])
|
|
28
23
|
const az = Math.abs(normal[2])
|
|
29
|
-
|
|
24
|
+
|
|
25
|
+
if (ax + ay + az === 0) {
|
|
26
|
+
// normal does not exist
|
|
27
|
+
return 0
|
|
28
|
+
}
|
|
30
29
|
|
|
31
30
|
let coord = 3 // ignore Z coordinates
|
|
32
31
|
if ((ax > ay) && (ax > az)) {
|
|
@@ -50,7 +49,7 @@ const measureArea = (poly3) => {
|
|
|
50
49
|
}
|
|
51
50
|
area += (vertices[0][1] * (vertices[1][2] - vertices[n - 1][2]))
|
|
52
51
|
// scale to get area
|
|
53
|
-
area
|
|
52
|
+
area /= (2 * normal[0])
|
|
54
53
|
break
|
|
55
54
|
|
|
56
55
|
case 2: // ignore Y coordinates
|
|
@@ -62,7 +61,7 @@ const measureArea = (poly3) => {
|
|
|
62
61
|
}
|
|
63
62
|
area += (vertices[0][2] * (vertices[1][0] - vertices[n - 1][0]))
|
|
64
63
|
// scale to get area
|
|
65
|
-
area
|
|
64
|
+
area /= (2 * normal[1])
|
|
66
65
|
break
|
|
67
66
|
|
|
68
67
|
case 3: // ignore Z coordinates
|
|
@@ -75,7 +74,7 @@ const measureArea = (poly3) => {
|
|
|
75
74
|
}
|
|
76
75
|
area += (vertices[0][0] * (vertices[1][1] - vertices[n - 1][1]))
|
|
77
76
|
// scale to get area
|
|
78
|
-
area
|
|
77
|
+
area /= (2 * normal[2])
|
|
79
78
|
break
|
|
80
79
|
}
|
|
81
80
|
return area
|
|
@@ -37,6 +37,21 @@ test('poly3: measureArea() should return correct values', (t) => {
|
|
|
37
37
|
let ret4 = measureArea(ply4)
|
|
38
38
|
t.is(ret4, 19.5)
|
|
39
39
|
|
|
40
|
+
// colinear vertices non-zero area
|
|
41
|
+
const ply5 = fromPoints([[0, 0, 0], [1, 0, 0], [2, 0, 0], [0, 1, 0]])
|
|
42
|
+
const ret5 = measureArea(ply5)
|
|
43
|
+
t.is(ret5, 1)
|
|
44
|
+
|
|
45
|
+
// colinear vertices empty area
|
|
46
|
+
const ply6 = fromPoints([[0, 0, 0], [1, 0, 0], [2, 0, 0]])
|
|
47
|
+
const ret6 = measureArea(ply6)
|
|
48
|
+
t.is(ret6, 0)
|
|
49
|
+
|
|
50
|
+
// duplicate vertices empty area
|
|
51
|
+
const ply7 = fromPoints([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
|
|
52
|
+
const ret7 = measureArea(ply7)
|
|
53
|
+
t.is(ret7, 0)
|
|
54
|
+
|
|
40
55
|
// rotated to various angles
|
|
41
56
|
let rotation = mat4.fromZRotation(mat4.create(), (45 * 0.017453292519943295))
|
|
42
57
|
ply1 = transform(rotation, ply1)
|
|
@@ -2,8 +2,7 @@ const mplane = require('../../maths/plane/')
|
|
|
2
2
|
|
|
3
3
|
const plane = (polygon) => {
|
|
4
4
|
if (!polygon.plane) {
|
|
5
|
-
|
|
6
|
-
polygon.plane = mplane.fromPoints(mplane.create(), vertices[0], vertices[1], vertices[2])
|
|
5
|
+
polygon.plane = mplane.fromPoints(mplane.create(), ...polygon.vertices)
|
|
7
6
|
}
|
|
8
7
|
return polygon.plane
|
|
9
8
|
}
|
package/src/maths/mat4/index.js
CHANGED
|
@@ -21,6 +21,7 @@ module.exports = {
|
|
|
21
21
|
fromZRotation: require('./fromZRotation'),
|
|
22
22
|
identity: require('./identity'),
|
|
23
23
|
isIdentity: require('./isIdentity'),
|
|
24
|
+
isOnlyTransformScale: require('./isOnlyTransformScale'),
|
|
24
25
|
isMirroring: require('./isMirroring'),
|
|
25
26
|
mirrorByPlane: require('./mirrorByPlane'),
|
|
26
27
|
multiply: require('./multiply'),
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Determine whether the given matrix is translate+scale.
|
|
4
|
+
* this code returns true for 180 rotation as it can be interpreted as scale (-1,-1)
|
|
5
|
+
*
|
|
6
|
+
* this method is primarily useful for measureBoundingBox
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
const isOnlyTransformScale = (matrix) => (
|
|
10
|
+
|
|
11
|
+
// TODO check if it is worth the effort to add recognition of 90 deg rotations
|
|
12
|
+
|
|
13
|
+
isZero(matrix[1]) && isZero(matrix[2]) && isZero(matrix[3]) &&
|
|
14
|
+
isZero(matrix[4]) && isZero(matrix[6]) && isZero(matrix[7]) &&
|
|
15
|
+
isZero(matrix[8]) && isZero(matrix[9]) && isZero(matrix[11]) &&
|
|
16
|
+
matrix[15] === 1
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
const isZero = (num) => Math.abs(num) < Number.EPSILON
|
|
20
|
+
|
|
21
|
+
module.exports = isOnlyTransformScale
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const test = require('ava')
|
|
2
|
+
|
|
3
|
+
const { isOnlyTransformScale, create, fromTranslation, fromTaitBryanRotation, fromScaling, invert, multiply } = require('./index')
|
|
4
|
+
|
|
5
|
+
test('mat4: isOnlyTransformScale() should return true for right angles', (t) => {
|
|
6
|
+
let someRotation = fromTaitBryanRotation(create(), Math.PI, 0, 0)
|
|
7
|
+
t.true(isOnlyTransformScale(someRotation))
|
|
8
|
+
t.true(isOnlyTransformScale(invert(create(), someRotation)))
|
|
9
|
+
|
|
10
|
+
someRotation = fromTaitBryanRotation(create(), 0, 0, 0)
|
|
11
|
+
t.true(isOnlyTransformScale(someRotation))
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('mat4: isOnlyTransformScale() should return correct values', (t) => {
|
|
15
|
+
const identity = create() // identity matrix
|
|
16
|
+
t.true(isOnlyTransformScale(identity))
|
|
17
|
+
|
|
18
|
+
const someTranslation = fromTranslation(create(), [5, 5, 5])
|
|
19
|
+
t.true(isOnlyTransformScale(someTranslation))
|
|
20
|
+
|
|
21
|
+
const someScaling = fromScaling(create(), [5, 5, 5])
|
|
22
|
+
t.true(isOnlyTransformScale(someScaling))
|
|
23
|
+
t.true(isOnlyTransformScale(invert(create(), someScaling)))
|
|
24
|
+
|
|
25
|
+
const combined = multiply(create(), someTranslation, someScaling)
|
|
26
|
+
t.true(isOnlyTransformScale(combined))
|
|
27
|
+
})
|
|
@@ -10,17 +10,39 @@ const vec3 = require('../vec3')
|
|
|
10
10
|
* @returns {plane} out
|
|
11
11
|
* @alias module:modeling/maths/plane.fromPoints
|
|
12
12
|
*/
|
|
13
|
-
const fromPoints = (out,
|
|
14
|
-
const
|
|
15
|
-
const ca = vec3.subtract(vec3.create(), c, a)
|
|
16
|
-
vec3.cross(ba, ba, ca)
|
|
17
|
-
vec3.normalize(ba, ba) // normal part
|
|
18
|
-
const w = vec3.dot(ba, a)
|
|
13
|
+
const fromPoints = (out, ...vertices) => {
|
|
14
|
+
const len = vertices.length
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
// Calculate normal vector for a single vertex
|
|
17
|
+
// Inline to avoid allocations
|
|
18
|
+
const ba = vec3.create()
|
|
19
|
+
const ca = vec3.create()
|
|
20
|
+
const vertexNormal = (index) => {
|
|
21
|
+
const a = vertices[index]
|
|
22
|
+
const b = vertices[(index + 1) % len]
|
|
23
|
+
const c = vertices[(index + 2) % len]
|
|
24
|
+
vec3.subtract(ba, b, a) // ba = b - a
|
|
25
|
+
vec3.subtract(ca, c, a) // ca = c - a
|
|
26
|
+
vec3.cross(ba, ba, ca) // ba = ba x ca
|
|
27
|
+
vec3.normalize(ba, ba)
|
|
28
|
+
return ba
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
out[0] = 0
|
|
32
|
+
out[1] = 0
|
|
33
|
+
out[2] = 0
|
|
34
|
+
if (len === 3) {
|
|
35
|
+
// optimization for triangles, which are always coplanar
|
|
36
|
+
vec3.copy(out, vertexNormal(0))
|
|
37
|
+
} else {
|
|
38
|
+
// sum of vertex normals
|
|
39
|
+
vertices.forEach((v, i) => {
|
|
40
|
+
vec3.add(out, out, vertexNormal(i))
|
|
41
|
+
})
|
|
42
|
+
// renormalize normal vector
|
|
43
|
+
vec3.normalize(out, out)
|
|
44
|
+
}
|
|
45
|
+
out[3] = vec3.dot(out, vertices[0])
|
|
24
46
|
return out
|
|
25
47
|
}
|
|
26
48
|
|