@jscad/modeling 2.7.0 → 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 +46 -0
- package/dist/jscad-modeling.min.js +142 -139
- package/package.json +2 -2
- package/src/colors/hslToRgb.js +1 -1
- package/src/colors/hueToColorComponent.js +1 -0
- package/src/curves/bezier/create.js +3 -8
- package/src/curves/bezier/tangentAt.js +2 -2
- package/src/curves/bezier/tangentAt.test.js +1 -1
- package/src/curves/bezier/valueAt.test.js +1 -1
- package/src/curves/index.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/toOutlines.js +6 -11
- package/src/geometries/geom2/toString.js +1 -1
- package/src/geometries/geom2/transform.test.js +1 -1
- package/src/geometries/geom3/create.js +1 -1
- package/src/geometries/geom3/fromCompactBinary.js +1 -1
- package/src/geometries/geom3/index.js +17 -0
- package/src/geometries/geom3/invert.js +2 -2
- package/src/geometries/geom3/isA.js +2 -2
- package/src/geometries/geom3/toCompactBinary.js +4 -4
- package/src/geometries/geom3/toPoints.js +1 -0
- 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/create.js +2 -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/fromRotation.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/isIdentity.test.js +0 -2
- package/src/maths/mat4/isMirroring.js +4 -4
- package/src/maths/mat4/isMirroring.test.js +1 -1
- package/src/maths/mat4/isOnlyTransformScale.js +5 -4
- package/src/maths/mat4/leftMultiplyVec3.js +2 -2
- package/src/maths/mat4/rotate.js +1 -1
- 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/length.test.js +10 -0
- 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/angle.js +2 -2
- package/src/maths/vec3/angle.test.js +17 -0
- 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/length.test.js +10 -0
- 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/measureBoundingBox.js +38 -128
- 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 +3 -5
- 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 +3 -1
- package/src/operations/expansions/expand.test.js +3 -35
- package/src/operations/expansions/expandShell.js +24 -18
- package/src/operations/expansions/offset.js +2 -1
- package/src/operations/expansions/offset.test.js +25 -89
- package/src/operations/expansions/offsetFromPoints.js +11 -6
- package/src/operations/extrusions/extrudeLinear.js +7 -3
- package/src/operations/extrusions/extrudeLinear.test.js +25 -1
- package/src/operations/extrusions/extrudeLinearPath2.js +24 -0
- package/src/operations/extrusions/extrudeRectangular.js +3 -2
- 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/hulls/quickhull/QuickHull.js +2 -2
- package/src/operations/modifiers/edges.js +2 -4
- package/src/operations/modifiers/generalize.js +4 -7
- 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 +11 -11
- 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/ellipse.js +1 -1
- 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/geodesicSphere.js +2 -2
- package/src/primitives/polyhedron.js +1 -1
- package/src/primitives/roundedCylinder.js +1 -1
- package/src/primitives/torus.d.ts +0 -1
- package/src/primitives/triangle.js +2 -2
- package/src/text/vectorText.js +2 -2
- package/src/utils/insertSorted.js +1 -0
- package/src/utils/padArrayToLength.js +1 -1
- package/test/helpers/comparePolygons.js +1 -3
- package/test/helpers/nearlyEqual.js +2 -6
|
@@ -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,12 +10,14 @@ const expandPath2 = require('./expandPath2')
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Expand the given geometry using the given options.
|
|
13
|
+
* Both internal and external space is expanded for 2D and 3D shapes.
|
|
14
|
+
*
|
|
13
15
|
* Note: Contract is expand using a negative delta.
|
|
14
16
|
* @param {Object} options - options for expand
|
|
15
17
|
* @param {Number} [options.delta=1] - delta (+/-) of expansion
|
|
16
18
|
* @param {String} [options.corners='edge'] - type of corner to create after expanding; edge, chamfer, round
|
|
17
19
|
* @param {Integer} [options.segments=16] - number of segments when creating round corners
|
|
18
|
-
* @param {...Objects}
|
|
20
|
+
* @param {...Objects} objects - the geometries to expand
|
|
19
21
|
* @return {Object|Array} new geometry, or list of new geometries
|
|
20
22
|
* @alias module:modeling/expansions.expand
|
|
21
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()
|
|
@@ -8,11 +8,12 @@ const offsetPath2 = require('./offsetPath2')
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Create offset geometry from the given geometry using the given options.
|
|
11
|
+
* Offsets from internal and external space are created.
|
|
11
12
|
* @param {Object} options - options for offset
|
|
12
13
|
* @param {Float} [options.delta=1] - delta of offset (+ to exterior, - from interior)
|
|
13
14
|
* @param {String} [options.corners='edge'] - type of corner to create after offseting; edge, chamfer, round
|
|
14
15
|
* @param {Integer} [options.segments=16] - number of segments when creating round corners
|
|
15
|
-
* @param {...Object}
|
|
16
|
+
* @param {...Object} objects - the geometries to offset
|
|
16
17
|
* @return {Object|Array} new geometry, or list of new geometries
|
|
17
18
|
* @alias module:modeling/expansions.offset
|
|
18
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
|
-
* @param {
|
|
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
|
|
@@ -12,12 +12,13 @@ const extrudeRectangularGeom2 = require('./extrudeRectangularGeom2')
|
|
|
12
12
|
* @param {Object} options - options for extrusion, if any
|
|
13
13
|
* @param {Number} [options.size=1] - size of the rectangle
|
|
14
14
|
* @param {Number} [options.height=1] - height of the extrusion
|
|
15
|
-
* @param {...Object}
|
|
15
|
+
* @param {...Object} objects - the geometries to extrude
|
|
16
16
|
* @return {Object|Array} the extruded object, or a list of extruded objects
|
|
17
17
|
* @alias module:modeling/extrusions.extrudeRectangular
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
|
-
* let mywalls = extrudeRectangular({
|
|
20
|
+
* let mywalls = extrudeRectangular({size: 1, height: 3}, square({size: 20}))
|
|
21
|
+
* let mywalls = extrudeRectangular({size: 1, height: 300, twistAngle: Math.PI}, square({size: 20}))
|
|
21
22
|
*/
|
|
22
23
|
const extrudeRectangular = (options, ...objects) => {
|
|
23
24
|
const defaults = {
|
|
@@ -12,11 +12,11 @@ test('extrudeRectangular (defaults)', (t) => {
|
|
|
12
12
|
|
|
13
13
|
let obs = extrudeRectangular({ }, geometry1)
|
|
14
14
|
let pts = geom3.toPoints(obs)
|
|
15
|
-
t.is(pts.length,
|
|
15
|
+
t.is(pts.length, 34)
|
|
16
16
|
|
|
17
17
|
obs = extrudeRectangular({ }, geometry2)
|
|
18
18
|
pts = geom3.toPoints(obs)
|
|
19
|
-
t.is(pts.length,
|
|
19
|
+
t.is(pts.length, 24)
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
test('extrudeRectangular (chamfer)', (t) => {
|
|
@@ -60,7 +60,7 @@ const projectGeom3 = (options, geometry) => {
|
|
|
60
60
|
* @param {Object} options - options for project
|
|
61
61
|
* @param {Array} [options.axis=[0,0,1]] the axis of the plane (default is Z axis)
|
|
62
62
|
* @param {Array} [options.origin=[0,0,0]] the origin of the plane
|
|
63
|
-
* @param {...Object}
|
|
63
|
+
* @param {...Object} objects - the list of 3D geometry to project
|
|
64
64
|
* @return {geom2|Array} the projected 2D geometry, or a list of 2D projected geometry
|
|
65
65
|
* @alias module:modeling/extrusions.project
|
|
66
66
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param {slice} object - the object to
|
|
2
|
+
* Determine if the given object is a slice.
|
|
3
|
+
* @param {slice} object - the object to interrogate
|
|
4
4
|
* @returns {Boolean} true if the object matches a slice
|
|
5
5
|
* @alias module:modeling/extrusions/slice.isA
|
|
6
6
|
*/
|
|
@@ -60,7 +60,7 @@ const toPolygons = (slice) => {
|
|
|
60
60
|
const wallPolygons = edges.map((edge) => toPolygon3D(splane, edge))
|
|
61
61
|
const walls = geom3.create(wallPolygons)
|
|
62
62
|
|
|
63
|
-
// make an
|
|
63
|
+
// make an intersection of the base and the walls, creating... a set of polygons!
|
|
64
64
|
const geometry3 = intersectGeom3Sub(base, walls)
|
|
65
65
|
|
|
66
66
|
// return only those polygons from the base
|
|
@@ -43,7 +43,7 @@ test('hull (single, geom2)', (t) => {
|
|
|
43
43
|
t.is(pts.length, 7)
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
test('hull (multiple,
|
|
46
|
+
test('hull (multiple, overlapping, geom2)', (t) => {
|
|
47
47
|
const geometry1 = geom2.fromPoints([[5, 5], [-5, 5], [-5, -5], [5, -5]])
|
|
48
48
|
const geometry2 = geom2.fromPoints([[3, 3], [-3, 3], [-3, -3], [3, -3]])
|
|
49
49
|
const geometry3 = geom2.fromPoints([[6, 3], [-6, 3], [-6, -3], [6, -3]])
|
|
@@ -5,7 +5,7 @@ const union = require('../booleans/union')
|
|
|
5
5
|
const hull = require('./hull')
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Create a chain of hulled geometries from the given
|
|
8
|
+
* Create a chain of hulled geometries from the given geometries.
|
|
9
9
|
* Essentially hull A+B, B+C, C+D, etc., then union the results.
|
|
10
10
|
* The given geometries should be of the same type, either geom2 or geom3 or path2.
|
|
11
11
|
*
|
|
@@ -30,7 +30,7 @@ const hullGeom2 = (...geometries) => {
|
|
|
30
30
|
|
|
31
31
|
const hullpoints = hullPoints2(uniquepoints)
|
|
32
32
|
|
|
33
|
-
// NOTE: more
|
|
33
|
+
// NOTE: more than three points are required to create a new geometry
|
|
34
34
|
if (hullpoints.length < 3) return geom2.create()
|
|
35
35
|
|
|
36
36
|
// assemble a new geometry from the list of points
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
const flatten = require('../../utils/flatten')
|
|
2
2
|
|
|
3
|
-
const vec2 = require('../../maths/vec2')
|
|
4
|
-
|
|
5
3
|
const path2 = require('../../geometries/path2')
|
|
6
4
|
|
|
7
5
|
const hullPoints2 = require('./hullPoints2')
|
|
@@ -16,11 +14,15 @@ const hullPath2 = (...geometries) => {
|
|
|
16
14
|
|
|
17
15
|
// extract the unique points from the geometries
|
|
18
16
|
const uniquepoints = []
|
|
17
|
+
const found = new Set()
|
|
19
18
|
geometries.forEach((geometry) => {
|
|
20
19
|
const points = path2.toPoints(geometry)
|
|
21
20
|
points.forEach((point) => {
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
21
|
+
const key = point.toString()
|
|
22
|
+
if (!found.has(key)) {
|
|
23
|
+
uniquepoints.push(point)
|
|
24
|
+
found.add(key)
|
|
25
|
+
}
|
|
24
26
|
})
|
|
25
27
|
})
|
|
26
28
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const test = require('ava')
|
|
2
|
+
|
|
3
|
+
const { path2 } = require('../../geometries')
|
|
4
|
+
|
|
5
|
+
const hullPath2 = require('./hullPath2')
|
|
6
|
+
|
|
7
|
+
test('hullPath2', (t) => {
|
|
8
|
+
const closed = true
|
|
9
|
+
const geometry1 = path2.fromPoints({ closed }, [[0, 0], [-4, 4], [-4, -4]])
|
|
10
|
+
const geometry2 = path2.fromPoints({ closed }, [[0, 0], [4, -4], [4, 4]])
|
|
11
|
+
|
|
12
|
+
const obs = hullPath2(geometry1, geometry2)
|
|
13
|
+
t.true(path2.isA(obs))
|
|
14
|
+
const pts = path2.toPoints(obs)
|
|
15
|
+
t.is(pts.length, 4)
|
|
16
|
+
})
|