@jscad/modeling 2.7.2 → 2.8.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 +23 -0
- package/dist/jscad-modeling.min.js +136 -133
- 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.js +10 -0
- 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/geom3/fromCompactBinary.js +1 -1
- package/src/geometries/geom3/index.js +17 -0
- 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/index.js +8 -1
- package/src/geometries/path2/eachPoint.js +3 -3
- package/src/geometries/path2/index.js +11 -0
- 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/poly2/arePointsInside.test.js +1 -1
- package/src/geometries/poly2/index.js +6 -0
- package/src/geometries/poly3/index.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/measureBoundingBox.js +2 -2
- package/src/geometries/poly3/measureBoundingSphere.js +2 -2
- 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/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/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/subtract.js +1 -1
- package/src/operations/booleans/union.test.js +1 -1
- package/src/operations/booleans/unionGeom3Sub.js +1 -1
- package/src/operations/expansions/expand.js +2 -2
- package/src/operations/expansions/expand.test.js +3 -35
- package/src/operations/expansions/expandShell.js +24 -18
- package/src/operations/expansions/offset.js +1 -1
- package/src/operations/expansions/offset.test.js +25 -89
- package/src/operations/expansions/offsetFromPoints.js +11 -6
- package/src/operations/extrusions/extrudeLinear.js +6 -2
- package/src/operations/extrusions/extrudeLinear.test.js +25 -1
- package/src/operations/extrusions/extrudeLinearPath2.js +24 -0
- package/src/operations/extrusions/extrudeRectangular.js +1 -1
- package/src/operations/extrusions/extrudeRectangular.test.js +2 -2
- package/src/operations/extrusions/project.js +1 -1
- package/src/operations/extrusions/slice/isA.js +2 -2
- package/src/operations/extrusions/slice/toPolygons.js +1 -1
- package/src/operations/hulls/hull.test.js +1 -1
- package/src/operations/hulls/hullChain.js +1 -1
- 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/snap.test.js +3 -3
- package/src/operations/transforms/align.d.ts +1 -1
- package/src/operations/transforms/center.js +17 -17
- package/src/operations/transforms/mirror.js +12 -12
- package/src/operations/transforms/rotate.js +12 -12
- package/src/operations/transforms/scale.js +19 -19
- package/src/operations/transforms/transform.js +3 -3
- package/src/operations/transforms/translate.js +14 -14
- package/src/primitives/arc.js +1 -1
- package/src/primitives/cylinderElliptic.test.js +0 -2
- package/src/primitives/ellipsoid.js +1 -1
- package/src/primitives/ellipsoid.test.js +0 -2
- package/src/primitives/geodesicSphere.d.ts +0 -1
- package/src/primitives/polyhedron.js +1 -1
- package/src/primitives/torus.d.ts +0 -1
- package/src/primitives/triangle.js +1 -1
- package/src/text/vectorText.js +2 -2
- package/src/utils/padArrayToLength.js +1 -1
|
@@ -3,7 +3,7 @@ const { transform, fromValues } = require('./index')
|
|
|
3
3
|
|
|
4
4
|
const { compareVectors } = require('../../../test/helpers/index')
|
|
5
5
|
|
|
6
|
-
test('vec3: transform() called with three
|
|
6
|
+
test('vec3: transform() called with three parameters should update a vec3 with correct values', (t) => {
|
|
7
7
|
const identityMatrix = [
|
|
8
8
|
1, 0, 0, 0,
|
|
9
9
|
0, 1, 0, 0,
|
|
@@ -3,7 +3,7 @@ const { transform, fromValues } = require('./index')
|
|
|
3
3
|
|
|
4
4
|
const { compareVectors } = require('../../../test/helpers/index')
|
|
5
5
|
|
|
6
|
-
test('vec4: transform() called with three
|
|
6
|
+
test('vec4: transform() called with three parameters should update a vec4 with correct values', (t) => {
|
|
7
7
|
const identityMatrix = [
|
|
8
8
|
1, 0, 0, 0,
|
|
9
9
|
0, 1, 0, 0,
|
|
@@ -24,7 +24,7 @@ const measureBoundingSphereOfPath2 = (geometry) => {
|
|
|
24
24
|
const points = path2.toPoints(geometry)
|
|
25
25
|
|
|
26
26
|
if (points.length > 0) {
|
|
27
|
-
// calculate the
|
|
27
|
+
// calculate the centroid of the geometry
|
|
28
28
|
let numPoints = 0
|
|
29
29
|
const temp = vec3.create()
|
|
30
30
|
points.forEach((point) => {
|
|
@@ -60,7 +60,7 @@ const measureBoundingSphereOfGeom2 = (geometry) => {
|
|
|
60
60
|
const sides = geom2.toSides(geometry)
|
|
61
61
|
|
|
62
62
|
if (sides.length > 0) {
|
|
63
|
-
// calculate the
|
|
63
|
+
// calculate the centroid of the geometry
|
|
64
64
|
let numPoints = 0
|
|
65
65
|
const temp = vec3.create()
|
|
66
66
|
sides.forEach((side) => {
|
|
@@ -96,7 +96,7 @@ const measureBoundingSphereOfGeom3 = (geometry) => {
|
|
|
96
96
|
const polygons = geom3.toPolygons(geometry)
|
|
97
97
|
|
|
98
98
|
if (polygons.length > 0) {
|
|
99
|
-
// calculate the
|
|
99
|
+
// calculate the centroid of the geometry
|
|
100
100
|
let numPoints = 0
|
|
101
101
|
polygons.forEach((polygon) => {
|
|
102
102
|
poly3.toPoints(polygon).forEach((point) => {
|
|
@@ -122,7 +122,7 @@ const measureBoundingSphereOfGeom3 = (geometry) => {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
* Measure the (
|
|
125
|
+
* Measure the (approximate) bounding sphere of the given geometries.
|
|
126
126
|
* @see https://en.wikipedia.org/wiki/Bounding_sphere
|
|
127
127
|
* @param {...Object} geometries - the geometries to measure
|
|
128
128
|
* @return {Array} the bounding sphere for each geometry, i.e. [centroid, radius]
|
|
@@ -61,7 +61,7 @@ const measureCenterOfMassGeom3 = (geometry) => {
|
|
|
61
61
|
let totalVolume = 0
|
|
62
62
|
const vector = vec3.create() // for speed
|
|
63
63
|
polygons.forEach((polygon) => {
|
|
64
|
-
// calculate volume and center of each
|
|
64
|
+
// calculate volume and center of each tetrahedron
|
|
65
65
|
const vertices = polygon.vertices
|
|
66
66
|
for (let i = 0; i < vertices.length - 2; i++) {
|
|
67
67
|
vec3.cross(vector, vertices[i + 1], vertices[i + 2])
|
|
@@ -4,9 +4,9 @@ const measureBoundingBox = require('../../measurements/measureBoundingBox')
|
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
6
|
* Determine if the given geometries overlap by comparing min and max bounds.
|
|
7
|
-
* NOTE: This is used in union for
|
|
8
|
-
* @param {geom3} geometry1 - geometry for
|
|
9
|
-
* @param {geom3} geometry2 - geometry for
|
|
7
|
+
* NOTE: This is used in union for performance gains.
|
|
8
|
+
* @param {geom3} geometry1 - geometry for comparison
|
|
9
|
+
* @param {geom3} geometry2 - geometry for comparison
|
|
10
10
|
* @returns {boolean} true if the geometries overlap
|
|
11
11
|
*/
|
|
12
12
|
const mayOverlap = (geometry1, geometry2) => {
|
|
@@ -3,8 +3,8 @@ const poly3 = require('../../geometries/poly3')
|
|
|
3
3
|
|
|
4
4
|
const reTesselateCoplanarPolygons = require('./reTesselateCoplanarPolygons')
|
|
5
5
|
|
|
6
|
-
// Normals are directional vectors with component values from 0 to 1.0, requiring specialized
|
|
7
|
-
// This EPS is derived from a
|
|
6
|
+
// Normals are directional vectors with component values from 0 to 1.0, requiring specialized comparison
|
|
7
|
+
// This EPS is derived from a series of tests to determine the optimal precision for comparing coplanar polygons,
|
|
8
8
|
// as provided by the sphere primitive at high segmentation
|
|
9
9
|
// This EPS is for 64 bit Number values
|
|
10
10
|
const NEPS = 1e-13
|
|
@@ -9,7 +9,7 @@ const scissionGeom3 = require('./scissionGeom3')
|
|
|
9
9
|
/**
|
|
10
10
|
* Scission (divide) the given geometry into the component pieces.
|
|
11
11
|
*
|
|
12
|
-
* @param {...Object}
|
|
12
|
+
* @param {...Object} objects - list of geometries
|
|
13
13
|
* @returns {Array} list of pieces from each geometry
|
|
14
14
|
* @alias module:modeling/booleans.scission
|
|
15
15
|
*
|
|
@@ -17,7 +17,7 @@ const subtractGeom3 = require('./subtractGeom3')
|
|
|
17
17
|
* @alias module:modeling/booleans.subtract
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
|
-
* let myshape = subtract(
|
|
20
|
+
* let myshape = subtract(cuboid({size: [5,5,5]}), cuboid({size: [5,5,5], center: [5,5,5]}))
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* +-------+ +-------+
|
|
@@ -198,7 +198,7 @@ test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
|
198
198
|
|
|
199
199
|
test('union of geom3 with rounding issues #137', (t) => {
|
|
200
200
|
const geometry1 = center({ relativeTo: [0, 0, -1] }, cuboid({ size: [44, 26, 5] }))
|
|
201
|
-
const geometry2 = center({ relativeTo: [0, 0, -4.400001] }, cuboid({ size: [44, 26, 1.8] })) // introduce
|
|
201
|
+
const geometry2 = center({ relativeTo: [0, 0, -4.400001] }, cuboid({ size: [44, 26, 1.8] })) // introduce precision error
|
|
202
202
|
|
|
203
203
|
const obs = union(geometry1, geometry2)
|
|
204
204
|
const pts = geom3.toPoints(obs)
|
|
@@ -7,7 +7,7 @@ const { Tree } = require('./trees')
|
|
|
7
7
|
* Return a new 3D geometry representing the space in the given geometries.
|
|
8
8
|
* @param {geom3} geometry1 - geometry to union
|
|
9
9
|
* @param {geom3} geometry2 - geometry to union
|
|
10
|
-
* @returns {
|
|
10
|
+
* @returns {geom3} new 3D geometry
|
|
11
11
|
*/
|
|
12
12
|
const unionSub = (geometry1, geometry2) => {
|
|
13
13
|
if (!mayOverlap(geometry1, geometry2)) {
|
|
@@ -10,14 +10,14 @@ const expandPath2 = require('./expandPath2')
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Expand the given geometry using the given options.
|
|
13
|
-
* Both
|
|
13
|
+
* Both internal and external space is expanded for 2D and 3D shapes.
|
|
14
14
|
*
|
|
15
15
|
* Note: Contract is expand using a negative delta.
|
|
16
16
|
* @param {Object} options - options for expand
|
|
17
17
|
* @param {Number} [options.delta=1] - delta (+/-) of expansion
|
|
18
18
|
* @param {String} [options.corners='edge'] - type of corner to create after expanding; edge, chamfer, round
|
|
19
19
|
* @param {Integer} [options.segments=16] - number of segments when creating round corners
|
|
20
|
-
* @param {...Objects}
|
|
20
|
+
* @param {...Objects} objects - the geometries to expand
|
|
21
21
|
* @return {Object|Array} new geometry, or list of new geometries
|
|
22
22
|
* @alias module:modeling/expansions.expand
|
|
23
23
|
*
|
|
@@ -120,7 +120,7 @@ test('expand: expanding of a geom3 produces expected changes to polygons', (t) =
|
|
|
120
120
|
const geometry2 = sphere({ radius: 5, segments: 8 })
|
|
121
121
|
const obs2 = expand({ delta: 5 }, geometry2)
|
|
122
122
|
const pts2 = geom3.toPoints(obs2)
|
|
123
|
-
t.is(pts2.length,
|
|
123
|
+
t.is(pts2.length, 1612)
|
|
124
124
|
})
|
|
125
125
|
|
|
126
126
|
test('expand (options): offsetting of a complex geom2 produces expected offset geom2', (t) => {
|
|
@@ -151,59 +151,27 @@ test('expand (options): offsetting of a complex geom2 produces expected offset g
|
|
|
151
151
|
const obs = expand({ delta: 2, corners: 'edge' }, geometry)
|
|
152
152
|
const pts = geom2.toPoints(obs)
|
|
153
153
|
const exp = [
|
|
154
|
-
[-77, -77],
|
|
155
|
-
[-75, -77],
|
|
156
|
-
[75, -77],
|
|
157
154
|
[77, -77],
|
|
158
|
-
[77, -75],
|
|
159
|
-
[77, 75],
|
|
160
155
|
[77, 77],
|
|
161
|
-
[75, 77],
|
|
162
|
-
[40, 77],
|
|
163
156
|
[38, 77],
|
|
164
|
-
[38, 75],
|
|
165
157
|
[38, 2],
|
|
166
158
|
[-38, 2],
|
|
167
|
-
[-38, 75],
|
|
168
159
|
[-37.99999999999999, 77],
|
|
169
|
-
[-40, 77],
|
|
170
|
-
[-75, 77],
|
|
171
160
|
[-77, 77],
|
|
172
|
-
[-77, 75],
|
|
173
|
-
[17, -40],
|
|
174
161
|
[16.999999999999996, -42],
|
|
175
|
-
[15, -42],
|
|
176
|
-
[8, -42],
|
|
177
162
|
[6, -42],
|
|
178
|
-
[6, -40],
|
|
179
163
|
[6, -27],
|
|
180
164
|
[-6, -27],
|
|
181
|
-
[-6, -40],
|
|
182
165
|
[-6.000000000000001, -42],
|
|
183
|
-
[-8, -42],
|
|
184
|
-
[-15, -42],
|
|
185
166
|
[-17, -42],
|
|
186
|
-
[-17, -40],
|
|
187
|
-
[-17, -10],
|
|
188
167
|
[-16.999999999999996, -8],
|
|
189
|
-
[-15, -8],
|
|
190
|
-
[15, -8],
|
|
191
168
|
[17, -8.000000000000004],
|
|
192
|
-
[17, -10],
|
|
193
|
-
[-4, -19],
|
|
194
169
|
[-4, -21],
|
|
195
|
-
[-2, -21],
|
|
196
|
-
[1.9999999999999998, -21],
|
|
197
170
|
[3.9999999999999996, -21],
|
|
198
|
-
[4, -19],
|
|
199
|
-
[4, -15],
|
|
200
171
|
[4, -13],
|
|
201
|
-
[2, -13],
|
|
202
|
-
[-1.9999999999999998, -13],
|
|
203
172
|
[-4, -13],
|
|
204
|
-
[-
|
|
205
|
-
[-77, -75]
|
|
173
|
+
[-77, -77]
|
|
206
174
|
]
|
|
207
|
-
t.is(pts.length,
|
|
175
|
+
t.is(pts.length, 20)
|
|
208
176
|
t.true(comparePoints(pts, exp))
|
|
209
177
|
})
|
|
@@ -15,37 +15,43 @@ const unionGeom3Sub = require('../booleans/unionGeom3Sub')
|
|
|
15
15
|
|
|
16
16
|
const extrudePolygon = require('./extrudePolygon')
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Collect all planes adjacent to each vertex
|
|
20
|
+
*/
|
|
18
21
|
const mapPlaneToVertex = (map, vertex, plane) => {
|
|
19
|
-
const
|
|
20
|
-
if (
|
|
22
|
+
const key = vertex.toString()
|
|
23
|
+
if (!map.has(key)) {
|
|
21
24
|
const entry = [vertex, [plane]]
|
|
22
|
-
map.
|
|
23
|
-
|
|
25
|
+
map.set(key, entry)
|
|
26
|
+
} else {
|
|
27
|
+
const planes = map.get(key)[1]
|
|
28
|
+
planes.push(plane)
|
|
24
29
|
}
|
|
25
|
-
const planes = map[i][1]
|
|
26
|
-
planes.push(plane)
|
|
27
|
-
return i
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Collect all planes adjacent to each edge.
|
|
34
|
+
* Combine undirected edges, no need for duplicate cylinders.
|
|
35
|
+
*/
|
|
30
36
|
const mapPlaneToEdge = (map, edge, plane) => {
|
|
31
|
-
const
|
|
32
|
-
|
|
37
|
+
const key0 = edge[0].toString()
|
|
38
|
+
const key1 = edge[1].toString()
|
|
39
|
+
// Sort keys to make edges undirected
|
|
40
|
+
const key = key0 < key1 ? `${key0},${key1}` : `${key1},${key0}`
|
|
41
|
+
if (!map.has(key)) {
|
|
33
42
|
const entry = [edge, [plane]]
|
|
34
|
-
map.
|
|
35
|
-
|
|
43
|
+
map.set(key, entry)
|
|
44
|
+
} else {
|
|
45
|
+
const planes = map.get(key)[1]
|
|
46
|
+
planes.push(plane)
|
|
36
47
|
}
|
|
37
|
-
const planes = map[i][1]
|
|
38
|
-
planes.push(plane)
|
|
39
|
-
return i
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
const addUniqueAngle = (map, angle) => {
|
|
43
51
|
const i = map.findIndex((item) => item === angle)
|
|
44
52
|
if (i < 0) {
|
|
45
53
|
map.push(angle)
|
|
46
|
-
return map.length
|
|
47
54
|
}
|
|
48
|
-
return i
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
/*
|
|
@@ -65,8 +71,8 @@ const expandShell = (options, geometry) => {
|
|
|
65
71
|
const { delta, segments } = Object.assign({ }, defaults, options)
|
|
66
72
|
|
|
67
73
|
let result = geom3.create()
|
|
68
|
-
const vertices2planes =
|
|
69
|
-
const edges2planes =
|
|
74
|
+
const vertices2planes = new Map() // {vertex: [vertex, [plane, ...]]}
|
|
75
|
+
const edges2planes = new Map() // {edge: [[vertex, vertex], [plane, ...]]}
|
|
70
76
|
|
|
71
77
|
const v1 = vec3.create()
|
|
72
78
|
const v2 = vec3.create()
|
|
@@ -13,7 +13,7 @@ const offsetPath2 = require('./offsetPath2')
|
|
|
13
13
|
* @param {Float} [options.delta=1] - delta of offset (+ to exterior, - from interior)
|
|
14
14
|
* @param {String} [options.corners='edge'] - type of corner to create after offseting; edge, chamfer, round
|
|
15
15
|
* @param {Integer} [options.segments=16] - number of segments when creating round corners
|
|
16
|
-
* @param {...Object}
|
|
16
|
+
* @param {...Object} objects - the geometries to offset
|
|
17
17
|
* @return {Object|Array} new geometry, or list of new geometries
|
|
18
18
|
* @alias module:modeling/expansions.offset
|
|
19
19
|
*
|
|
@@ -46,7 +46,7 @@ test('offset: offsetting a bent line produces expected geometry', (t) => {
|
|
|
46
46
|
// offset it by -2.
|
|
47
47
|
offsetLinePath2 = offset({ delta: -2, corners: 'edge', segments: 8 }, linePath2)
|
|
48
48
|
offsetPoints = path2.toPoints(offsetLinePath2)
|
|
49
|
-
t.is(offsetPoints.length,
|
|
49
|
+
t.is(offsetPoints.length, 5)
|
|
50
50
|
boundingBox = measureBoundingBox(offsetLinePath2)
|
|
51
51
|
t.true(comparePoints(boundingBox, [[-2, 0, 0], [10, 12, 0]]), 'Unexpected bounding box: ' + JSON.stringify(boundingBox))
|
|
52
52
|
})
|
|
@@ -124,20 +124,12 @@ test('offset (corners: edge): offset of a path2 produces expected offset path2',
|
|
|
124
124
|
let pts = path2.toPoints(obs)
|
|
125
125
|
let exp = [
|
|
126
126
|
[-5, -6],
|
|
127
|
-
[5, -6],
|
|
128
127
|
[6, -6],
|
|
129
|
-
[6, -5],
|
|
130
|
-
[6, 5],
|
|
131
128
|
[6, 6],
|
|
132
|
-
[5, 6],
|
|
133
|
-
[3, 6],
|
|
134
129
|
[2, 6],
|
|
135
|
-
[2, 5],
|
|
136
130
|
[2, 1],
|
|
137
131
|
[-2, 1],
|
|
138
|
-
[-2, 5],
|
|
139
132
|
[-1.9999999999999996, 6],
|
|
140
|
-
[-3, 6],
|
|
141
133
|
[-5, 6]
|
|
142
134
|
]
|
|
143
135
|
t.true(comparePoints(pts, exp))
|
|
@@ -145,26 +137,14 @@ test('offset (corners: edge): offset of a path2 produces expected offset path2',
|
|
|
145
137
|
obs = offset({ delta: 1, corners: 'edge' }, closeline)
|
|
146
138
|
pts = path2.toPoints(obs)
|
|
147
139
|
exp = [
|
|
148
|
-
[-6, -6],
|
|
149
|
-
[-5, -6],
|
|
150
|
-
[5, -6],
|
|
151
140
|
[6, -6],
|
|
152
|
-
[6, -5],
|
|
153
|
-
[6, 5],
|
|
154
141
|
[6, 6],
|
|
155
|
-
[5, 6],
|
|
156
|
-
[3, 6],
|
|
157
142
|
[2, 6],
|
|
158
|
-
[2, 5],
|
|
159
143
|
[2, 1],
|
|
160
144
|
[-2, 1],
|
|
161
|
-
[-2, 5],
|
|
162
145
|
[-1.9999999999999996, 6],
|
|
163
|
-
[-3, 6],
|
|
164
|
-
[-5, 6],
|
|
165
146
|
[-6, 6],
|
|
166
|
-
[-6,
|
|
167
|
-
[-6, -5]
|
|
147
|
+
[-6, -6]
|
|
168
148
|
]
|
|
169
149
|
t.true(comparePoints(pts, exp))
|
|
170
150
|
|
|
@@ -175,12 +155,8 @@ test('offset (corners: edge): offset of a path2 produces expected offset path2',
|
|
|
175
155
|
[4.5, -4.5],
|
|
176
156
|
[4.5, 4.5],
|
|
177
157
|
[3.5, 4.5],
|
|
178
|
-
[3.5, -3.061616997868383e-17],
|
|
179
158
|
[3.4999999999999996, -0.5],
|
|
180
|
-
[3, -0.5],
|
|
181
|
-
[-3, -0.5],
|
|
182
159
|
[-3.5, -0.4999999999999996],
|
|
183
|
-
[-3.5, 3.061616997868383e-17],
|
|
184
160
|
[-3.5, 4.5],
|
|
185
161
|
[-5, 4.5]
|
|
186
162
|
]
|
|
@@ -193,12 +169,8 @@ test('offset (corners: edge): offset of a path2 produces expected offset path2',
|
|
|
193
169
|
[4.5, -4.5],
|
|
194
170
|
[4.5, 4.5],
|
|
195
171
|
[3.5, 4.5],
|
|
196
|
-
[3.5, -3.061616997868383e-17],
|
|
197
172
|
[3.4999999999999996, -0.5],
|
|
198
|
-
[3, -0.5],
|
|
199
|
-
[-3, -0.5],
|
|
200
173
|
[-3.5, -0.4999999999999996],
|
|
201
|
-
[-3.5, 3.061616997868383e-17],
|
|
202
174
|
[-3.5, 4.5],
|
|
203
175
|
[-4.5, 4.5]
|
|
204
176
|
]
|
|
@@ -373,26 +345,14 @@ test('offset (options): offsetting of a simple geom2 produces expected offset ge
|
|
|
373
345
|
obs = offset({ delta: 1, corners: 'edge' }, geometry)
|
|
374
346
|
pts = geom2.toPoints(obs)
|
|
375
347
|
exp = [
|
|
376
|
-
[-6, -6],
|
|
377
|
-
[-5, -6],
|
|
378
|
-
[5, -6],
|
|
379
348
|
[6, -6],
|
|
380
|
-
[6, -5],
|
|
381
|
-
[6, 5],
|
|
382
349
|
[6, 6],
|
|
383
|
-
[5, 6],
|
|
384
|
-
[3, 6],
|
|
385
350
|
[2, 6],
|
|
386
|
-
[2, 5],
|
|
387
351
|
[2, 1],
|
|
388
352
|
[-2, 1],
|
|
389
|
-
[-2, 5],
|
|
390
353
|
[-1.9999999999999996, 6],
|
|
391
|
-
[-3, 6],
|
|
392
|
-
[-5, 6],
|
|
393
354
|
[-6, 6],
|
|
394
|
-
[-6,
|
|
395
|
-
[-6, -5]
|
|
355
|
+
[-6, -6]
|
|
396
356
|
]
|
|
397
357
|
t.true(comparePoints(pts, exp))
|
|
398
358
|
|
|
@@ -422,78 +382,54 @@ test('offset (options): offsetting of a simple geom2 produces expected offset ge
|
|
|
422
382
|
|
|
423
383
|
test('offset (options): offsetting of a complex geom2 produces expected offset geom2', (t) => {
|
|
424
384
|
const geometry = geom2.create([
|
|
425
|
-
[[-75
|
|
426
|
-
[[-75
|
|
427
|
-
[[75
|
|
428
|
-
[[-40
|
|
429
|
-
[[75
|
|
430
|
-
[[40
|
|
431
|
-
[[40
|
|
432
|
-
[[-40
|
|
433
|
-
[[15
|
|
434
|
-
[[-15
|
|
435
|
-
[[-15
|
|
436
|
-
[[-8
|
|
437
|
-
[[15
|
|
438
|
-
[[-8
|
|
439
|
-
[[8
|
|
440
|
-
[[8
|
|
441
|
-
[[-2
|
|
442
|
-
[[-2
|
|
443
|
-
[[2
|
|
444
|
-
[[2
|
|
385
|
+
[[-75, 75], [-75, -75]],
|
|
386
|
+
[[-75, -75], [75, -75]],
|
|
387
|
+
[[75, -75], [75, 75]],
|
|
388
|
+
[[-40, 75], [-75, 75]],
|
|
389
|
+
[[75, 75], [40, 75]],
|
|
390
|
+
[[40, 75], [40, 0]],
|
|
391
|
+
[[40, 0], [-40, 0]],
|
|
392
|
+
[[-40, 0], [-40, 75]],
|
|
393
|
+
[[15, -10], [15, -40]],
|
|
394
|
+
[[-15, -10], [15, -10]],
|
|
395
|
+
[[-15, -40], [-15, -10]],
|
|
396
|
+
[[-8, -40], [-15, -40]],
|
|
397
|
+
[[15, -40], [8, -40]],
|
|
398
|
+
[[-8, -25], [-8, -40]],
|
|
399
|
+
[[8, -25], [-8, -25]],
|
|
400
|
+
[[8, -40], [8, -25]],
|
|
401
|
+
[[-2, -15], [-2, -19]],
|
|
402
|
+
[[-2, -19], [2, -19]],
|
|
403
|
+
[[2, -19], [2, -15]],
|
|
404
|
+
[[2, -15], [-2, -15]]
|
|
445
405
|
])
|
|
446
406
|
|
|
447
407
|
// expand +
|
|
448
408
|
const obs = offset({ delta: 2, corners: 'edge' }, geometry)
|
|
449
409
|
const pts = geom2.toPoints(obs)
|
|
450
410
|
const exp = [
|
|
451
|
-
[-77, -77],
|
|
452
|
-
[-75, -77],
|
|
453
|
-
[75, -77],
|
|
454
411
|
[77, -77],
|
|
455
|
-
[77, -75],
|
|
456
|
-
[77, 75],
|
|
457
412
|
[77, 77],
|
|
458
|
-
[75, 77],
|
|
459
|
-
[40, 77],
|
|
460
413
|
[38, 77],
|
|
461
|
-
[38, 75],
|
|
462
414
|
[38, 2],
|
|
463
415
|
[-38, 2],
|
|
464
|
-
[-38, 75],
|
|
465
416
|
[-37.99999999999999, 77],
|
|
466
|
-
[-40, 77],
|
|
467
|
-
[-75, 77],
|
|
468
417
|
[-77, 77],
|
|
469
|
-
[-77, 75],
|
|
470
418
|
[13, -12],
|
|
471
419
|
[13, -38],
|
|
472
420
|
[10, -38],
|
|
473
|
-
[10, -25],
|
|
474
421
|
[10, -23],
|
|
475
|
-
[8, -23],
|
|
476
|
-
[-8, -23],
|
|
477
422
|
[-10, -23],
|
|
478
|
-
[-10, -25],
|
|
479
423
|
[-10, -38],
|
|
480
424
|
[-13, -38],
|
|
481
425
|
[-13, -12],
|
|
482
|
-
[-4, -19],
|
|
483
426
|
[-4, -21],
|
|
484
|
-
[-2, -21],
|
|
485
|
-
[1.9999999999999998, -21],
|
|
486
427
|
[3.9999999999999996, -21],
|
|
487
|
-
[4, -19],
|
|
488
|
-
[4, -15],
|
|
489
428
|
[4, -13],
|
|
490
|
-
[2, -13],
|
|
491
|
-
[-1.9999999999999998, -13],
|
|
492
429
|
[-4, -13],
|
|
493
|
-
[-
|
|
494
|
-
[-77, -75]
|
|
430
|
+
[-77, -77]
|
|
495
431
|
]
|
|
496
|
-
t.is(pts.length,
|
|
432
|
+
t.is(pts.length, 20)
|
|
497
433
|
t.true(comparePoints(pts, exp))
|
|
498
434
|
})
|
|
499
435
|
|
|
@@ -34,7 +34,7 @@ const offsetFromPoints = (options, points) => {
|
|
|
34
34
|
delta = Math.abs(delta) // sign is no longer required
|
|
35
35
|
|
|
36
36
|
let previousSegment = null
|
|
37
|
-
|
|
37
|
+
let newPoints = []
|
|
38
38
|
const newCorners = []
|
|
39
39
|
const of = vec2.create()
|
|
40
40
|
const n = points.length
|
|
@@ -94,6 +94,10 @@ const offsetFromPoints = (options, points) => {
|
|
|
94
94
|
// generate corners if necessary
|
|
95
95
|
|
|
96
96
|
if (corners === 'edge') {
|
|
97
|
+
// map for fast point index lookup
|
|
98
|
+
const pointIndex = new Map() // {point: index}
|
|
99
|
+
newPoints.forEach((point, index) => pointIndex.set(point, index))
|
|
100
|
+
|
|
97
101
|
// create edge corners
|
|
98
102
|
const line0 = line2.create()
|
|
99
103
|
const line1 = line2.create()
|
|
@@ -103,16 +107,17 @@ const offsetFromPoints = (options, points) => {
|
|
|
103
107
|
const ip = line2.intersectPointOfLines(line0, line1)
|
|
104
108
|
if (Number.isFinite(ip[0]) && Number.isFinite(ip[1])) {
|
|
105
109
|
const p0 = corner.s0[1]
|
|
106
|
-
|
|
107
|
-
i =
|
|
108
|
-
newPoints
|
|
110
|
+
const i = pointIndex.get(p0)
|
|
111
|
+
newPoints[i] = ip
|
|
112
|
+
newPoints[(i + 1) % newPoints.length] = undefined
|
|
109
113
|
} else {
|
|
110
114
|
// paralell segments, drop one
|
|
111
115
|
const p0 = corner.s1[0]
|
|
112
|
-
const i =
|
|
113
|
-
newPoints
|
|
116
|
+
const i = pointIndex.get(p0)
|
|
117
|
+
newPoints[i] = undefined
|
|
114
118
|
}
|
|
115
119
|
})
|
|
120
|
+
newPoints = newPoints.filter((p) => p !== undefined)
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
if (corners === 'round') {
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
const flatten = require('../../utils/flatten')
|
|
2
2
|
|
|
3
3
|
const geom2 = require('../../geometries/geom2')
|
|
4
|
+
const path2 = require('../../geometries/path2')
|
|
4
5
|
|
|
5
6
|
const extrudeLinearGeom2 = require('./extrudeLinearGeom2')
|
|
7
|
+
const extrudeLinearPath2 = require('./extrudeLinearPath2')
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Extrude the given geometry in an upward linear direction using the given options.
|
|
11
|
+
* Accepts path2 or geom2 objects as input. Paths must be closed.
|
|
12
|
+
*
|
|
9
13
|
* @param {Object} options - options for extrude
|
|
10
14
|
* @param {Number} [options.height=1] the height of the extrusion
|
|
11
15
|
* @param {Number} [options.twistAngle=0] the final rotation (RADIANS) about the origin of the shape (if any)
|
|
12
16
|
* @param {Integer} [options.twistSteps=1] the resolution of the twist about the axis (if any)
|
|
13
|
-
* @param {...Object}
|
|
17
|
+
* @param {...Object} objects - the geometries to extrude
|
|
14
18
|
* @return {Object|Array} the extruded geometry, or a list of extruded geometry
|
|
15
19
|
* @alias module:modeling/extrusions.extrudeLinear
|
|
16
20
|
*
|
|
@@ -31,7 +35,7 @@ const extrudeLinear = (options, ...objects) => {
|
|
|
31
35
|
options = { offset: [0, 0, height], twistAngle: twistAngle, twistSteps: twistSteps }
|
|
32
36
|
|
|
33
37
|
const results = objects.map((object) => {
|
|
34
|
-
|
|
38
|
+
if (path2.isA(object)) return extrudeLinearPath2(options, object)
|
|
35
39
|
if (geom2.isA(object)) return extrudeLinearGeom2(options, object)
|
|
36
40
|
// if (geom3.isA(object)) return geom3.extrude(options, object)
|
|
37
41
|
return object
|
|
@@ -2,7 +2,7 @@ const test = require('ava')
|
|
|
2
2
|
|
|
3
3
|
const comparePolygonsAsPoints = require('../../../test/helpers/comparePolygonsAsPoints')
|
|
4
4
|
|
|
5
|
-
const { geom2, geom3 } = require('../../geometries')
|
|
5
|
+
const { geom2, geom3, path2 } = require('../../geometries')
|
|
6
6
|
|
|
7
7
|
const { extrudeLinear } = require('./index')
|
|
8
8
|
|
|
@@ -166,3 +166,27 @@ test('extrudeLinear (holes)', (t) => {
|
|
|
166
166
|
t.is(pts.length, 24)
|
|
167
167
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
168
168
|
})
|
|
169
|
+
|
|
170
|
+
test('extrudeLinear (path2)', (t) => {
|
|
171
|
+
const geometry2 = path2.fromPoints({ closed: true }, [[0, 0], [12, 0], [6, 10]])
|
|
172
|
+
const geometry3 = extrudeLinear({ height: 15 }, geometry2)
|
|
173
|
+
t.true(geom3.isA(geometry3))
|
|
174
|
+
const pts = geom3.toPoints(geometry3)
|
|
175
|
+
const exp = [
|
|
176
|
+
[[6, 10, 0], [0, 0, 0], [0, 0, 15]],
|
|
177
|
+
[[6, 10, 0], [0, 0, 15], [6, 10, 15]],
|
|
178
|
+
[[0, 0, 0], [12, 0, 0], [12, 0, 15]],
|
|
179
|
+
[[0, 0, 0], [12, 0, 15], [0, 0, 15]],
|
|
180
|
+
[[12, 0, 0], [6, 10, 0], [6, 10, 15]],
|
|
181
|
+
[[12, 0, 0], [6, 10, 15], [12, 0, 15]],
|
|
182
|
+
[[0, 0, 15], [11.999999999999998, 0, 15], [6, 10, 15]],
|
|
183
|
+
[[6.000000000000001, 10, 0], [12, 0, 0], [8.881784197001252e-16, 0, 0]]
|
|
184
|
+
]
|
|
185
|
+
t.is(pts.length, 8)
|
|
186
|
+
t.true(comparePolygonsAsPoints(pts, exp))
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('extrudeLinear (unclosed path throws error)', (t) => {
|
|
190
|
+
const geometry2 = path2.fromPoints({ closed: false }, [[0, 0], [12, 0], [6, 10]])
|
|
191
|
+
t.throws(() => extrudeLinear({}, geometry2))
|
|
192
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const geom2 = require('../../geometries/geom2')
|
|
2
|
+
const path2 = require('../../geometries/path2')
|
|
3
|
+
|
|
4
|
+
const extrudeLinearGeom2 = require('./extrudeLinearGeom2')
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* Extrude the given geometry using the given options.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} [options] - options for extrude
|
|
10
|
+
* @param {Array} [options.offset] - the direction of the extrusion as a 3D vector
|
|
11
|
+
* @param {Number} [options.twistAngle] - the final rotation (RADIANS) about the origin
|
|
12
|
+
* @param {Integer} [options.twistSteps] - the number of steps created to produce the twist (if any)
|
|
13
|
+
* @param {path2} geometry - the geometry to extrude
|
|
14
|
+
* @returns {geom3} the extruded 3D geometry
|
|
15
|
+
*/
|
|
16
|
+
const extrudePath2 = (options, geometry) => {
|
|
17
|
+
if (!geometry.isClosed) throw new Error('extruded path must be closed')
|
|
18
|
+
// Convert path2 to geom2
|
|
19
|
+
const points = path2.toPoints(geometry)
|
|
20
|
+
const geometry2 = geom2.fromPoints(points)
|
|
21
|
+
return extrudeLinearGeom2(options, geometry2)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = extrudePath2
|