@jscad/modeling 2.9.3 → 2.9.6
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 +133 -136
- package/package.json +2 -2
- package/src/colors/colorize.d.ts +6 -5
- package/src/geometries/geom2/type.d.ts +3 -2
- package/src/geometries/geom3/applyTransforms.js +1 -2
- package/src/geometries/geom3/type.d.ts +3 -2
- package/src/geometries/path2/appendPoints.js +3 -12
- package/src/geometries/path2/appendPoints.test.js +16 -0
- package/src/geometries/path2/concat.js +9 -8
- package/src/geometries/path2/concat.test.js +13 -7
- package/src/geometries/path2/type.d.ts +3 -2
- package/src/geometries/poly3/measureBoundingSphere.d.ts +2 -2
- package/src/geometries/poly3/measureBoundingSphere.js +46 -8
- package/src/geometries/poly3/measureBoundingSphere.test.js +16 -26
- package/src/geometries/poly3/type.d.ts +3 -2
- package/src/geometries/types.d.ts +4 -2
- package/src/maths/mat4/fromRotation.js +9 -7
- package/src/maths/mat4/fromTaitBryanRotation.js +8 -6
- package/src/maths/mat4/fromXRotation.js +4 -2
- package/src/maths/mat4/fromYRotation.js +4 -2
- package/src/maths/mat4/fromZRotation.js +4 -2
- package/src/maths/mat4/isMirroring.js +11 -11
- package/src/maths/mat4/rotate.js +9 -5
- package/src/maths/mat4/rotateX.js +4 -2
- package/src/maths/mat4/rotateY.js +4 -2
- package/src/maths/mat4/rotateZ.js +4 -2
- package/src/maths/mat4/translate.test.js +2 -3
- package/src/maths/utils/index.d.ts +1 -0
- package/src/maths/utils/index.js +2 -0
- package/src/{utils → maths/utils}/trigonometry.d.ts +0 -0
- package/src/{utils → maths/utils}/trigonometry.js +1 -1
- package/src/{utils → maths/utils}/trigonometry.test.js +0 -0
- package/src/maths/vec2/distance.js +1 -1
- package/src/maths/vec2/fromAngleRadians.js +4 -2
- package/src/maths/vec2/length.js +1 -1
- package/src/maths/vec2/length.test.js +0 -10
- package/src/maths/vec3/angle.js +2 -2
- package/src/maths/vec3/angle.test.js +0 -12
- package/src/maths/vec3/distance.js +1 -1
- package/src/maths/vec3/length.js +1 -1
- package/src/maths/vec3/length.test.js +0 -10
- package/src/operations/booleans/intersectGeom2.test.js +69 -0
- package/src/operations/booleans/{intersect.test.js → intersectGeom3.test.js} +3 -71
- package/src/operations/booleans/subtractGeom2.test.js +72 -0
- package/src/operations/booleans/{subtract.test.js → subtractGeom3.test.js} +3 -74
- package/src/operations/booleans/trees/PolygonTreeNode.js +2 -2
- package/src/operations/booleans/unionGeom2.test.js +166 -0
- package/src/operations/booleans/{union.test.js → unionGeom3.test.js} +3 -168
- package/src/operations/extrusions/extrudeFromSlices.js +3 -2
- package/src/operations/extrusions/extrudeRotate.test.js +42 -42
- package/src/operations/extrusions/project.test.js +2 -2
- package/src/operations/extrusions/slice/repair.js +62 -0
- package/src/operations/modifiers/insertTjunctions.js +34 -35
- package/src/operations/modifiers/reTesselateCoplanarPolygons.js +32 -31
- package/src/primitives/circle.test.js +7 -0
- package/src/primitives/cylinderElliptic.js +4 -2
- package/src/primitives/cylinderElliptic.test.js +7 -1
- package/src/primitives/ellipse.js +1 -1
- package/src/primitives/ellipse.test.js +7 -0
- package/src/primitives/ellipsoid.js +1 -1
- package/src/primitives/geodesicSphere.js +3 -2
- package/src/primitives/roundedCuboid.js +4 -2
- package/src/primitives/roundedCylinder.js +1 -1
- package/src/primitives/torus.test.js +7 -3
- package/src/utils/index.d.ts +0 -1
- package/src/utils/index.js +1 -3
- package/src/maths/mat4/constants.d.ts +0 -1
- package/src/maths/mat4/constants.js +0 -5
- package/src/operations/extrusions/slice/repairSlice.js +0 -47
|
@@ -1,113 +1,14 @@
|
|
|
1
1
|
const test = require('ava')
|
|
2
2
|
|
|
3
|
-
const { comparePolygonsAsPoints
|
|
3
|
+
const { comparePolygonsAsPoints } = require('../../../test/helpers')
|
|
4
4
|
|
|
5
|
-
const {
|
|
5
|
+
const { geom3 } = require('../../geometries')
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const { sphere, cuboid } = require('../../primitives')
|
|
8
8
|
|
|
9
9
|
const { union } = require('./index')
|
|
10
10
|
|
|
11
11
|
const { center } = require('../transforms/center')
|
|
12
|
-
const { translate } = require('../transforms/translate')
|
|
13
|
-
|
|
14
|
-
// test('union: union of a path produces expected changes to points', (t) => {
|
|
15
|
-
// let geometry = path.fromPoints({}, [[0, 1, 0], [1, 0, 0]])
|
|
16
|
-
//
|
|
17
|
-
// geometry = union({normal: [1, 0, 0]}, geometry)
|
|
18
|
-
// let obs = path.toPoints(geometry)
|
|
19
|
-
// let exp = []
|
|
20
|
-
//
|
|
21
|
-
// t.deepEqual(obs, exp)
|
|
22
|
-
// })
|
|
23
|
-
|
|
24
|
-
test('union of one or more geom2 objects produces expected geometry', (t) => {
|
|
25
|
-
const geometry1 = circle({ radius: 2, segments: 8 })
|
|
26
|
-
|
|
27
|
-
// union of one object
|
|
28
|
-
const result1 = union(geometry1)
|
|
29
|
-
let obs = geom2.toPoints(result1)
|
|
30
|
-
let exp = [
|
|
31
|
-
[2, 0],
|
|
32
|
-
[1.4142000000000001, 1.4142000000000001],
|
|
33
|
-
[0, 2],
|
|
34
|
-
[-1.4142000000000001, 1.4142000000000001],
|
|
35
|
-
[-2, 0],
|
|
36
|
-
[-1.4142000000000001, -1.4142000000000001],
|
|
37
|
-
[0, -2],
|
|
38
|
-
[1.4142000000000001, -1.4142000000000001]
|
|
39
|
-
]
|
|
40
|
-
t.notThrows(() => geom2.validate(result1))
|
|
41
|
-
t.true(comparePoints(obs, exp))
|
|
42
|
-
|
|
43
|
-
// union of two non-overlapping objects
|
|
44
|
-
const geometry2 = center({ relativeTo: [10, 10, 0] }, rectangle({ size: [4, 4] }))
|
|
45
|
-
|
|
46
|
-
const result2 = union(geometry1, geometry2)
|
|
47
|
-
obs = geom2.toPoints(result2)
|
|
48
|
-
exp = [
|
|
49
|
-
[2, 0],
|
|
50
|
-
[1.4142000000000001, 1.4142000000000001],
|
|
51
|
-
[0, 2],
|
|
52
|
-
[-1.4142000000000001, 1.4142000000000001],
|
|
53
|
-
[-2, 0],
|
|
54
|
-
[-1.4142000000000001, -1.4142000000000001],
|
|
55
|
-
[0, -2],
|
|
56
|
-
[8, 12],
|
|
57
|
-
[8, 8],
|
|
58
|
-
[12, 8],
|
|
59
|
-
[12, 12],
|
|
60
|
-
[1.4142000000000001, -1.4142000000000001]
|
|
61
|
-
]
|
|
62
|
-
t.notThrows(() => geom2.validate(result2))
|
|
63
|
-
t.true(comparePoints(obs, exp))
|
|
64
|
-
|
|
65
|
-
// union of two partially overlapping objects
|
|
66
|
-
const geometry3 = rectangle({ size: [18, 18] })
|
|
67
|
-
|
|
68
|
-
const result3 = union(geometry2, geometry3)
|
|
69
|
-
obs = geom2.toPoints(result3)
|
|
70
|
-
exp = [
|
|
71
|
-
[11.999973333333333, 11.999973333333333],
|
|
72
|
-
[7.999933333333333, 11.999973333333333],
|
|
73
|
-
[9.000053333333334, 7.999933333333333],
|
|
74
|
-
[-9.000053333333334, 9.000053333333334],
|
|
75
|
-
[-9.000053333333334, -9.000053333333334],
|
|
76
|
-
[9.000053333333334, -9.000053333333334],
|
|
77
|
-
[7.999933333333333, 9.000053333333334],
|
|
78
|
-
[11.999973333333333, 7.999933333333333]
|
|
79
|
-
]
|
|
80
|
-
t.notThrows(() => geom2.validate(result3))
|
|
81
|
-
t.true(comparePoints(obs, exp))
|
|
82
|
-
|
|
83
|
-
// union of two completely overlapping objects
|
|
84
|
-
const result4 = union(geometry1, geometry3)
|
|
85
|
-
obs = geom2.toPoints(result4)
|
|
86
|
-
exp = [
|
|
87
|
-
[-9.000046666666666, -9.000046666666666],
|
|
88
|
-
[9.000046666666666, -9.000046666666666],
|
|
89
|
-
[9.000046666666666, 9.000046666666666],
|
|
90
|
-
[-9.000046666666666, 9.000046666666666]
|
|
91
|
-
]
|
|
92
|
-
t.notThrows(() => geom2.validate(result4))
|
|
93
|
-
t.true(comparePoints(obs, exp))
|
|
94
|
-
|
|
95
|
-
// union of unions of non-overlapping objects (BSP gap from #907)
|
|
96
|
-
const circ = circle({ radius: 1, segments: 32 })
|
|
97
|
-
const result5 = union(
|
|
98
|
-
union(
|
|
99
|
-
translate([17, 21], circ),
|
|
100
|
-
translate([7, 0], circ),
|
|
101
|
-
),
|
|
102
|
-
union(
|
|
103
|
-
translate([3, 21], circ),
|
|
104
|
-
translate([17, 21], circ),
|
|
105
|
-
)
|
|
106
|
-
)
|
|
107
|
-
obs = geom2.toPoints(result5)
|
|
108
|
-
t.notThrows.skip(() => geom2.validate(result5))
|
|
109
|
-
t.is(obs.length, 112)
|
|
110
|
-
})
|
|
111
12
|
|
|
112
13
|
test('union of one or more geom3 objects produces expected geometry', (t) => {
|
|
113
14
|
const geometry1 = sphere({ radius: 2, segments: 8 })
|
|
@@ -230,69 +131,3 @@ test('union of geom3 with rounding issues #137', (t) => {
|
|
|
230
131
|
t.notThrows(() => geom3.validate(obs))
|
|
231
132
|
t.is(pts.length, 6) // number of polygons in union
|
|
232
133
|
})
|
|
233
|
-
|
|
234
|
-
test('union of geom2 with closing issues #15', (t) => {
|
|
235
|
-
const c = geom2.create([
|
|
236
|
-
[[-45.82118740347841168159, -16.85726810555620147625], [-49.30331715865012398581, -14.68093629710870118288]],
|
|
237
|
-
[[-49.10586702080816223770, -15.27604177352110781385], [-48.16645938811709015681, -15.86317173589183227023]],
|
|
238
|
-
[[-49.60419521731581937729, -14.89550781504266296906], [-49.42407001323204696064, -15.67605088949303393520]],
|
|
239
|
-
[[-49.05727291218684626983, -15.48661638542171203881], [-49.10586702080816223770, -15.27604177352110781385]],
|
|
240
|
-
[[-49.30706235399220815907, -15.81529674600091794900], [-46.00505780290426827150, -17.21108547999804727624]],
|
|
241
|
-
[[-46.00505780290426827150, -17.21108547999804727624], [-45.85939703723252591772, -17.21502856394236857795]],
|
|
242
|
-
[[-45.85939703723252591772, -17.21502856394236857795], [-45.74972032664388166268, -17.11909303495791334626]],
|
|
243
|
-
[[-45.74972032664388166268, -17.11909303495791334626], [-45.73424573227583067592, -16.97420292661295349035]],
|
|
244
|
-
[[-45.73424573227583067592, -16.97420292661295349035], [-45.82118740347841168159, -16.85726810555620147625]],
|
|
245
|
-
[[-49.30331715865012398581, -14.68093629710870118288], [-49.45428884427643367871, -14.65565769658912387285]],
|
|
246
|
-
[[-49.45428884427643367871, -14.65565769658912387285], [-49.57891661679624917269, -14.74453612941635327616]],
|
|
247
|
-
[[-49.57891661679624917269, -14.74453612941635327616], [-49.60419521731581937729, -14.89550781504266296906]],
|
|
248
|
-
[[-49.42407001323204696064, -15.67605088949303393520], [-49.30706235399220815907, -15.81529674600091794900]],
|
|
249
|
-
[[-48.16645938811709015681, -15.86317173589183227023], [-49.05727291218684626983, -15.48661638542171203881]]
|
|
250
|
-
])
|
|
251
|
-
const d = geom2.create([
|
|
252
|
-
[[-49.03431352173912216585, -15.58610714407888764299], [-49.21443872582289458251, -14.80556406962851667686]],
|
|
253
|
-
[[-68.31614651314507113966, -3.10790373951434872879], [-49.34036769611472550423, -15.79733157434056778357]],
|
|
254
|
-
[[-49.58572929483430868913, -14.97552686612213790340], [-49.53755741140093959984, -15.18427183431472826669]],
|
|
255
|
-
[[-49.53755741140093959984, -15.18427183431472826669], [-54.61235529924312714911, -11.79066769321313756791]],
|
|
256
|
-
[[-49.30227466841120076424, -14.68159232649114187552], [-68.09792828135776687759, -2.77270756611528668145]],
|
|
257
|
-
[[-49.21443872582289458251, -14.80556406962851667686], [-49.30227466841120076424, -14.68159232649114187552]],
|
|
258
|
-
[[-49.34036769611472550423, -15.79733157434056778357], [-49.18823337756091262918, -15.82684012194931710837]],
|
|
259
|
-
[[-49.18823337756091262918, -15.82684012194931710837], [-49.06069007212390431505, -15.73881563386780157998]],
|
|
260
|
-
[[-49.06069007212390431505, -15.73881563386780157998], [-49.03431352173912216585, -15.58610714407888764299]],
|
|
261
|
-
[[-68.09792828135776687759, -2.77270756611528668145], [-68.24753735887460948106, -2.74623350179570024920]],
|
|
262
|
-
[[-68.24753735887460948106, -2.74623350179570024920], [-68.37258141465594007968, -2.83253376987636329432]],
|
|
263
|
-
[[-68.37258141465594007968, -2.83253376987636329432], [-68.40089829889257089235, -2.98180502037078554167]],
|
|
264
|
-
[[-68.40089829889257089235, -2.98180502037078554167], [-68.31614651314507113966, -3.10790373951434872879]],
|
|
265
|
-
[[-54.61235529924312714911, -11.79066769321313756791], [-49.58572929483430868913, -14.97552686612213790340]]
|
|
266
|
-
])
|
|
267
|
-
// geom2.toOutlines(c)
|
|
268
|
-
// geom2.toOutlines(d)
|
|
269
|
-
|
|
270
|
-
const obs = union(c, d)
|
|
271
|
-
// const outlines = geom2.toOutlines(obs)
|
|
272
|
-
const pts = geom2.toPoints(obs)
|
|
273
|
-
const exp = [
|
|
274
|
-
[-49.10585516965137, -15.276000175919414],
|
|
275
|
-
[-49.0573272145917, -15.486679335654257],
|
|
276
|
-
[-49.307011370463215, -15.815286644243773],
|
|
277
|
-
[-46.00502320253235, -17.211117609669667],
|
|
278
|
-
[-45.85943933735334, -17.215031154432545],
|
|
279
|
-
[-45.74972963250071, -17.119149307742074],
|
|
280
|
-
[-45.734205904941305, -16.974217700023555],
|
|
281
|
-
[-48.166473975068946, -15.86316234184296],
|
|
282
|
-
[-49.318621553259746, -15.801589237573706],
|
|
283
|
-
[-49.585786209072104, -14.975570389622606],
|
|
284
|
-
[-68.31614189569036, -3.1078763476921982],
|
|
285
|
-
[-49.53751915699663, -15.184292776976012],
|
|
286
|
-
[-68.09789654941396, -2.7727464644978874],
|
|
287
|
-
[-68.24752441084793, -2.7462648116024244],
|
|
288
|
-
[-68.37262739176788, -2.8324932478777995],
|
|
289
|
-
[-68.40093536555268, -2.98186020632758],
|
|
290
|
-
[-54.61234310251047, -11.79072766159384],
|
|
291
|
-
[-49.30335872868453, -14.680880468978017],
|
|
292
|
-
[-49.34040695243976, -15.797284338334542],
|
|
293
|
-
[-45.82121705016925, -16.857333163105647]
|
|
294
|
-
]
|
|
295
|
-
t.notThrows(() => geom2.validate(obs))
|
|
296
|
-
t.is(pts.length, 20) // number of sides in union
|
|
297
|
-
t.true(comparePoints(pts, exp))
|
|
298
|
-
})
|
|
@@ -5,7 +5,7 @@ const geom3 = require('../../geometries/geom3')
|
|
|
5
5
|
const poly3 = require('../../geometries/poly3')
|
|
6
6
|
|
|
7
7
|
const slice = require('./slice')
|
|
8
|
-
const repairSlice = require('./slice/
|
|
8
|
+
const repairSlice = require('./slice/repair')
|
|
9
9
|
|
|
10
10
|
const extrudeWalls = require('./extrudeWalls')
|
|
11
11
|
|
|
@@ -59,7 +59,8 @@ const extrudeFromSlices = (options, base) => {
|
|
|
59
59
|
|
|
60
60
|
// Repair gaps in the base slice
|
|
61
61
|
if (repair) {
|
|
62
|
-
|
|
62
|
+
// note: base must be a slice, if base is geom2 this doesn't repair
|
|
63
|
+
base = repairSlice(base)
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
const sMax = numberOfSlices - 1
|
|
@@ -22,18 +22,18 @@ test('extrudeRotate: (angle) extruding of a geom2 produces an expected geom3', (
|
|
|
22
22
|
let geometry3 = extrudeRotate({ segments: 4, angle: Math.PI / 4 }, geometry2)
|
|
23
23
|
let pts = geom3.toPoints(geometry3)
|
|
24
24
|
const exp = [
|
|
25
|
-
[[10,
|
|
26
|
-
[[10,
|
|
27
|
-
[[10,
|
|
28
|
-
[[10,
|
|
29
|
-
[[26,
|
|
30
|
-
[[26,
|
|
31
|
-
[[26,
|
|
32
|
-
[[26,
|
|
33
|
-
[[7.
|
|
34
|
-
[[18.
|
|
35
|
-
[[26,
|
|
36
|
-
[[10,
|
|
25
|
+
[[10, 0, 8], [26, 0, 8], [18.38477631085024, 18.384776310850235, 8]],
|
|
26
|
+
[[10, 0, 8], [18.38477631085024, 18.384776310850235, 8], [7.0710678118654755, 7.071067811865475, 8]],
|
|
27
|
+
[[10, 0, -8], [10, 0, 8], [7.0710678118654755, 7.071067811865475, 8]],
|
|
28
|
+
[[10, 0, -8], [7.0710678118654755, 7.071067811865475, 8], [7.0710678118654755, 7.071067811865475, -8]],
|
|
29
|
+
[[26, 0, -8], [10, 0, -8], [7.0710678118654755, 7.071067811865475, -8]],
|
|
30
|
+
[[26, 0, -8], [7.0710678118654755, 7.071067811865475, -8], [18.38477631085024, 18.384776310850235, -8]],
|
|
31
|
+
[[26, 0, 8], [26, 0, -8], [18.38477631085024, 18.384776310850235, -8]],
|
|
32
|
+
[[26, 0, 8], [18.38477631085024, 18.384776310850235, -8], [18.38477631085024, 18.384776310850235, 8]],
|
|
33
|
+
[[7.0710678118654755, 7.071067811865475, -8], [7.0710678118654755, 7.071067811865475, 8], [18.38477631085024, 18.384776310850235, 8]],
|
|
34
|
+
[[18.38477631085024, 18.384776310850235, 8], [18.38477631085024, 18.384776310850235, -8], [7.0710678118654755, 7.071067811865475, -8]],
|
|
35
|
+
[[26, 0, 8], [10, 0, 8], [10, 0, -8]],
|
|
36
|
+
[[10, 0, -8], [26, 0, -8], [26, 0, 8]]
|
|
37
37
|
]
|
|
38
38
|
t.notThrows(() => geom3.validate(geometry3))
|
|
39
39
|
t.is(pts.length, 12)
|
|
@@ -95,14 +95,14 @@ test('extrudeRotate: (segments) extruding of a geom2 produces an expected geom3'
|
|
|
95
95
|
geometry2 = geom2.fromPoints([[0, 0], [2, 1], [1, 2], [1, 3], [3, 4], [0, 5]])
|
|
96
96
|
geometry3 = extrudeRotate({ segments: 8 }, geometry2)
|
|
97
97
|
pts = geom3.toPoints(geometry3)
|
|
98
|
-
t.notThrows
|
|
98
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
99
99
|
t.is(pts.length, 64)
|
|
100
100
|
|
|
101
101
|
// test overlapping edges that produce hollow shape
|
|
102
102
|
geometry2 = geom2.fromPoints([[30, 0], [30, 60], [0, 60], [0, 50], [10, 40], [10, 30], [0, 20], [0, 10], [10, 0]])
|
|
103
103
|
geometry3 = extrudeRotate({ segments: 8 }, geometry2)
|
|
104
104
|
pts = geom3.toPoints(geometry3)
|
|
105
|
-
t.notThrows
|
|
105
|
+
t.notThrows(() => geom3.validate(geometry3))
|
|
106
106
|
t.is(pts.length, 80)
|
|
107
107
|
})
|
|
108
108
|
|
|
@@ -113,16 +113,16 @@ test('extrudeRotate: (overlap +/-) extruding of a geom2 produces an expected geo
|
|
|
113
113
|
let obs = extrudeRotate({ segments: 4, angle: Math.PI / 2 }, geometry)
|
|
114
114
|
let pts = geom3.toPoints(obs)
|
|
115
115
|
let exp = [
|
|
116
|
-
[[0,
|
|
117
|
-
[[7,
|
|
118
|
-
[[7,
|
|
119
|
-
[[7,
|
|
120
|
-
[[
|
|
121
|
-
[[
|
|
122
|
-
[[7,
|
|
123
|
-
[[0,
|
|
116
|
+
[[0, 0, 8], [7, 0, 8], [0, 7, 8]],
|
|
117
|
+
[[7, 0, -8], [0, 0, -8], [0, 7, -8]],
|
|
118
|
+
[[7, 0, 8], [7, 0, -8], [0, 7, -8]],
|
|
119
|
+
[[7, 0, 8], [0, 7, -8], [0, 7, 8]],
|
|
120
|
+
[[0, 0, -8], [0, 0, 8], [0, 7, 8]],
|
|
121
|
+
[[0, 7, 8], [0, 7, -8], [0, 0, -8]],
|
|
122
|
+
[[7, 0, 8], [0, 0, 8], [0, 0, -8]],
|
|
123
|
+
[[0, 0, -8], [7, 0, -8], [7, 0, 8]]
|
|
124
124
|
]
|
|
125
|
-
t.notThrows
|
|
125
|
+
t.notThrows(() => geom3.validate(obs))
|
|
126
126
|
t.is(pts.length, 8)
|
|
127
127
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
128
128
|
|
|
@@ -132,26 +132,26 @@ test('extrudeRotate: (overlap +/-) extruding of a geom2 produces an expected geo
|
|
|
132
132
|
obs = extrudeRotate({ segments: 8, angle: Math.PI / 2 }, geometry)
|
|
133
133
|
pts = geom3.toPoints(obs)
|
|
134
134
|
exp = [
|
|
135
|
-
[[1,
|
|
136
|
-
[[2,
|
|
137
|
-
[[2,
|
|
138
|
-
[[1,
|
|
139
|
-
[[1,
|
|
140
|
-
[[0,
|
|
141
|
-
[[0.
|
|
142
|
-
[[1.
|
|
143
|
-
[[1.
|
|
144
|
-
[[0.
|
|
145
|
-
[[0.
|
|
146
|
-
[[
|
|
147
|
-
[[
|
|
148
|
-
[[
|
|
149
|
-
[[
|
|
150
|
-
[[0,
|
|
151
|
-
[[2,
|
|
152
|
-
[[0,
|
|
135
|
+
[[1, 0, -8], [0, 0, -8], [0.7071067811865476, 0.7071067811865475, -8]],
|
|
136
|
+
[[2, 0, 4], [1, 0, -8], [0.7071067811865476, 0.7071067811865475, -8]],
|
|
137
|
+
[[2, 0, 4], [0.7071067811865476, 0.7071067811865475, -8], [1.4142135623730951, 1.414213562373095, 4]],
|
|
138
|
+
[[1, 0, 8], [2, 0, 4], [1.4142135623730951, 1.414213562373095, 4]],
|
|
139
|
+
[[1, 0, 8], [1.4142135623730951, 1.414213562373095, 4], [0.7071067811865476, 0.7071067811865475, 8]],
|
|
140
|
+
[[0, 0, 8], [1, 0, 8], [0.7071067811865476, 0.7071067811865475, 8]],
|
|
141
|
+
[[0.7071067811865476, 0.7071067811865475, -8], [0, 0, -8], [0, 1, -8]],
|
|
142
|
+
[[1.4142135623730951, 1.414213562373095, 4], [0.7071067811865476, 0.7071067811865475, -8], [0, 1, -8]],
|
|
143
|
+
[[1.4142135623730951, 1.414213562373095, 4], [0, 1, -8], [0, 2, 4]],
|
|
144
|
+
[[0.7071067811865476, 0.7071067811865475, 8], [1.4142135623730951, 1.414213562373095, 4], [0, 2, 4]],
|
|
145
|
+
[[0.7071067811865476, 0.7071067811865475, 8], [0, 2, 4], [0, 1, 8]],
|
|
146
|
+
[[0, 0, 8], [0.7071067811865476, 0.7071067811865475, 8], [0, 1, 8]],
|
|
147
|
+
[[0, 1, -8], [0, 0, -8], [0, 0, 8]],
|
|
148
|
+
[[0, 0, 8], [0, 1, 8], [0, 2, 4]],
|
|
149
|
+
[[0, 2, 4], [0, 1, -8], [0, 0, 8]],
|
|
150
|
+
[[0, 0, 8], [0, 0, -8], [1, 0, -8]],
|
|
151
|
+
[[2, 0, 4], [1, 0, 8], [0, 0, 8]],
|
|
152
|
+
[[0, 0, 8], [1, 0, -8], [2, 0, 4]]
|
|
153
153
|
]
|
|
154
|
-
t.notThrows
|
|
154
|
+
t.notThrows(() => geom3.validate(obs))
|
|
155
155
|
t.is(pts.length, 18)
|
|
156
156
|
t.true(comparePolygonsAsPoints(pts, exp))
|
|
157
157
|
})
|
|
@@ -29,10 +29,10 @@ test('project (defaults)', (t) => {
|
|
|
29
29
|
[0, -5.000013333333333],
|
|
30
30
|
[5.000013333333333, 0],
|
|
31
31
|
[-5.000013333333333, 0],
|
|
32
|
-
[-2.9999933333333333, 0],
|
|
33
|
-
[2.9999933333333333, 0],
|
|
34
32
|
[0, 2.9999933333333333],
|
|
33
|
+
[-2.9999933333333333, 0],
|
|
35
34
|
[0, -2.9999933333333333],
|
|
35
|
+
[2.9999933333333333, 0],
|
|
36
36
|
[0, 5.000013333333333]
|
|
37
37
|
]
|
|
38
38
|
t.true(comparePoints(pts, exp))
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const vec3 = require('../../../maths/vec3')
|
|
2
|
+
const create = require('./create')
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Mend gaps in a 2D slice to make it a closed polygon
|
|
6
|
+
*/
|
|
7
|
+
const repair = (slice) => {
|
|
8
|
+
if (!slice.edges) return slice
|
|
9
|
+
let edges = slice.edges
|
|
10
|
+
const vertexMap = new Map() // string key to vertex map
|
|
11
|
+
const edgeCount = new Map() // count of (in - out) edges
|
|
12
|
+
|
|
13
|
+
// Remove self-edges
|
|
14
|
+
edges = edges.filter((e) => !vec3.equals(e[0], e[1]))
|
|
15
|
+
|
|
16
|
+
// build vertex and edge count maps
|
|
17
|
+
edges.forEach((edge) => {
|
|
18
|
+
const inKey = edge[0].toString()
|
|
19
|
+
const outKey = edge[1].toString()
|
|
20
|
+
vertexMap.set(inKey, edge[0])
|
|
21
|
+
vertexMap.set(outKey, edge[1])
|
|
22
|
+
edgeCount.set(inKey, (edgeCount.get(inKey) || 0) + 1) // in
|
|
23
|
+
edgeCount.set(outKey, (edgeCount.get(outKey) || 0) - 1) // out
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// find vertices which are missing in or out edges
|
|
27
|
+
const missingIn = []
|
|
28
|
+
const missingOut = []
|
|
29
|
+
edgeCount.forEach((count, vertex) => {
|
|
30
|
+
if (count < 0) missingIn.push(vertex)
|
|
31
|
+
if (count > 0) missingOut.push(vertex)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// pairwise distance of bad vertices
|
|
35
|
+
missingIn.forEach((key1) => {
|
|
36
|
+
const v1 = vertexMap.get(key1)
|
|
37
|
+
|
|
38
|
+
// find the closest vertex that is missing an out edge
|
|
39
|
+
let bestDistance = Infinity
|
|
40
|
+
let bestReplacement
|
|
41
|
+
missingOut.forEach((key2) => {
|
|
42
|
+
const v2 = vertexMap.get(key2)
|
|
43
|
+
const distance = vec3.distance(v1, v2)
|
|
44
|
+
if (distance < bestDistance) {
|
|
45
|
+
bestDistance = distance
|
|
46
|
+
bestReplacement = v2
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
console.warn(`slice.repair: repairing vertex gap ${v1} to ${bestReplacement} distance ${bestDistance}`)
|
|
50
|
+
|
|
51
|
+
// merge broken vertices
|
|
52
|
+
edges = edges.map((edge) => {
|
|
53
|
+
if (edge[0].toString() === key1) return [bestReplacement, edge[1]]
|
|
54
|
+
if (edge[1].toString() === key1) return [edge[0], bestReplacement]
|
|
55
|
+
return edge
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return create(edges)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = repair
|
|
@@ -28,15 +28,15 @@ const addSide = (sidemap, vertextag2sidestart, vertextag2sideend, vertex0, verte
|
|
|
28
28
|
} else {
|
|
29
29
|
sidemap.get(newsidetag).push(newsideobj)
|
|
30
30
|
}
|
|
31
|
-
if (starttag
|
|
32
|
-
vertextag2sidestart
|
|
31
|
+
if (vertextag2sidestart.has(starttag)) {
|
|
32
|
+
vertextag2sidestart.get(starttag).push(newsidetag)
|
|
33
33
|
} else {
|
|
34
|
-
vertextag2sidestart
|
|
34
|
+
vertextag2sidestart.set(starttag, [newsidetag])
|
|
35
35
|
}
|
|
36
|
-
if (endtag
|
|
37
|
-
vertextag2sideend
|
|
36
|
+
if (vertextag2sideend.has(endtag)) {
|
|
37
|
+
vertextag2sideend.get(endtag).push(newsidetag)
|
|
38
38
|
} else {
|
|
39
|
-
vertextag2sideend
|
|
39
|
+
vertextag2sideend.set(endtag, [newsidetag])
|
|
40
40
|
}
|
|
41
41
|
return newsidetag
|
|
42
42
|
}
|
|
@@ -67,18 +67,18 @@ const deleteSide = (sidemap, vertextag2sidestart, vertextag2sideend, vertex0, ve
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// adjust start and end lists
|
|
70
|
-
idx = vertextag2sidestart
|
|
70
|
+
idx = vertextag2sidestart.get(starttag).indexOf(sidetag)
|
|
71
71
|
if (assert && idx < 0) throw new Error('assert failed')
|
|
72
|
-
vertextag2sidestart
|
|
73
|
-
if (vertextag2sidestart
|
|
74
|
-
delete
|
|
72
|
+
vertextag2sidestart.get(starttag).splice(idx, 1)
|
|
73
|
+
if (vertextag2sidestart.get(starttag).length === 0) {
|
|
74
|
+
vertextag2sidestart.delete(starttag)
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
idx = vertextag2sideend
|
|
77
|
+
idx = vertextag2sideend.get(endtag).indexOf(sidetag)
|
|
78
78
|
if (assert && idx < 0) throw new Error('assert failed')
|
|
79
|
-
vertextag2sideend
|
|
80
|
-
if (vertextag2sideend
|
|
81
|
-
delete
|
|
79
|
+
vertextag2sideend.get(endtag).splice(idx, 1)
|
|
80
|
+
if (vertextag2sideend.get(endtag).length === 0) {
|
|
81
|
+
vertextag2sideend.delete(endtag)
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -158,25 +158,24 @@ const insertTjunctions = (polygons) => {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
if (sidemap.size > 0) {
|
|
161
|
-
// console.log('insertTjunctions',sidemap.size)
|
|
162
161
|
// STEP 2 : create a list of starting sides and ending sides
|
|
163
|
-
const vertextag2sidestart =
|
|
164
|
-
const vertextag2sideend =
|
|
165
|
-
const
|
|
162
|
+
const vertextag2sidestart = new Map()
|
|
163
|
+
const vertextag2sideend = new Map()
|
|
164
|
+
const sidesToCheck = new Map()
|
|
166
165
|
for (const [sidetag, sideobjs] of sidemap) {
|
|
167
|
-
|
|
166
|
+
sidesToCheck.set(sidetag, true)
|
|
168
167
|
sideobjs.forEach((sideobj) => {
|
|
169
168
|
const starttag = getTag(sideobj.vertex0)
|
|
170
169
|
const endtag = getTag(sideobj.vertex1)
|
|
171
|
-
if (starttag
|
|
172
|
-
vertextag2sidestart
|
|
170
|
+
if (vertextag2sidestart.has(starttag)) {
|
|
171
|
+
vertextag2sidestart.get(starttag).push(sidetag)
|
|
173
172
|
} else {
|
|
174
|
-
vertextag2sidestart
|
|
173
|
+
vertextag2sidestart.set(starttag, [sidetag])
|
|
175
174
|
}
|
|
176
|
-
if (endtag
|
|
177
|
-
vertextag2sideend
|
|
175
|
+
if (vertextag2sideend.has(endtag)) {
|
|
176
|
+
vertextag2sideend.get(endtag).push(sidetag)
|
|
178
177
|
} else {
|
|
179
|
-
vertextag2sideend
|
|
178
|
+
vertextag2sideend.set(endtag, [sidetag])
|
|
180
179
|
}
|
|
181
180
|
})
|
|
182
181
|
}
|
|
@@ -187,13 +186,13 @@ const insertTjunctions = (polygons) => {
|
|
|
187
186
|
if (sidemap.size === 0) break
|
|
188
187
|
|
|
189
188
|
for (const sidetag of sidemap.keys()) {
|
|
190
|
-
|
|
189
|
+
sidesToCheck.set(sidetag, true)
|
|
191
190
|
}
|
|
192
191
|
|
|
193
192
|
let donesomething = false
|
|
194
193
|
while (true) {
|
|
195
|
-
const sidetags =
|
|
196
|
-
if (sidetags.length === 0) break //
|
|
194
|
+
const sidetags = Array.from(sidesToCheck.keys())
|
|
195
|
+
if (sidetags.length === 0) break // sidesToCheck is empty, we're done!
|
|
197
196
|
const sidetagtocheck = sidetags[0]
|
|
198
197
|
let donewithside = true
|
|
199
198
|
if (sidemap.has(sidetagtocheck)) {
|
|
@@ -207,12 +206,12 @@ const insertTjunctions = (polygons) => {
|
|
|
207
206
|
const endvertextag = getTag(endvertex)
|
|
208
207
|
let matchingsides = []
|
|
209
208
|
if (directionindex === 0) {
|
|
210
|
-
if (startvertextag
|
|
211
|
-
matchingsides = vertextag2sideend
|
|
209
|
+
if (vertextag2sideend.has(startvertextag)) {
|
|
210
|
+
matchingsides = vertextag2sideend.get(startvertextag)
|
|
212
211
|
}
|
|
213
212
|
} else {
|
|
214
|
-
if (startvertextag
|
|
215
|
-
matchingsides = vertextag2sidestart
|
|
213
|
+
if (vertextag2sidestart.has(startvertextag)) {
|
|
214
|
+
matchingsides = vertextag2sidestart.get(startvertextag)
|
|
216
215
|
}
|
|
217
216
|
}
|
|
218
217
|
for (let matchingsideindex = 0; matchingsideindex < matchingsides.length; matchingsideindex++) {
|
|
@@ -267,8 +266,8 @@ const insertTjunctions = (polygons) => {
|
|
|
267
266
|
deleteSide(sidemap, vertextag2sidestart, vertextag2sideend, matchingside.vertex0, matchingside.vertex1, polygonindex)
|
|
268
267
|
const newsidetag1 = addSide(sidemap, vertextag2sidestart, vertextag2sideend, matchingside.vertex0, endvertex, polygonindex)
|
|
269
268
|
const newsidetag2 = addSide(sidemap, vertextag2sidestart, vertextag2sideend, endvertex, matchingside.vertex1, polygonindex)
|
|
270
|
-
if (newsidetag1 !== null)
|
|
271
|
-
if (newsidetag2 !== null)
|
|
269
|
+
if (newsidetag1 !== null) sidesToCheck.set(newsidetag1, true)
|
|
270
|
+
if (newsidetag2 !== null) sidesToCheck.set(newsidetag2, true)
|
|
272
271
|
donewithside = false
|
|
273
272
|
directionindex = 2 // skip reverse direction check
|
|
274
273
|
donesomething = true
|
|
@@ -280,7 +279,7 @@ const insertTjunctions = (polygons) => {
|
|
|
280
279
|
} // for directionindex
|
|
281
280
|
} // if(sidetagtocheck in sidemap)
|
|
282
281
|
if (donewithside) {
|
|
283
|
-
delete
|
|
282
|
+
sidesToCheck.delete(sidetagtocheck)
|
|
284
283
|
}
|
|
285
284
|
}
|
|
286
285
|
if (!donesomething) break
|