@jscad/modeling 2.9.2 → 2.9.3
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 +20 -0
- package/README.md +4 -4
- package/dist/jscad-modeling.min.js +411 -417
- package/package.json +3 -2
- package/src/colors/colorize.test.js +1 -1
- package/src/geometries/geom2/toOutlines.js +66 -52
- package/src/geometries/geom3/create.js +1 -1
- package/src/geometries/geom3/create.test.js +1 -1
- package/src/geometries/geom3/fromPoints.js +1 -1
- package/src/geometries/path2/index.d.ts +0 -1
- package/src/geometries/path2/index.js +0 -1
- package/src/geometries/poly3/create.js +1 -1
- package/src/geometries/poly3/validate.js +14 -0
- package/src/maths/constants.d.ts +1 -0
- package/src/maths/constants.js +11 -0
- package/src/maths/utils/aboutEqualNormals.js +1 -5
- package/src/operations/booleans/intersectGeom3.js +2 -1
- package/src/operations/booleans/subtractGeom3.js +2 -1
- package/src/operations/booleans/to3DWalls.js +1 -1
- package/src/operations/booleans/unionGeom3.js +2 -1
- package/src/operations/expansions/expandGeom3.test.js +14 -14
- package/src/operations/expansions/expandShell.js +5 -4
- package/src/operations/expansions/extrudePolygon.js +7 -7
- package/src/operations/extrusions/extrudeFromSlices.test.js +2 -2
- package/src/operations/extrusions/extrudeRotate.js +5 -1
- package/src/operations/extrusions/extrudeRotate.test.js +5 -5
- package/src/operations/extrusions/extrudeWalls.js +2 -2
- package/src/operations/extrusions/project.js +11 -14
- package/src/operations/extrusions/project.test.js +50 -50
- package/src/operations/hulls/hullChain.test.js +4 -4
- package/src/operations/hulls/hullGeom2.js +6 -18
- package/src/operations/hulls/hullGeom3.js +5 -18
- package/src/operations/hulls/hullPath2.js +4 -14
- package/src/operations/hulls/hullPoints2.js +43 -92
- package/src/operations/hulls/toUniquePoints.js +34 -0
- package/src/operations/modifiers/generalize.js +2 -13
- package/src/operations/modifiers/generalize.test.js +0 -32
- package/src/operations/modifiers/insertTjunctions.js +1 -1
- package/src/operations/modifiers/insertTjunctions.test.js +21 -21
- package/src/operations/modifiers/mergePolygons.js +11 -14
- package/src/operations/{booleans → modifiers}/reTesselateCoplanarPolygons.js +1 -1
- package/src/operations/{booleans → modifiers}/reTesselateCoplanarPolygons.test.js +5 -5
- package/src/operations/{booleans → modifiers}/retessellate.js +2 -9
- package/src/operations/{booleans → modifiers}/retessellate.test.js +0 -0
- package/src/operations/modifiers/snapPolygons.test.js +12 -12
- package/src/operations/modifiers/triangulatePolygons.js +3 -3
- package/src/operations/transforms/center.js +1 -1
- package/src/primitives/cuboid.js +1 -1
- package/src/primitives/cylinderElliptic.js +1 -1
- package/src/primitives/ellipsoid.js +2 -2
- package/src/primitives/polyhedron.js +1 -1
- package/src/primitives/roundedCuboid.js +6 -6
- package/src/primitives/roundedCylinder.js +1 -1
- package/src/primitives/torus.test.js +6 -6
- package/src/primitives/triangle.js +1 -2
- package/src/utils/trigonometry.js +1 -2
- package/src/geometries/path2/eachPoint.d.ts +0 -9
- package/src/geometries/path2/eachPoint.js +0 -17
- package/src/geometries/path2/eachPoint.test.js +0 -11
- package/src/operations/modifiers/edges.js +0 -195
- package/src/operations/modifiers/repairTjunctions.js +0 -44
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const aboutEqualNormals = require('../../maths/utils/aboutEqualNormals')
|
|
1
2
|
const vec3 = require('../../maths/vec3')
|
|
2
3
|
|
|
3
4
|
const poly3 = require('../../geometries/poly3')
|
|
@@ -53,9 +54,12 @@ const calculateAnglesBetween = (current, opposite, normal) => {
|
|
|
53
54
|
return [angle1, angle2]
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
const v1 = vec3.create()
|
|
58
|
+
const v2 = vec3.create()
|
|
59
|
+
|
|
56
60
|
const calculateAngle = (prevpoint, point, nextpoint, normal) => {
|
|
57
|
-
const d0 = vec3.subtract(
|
|
58
|
-
const d1 = vec3.subtract(
|
|
61
|
+
const d0 = vec3.subtract(v1, point, prevpoint)
|
|
62
|
+
const d1 = vec3.subtract(v2, nextpoint, point)
|
|
59
63
|
vec3.cross(d0, d0, d1)
|
|
60
64
|
return vec3.dot(d0, normal)
|
|
61
65
|
}
|
|
@@ -76,7 +80,7 @@ const createPolygonAnd = (edge) => {
|
|
|
76
80
|
|
|
77
81
|
edge = next
|
|
78
82
|
}
|
|
79
|
-
if (points.length > 0) polygon = poly3.
|
|
83
|
+
if (points.length > 0) polygon = poly3.create(points)
|
|
80
84
|
return polygon
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -85,7 +89,7 @@ const createPolygonAnd = (edge) => {
|
|
|
85
89
|
* @param {poly3[]} sourcepolygons - list of polygons
|
|
86
90
|
* @returns {poly3[]} new set of polygons
|
|
87
91
|
*/
|
|
88
|
-
const mergeCoplanarPolygons = (
|
|
92
|
+
const mergeCoplanarPolygons = (sourcepolygons) => {
|
|
89
93
|
if (sourcepolygons.length < 2) return sourcepolygons
|
|
90
94
|
|
|
91
95
|
const normal = sourcepolygons[0].plane
|
|
@@ -167,18 +171,11 @@ const mergeCoplanarPolygons = (epsilon, sourcepolygons) => {
|
|
|
167
171
|
if (polygon) destpolygons.push(polygon)
|
|
168
172
|
})
|
|
169
173
|
|
|
174
|
+
edgeList.clear()
|
|
175
|
+
|
|
170
176
|
return destpolygons
|
|
171
177
|
}
|
|
172
178
|
|
|
173
|
-
// Normals are directional vectors with component values from 0 to 1.0, requiring specialized comparision
|
|
174
|
-
// This EPS is derived from a serieas of tests to determine the optimal precision for comparing coplanar polygons,
|
|
175
|
-
// as provided by the sphere primitive at high segmentation
|
|
176
|
-
// This EPS is for 64 bit Number values
|
|
177
|
-
const NEPS = 1e-13
|
|
178
|
-
|
|
179
|
-
// Compare two normals (unit vectors) for equality.
|
|
180
|
-
const aboutEqualNormals = (a, b) => (Math.abs(a[0] - b[0]) <= NEPS && Math.abs(a[1] - b[1]) <= NEPS && Math.abs(a[2] - b[2]) <= NEPS)
|
|
181
|
-
|
|
182
179
|
const coplanar = (plane1, plane2) => {
|
|
183
180
|
// expect the same distance from the origin, within tolerance
|
|
184
181
|
if (Math.abs(plane1[3] - plane2[3]) < 0.00000015) {
|
|
@@ -202,7 +199,7 @@ const mergePolygons = (epsilon, polygons) => {
|
|
|
202
199
|
let destpolygons = []
|
|
203
200
|
polygonsPerPlane.forEach((mapping) => {
|
|
204
201
|
const sourcepolygons = mapping[1]
|
|
205
|
-
const retesselayedpolygons = mergeCoplanarPolygons(
|
|
202
|
+
const retesselayedpolygons = mergeCoplanarPolygons(sourcepolygons)
|
|
206
203
|
destpolygons = destpolygons.concat(retesselayedpolygons)
|
|
207
204
|
})
|
|
208
205
|
return destpolygons
|
|
@@ -31,7 +31,7 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
|
|
|
31
31
|
// convert all polygon vertices to 2D
|
|
32
32
|
// Make a list of all encountered y coordinates
|
|
33
33
|
// And build a map of all polygons that have a vertex at a certain y coordinate:
|
|
34
|
-
const ycoordinateBinningFactor =
|
|
34
|
+
const ycoordinateBinningFactor = 10 / EPS
|
|
35
35
|
for (let polygonindex = 0; polygonindex < numpolygons; polygonindex++) {
|
|
36
36
|
const poly3d = sourcepolygons[polygonindex]
|
|
37
37
|
let vertices2d = []
|
|
@@ -17,11 +17,11 @@ const rotatePoly3 = (angles, polygon) => {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
test.only('retessellateCoplanarPolygons: should merge coplanar polygons', (t) => {
|
|
20
|
-
const polyA = poly3.
|
|
21
|
-
const polyB = poly3.
|
|
22
|
-
const polyC = poly3.
|
|
23
|
-
const polyD = poly3.
|
|
24
|
-
const polyE = poly3.
|
|
20
|
+
const polyA = poly3.create([[-5, -5, 0], [5, -5, 0], [5, 5, 0], [-5, 5, 0]])
|
|
21
|
+
const polyB = poly3.create([[5, -5, 0], [8, 0, 0], [5, 5, 0]])
|
|
22
|
+
const polyC = poly3.create([[-5, 5, 0], [-8, 0, 0], [-5, -5, 0]])
|
|
23
|
+
const polyD = poly3.create([[-5, 5, 0], [5, 5, 0], [0, 8, 0]])
|
|
24
|
+
const polyE = poly3.create([[5, -5, 0], [-5, -5, 0], [0, -8, 0]])
|
|
25
25
|
|
|
26
26
|
// combine polygons in each direction
|
|
27
27
|
let obs = reTesselateCoplanarPolygons([polyA, polyB])
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
const geom3 = require('../../geometries/geom3')
|
|
2
2
|
const poly3 = require('../../geometries/poly3')
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
|
|
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
|
-
// as provided by the sphere primitive at high segmentation
|
|
9
|
-
// This EPS is for 64 bit Number values
|
|
10
|
-
const NEPS = 1e-13
|
|
4
|
+
const aboutEqualNormals = require('../../maths/utils/aboutEqualNormals')
|
|
11
5
|
|
|
12
|
-
|
|
13
|
-
const aboutEqualNormals = (a, b) => (Math.abs(a[0] - b[0]) <= NEPS && Math.abs(a[1] - b[1]) <= NEPS && Math.abs(a[2] - b[2]) <= NEPS)
|
|
6
|
+
const reTesselateCoplanarPolygons = require('./reTesselateCoplanarPolygons')
|
|
14
7
|
|
|
15
8
|
const coplanar = (plane1, plane2) => {
|
|
16
9
|
// expect the same distance from the origin, within tolerance
|
|
File without changes
|
|
@@ -7,49 +7,49 @@ const snapPolygons = require('./snapPolygons')
|
|
|
7
7
|
test('snapPolygons: snap of polygons produces expected results', (t) => {
|
|
8
8
|
const polygons = [
|
|
9
9
|
// valid polygons
|
|
10
|
-
poly3.
|
|
11
|
-
poly3.
|
|
10
|
+
poly3.create([[0, 0, 0], [0, 10, 0], [0, 10, 10]]), // OK
|
|
11
|
+
poly3.create([[0, 0, 0], [0, 10, 0], [0, 10, 10], [0, 0, 10]]), // OK
|
|
12
12
|
// invalid polygons
|
|
13
13
|
poly3.create(),
|
|
14
|
-
poly3.
|
|
15
|
-
poly3.
|
|
14
|
+
poly3.create([[0, 0, 0]]),
|
|
15
|
+
poly3.create([[0, 0, 0], [0, 10, 0]]),
|
|
16
16
|
// duplicated vertices
|
|
17
|
-
poly3.
|
|
17
|
+
poly3.create([
|
|
18
18
|
[-24.445112000000115, 19.346837333333426, 46.47572533333356],
|
|
19
19
|
[-24.44446933333345, 19.346837333333426, 46.47508266666689],
|
|
20
20
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
21
21
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019]
|
|
22
22
|
]), // OK
|
|
23
|
-
poly3.
|
|
23
|
+
poly3.create([
|
|
24
24
|
[-24.445112000000115, 19.346837333333426, 46.47572533333356],
|
|
25
25
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
26
26
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019]
|
|
27
27
|
]),
|
|
28
|
-
poly3.
|
|
28
|
+
poly3.create([
|
|
29
29
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
30
30
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019]
|
|
31
31
|
]),
|
|
32
32
|
// duplicate vertices after snap
|
|
33
|
-
poly3.
|
|
33
|
+
poly3.create([
|
|
34
34
|
[-24.445112000000115, 19.346837333333426, 46.47572533333356],
|
|
35
35
|
[-24.44446933333345, 19.346837333333426, 46.47508266666689],
|
|
36
36
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
37
37
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
|
|
38
38
|
]), // OK
|
|
39
|
-
poly3.
|
|
39
|
+
poly3.create([
|
|
40
40
|
[-24.445112000000115, 19.346837333333426, 46.47572533333356],
|
|
41
41
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234],
|
|
42
42
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
43
43
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
|
|
44
44
|
]),
|
|
45
|
-
poly3.
|
|
45
|
+
poly3.create([
|
|
46
46
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
47
47
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234],
|
|
48
48
|
[-23.70540266666678, 18.79864266666676, 39.56448800000019],
|
|
49
49
|
[-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
|
|
50
50
|
]),
|
|
51
51
|
// inverted polygon
|
|
52
|
-
poly3.
|
|
52
|
+
poly3.create([
|
|
53
53
|
[20.109133333333336, -4.894033333333335, -1.0001266666666668],
|
|
54
54
|
[20.021120000000003, -5.1802133333333344, -1.0001266666666668],
|
|
55
55
|
[20.020300000000002, -5.182946666666668, -1.0001266666666668],
|
|
@@ -61,7 +61,7 @@ test('snapPolygons: snap of polygons produces expected results', (t) => {
|
|
|
61
61
|
const results = snapPolygons(0.0001, polygons)
|
|
62
62
|
t.is(results.length, 5)
|
|
63
63
|
|
|
64
|
-
const exp3 = poly3.
|
|
64
|
+
const exp3 = poly3.create([
|
|
65
65
|
[-24.4451, 19.3468, 46.4757],
|
|
66
66
|
[-24.4445, 19.3468, 46.475100000000005],
|
|
67
67
|
[-23.7054, 18.7986, 39.5645]
|
|
@@ -10,15 +10,15 @@ const triangulatePolygon = (epsilon, polygon, triangles) => {
|
|
|
10
10
|
polygon.vertices.forEach((vertice) => vec3.add(midpoint, midpoint, vertice))
|
|
11
11
|
vec3.snap(midpoint, vec3.divide(midpoint, midpoint, [nv, nv, nv]), epsilon)
|
|
12
12
|
for (let i = 0; i < nv; i++) {
|
|
13
|
-
const poly = poly3.
|
|
13
|
+
const poly = poly3.create([midpoint, polygon.vertices[i], polygon.vertices[(i + 1) % nv]])
|
|
14
14
|
if (polygon.color) poly.color = polygon.color
|
|
15
15
|
triangles.push(poly)
|
|
16
16
|
}
|
|
17
17
|
return
|
|
18
18
|
}
|
|
19
19
|
// exactly 4 vertices, use simple triangulation
|
|
20
|
-
const poly0 = poly3.
|
|
21
|
-
const poly1 = poly3.
|
|
20
|
+
const poly0 = poly3.create([polygon.vertices[0], polygon.vertices[1], polygon.vertices[2]])
|
|
21
|
+
const poly1 = poly3.create([polygon.vertices[0], polygon.vertices[2], polygon.vertices[3]])
|
|
22
22
|
if (polygon.color) {
|
|
23
23
|
poly0.color = polygon.color
|
|
24
24
|
poly1.color = polygon.color
|
|
@@ -39,7 +39,7 @@ const center = (options, ...objects) => {
|
|
|
39
39
|
const defaults = {
|
|
40
40
|
axes: [true, true, true],
|
|
41
41
|
relativeTo: [0, 0, 0]
|
|
42
|
-
// TODO
|
|
42
|
+
// TODO: Add additional 'methods' of centering: midpoint, centroid
|
|
43
43
|
}
|
|
44
44
|
const { axes, relativeTo } = Object.assign({}, defaults, options)
|
|
45
45
|
|
package/src/primitives/cuboid.js
CHANGED
|
@@ -90,7 +90,7 @@ const cylinderElliptic = (options) => {
|
|
|
90
90
|
// adjust the points to center
|
|
91
91
|
const fromPoints = (...points) => {
|
|
92
92
|
const newpoints = points.map((point) => vec3.add(vec3.create(), point, center))
|
|
93
|
-
return poly3.
|
|
93
|
+
return poly3.create(newpoints)
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const polygons = []
|
|
@@ -66,7 +66,7 @@ const ellipsoid = (options) => {
|
|
|
66
66
|
point = vec3.subtract(vec3.create(), vec3.scale(p1, prevcylinderpoint, cospitch), vec3.scale(p2, zvector, sinpitch))
|
|
67
67
|
points.push(vec3.add(point, point, center))
|
|
68
68
|
|
|
69
|
-
polygons.push(poly3.
|
|
69
|
+
polygons.push(poly3.create(points))
|
|
70
70
|
|
|
71
71
|
points = []
|
|
72
72
|
point = vec3.add(vec3.create(), vec3.scale(p1, prevcylinderpoint, prevcospitch), vec3.scale(p2, zvector, prevsinpitch))
|
|
@@ -81,7 +81,7 @@ const ellipsoid = (options) => {
|
|
|
81
81
|
points.push(vec3.add(vec3.create(), center, point))
|
|
82
82
|
points.reverse()
|
|
83
83
|
|
|
84
|
-
polygons.push(poly3.
|
|
84
|
+
polygons.push(poly3.create(points))
|
|
85
85
|
}
|
|
86
86
|
prevcospitch = cospitch
|
|
87
87
|
prevsinpitch = sinpitch
|
|
@@ -60,7 +60,7 @@ const polyhedron = (options) => {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const polygons = faces.map((face, findex) => {
|
|
63
|
-
const polygon = poly3.
|
|
63
|
+
const polygon = poly3.create(face.map((pindex) => points[pindex]))
|
|
64
64
|
if (colors && colors[findex]) polygon.color = colors[findex]
|
|
65
65
|
return polygon
|
|
66
66
|
})
|
|
@@ -57,10 +57,10 @@ const stitchCorners = (previousCorners, currentCorners) => {
|
|
|
57
57
|
const previous = previousCorners[i]
|
|
58
58
|
const current = currentCorners[i]
|
|
59
59
|
for (let j = 0; j < (previous.length - 1); j++) {
|
|
60
|
-
polygons.push(poly3.
|
|
60
|
+
polygons.push(poly3.create([previous[j], previous[j + 1], current[j]]))
|
|
61
61
|
|
|
62
62
|
if (j < (current.length - 1)) {
|
|
63
|
-
polygons.push(poly3.
|
|
63
|
+
polygons.push(poly3.create([current[j], previous[j + 1], current[j + 1]]))
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -81,7 +81,7 @@ const stitchWalls = (previousCorners, currentCorners) => {
|
|
|
81
81
|
const p1 = previous[0]
|
|
82
82
|
const c1 = current[0]
|
|
83
83
|
|
|
84
|
-
polygons.push(poly3.
|
|
84
|
+
polygons.push(poly3.create([p0, p1, c1, c0]))
|
|
85
85
|
}
|
|
86
86
|
return polygons
|
|
87
87
|
}
|
|
@@ -104,7 +104,7 @@ const stitchSides = (bottomCorners, topCorners) => {
|
|
|
104
104
|
const polygons = []
|
|
105
105
|
for (let i = 0; i < topPoints.length; i++) {
|
|
106
106
|
const j = (i + 1) % topPoints.length
|
|
107
|
-
polygons.push(poly3.
|
|
107
|
+
polygons.push(poly3.create([bottomPoints[i], bottomPoints[j], topPoints[j], topPoints[i]]))
|
|
108
108
|
}
|
|
109
109
|
return polygons
|
|
110
110
|
}
|
|
@@ -168,10 +168,10 @@ const roundedCuboid = (options) => {
|
|
|
168
168
|
if (slice === segments) {
|
|
169
169
|
// add the top
|
|
170
170
|
let points = cornersPos.map((corner) => corner[0])
|
|
171
|
-
polygons.push(poly3.
|
|
171
|
+
polygons.push(poly3.create(points))
|
|
172
172
|
// add the bottom
|
|
173
173
|
points = cornersNeg.map((corner) => corner[0])
|
|
174
|
-
polygons.push(poly3.
|
|
174
|
+
polygons.push(poly3.create(points))
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
prevCornersPos = cornersPos
|
|
@@ -66,7 +66,7 @@ const roundedCylinder = (options) => {
|
|
|
66
66
|
const fromPoints = (points) => {
|
|
67
67
|
// adjust the points to center
|
|
68
68
|
const newpoints = points.map((point) => vec3.add(point, point, center))
|
|
69
|
-
return poly3.
|
|
69
|
+
return poly3.create(newpoints)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const polygons = []
|
|
@@ -10,24 +10,24 @@ test('torus (defaults)', (t) => {
|
|
|
10
10
|
const obs = torus()
|
|
11
11
|
const pts = geom3.toPoints(obs)
|
|
12
12
|
|
|
13
|
-
t.notThrows
|
|
13
|
+
t.notThrows(() => geom3.validate(obs))
|
|
14
14
|
t.is(pts.length, 2048) // 32 * 32 * 2 (polys/segment) = 2048
|
|
15
15
|
|
|
16
16
|
const bounds = measureBoundingBox(obs)
|
|
17
17
|
const expectedBounds = [[-5, -5, -1], [5, 5, 1]]
|
|
18
|
-
t.notThrows
|
|
18
|
+
t.notThrows(() => geom3.validate(obs))
|
|
19
19
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected: ' + JSON.stringify(bounds))
|
|
20
20
|
})
|
|
21
21
|
|
|
22
|
-
test('torus (
|
|
22
|
+
test('torus (simple options)', (t) => {
|
|
23
23
|
const obs = torus({ innerRadius: 0.5, innerSegments: 4, outerRadius: 5, outerSegments: 8 })
|
|
24
24
|
const pts = geom3.toPoints(obs)
|
|
25
|
-
t.notThrows
|
|
25
|
+
t.notThrows(() => geom3.validate(obs))
|
|
26
26
|
t.is(pts.length, 64) // 4 * 8 * 2 (polys/segment) = 64
|
|
27
27
|
|
|
28
28
|
const bounds = measureBoundingBox(obs)
|
|
29
29
|
const expectedBounds = [[-5.5, -5.5, -0.5], [5.5, 5.5, 0.5]]
|
|
30
|
-
t.notThrows
|
|
30
|
+
t.notThrows(() => geom3.validate(obs))
|
|
31
31
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected: ' + JSON.stringify(bounds))
|
|
32
32
|
})
|
|
33
33
|
|
|
@@ -48,6 +48,6 @@ test('torus (square by square)', (t) => {
|
|
|
48
48
|
|
|
49
49
|
const bounds = measureBoundingBox(obs)
|
|
50
50
|
const expectedBounds = [[-5, -5, -1], [5, 5, 1]]
|
|
51
|
-
t.notThrows
|
|
51
|
+
t.notThrows(() => geom3.validate(obs))
|
|
52
52
|
t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected: ' + JSON.stringify(bounds))
|
|
53
53
|
})
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
const { NEPS } = require('../maths/constants')
|
|
1
2
|
const vec2 = require('../maths/vec2')
|
|
2
3
|
|
|
3
4
|
const geom2 = require('../geometries/geom2')
|
|
4
5
|
|
|
5
6
|
const { isNumberArray } = require('./commonChecks')
|
|
6
7
|
|
|
7
|
-
const NEPS = 1e-13
|
|
8
|
-
|
|
9
8
|
// returns angle C
|
|
10
9
|
const solveAngleFromSSS = (a, b, c) => Math.acos(((a * a) + (b * b) - (c * c)) / (2 * a * b))
|
|
11
10
|
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import Path2 from './type'
|
|
2
|
-
import Vec2 from '../../maths/vec2/type'
|
|
3
|
-
|
|
4
|
-
export default eachPoint
|
|
5
|
-
|
|
6
|
-
export interface EachPointOptions {}
|
|
7
|
-
export type EachPointThunk = (value: Vec2, index: number, array: Array<Vec2>) => void
|
|
8
|
-
|
|
9
|
-
declare function eachPoint(options: EachPointOptions, thunk: EachPointThunk, path: Path2): void
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const toPoints = require('./toPoints')
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Calls a function for each point in the path.
|
|
5
|
-
* @param {Object} options - options
|
|
6
|
-
* @param {Function} thunk - the function to call
|
|
7
|
-
* @param {path2} path - the path to traverse
|
|
8
|
-
* @alias module:modeling/geometries/path2.eachPoint
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* eachPoint({}, accumulate, path)
|
|
12
|
-
*/
|
|
13
|
-
const eachPoint = (options, thunk, path) => {
|
|
14
|
-
toPoints(path).forEach(thunk)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = eachPoint
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const test = require('ava')
|
|
2
|
-
|
|
3
|
-
const vec2 = require('../../maths/vec2')
|
|
4
|
-
|
|
5
|
-
const { eachPoint, fromPoints } = require('./index')
|
|
6
|
-
|
|
7
|
-
test('eachPoint: Each point is emitted', (t) => {
|
|
8
|
-
const collector = []
|
|
9
|
-
eachPoint({}, (point) => collector.push(point), fromPoints({}, [[1, 1], [2, 2]]))
|
|
10
|
-
t.deepEqual(collector, [vec2.fromValues(1, 1), vec2.fromValues(2, 2)])
|
|
11
|
-
})
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
const vec3 = require('../../maths/vec3')
|
|
2
|
-
const line3 = require('../../maths/line3')
|
|
3
|
-
|
|
4
|
-
const poly3 = require('../../geometries/poly3')
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
* Add a unique edge to the given list of edges.
|
|
8
|
-
* Each edge has a list of associated polygons.
|
|
9
|
-
* Edges with two polygons are complete, while edges with one polygon are open, i.e hole or t-junction..
|
|
10
|
-
*/
|
|
11
|
-
const addEdge = (edges, edge, polygon) => {
|
|
12
|
-
const ei = edges.findIndex((element) => {
|
|
13
|
-
if (element) {
|
|
14
|
-
if (vec3.equals(element[0], edge[0]) && vec3.equals(element[1], edge[1])) return true
|
|
15
|
-
if (vec3.equals(element[0], edge[1]) && vec3.equals(element[1], edge[0])) return true
|
|
16
|
-
}
|
|
17
|
-
return false
|
|
18
|
-
})
|
|
19
|
-
if (ei >= 0) {
|
|
20
|
-
edge = edges[ei]
|
|
21
|
-
edge.polygons.push(polygon)
|
|
22
|
-
} else {
|
|
23
|
-
edge.polygons = [polygon]
|
|
24
|
-
edges.push(edge)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/*
|
|
29
|
-
* Remove the edge from the given list of edges.
|
|
30
|
-
*/
|
|
31
|
-
const removeEdge = (edges, edge) => {
|
|
32
|
-
const ei = edges.findIndex((element) => {
|
|
33
|
-
if (element) {
|
|
34
|
-
if (vec3.equals(element[0], edge[0]) && vec3.equals(element[1], edge[1])) return true
|
|
35
|
-
if (vec3.equals(element[0], edge[1]) && vec3.equals(element[1], edge[0])) return true
|
|
36
|
-
}
|
|
37
|
-
return false
|
|
38
|
-
})
|
|
39
|
-
if (ei >= 0) {
|
|
40
|
-
edges[ei].polygons = []
|
|
41
|
-
edges[ei] = null
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/*
|
|
46
|
-
* Add all edges of the polygon to the given list of edges.
|
|
47
|
-
*/
|
|
48
|
-
const addPolygon = (edges, polygon) => {
|
|
49
|
-
const vertices = polygon.vertices
|
|
50
|
-
const nv = vertices.length
|
|
51
|
-
|
|
52
|
-
let edge = [vertices[nv - 1], vertices[0]]
|
|
53
|
-
addEdge(edges, edge, polygon)
|
|
54
|
-
|
|
55
|
-
for (let i = 0; i < (nv - 1); i++) {
|
|
56
|
-
edge = [vertices[i], vertices[i + 1]]
|
|
57
|
-
addEdge(edges, edge, polygon)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/*
|
|
62
|
-
* Remove all polygons associated with the old edge from the given list of edges.
|
|
63
|
-
*/
|
|
64
|
-
const removePolygons = (edges, oldedge) => {
|
|
65
|
-
// console.log('removePolygons',oldedge)
|
|
66
|
-
const polygons = oldedge.polygons
|
|
67
|
-
polygons.forEach((polygon) => {
|
|
68
|
-
const vertices = polygon.vertices
|
|
69
|
-
const nv = vertices.length
|
|
70
|
-
|
|
71
|
-
let edge = [vertices[nv - 1], vertices[0]]
|
|
72
|
-
removeEdge(edges, edge)
|
|
73
|
-
|
|
74
|
-
for (let i = 0; i < (nv - 1); i++) {
|
|
75
|
-
edge = [vertices[i], vertices[i + 1]]
|
|
76
|
-
removeEdge(edges, edge)
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/*
|
|
82
|
-
* Split the polygon, ensuring one polygon includes the open edge.
|
|
83
|
-
*/
|
|
84
|
-
const splitPolygon = (openedge, polygon, eps) => {
|
|
85
|
-
// console.log('splitPolygon',openedge,polygon)
|
|
86
|
-
const vertices = polygon.vertices
|
|
87
|
-
const i = vertices.findIndex((point) => almostEquals(eps, point, openedge[0]))
|
|
88
|
-
const polygon1 = poly3.fromPoints([vertices[(i + 0) % 3], vertices[(i + 1) % 3], openedge[1]])
|
|
89
|
-
const polygon2 = poly3.fromPoints([openedge[1], vertices[(i + 1) % 3], vertices[(i + 2) % 3]])
|
|
90
|
-
if (polygon.color) {
|
|
91
|
-
polygon1.color = polygon.color
|
|
92
|
-
polygon2.color = polygon.color
|
|
93
|
-
}
|
|
94
|
-
// console.log('polygon1',polygon1)
|
|
95
|
-
// console.log('polygon2',polygon2)
|
|
96
|
-
return [polygon1, polygon2]
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/*
|
|
100
|
-
* TBD This should be part of vec3.
|
|
101
|
-
*/
|
|
102
|
-
const almostEquals = (eps, v1, v2) => (Math.abs(v1[0] - v2[0]) <= eps && Math.abs(v1[1] - v2[1]) <= eps && Math.abs(v1[2] - v2[2]) <= eps)
|
|
103
|
-
|
|
104
|
-
const enclosedEdge = (openedge, edge, eps) => {
|
|
105
|
-
if (openedge.distance < edge.distance) {
|
|
106
|
-
// only look for opposing edges
|
|
107
|
-
if (vec3.equals(openedge[0], edge[1])) {
|
|
108
|
-
// only opposing open edges enclosed by the edge
|
|
109
|
-
const distanceE0O0 = vec3.squaredDistance(openedge[0], edge[0])
|
|
110
|
-
const distanceE0O1 = vec3.squaredDistance(openedge[1], edge[0])
|
|
111
|
-
const distanceE1O0 = vec3.squaredDistance(openedge[0], edge[1])
|
|
112
|
-
const distanceE1O1 = vec3.squaredDistance(openedge[1], edge[1])
|
|
113
|
-
if (distanceE0O0 <= edge.distance && distanceE0O1 < edge.distance && distanceE1O0 < edge.distance && distanceE1O1 < edge.distance) {
|
|
114
|
-
// only look for paralell open edges
|
|
115
|
-
const line3d = line3.fromPoints(edge[0], edge[1])
|
|
116
|
-
const closest0 = vec3.snap(vec3.create(), eps, line3.closestPoint(openedge[0], line3d))
|
|
117
|
-
const closest1 = vec3.snap(vec3.create(), eps, line3.closestPoint(openedge[1], line3d))
|
|
118
|
-
if (almostEquals(eps, closest0, openedge[0]) && almostEquals(eps, closest1, openedge[1])) {
|
|
119
|
-
return true
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return false
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/*
|
|
128
|
-
* Split the edge if posssible from the list of open edges.
|
|
129
|
-
* Return a list of new polygons, or null if not possible
|
|
130
|
-
*/
|
|
131
|
-
const splitEdge = (openedges, edge, eps) => {
|
|
132
|
-
// console.log('splitEdge',edge)
|
|
133
|
-
for (let i = 0; i < openedges.length; i++) {
|
|
134
|
-
const openedge = openedges[i]
|
|
135
|
-
if (openedge) {
|
|
136
|
-
if (enclosedEdge(openedge, edge, eps)) {
|
|
137
|
-
// spit the polygon associated with the edge
|
|
138
|
-
const polygon = edge.polygons[0]
|
|
139
|
-
const newpolygons = splitPolygon(openedge, polygon, eps)
|
|
140
|
-
return newpolygons
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return null
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/*
|
|
148
|
-
* Cull a list of open edges (see above) from the list of edges.
|
|
149
|
-
*/
|
|
150
|
-
const cullOpenEdges = (edges) => {
|
|
151
|
-
const openedges = []
|
|
152
|
-
edges.forEach((edge) => {
|
|
153
|
-
const polygons = edge.polygons
|
|
154
|
-
if (polygons.length === 1) {
|
|
155
|
-
// console.log('open edge: ',edge[0],'<-->',edge[1])
|
|
156
|
-
edge.distance = vec3.squaredDistance(edge[0], edge[1])
|
|
157
|
-
openedges.push(edge)
|
|
158
|
-
}
|
|
159
|
-
})
|
|
160
|
-
// console.log('open edges:',openedges.length)
|
|
161
|
-
// console.log('**********OPEN*********')
|
|
162
|
-
// console.log(openedges)
|
|
163
|
-
// console.log('**********OPEN*********')
|
|
164
|
-
return openedges
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/*
|
|
168
|
-
* Convert the list of edges into a list of polygons.
|
|
169
|
-
*/
|
|
170
|
-
const edgesToPolygons = (edges) => {
|
|
171
|
-
const polygons = []
|
|
172
|
-
edges.forEach((edge) => {
|
|
173
|
-
if (edge && edge.polygons) {
|
|
174
|
-
edge.polygons.forEach((polygon) => {
|
|
175
|
-
if (polygon.visited) return
|
|
176
|
-
polygon.visited = true
|
|
177
|
-
polygons.push(polygon)
|
|
178
|
-
})
|
|
179
|
-
}
|
|
180
|
-
})
|
|
181
|
-
return polygons
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/*
|
|
185
|
-
* Convert the given list of polygons to a list of edges.
|
|
186
|
-
*/
|
|
187
|
-
const polygonsToEdges = (polygons) => {
|
|
188
|
-
const edges = []
|
|
189
|
-
polygons.forEach((polygon) => {
|
|
190
|
-
addPolygon(edges, polygon)
|
|
191
|
-
})
|
|
192
|
-
return edges
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
module.exports = { polygonsToEdges, edgesToPolygons, cullOpenEdges, splitEdge, removePolygons, addPolygon }
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
const { polygonsToEdges, edgesToPolygons, cullOpenEdges, splitEdge, removePolygons, addPolygon } = require('./edges')
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
*/
|
|
5
|
-
const repairTjunctions = (epsilon, polygons) => {
|
|
6
|
-
const edges = polygonsToEdges(polygons)
|
|
7
|
-
let openedges = cullOpenEdges(edges)
|
|
8
|
-
if (openedges.length === 0) return polygons
|
|
9
|
-
|
|
10
|
-
// split open edges until no longer possible
|
|
11
|
-
let splitting = true
|
|
12
|
-
while (splitting) {
|
|
13
|
-
let splitcount = 0
|
|
14
|
-
for (let i = 0; i < openedges.length; i++) {
|
|
15
|
-
const edge = openedges[i]
|
|
16
|
-
if (edge && edge.polygons && edge.polygons.length === 1) {
|
|
17
|
-
const newpolygons = splitEdge(openedges, edge, epsilon)
|
|
18
|
-
if (newpolygons) {
|
|
19
|
-
openedges[i] = null
|
|
20
|
-
addPolygon(openedges, newpolygons[0])
|
|
21
|
-
addPolygon(openedges, newpolygons[1])
|
|
22
|
-
|
|
23
|
-
// adjust the master list as well
|
|
24
|
-
removePolygons(edges, edge)
|
|
25
|
-
// add edges for each new polygon
|
|
26
|
-
addPolygon(edges, newpolygons[0])
|
|
27
|
-
addPolygon(edges, newpolygons[1])
|
|
28
|
-
|
|
29
|
-
splitcount++
|
|
30
|
-
break // start again
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
splitting = (splitcount > 0)
|
|
35
|
-
}
|
|
36
|
-
openedges = openedges.filter((edge) => (edge && edge.polygons && edge.polygons.length === 1))
|
|
37
|
-
if (openedges.length > 0) console.warn('Repair of all T-junctions failed:', openedges.length)
|
|
38
|
-
|
|
39
|
-
// rebuild the list of polygons from the edges
|
|
40
|
-
polygons = edgesToPolygons(edges)
|
|
41
|
-
return polygons
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
module.exports = repairTjunctions
|