@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
|
@@ -26,14 +26,14 @@ test('project (defaults)', (t) => {
|
|
|
26
26
|
t.notThrows(() => geom2.validate(result))
|
|
27
27
|
const pts = geom2.toPoints(result)
|
|
28
28
|
const exp = [
|
|
29
|
-
[0, -2.9999933333333333],
|
|
30
29
|
[0, -5.000013333333333],
|
|
31
|
-
[0, 5.000013333333333],
|
|
32
30
|
[5.000013333333333, 0],
|
|
33
|
-
[
|
|
31
|
+
[-5.000013333333333, 0],
|
|
34
32
|
[-2.9999933333333333, 0],
|
|
35
33
|
[2.9999933333333333, 0],
|
|
36
|
-
[
|
|
34
|
+
[0, 2.9999933333333333],
|
|
35
|
+
[0, -2.9999933333333333],
|
|
36
|
+
[0, 5.000013333333333]
|
|
37
37
|
]
|
|
38
38
|
t.true(comparePoints(pts, exp))
|
|
39
39
|
})
|
|
@@ -43,40 +43,40 @@ test('project (X and Y axis)', (t) => {
|
|
|
43
43
|
t.notThrows(() => geom2.validate(result))
|
|
44
44
|
let pts = geom2.toPoints(result)
|
|
45
45
|
let exp = [
|
|
46
|
-
[-1.0000200000000001, -3.999986666666667],
|
|
47
|
-
[-0.8314600000000001, 4.555553333333334],
|
|
48
|
-
[-0.9807933333333334, -4.1951],
|
|
49
|
-
[-0.7070933333333335, 4.707126666666667],
|
|
50
|
-
[-0.9238600000000001, -4.382700000000001],
|
|
51
|
-
[-0.5555666666666668, 4.831446666666667],
|
|
52
|
-
[-0.8314600000000001, -4.555553333333334],
|
|
53
|
-
[-0.3826666666666667, 4.923893333333334],
|
|
54
|
-
[-0.7070933333333335, -4.707126666666667],
|
|
55
|
-
[-0.19511333333333336, 4.98078],
|
|
56
|
-
[-0.5555666666666668, -4.831446666666667],
|
|
57
|
-
[0, 5.000006666666668],
|
|
58
|
-
[-0.3826666666666667, -4.923893333333334],
|
|
59
|
-
[0.19511333333333336, 4.98078],
|
|
60
46
|
[-0.19511333333333336, -4.98078],
|
|
61
|
-
[0.3826666666666667, 4.923893333333334],
|
|
62
47
|
[0, -5.000006666666668],
|
|
63
|
-
[0
|
|
48
|
+
[0, 5.000006666666668],
|
|
49
|
+
[0.3826666666666667, 4.923893333333334],
|
|
50
|
+
[-0.3826666666666667, -4.923893333333334],
|
|
64
51
|
[0.19511333333333336, -4.98078],
|
|
65
|
-
[0.
|
|
52
|
+
[-0.19511333333333336, 4.98078],
|
|
53
|
+
[-0.5555666666666668, -4.831446666666667],
|
|
54
|
+
[0.5555666666666668, 4.831446666666667],
|
|
55
|
+
[-0.3826666666666667, 4.923893333333334],
|
|
66
56
|
[0.3826666666666667, -4.923893333333334],
|
|
67
|
-
[0.
|
|
57
|
+
[0.7070933333333335, 4.707126666666667],
|
|
58
|
+
[-0.7070933333333335, -4.707126666666667],
|
|
68
59
|
[0.5555666666666668, -4.831446666666667],
|
|
69
|
-
[0.
|
|
60
|
+
[-0.5555666666666668, 4.831446666666667],
|
|
61
|
+
[0.8314600000000001, 4.555553333333334],
|
|
62
|
+
[-0.8314600000000001, -4.555553333333334],
|
|
70
63
|
[0.7070933333333335, -4.707126666666667],
|
|
71
|
-
[0.
|
|
64
|
+
[-0.7070933333333335, 4.707126666666667],
|
|
65
|
+
[-0.9238600000000001, -4.382700000000001],
|
|
66
|
+
[0.9238600000000001, 4.382700000000001],
|
|
67
|
+
[-0.8314600000000001, 4.555553333333334],
|
|
72
68
|
[0.8314600000000001, -4.555553333333334],
|
|
69
|
+
[0.9807933333333334, 4.1951],
|
|
70
|
+
[-0.9807933333333334, -4.1951],
|
|
71
|
+
[0.9238600000000001, -4.382700000000001],
|
|
72
|
+
[-0.9238600000000001, 4.382700000000001],
|
|
73
73
|
[1.0000200000000001, 3.999986666666667],
|
|
74
|
+
[-1.0000200000000001, -3.999986666666667],
|
|
74
75
|
[1.0000200000000001, -3.999986666666667],
|
|
75
|
-
[0.9238600000000001, -4.382700000000001],
|
|
76
|
-
[0.9807933333333334, -4.1951],
|
|
77
76
|
[-1.0000200000000001, 3.999986666666667],
|
|
78
77
|
[-0.9807933333333334, 4.1951],
|
|
79
|
-
[
|
|
78
|
+
[0.9807933333333334, -4.1951],
|
|
79
|
+
[0.19511333333333336, 4.98078]
|
|
80
80
|
]
|
|
81
81
|
t.true(comparePoints(pts, exp))
|
|
82
82
|
|
|
@@ -84,40 +84,40 @@ test('project (X and Y axis)', (t) => {
|
|
|
84
84
|
t.notThrows(() => geom2.validate(result))
|
|
85
85
|
pts = geom2.toPoints(result)
|
|
86
86
|
exp = [
|
|
87
|
-
[3.999986666666667, -1.0000200000000001],
|
|
88
|
-
[-4.555553333333334, -0.8314600000000001],
|
|
89
|
-
[4.1951, -0.9807933333333334],
|
|
90
|
-
[-4.707126666666667, -0.7070933333333335],
|
|
91
|
-
[4.382700000000001, -0.9238600000000001],
|
|
92
|
-
[-4.831446666666667, -0.5555666666666668],
|
|
93
|
-
[4.555553333333334, -0.8314600000000001],
|
|
94
|
-
[-4.923893333333334, -0.3826666666666667],
|
|
95
|
-
[4.707126666666667, -0.7070933333333335],
|
|
96
|
-
[-4.98078, -0.19511333333333336],
|
|
97
|
-
[4.831446666666667, -0.5555666666666668],
|
|
98
|
-
[-5.000006666666668, 0],
|
|
99
|
-
[4.923893333333334, -0.3826666666666667],
|
|
100
|
-
[-4.98078, 0.19511333333333336],
|
|
101
87
|
[4.98078, -0.19511333333333336],
|
|
102
|
-
[-4.923893333333334, 0.3826666666666667],
|
|
103
88
|
[5.000006666666668, 0],
|
|
104
|
-
[-
|
|
89
|
+
[-5.000006666666668, 0],
|
|
90
|
+
[-4.923893333333334, 0.3826666666666667],
|
|
91
|
+
[4.923893333333334, -0.3826666666666667],
|
|
105
92
|
[4.98078, 0.19511333333333336],
|
|
106
|
-
[-4.
|
|
93
|
+
[-4.98078, -0.19511333333333336],
|
|
94
|
+
[4.831446666666667, -0.5555666666666668],
|
|
95
|
+
[-4.831446666666667, 0.5555666666666668],
|
|
96
|
+
[-4.923893333333334, -0.3826666666666667],
|
|
107
97
|
[4.923893333333334, 0.3826666666666667],
|
|
108
|
-
[-4.
|
|
98
|
+
[-4.707126666666667, 0.7070933333333335],
|
|
99
|
+
[4.707126666666667, -0.7070933333333335],
|
|
109
100
|
[4.831446666666667, 0.5555666666666668],
|
|
110
|
-
[-4.
|
|
101
|
+
[-4.831446666666667, -0.5555666666666668],
|
|
102
|
+
[4.555553333333334, -0.8314600000000001],
|
|
103
|
+
[-4.555553333333334, 0.8314600000000001],
|
|
111
104
|
[4.707126666666667, 0.7070933333333335],
|
|
112
|
-
[-4.
|
|
105
|
+
[-4.707126666666667, -0.7070933333333335],
|
|
106
|
+
[4.382700000000001, -0.9238600000000001],
|
|
107
|
+
[-4.382700000000001, 0.9238600000000001],
|
|
108
|
+
[-4.555553333333334, -0.8314600000000001],
|
|
113
109
|
[4.555553333333334, 0.8314600000000001],
|
|
110
|
+
[-4.1951, 0.9807933333333334],
|
|
111
|
+
[4.1951, -0.9807933333333334],
|
|
112
|
+
[4.382700000000001, 0.9238600000000001],
|
|
113
|
+
[-4.382700000000001, -0.9238600000000001],
|
|
114
|
+
[3.999986666666667, -1.0000200000000001],
|
|
114
115
|
[-3.999986666666667, 1.0000200000000001],
|
|
115
116
|
[3.999986666666667, 1.0000200000000001],
|
|
116
|
-
[4.382700000000001, 0.9238600000000001],
|
|
117
|
-
[4.1951, 0.9807933333333334],
|
|
118
117
|
[-3.999986666666667, -1.0000200000000001],
|
|
118
|
+
[4.1951, 0.9807933333333334],
|
|
119
119
|
[-4.1951, -0.9807933333333334],
|
|
120
|
-
[-4.
|
|
120
|
+
[-4.98078, 0.19511333333333336]
|
|
121
121
|
]
|
|
122
122
|
t.true(comparePoints(pts, exp))
|
|
123
123
|
})
|
|
@@ -12,14 +12,14 @@ test('hullChain (two, geom2)', (t) => {
|
|
|
12
12
|
let obs = hullChain(geometry1, geometry1)
|
|
13
13
|
let pts = geom2.toPoints(obs)
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
t.notThrows(() => geom2.validate(obs))
|
|
16
16
|
t.is(pts.length, 4)
|
|
17
17
|
|
|
18
18
|
// different
|
|
19
19
|
obs = hullChain(geometry1, geometry2)
|
|
20
20
|
pts = geom2.toPoints(obs)
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
t.notThrows(() => geom2.validate(obs))
|
|
23
23
|
t.is(pts.length, 6)
|
|
24
24
|
})
|
|
25
25
|
|
|
@@ -33,7 +33,7 @@ test('hullChain (three, geom2)', (t) => {
|
|
|
33
33
|
let pts = geom2.toPoints(obs)
|
|
34
34
|
|
|
35
35
|
// the sides change based on the bestplane chosen in trees/Node.js
|
|
36
|
-
|
|
36
|
+
t.notThrows(() => geom2.validate(obs))
|
|
37
37
|
t.is(pts.length, 10)
|
|
38
38
|
|
|
39
39
|
// closed
|
|
@@ -41,7 +41,7 @@ test('hullChain (three, geom2)', (t) => {
|
|
|
41
41
|
pts = geom2.toPoints(obs)
|
|
42
42
|
|
|
43
43
|
// the sides change based on the bestplane chosen in trees/Node.js
|
|
44
|
-
|
|
44
|
+
t.notThrows(() => geom2.validate(obs))
|
|
45
45
|
t.is(pts.length, 10)
|
|
46
46
|
})
|
|
47
47
|
|
|
@@ -3,6 +3,7 @@ const flatten = require('../../utils/flatten')
|
|
|
3
3
|
const geom2 = require('../../geometries/geom2')
|
|
4
4
|
|
|
5
5
|
const hullPoints2 = require('./hullPoints2')
|
|
6
|
+
const toUniquePoints = require('./toUniquePoints')
|
|
6
7
|
|
|
7
8
|
/*
|
|
8
9
|
* Create a convex hull of the given geom2 geometries.
|
|
@@ -13,28 +14,15 @@ const hullGeom2 = (...geometries) => {
|
|
|
13
14
|
geometries = flatten(geometries)
|
|
14
15
|
|
|
15
16
|
// extract the unique points from the geometries
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const sides = geom2.toSides(geometries[g])
|
|
20
|
-
for (let s = 0; s < sides.length; s++) {
|
|
21
|
-
const side = sides[s]
|
|
22
|
-
const point = side[0]
|
|
23
|
-
const id = `${point[0]},${point[1]}`
|
|
24
|
-
if (found.has(id)) continue
|
|
25
|
-
uniquepoints.push(point)
|
|
26
|
-
found.set(id, true)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
found.clear()
|
|
30
|
-
|
|
31
|
-
const hullpoints = hullPoints2(uniquepoints)
|
|
17
|
+
const unique = toUniquePoints(geometries)
|
|
18
|
+
|
|
19
|
+
const hullPoints = hullPoints2(unique)
|
|
32
20
|
|
|
33
21
|
// NOTE: more than three points are required to create a new geometry
|
|
34
|
-
if (
|
|
22
|
+
if (hullPoints.length < 3) return geom2.create()
|
|
35
23
|
|
|
36
24
|
// assemble a new geometry from the list of points
|
|
37
|
-
return geom2.fromPoints(
|
|
25
|
+
return geom2.fromPoints(hullPoints)
|
|
38
26
|
}
|
|
39
27
|
|
|
40
28
|
module.exports = hullGeom2
|
|
@@ -4,6 +4,7 @@ const geom3 = require('../../geometries/geom3')
|
|
|
4
4
|
const poly3 = require('../../geometries/poly3')
|
|
5
5
|
|
|
6
6
|
const quickhull = require('./quickhull')
|
|
7
|
+
const toUniquePoints = require('./toUniquePoints')
|
|
7
8
|
|
|
8
9
|
/*
|
|
9
10
|
* Create a convex hull of the given geometries (geom3).
|
|
@@ -16,26 +17,12 @@ const hullGeom3 = (...geometries) => {
|
|
|
16
17
|
if (geometries.length === 1) return geometries[0]
|
|
17
18
|
|
|
18
19
|
// extract the unique vertices from the geometries
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const polygons = geom3.toPolygons(geometries[g])
|
|
23
|
-
for (let p = 0; p < polygons.length; ++p) {
|
|
24
|
-
const vertices = polygons[p].vertices
|
|
25
|
-
for (let v = 0; v < vertices.length; ++v) {
|
|
26
|
-
const id = `${vertices[v]}`
|
|
27
|
-
if (found.has(id)) continue
|
|
28
|
-
uniquevertices.push(vertices[v])
|
|
29
|
-
found.set(id, true)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
found.clear()
|
|
34
|
-
|
|
35
|
-
const faces = quickhull(uniquevertices, { skipTriangulation: true })
|
|
20
|
+
const unique = toUniquePoints(geometries)
|
|
21
|
+
|
|
22
|
+
const faces = quickhull(unique, { skipTriangulation: true })
|
|
36
23
|
|
|
37
24
|
const polygons = faces.map((face) => {
|
|
38
|
-
const vertices = face.map((index) =>
|
|
25
|
+
const vertices = face.map((index) => unique[index])
|
|
39
26
|
return poly3.create(vertices)
|
|
40
27
|
})
|
|
41
28
|
|
|
@@ -3,6 +3,7 @@ const flatten = require('../../utils/flatten')
|
|
|
3
3
|
const path2 = require('../../geometries/path2')
|
|
4
4
|
|
|
5
5
|
const hullPoints2 = require('./hullPoints2')
|
|
6
|
+
const toUniquePoints = require('./toUniquePoints')
|
|
6
7
|
|
|
7
8
|
/*
|
|
8
9
|
* Create a convex hull of the given geometries (path2).
|
|
@@ -13,23 +14,12 @@ const hullPath2 = (...geometries) => {
|
|
|
13
14
|
geometries = flatten(geometries)
|
|
14
15
|
|
|
15
16
|
// extract the unique points from the geometries
|
|
16
|
-
const
|
|
17
|
-
const found = new Set()
|
|
18
|
-
geometries.forEach((geometry) => {
|
|
19
|
-
const points = path2.toPoints(geometry)
|
|
20
|
-
points.forEach((point) => {
|
|
21
|
-
const key = point.toString()
|
|
22
|
-
if (!found.has(key)) {
|
|
23
|
-
uniquepoints.push(point)
|
|
24
|
-
found.add(key)
|
|
25
|
-
}
|
|
26
|
-
})
|
|
27
|
-
})
|
|
17
|
+
const unique = toUniquePoints(geometries)
|
|
28
18
|
|
|
29
|
-
const
|
|
19
|
+
const hullPoints = hullPoints2(unique)
|
|
30
20
|
|
|
31
21
|
// assemble a new geometry from the list of points
|
|
32
|
-
return path2.fromPoints({ closed: true },
|
|
22
|
+
return path2.fromPoints({ closed: true }, hullPoints)
|
|
33
23
|
}
|
|
34
24
|
|
|
35
25
|
module.exports = hullPath2
|
|
@@ -1,108 +1,59 @@
|
|
|
1
1
|
const vec2 = require('../../maths/vec2')
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return 0
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Ported from https://github.com/bkiers/GrahamScan
|
|
21
|
-
const compute = (points) => {
|
|
22
|
-
if (points.length < 3) {
|
|
23
|
-
return points
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Find the lowest point
|
|
27
|
-
let min = 0
|
|
28
|
-
points.forEach((point, i) => {
|
|
29
|
-
const minpoint = points[min]
|
|
30
|
-
if (point[1] === minpoint[1]) {
|
|
31
|
-
if (point[0] < minpoint[0]) {
|
|
32
|
-
min = i
|
|
33
|
-
}
|
|
34
|
-
} else if (point[1] < minpoint[1]) {
|
|
35
|
-
min = i
|
|
3
|
+
/*
|
|
4
|
+
* Create a convex hull of the given set of points, where each point is an array of [x,y].
|
|
5
|
+
* Uses https://en.wikipedia.org/wiki/Graham_scan
|
|
6
|
+
* @param {Array} uniquePoints - list of UNIQUE points from which to create a hull
|
|
7
|
+
* @returns {Array} a list of points that form the hull
|
|
8
|
+
*/
|
|
9
|
+
const hullPoints2 = (uniquePoints) => {
|
|
10
|
+
// find min point
|
|
11
|
+
let min = vec2.fromValues(Infinity, Infinity)
|
|
12
|
+
uniquePoints.forEach((point) => {
|
|
13
|
+
if (point[1] < min[1] || (point[1] === min[1] && point[0] < min[0])) {
|
|
14
|
+
min = point
|
|
36
15
|
}
|
|
37
16
|
})
|
|
38
17
|
|
|
39
|
-
//
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
angle = angleBetweenPoints(points[min], points[i])
|
|
48
|
-
if (angle < 0) {
|
|
49
|
-
angle += Math.PI
|
|
50
|
-
}
|
|
51
|
-
dist = vec2.squaredDistance(points[min], points[i])
|
|
52
|
-
al.push({ index: i, angle: angle, distance: dist })
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
al.sort((a, b) => compareIndex(a, b))
|
|
56
|
-
|
|
57
|
-
// Wind around the points CCW, removing interior points
|
|
58
|
-
const stack = new Array(points.length + 1)
|
|
59
|
-
let j = 2
|
|
60
|
-
for (let i = 0; i < points.length; i++) {
|
|
61
|
-
if (i === min) {
|
|
62
|
-
continue
|
|
63
|
-
}
|
|
64
|
-
stack[j] = al[j - 2].index
|
|
65
|
-
j++
|
|
66
|
-
}
|
|
67
|
-
stack[0] = stack[points.length]
|
|
68
|
-
stack[1] = min
|
|
18
|
+
// gather information for sorting by polar coordinates (point, angle, distSq)
|
|
19
|
+
const points = []
|
|
20
|
+
uniquePoints.forEach((point) => {
|
|
21
|
+
// use faster fakeAtan2 instead of Math.atan2
|
|
22
|
+
const angle = fakeAtan2(point[1] - min[1], point[0] - min[0])
|
|
23
|
+
const distSq = vec2.squaredDistance(point, min)
|
|
24
|
+
points.push({ point, angle, distSq })
|
|
25
|
+
})
|
|
69
26
|
|
|
70
|
-
//
|
|
71
|
-
|
|
27
|
+
// sort by polar coordinates
|
|
28
|
+
points.sort((pt1, pt2) => pt1.angle < pt2.angle ? -1 : pt1.angle > pt2.angle ? 1 :
|
|
29
|
+
pt1.distSq < pt2.distSq ? -1 : pt1.distSq > pt2.distSq ? 1 : 0)
|
|
72
30
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
while (ccw(stack[
|
|
77
|
-
|
|
31
|
+
const stack = [] // start with empty stack
|
|
32
|
+
points.forEach((point) => {
|
|
33
|
+
let cnt = stack.length
|
|
34
|
+
while (cnt > 1 && ccw(stack[cnt - 2], stack[cnt - 1], point.point) <= Number.EPSILON) {
|
|
35
|
+
stack.pop() // get rid of colinear and interior (clockwise) points
|
|
36
|
+
cnt = stack.length
|
|
78
37
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
stack[i] = stack[M]
|
|
82
|
-
stack[M] = tmp
|
|
83
|
-
}
|
|
38
|
+
stack.push(point.point)
|
|
39
|
+
})
|
|
84
40
|
|
|
85
|
-
|
|
86
|
-
const indices = new Array(M)
|
|
87
|
-
for (let i = 0; i < M; i++) {
|
|
88
|
-
indices[i] = stack[i + 1]
|
|
89
|
-
}
|
|
90
|
-
return indices
|
|
41
|
+
return stack
|
|
91
42
|
}
|
|
92
43
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* @param {Array} uniquepoints - list of UNIQUE points from which to create a hull
|
|
96
|
-
* @returns {Array} a list of points that form the hull
|
|
97
|
-
*/
|
|
98
|
-
const hullPoints2 = (uniquepoints) => {
|
|
99
|
-
const indices = compute(uniquepoints)
|
|
44
|
+
// returns: < 0 clockwise, 0 colinear, > 0 counter-clockwise
|
|
45
|
+
const ccw = (v1, v2, v3) => (v2[0] - v1[0]) * (v3[1] - v1[1]) - (v2[1] - v1[1]) * (v3[0] - v1[0])
|
|
100
46
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
47
|
+
// Returned "angle" is really 1/tan (inverse of slope) made negative to increase with angle.
|
|
48
|
+
// This function is strictly for sorting in this algorithm.
|
|
49
|
+
const fakeAtan2 = (y, x) => {
|
|
50
|
+
// The "if" is a special case for when the minimum vector found in loop above is present.
|
|
51
|
+
// We need to ensure that it sorts as the minimum point. Otherwise, this becomes NaN.
|
|
52
|
+
if (y === 0 && x === 0) {
|
|
53
|
+
return -Infinity
|
|
54
|
+
} else {
|
|
55
|
+
return -x / y
|
|
104
56
|
}
|
|
105
|
-
return hullpoints
|
|
106
57
|
}
|
|
107
58
|
|
|
108
59
|
module.exports = hullPoints2
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const geom2 = require('../../geometries/geom2')
|
|
2
|
+
const geom3 = require('../../geometries/geom3')
|
|
3
|
+
const path2 = require('../../geometries/path2')
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Return the unique vertices of a geometry
|
|
7
|
+
*/
|
|
8
|
+
const toUniquePoints = (geometries) => {
|
|
9
|
+
const found = new Set()
|
|
10
|
+
const uniquePoints = []
|
|
11
|
+
|
|
12
|
+
const addPoint = (point) => {
|
|
13
|
+
const key = point.toString()
|
|
14
|
+
if (!found.has(key)) {
|
|
15
|
+
uniquePoints.push(point)
|
|
16
|
+
found.add(key)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
geometries.forEach((geometry) => {
|
|
21
|
+
if (geom2.isA(geometry)) {
|
|
22
|
+
geom2.toPoints(geometry).forEach(addPoint)
|
|
23
|
+
} else if (geom3.isA(geometry)) {
|
|
24
|
+
// points are grouped by polygon
|
|
25
|
+
geom3.toPoints(geometry).forEach((points) => points.forEach(addPoint))
|
|
26
|
+
} else if (path2.isA(geometry)) {
|
|
27
|
+
path2.toPoints(geometry).forEach(addPoint)
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return uniquePoints
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = toUniquePoints
|
|
@@ -11,8 +11,6 @@ const mergePolygons = require('./mergePolygons')
|
|
|
11
11
|
const insertTjunctions = require('./insertTjunctions')
|
|
12
12
|
const triangulatePolygons = require('./triangulatePolygons')
|
|
13
13
|
|
|
14
|
-
const repairTjunctions = require('./repairTjunctions')
|
|
15
|
-
|
|
16
14
|
/*
|
|
17
15
|
*/
|
|
18
16
|
const generalizePath2 = (options, geometry) => geometry
|
|
@@ -27,10 +25,9 @@ const generalizeGeom3 = (options, geometry) => {
|
|
|
27
25
|
const defaults = {
|
|
28
26
|
snap: false,
|
|
29
27
|
simplify: false,
|
|
30
|
-
triangulate: false
|
|
31
|
-
repair: false
|
|
28
|
+
triangulate: false
|
|
32
29
|
}
|
|
33
|
-
const { snap, simplify, triangulate
|
|
30
|
+
const { snap, simplify, triangulate } = Object.assign({}, defaults, options)
|
|
34
31
|
|
|
35
32
|
const epsilon = measureEpsilon(geometry)
|
|
36
33
|
let polygons = geom3.toPolygons(geometry)
|
|
@@ -52,13 +49,6 @@ const generalizeGeom3 = (options, geometry) => {
|
|
|
52
49
|
polygons = triangulatePolygons(epsilon, polygons)
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
// repair the polygons (possibly triangles) if requested
|
|
56
|
-
if (repair) {
|
|
57
|
-
// fix T junctions
|
|
58
|
-
polygons = repairTjunctions(epsilon, polygons)
|
|
59
|
-
// TODO fill holes
|
|
60
|
-
}
|
|
61
|
-
|
|
62
52
|
// FIXME replace with geom3.cloneShallow() when available
|
|
63
53
|
const clone = Object.assign({}, geometry)
|
|
64
54
|
clone.polygons = polygons
|
|
@@ -72,7 +62,6 @@ const generalizeGeom3 = (options, geometry) => {
|
|
|
72
62
|
* @param {Boolean} [options.snap=false] the geometries should be snapped to epsilons
|
|
73
63
|
* @param {Boolean} [options.simplify=false] the geometries should be simplified
|
|
74
64
|
* @param {Boolean} [options.triangulate=false] the geometries should be triangulated
|
|
75
|
-
* @param {Boolean} [options.repair=false] the geometries should be repaired
|
|
76
65
|
* @param {...Object} geometries - the geometries to generalize
|
|
77
66
|
* @return {Object|Array} the modified geometry, or a list of modified geometries
|
|
78
67
|
* @alias module:modeling/modifiers.generalize
|
|
@@ -104,38 +104,6 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
|
|
|
104
104
|
]
|
|
105
105
|
t.notThrows(() => geom3.validate(result))
|
|
106
106
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
107
|
-
|
|
108
|
-
// apply repairs only (triangles)
|
|
109
|
-
result = generalize({ repair: true }, geometry2)
|
|
110
|
-
pts = geom3.toPoints(result)
|
|
111
|
-
exp = [
|
|
112
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
113
|
-
[-1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
|
|
114
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
115
|
-
[-1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
|
|
116
|
-
[[1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, -3.141592653589793],
|
|
117
|
-
[1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
|
|
118
|
-
[[1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
119
|
-
[1.5707963267948966, -0.7853981633974483, 3.141592653589793]],
|
|
120
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, -0.7853981633974483, -3.141592653589793],
|
|
121
|
-
[1.5707963267948966, -0.7853981633974483, 3.141592653589793]],
|
|
122
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
123
|
-
[-1.5707963267948966, -0.7853981633974483, 3.141592653589793]],
|
|
124
|
-
[[-1.5707963267948966, 0.7853981633974483, -3.141592653589793], [-1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
125
|
-
[1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
|
|
126
|
-
[[-1.5707963267948966, 0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
127
|
-
[1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
|
|
128
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, 0.7853981633974483, -3.141592653589793],
|
|
129
|
-
[1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
|
|
130
|
-
[[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, -3.141592653589793],
|
|
131
|
-
[1.5707963267948966, -0.7853981633974483, -3.141592653589793]],
|
|
132
|
-
[[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, -0.7853981633974483, 3.141592653589793],
|
|
133
|
-
[1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
|
|
134
|
-
[[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
|
|
135
|
-
[-1.5707963267948966, 0.7853981633974483, 3.141592653589793]]
|
|
136
|
-
]
|
|
137
|
-
t.notThrows(() => geom3.validate(result))
|
|
138
|
-
t.true(comparePolygonsAsPoints(pts, exp))
|
|
139
107
|
})
|
|
140
108
|
|
|
141
109
|
test('generalize: generalize of a geom3 with T junctions produces an expected geom3', (t) => {
|
|
@@ -259,7 +259,7 @@ const insertTjunctions = (polygons) => {
|
|
|
259
259
|
// split the side by inserting the vertex:
|
|
260
260
|
const newvertices = polygon.vertices.slice(0)
|
|
261
261
|
newvertices.splice(insertionvertextagindex, 0, endvertex)
|
|
262
|
-
const newpolygon = poly3.
|
|
262
|
+
const newpolygon = poly3.create(newvertices)
|
|
263
263
|
|
|
264
264
|
newpolygons[polygonindex] = newpolygon
|
|
265
265
|
|
|
@@ -56,33 +56,33 @@ test('insertTjunctions: insertTjunctions produces expected polygons', (t) => {
|
|
|
56
56
|
|
|
57
57
|
const result3 = insertTjunctions(geom3.toPolygons(geometry3))
|
|
58
58
|
let exp = [
|
|
59
|
-
poly3.
|
|
60
|
-
poly3.
|
|
61
|
-
poly3.
|
|
62
|
-
poly3.
|
|
63
|
-
poly3.
|
|
64
|
-
poly3.
|
|
65
|
-
poly3.
|
|
66
|
-
poly3.
|
|
59
|
+
poly3.create([[-1, -1, -1], [-1, -1, 1], [-1, 1, 1], [-1, 1, -1]]),
|
|
60
|
+
poly3.create([[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, -1, 1]]),
|
|
61
|
+
poly3.create([[-1, -1, -1], [1, -1, -1], [1, -1, 1], [-1, -1, 1]]),
|
|
62
|
+
poly3.create([[-1, 1, -1], [-1, 1, 1], [1, 1, 1], [1, 1, -1]]),
|
|
63
|
+
poly3.create([[-1, -1, -1], [-1, 1, -1], [1, 1, -1], [1, -1, -1]]),
|
|
64
|
+
poly3.create([[0, 0, 1], [-1, -1, 1], [1, -1, 1], [1, 1, 1]]),
|
|
65
|
+
poly3.create([[1, 1, 1], [-1, 1, 1], [0, 0, 1]]),
|
|
66
|
+
poly3.create([[-1, 1, 1], [-1, -1, 1], [0, 0, 1]])
|
|
67
67
|
]
|
|
68
68
|
t.not(result3, geom3.toPolygons(geometry3))
|
|
69
69
|
t.true(comparePolygonLists(result3, exp))
|
|
70
70
|
|
|
71
71
|
const result4 = insertTjunctions(geom3.toPolygons(geometry4))
|
|
72
72
|
exp = [
|
|
73
|
-
poly3.
|
|
74
|
-
poly3.
|
|
75
|
-
poly3.
|
|
76
|
-
poly3.
|
|
77
|
-
poly3.
|
|
78
|
-
poly3.
|
|
79
|
-
poly3.
|
|
80
|
-
poly3.
|
|
81
|
-
poly3.
|
|
82
|
-
poly3.
|
|
83
|
-
poly3.
|
|
84
|
-
poly3.
|
|
85
|
-
poly3.
|
|
73
|
+
poly3.create([[-1, -1, -1], [-1, -1, 1], [-1, 0, 1], [-1, 1, 1], [-1, 1, -1]]),
|
|
74
|
+
poly3.create([[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, 0, 1], [1, -1, 1]]),
|
|
75
|
+
poly3.create([[-1, -1, -1], [1, -1, -1], [1, -1, 1], [0, -1, 1], [-1, -1, 1]]),
|
|
76
|
+
poly3.create([[-1, 1, -1], [-1, 1, 1], [0, 1, 1], [1, 1, 1], [1, 1, -1]]),
|
|
77
|
+
poly3.create([[-1, -1, -1], [-1, 1, -1], [1, 1, -1], [1, -1, -1]]),
|
|
78
|
+
poly3.create([[-1, -1, 1], [0, -1, 1], [0, 0, 1]]),
|
|
79
|
+
poly3.create([[-1, 0, 1], [-1, -1, 1], [0, 0, 1]]),
|
|
80
|
+
poly3.create([[0, -1, 1], [1, -1, 1], [0, 0, 1]]),
|
|
81
|
+
poly3.create([[1, -1, 1], [1, 0, 1], [0, 0, 1]]),
|
|
82
|
+
poly3.create([[1, 0, 1], [1, 1, 1], [0, 0, 1]]),
|
|
83
|
+
poly3.create([[1, 1, 1], [0, 1, 1], [0, 0, 1]]),
|
|
84
|
+
poly3.create([[0, 1, 1], [-1, 1, 1], [0, 0, 1]]),
|
|
85
|
+
poly3.create([[-1, 1, 1], [-1, 0, 1], [0, 0, 1]])
|
|
86
86
|
]
|
|
87
87
|
t.not(result4, geometry4)
|
|
88
88
|
t.true(comparePolygonLists(result4, exp))
|