@jscad/modeling 3.0.1-alpha.0 → 3.0.3-alpha.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.
Files changed (144) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/jscad-modeling.es.js +2 -2
  3. package/dist/jscad-modeling.min.js +2 -2
  4. package/package.json +2 -2
  5. package/src/colors/colorize.test.js +1 -1
  6. package/src/geometries/geom2/index.d.ts +0 -2
  7. package/src/geometries/geom2/index.js +0 -2
  8. package/src/geometries/geom3/applyTransforms.test.js +2 -2
  9. package/src/geometries/geom3/clone.test.js +2 -2
  10. package/src/geometries/geom3/create.js +1 -9
  11. package/src/geometries/geom3/{fromPoints.d.ts → fromVertices.d.ts} +1 -1
  12. package/src/geometries/geom3/{fromPoints.js → fromVertices.js} +3 -2
  13. package/src/geometries/geom3/{fromPoints.test.js → fromVertices.test.js} +6 -6
  14. package/src/geometries/geom3/{fromPointsConvex.d.ts → fromVerticesConvex.d.ts} +1 -1
  15. package/src/geometries/geom3/fromVerticesConvex.js +25 -0
  16. package/src/geometries/geom3/{fromPointsConvex.test.js → fromVerticesConvex.test.js} +3 -3
  17. package/src/geometries/geom3/index.d.ts +3 -5
  18. package/src/geometries/geom3/index.js +4 -6
  19. package/src/geometries/geom3/invert.test.js +2 -2
  20. package/src/geometries/geom3/isA.test.js +2 -2
  21. package/src/geometries/geom3/toString.test.js +2 -2
  22. package/src/geometries/geom3/{toPoints.d.ts → toVertices.d.ts} +1 -1
  23. package/src/geometries/geom3/{toPoints.js → toVertices.js} +3 -2
  24. package/src/geometries/geom3/{toPoints.test.js → toVertices.test.js} +4 -4
  25. package/src/geometries/geom3/transform.test.js +2 -2
  26. package/src/geometries/geom3/validate.test.js +4 -4
  27. package/src/geometries/index.d.ts +1 -0
  28. package/src/geometries/index.js +1 -0
  29. package/src/geometries/path2/appendBezier.js +1 -1
  30. package/src/geometries/path2/create.js +1 -10
  31. package/src/geometries/path2/index.d.ts +0 -2
  32. package/src/geometries/path2/index.js +0 -2
  33. package/src/geometries/path3/applyTransforms.js +22 -0
  34. package/src/geometries/path3/applyTransforms.test.js +28 -0
  35. package/src/geometries/path3/close.d.ts +3 -0
  36. package/src/geometries/path3/close.js +31 -0
  37. package/src/geometries/path3/close.test.js +43 -0
  38. package/src/geometries/path3/concat.d.ts +3 -0
  39. package/src/geometries/path3/concat.js +36 -0
  40. package/src/geometries/path3/concat.test.js +35 -0
  41. package/src/geometries/path3/create.d.ts +4 -0
  42. package/src/geometries/path3/create.js +30 -0
  43. package/src/geometries/path3/create.test.js +8 -0
  44. package/src/geometries/path3/equals.d.ts +3 -0
  45. package/src/geometries/path3/equals.js +48 -0
  46. package/src/geometries/path3/equals.test.js +38 -0
  47. package/src/geometries/path3/fromVertices.d.ts +8 -0
  48. package/src/geometries/path3/fromVertices.js +45 -0
  49. package/src/geometries/path3/fromVertices.test.js +33 -0
  50. package/src/geometries/path3/index.d.ts +13 -0
  51. package/src/geometries/path3/index.js +21 -0
  52. package/src/geometries/path3/isA.d.ts +3 -0
  53. package/src/geometries/path3/isA.js +20 -0
  54. package/src/geometries/path3/isA.test.js +19 -0
  55. package/src/geometries/path3/reverse.d.ts +3 -0
  56. package/src/geometries/path3/reverse.js +19 -0
  57. package/src/geometries/path3/reverse.test.js +9 -0
  58. package/src/geometries/path3/toString.d.ts +3 -0
  59. package/src/geometries/path3/toString.js +24 -0
  60. package/src/geometries/path3/toVertices.d.ts +4 -0
  61. package/src/geometries/path3/toVertices.js +16 -0
  62. package/src/geometries/path3/toVertices.test.js +13 -0
  63. package/src/geometries/path3/transform.d.ts +4 -0
  64. package/src/geometries/path3/transform.js +21 -0
  65. package/src/geometries/path3/transform.test.js +50 -0
  66. package/src/geometries/path3/type.d.ts +10 -0
  67. package/src/geometries/path3/validate.d.ts +1 -0
  68. package/src/geometries/path3/validate.js +41 -0
  69. package/src/geometries/poly2/create.js +1 -6
  70. package/src/geometries/poly3/create.js +1 -6
  71. package/src/geometries/poly3/index.js +1 -1
  72. package/src/geometries/poly3/measureBoundingBox.js +2 -0
  73. package/src/geometries/poly3/measureBoundingSphere.d.ts +2 -1
  74. package/src/geometries/poly3/measureBoundingSphere.js +25 -8
  75. package/src/geometries/poly3/measureBoundingSphere.test.js +12 -8
  76. package/src/index.js +41 -0
  77. package/src/measurements/measureBoundingSphere.js +2 -6
  78. package/src/operations/booleans/intersectGeom3.test.js +4 -4
  79. package/src/operations/booleans/martinez/compareEvents.js +2 -7
  80. package/src/operations/booleans/martinez/connectEdges.js +30 -41
  81. package/src/operations/booleans/martinez/contour.js +1 -1
  82. package/src/operations/booleans/martinez/divideSegment.js +12 -11
  83. package/src/operations/booleans/martinez/fillQueue.js +24 -28
  84. package/src/operations/booleans/martinez/index.js +2 -1
  85. package/src/operations/booleans/martinez/possibleIntersection.js +41 -30
  86. package/src/operations/booleans/martinez/segmentIntersection.js +7 -9
  87. package/src/operations/booleans/martinez/splaytree.js +59 -457
  88. package/src/operations/booleans/martinez/subdivideSegments.js +4 -4
  89. package/src/operations/booleans/martinez/sweepEvent.js +3 -17
  90. package/src/operations/booleans/subtractGeom3.test.js +4 -4
  91. package/src/operations/booleans/trees/Node.js +25 -27
  92. package/src/operations/booleans/trees/PolygonTreeNode.js +153 -106
  93. package/src/operations/booleans/trees/Tree.js +9 -4
  94. package/src/operations/booleans/trees/splitLineSegmentByPlane.js +5 -3
  95. package/src/operations/booleans/trees/splitPolygonByPlane.js +39 -34
  96. package/src/operations/booleans/unionGeom3.test.js +5 -5
  97. package/src/operations/extrusions/extrudeFromSlices.test.js +6 -6
  98. package/src/operations/extrusions/extrudeLinear.test.js +8 -8
  99. package/src/operations/extrusions/extrudeRotate.test.js +12 -12
  100. package/src/operations/extrusions/extrudeWalls.js +3 -1
  101. package/src/operations/hulls/hull.test.js +5 -5
  102. package/src/operations/hulls/hullChain.test.js +5 -5
  103. package/src/operations/hulls/hullPoints2.js +20 -28
  104. package/src/operations/hulls/toUniquePoints.js +2 -2
  105. package/src/operations/modifiers/generalize.test.js +6 -6
  106. package/src/operations/modifiers/insertTjunctions.test.js +2 -2
  107. package/src/operations/modifiers/mergePolygons.js +2 -3
  108. package/src/operations/modifiers/reTesselateCoplanarPolygons.js +7 -7
  109. package/src/operations/modifiers/retessellate.test.js +10 -10
  110. package/src/operations/modifiers/snap.test.js +4 -4
  111. package/src/operations/offsets/offsetGeom3.test.js +4 -4
  112. package/src/operations/transforms/center.test.js +7 -7
  113. package/src/operations/transforms/mirror.test.js +7 -7
  114. package/src/operations/transforms/rotate.test.js +7 -7
  115. package/src/operations/transforms/scale.test.js +7 -7
  116. package/src/operations/transforms/transform.test.js +2 -2
  117. package/src/operations/transforms/translate.test.js +7 -7
  118. package/src/primitives/cube.test.js +4 -4
  119. package/src/primitives/cuboid.test.js +4 -4
  120. package/src/primitives/cylinder.test.js +5 -5
  121. package/src/primitives/cylinderElliptic.test.js +9 -9
  122. package/src/primitives/ellipsoid.test.js +5 -5
  123. package/src/primitives/geodesicSphere.test.js +4 -4
  124. package/src/primitives/polyhedron.test.js +2 -2
  125. package/src/primitives/roundedCuboid.test.js +7 -7
  126. package/src/primitives/roundedCylinder.test.js +9 -9
  127. package/src/primitives/sphere.test.js +5 -5
  128. package/src/primitives/torus.test.js +4 -4
  129. package/src/geometries/geom2/fromCompactBinary.d.ts +0 -3
  130. package/src/geometries/geom2/fromCompactBinary.js +0 -40
  131. package/src/geometries/geom2/fromToCompactBinary.test.js +0 -100
  132. package/src/geometries/geom2/toCompactBinary.d.ts +0 -3
  133. package/src/geometries/geom2/toCompactBinary.js +0 -56
  134. package/src/geometries/geom3/fromCompactBinary.d.ts +0 -3
  135. package/src/geometries/geom3/fromCompactBinary.js +0 -42
  136. package/src/geometries/geom3/fromPointsConvex.js +0 -25
  137. package/src/geometries/geom3/fromToCompactBinary.test.js +0 -139
  138. package/src/geometries/geom3/toCompactBinary.d.ts +0 -3
  139. package/src/geometries/geom3/toCompactBinary.js +0 -66
  140. package/src/geometries/path2/fromCompactBinary.d.ts +0 -3
  141. package/src/geometries/path2/fromCompactBinary.js +0 -31
  142. package/src/geometries/path2/fromToCompactBinary.test.js +0 -114
  143. package/src/geometries/path2/toCompactBinary.d.ts +0 -3
  144. package/src/geometries/path2/toCompactBinary.js +0 -50
@@ -1,6 +1,8 @@
1
1
  import * as vec3 from '../../maths/vec3/index.js'
2
2
 
3
3
  /**
4
+ * Measure the bounding box of the given polygon.
5
+ *
4
6
  * @param {Poly3} polygon - the polygon to measure
5
7
  * @returns {Array} an array of two vectors (3D); minimum and maximum coordinates
6
8
  * @alias module:modeling/geometries/poly3.measureBoundingBox
@@ -1,4 +1,5 @@
1
1
  import type { Poly3 } from './type.d.ts'
2
2
  import type { Vec4 } from '../../maths/vec4/type.d.ts'
3
3
 
4
- export function measureBoundingSphere(polygon: Poly3): Vec4
4
+ export function measureBoundingSphere(out: Vec4, polygon: Poly3): Vec4
5
+ export function measureBoundingSphereAndCache(polygon: Poly3): Vec4
@@ -4,16 +4,14 @@ const cache = new WeakMap()
4
4
 
5
5
  /**
6
6
  * Measure the bounding sphere of the given polygon.
7
+ *
8
+ * @param {Vec4} out - receiving vector
7
9
  * @param {Poly3} polygon - the polygon to measure
8
10
  * @returns {Vec4} the computed bounding sphere; center vertex (3D) and radius
9
11
  * @alias module:modeling/geometries/poly3.measureBoundingSphere
10
12
  */
11
- export const measureBoundingSphere = (polygon) => {
12
- const boundingSphere = cache.get(polygon)
13
- if (boundingSphere) return boundingSphere
14
-
13
+ export const measureBoundingSphere = (out, polygon) => {
15
14
  const vertices = polygon.vertices
16
- const out = vec4.create()
17
15
 
18
16
  if (vertices.length === 0) {
19
17
  out[0] = 0
@@ -31,14 +29,15 @@ export const measureBoundingSphere = (polygon) => {
31
29
  let maxy = minx
32
30
  let maxz = minx
33
31
 
34
- vertices.forEach((v) => {
32
+ for (let i = 0; i < vertices.length; i++) {
33
+ const v = vertices[i]
35
34
  if (minx[0] > v[0]) minx = v
36
35
  if (miny[1] > v[1]) miny = v
37
36
  if (minz[2] > v[2]) minz = v
38
37
  if (maxx[0] < v[0]) maxx = v
39
38
  if (maxy[1] < v[1]) maxy = v
40
39
  if (maxz[2] < v[2]) maxz = v
41
- })
40
+ }
42
41
 
43
42
  out[0] = (minx[0] + maxx[0]) * 0.5 // center of sphere
44
43
  out[1] = (miny[1] + maxy[1]) * 0.5
@@ -48,7 +47,25 @@ export const measureBoundingSphere = (polygon) => {
48
47
  const z = out[2] - maxz[2]
49
48
  out[3] = Math.sqrt(x * x + y * y + z * z) // radius of sphere
50
49
 
51
- cache.set(polygon, out)
50
+ return out
51
+ }
52
+
53
+ /**
54
+ * Measure the bounding sphere of the given polygon.
55
+ *
56
+ * This version maintains a cache, retrievning previously calculated bounds if found.
57
+ *
58
+ * @param {Poly3} polygon - the polygon to measure
59
+ * @returns {Vec4} the computed bounding sphere; center vertex (3D) and radius
60
+ * @alias module:modeling/geometries/poly3.measureBoundingSphere
61
+ */
62
+ export const measureBoundingSphereAndCache = (polygon) => {
63
+ const boundingSphere = cache.get(polygon)
64
+ if (boundingSphere) return boundingSphere
52
65
 
66
+ const out = [0, 0, 0, 0]
67
+ measureBoundingSphere(out, polygon)
68
+
69
+ cache.set(polygon, out)
53
70
  return out
54
71
  }
@@ -7,19 +7,22 @@ import { measureBoundingSphere, create, transform } from './index.js'
7
7
  test('poly3: measureBoundingSphere() should return correct values', (t) => {
8
8
  let ply1 = create()
9
9
  let exp1 = [0, 0, 0, 0]
10
- let ret1 = measureBoundingSphere(ply1)
10
+ let ret1 = [0, 0, 0, 0]
11
+ ret1 = measureBoundingSphere(ret1, ply1)
11
12
  t.deepEqual(ret1, exp1)
12
13
 
13
14
  // simple triangle
14
15
  let ply2 = create([[0, 0, 0], [0, 10, 0], [0, 10, 10]])
15
16
  let exp2 = [0, 5, 5, 7.0710678118654755]
16
- let ret2 = measureBoundingSphere(ply2)
17
+ let ret2 = [0, 0, 0, 0]
18
+ ret2 = measureBoundingSphere(ret2, ply2)
17
19
  t.deepEqual(ret2, exp2)
18
20
 
19
21
  // simple square
20
22
  let ply3 = create([[0, 0, 0], [0, 10, 0], [0, 10, 10], [0, 0, 10]])
21
23
  let exp3 = [0, 5, 5, 7.0710678118654755]
22
- let ret3 = measureBoundingSphere(ply3)
24
+ let ret3 = [0, 0, 0, 0]
25
+ ret3 = measureBoundingSphere(ret3, ply3)
23
26
  t.deepEqual(ret3, exp3)
24
27
 
25
28
  // V-shape
@@ -37,7 +40,8 @@ test('poly3: measureBoundingSphere() should return correct values', (t) => {
37
40
  ]
38
41
  let ply4 = create(vertices)
39
42
  let exp4 = [0, 4.5, 3, 4.6097722286464435]
40
- let ret4 = measureBoundingSphere(ply4)
43
+ let ret4 = [0, 0, 0, 0]
44
+ ret4 = measureBoundingSphere(ret4, ply4)
41
45
  t.deepEqual(ret4, exp4)
42
46
 
43
47
  // rotated to various angles
@@ -46,10 +50,10 @@ test('poly3: measureBoundingSphere() should return correct values', (t) => {
46
50
  ply2 = transform(rotation, ply2)
47
51
  ply3 = transform(rotation, ply3)
48
52
  ply4 = transform(rotation, ply4)
49
- ret1 = measureBoundingSphere(ply1)
50
- ret2 = measureBoundingSphere(ply2)
51
- ret3 = measureBoundingSphere(ply3)
52
- ret4 = measureBoundingSphere(ply4)
53
+ ret1 = measureBoundingSphere(ret1, ply1)
54
+ ret2 = measureBoundingSphere(ret2, ply2)
55
+ ret3 = measureBoundingSphere(ret3, ply3)
56
+ ret4 = measureBoundingSphere(ret4, ply4)
53
57
  exp1 = [0, 0, 0, 0]
54
58
  t.deepEqual(ret1, exp1)
55
59
  exp2 = [-3.5355339059327373, 3.5355339059327378, 5, 7.0710678118654755]
package/src/index.js CHANGED
@@ -13,3 +13,44 @@ export * from './operations/hulls/index.js'
13
13
  export * from './operations/modifiers/index.js'
14
14
  export * from './operations/offsets/index.js'
15
15
  export * from './operations/transforms/index.js'
16
+
17
+ // V2 API compatibility:
18
+ export * as colors from './colors/index.js'
19
+ export * as curves from './curves/index.js'
20
+ import { geom2, geom3, path2, poly2, poly3 } from './geometries/index.js'
21
+ export const geometries = {
22
+ geom2: {
23
+ ...geom2,
24
+ create: (sides) => geom2.fromSides(sides),
25
+ fromPoints: (points) => geometries.geom2.create([points])
26
+ },
27
+ geom3,
28
+ path2,
29
+ poly2,
30
+ poly3: {
31
+ ...poly3,
32
+ fromPoints: (points) => poly3.create([points]),
33
+ fromPointsAndPlane: poly3.fromVerticesAndPlane,
34
+ toPoints: poly3.toVertices
35
+ }
36
+ }
37
+ export * as maths from './maths/index.js'
38
+ export * as measurements from './measurements/index.js'
39
+ export * as primitives from './primitives/index.js'
40
+ export * as text from './text/index.js'
41
+ export * as booleans from './operations/booleans/index.js'
42
+ import * as extrusion from './operations/extrusions/index.js'
43
+ import * as offsets from './operations/offsets/index.js'
44
+ export const extrusions = {
45
+ ...extrusion,
46
+ extrudeRectangular: (opt, geom) => extrusions.extrudeLinear(opt, offsets.offset(opt, geom)),
47
+ slice: geometries.slice
48
+ }
49
+ export * as hulls from './operations/hulls/index.js'
50
+ export * as modifiers from './operations/modifiers/index.js'
51
+ export const expansions = {
52
+ ...offsets,
53
+ expand: offsets.offset
54
+ }
55
+ export * as transforms from './operations/transforms/index.js'
56
+ export * as utils from './utils/index.js'
@@ -64,17 +64,13 @@ const measureBoundingSphereOfPoints = (points) => {
64
64
  * Measure the bounding sphere of the given (path2) geometry.
65
65
  * @return {[[x, y, z], radius]} the bounding sphere for the geometry
66
66
  */
67
- const measureBoundingSphereOfPath2 = (points) => {
68
- return measureBoundingSphereOfPoints(path2.toPoints(points))
69
- }
67
+ const measureBoundingSphereOfPath2 = (geometry) => measureBoundingSphereOfPoints(path2.toPoints(geometry))
70
68
 
71
69
  /*
72
70
  * Measure the bounding sphere of the given (geom2) geometry.
73
71
  * @return {[[x, y, z], radius]} the bounding sphere for the geometry
74
72
  */
75
- const measureBoundingSphereOfGeom2 = (geometry) => {
76
- return measureBoundingSphereOfPoints(geom2.toPoints(geometry))
77
- }
73
+ const measureBoundingSphereOfGeom2 = (geometry) => measureBoundingSphereOfPoints(geom2.toPoints(geometry))
78
74
 
79
75
  /*
80
76
  * Measure the bounding sphere of the given (geom3) geometry.
@@ -17,7 +17,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
17
17
 
18
18
  // intersect of one object
19
19
  const result1 = intersect(geometry1)
20
- let obs = geom3.toPoints(result1)
20
+ let obs = geom3.toVertices(result1)
21
21
  let exp = [
22
22
  [[2, 0, 0], [1.4142135623730951, -1.414213562373095, 0],
23
23
  [1.0000000000000002, -1, -1.414213562373095], [1.4142135623730951, 0, -1.414213562373095]],
@@ -78,7 +78,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
78
78
  const geometry2 = center({ relativeTo: [10, 10, 10] }, cuboid({ size: [4, 4, 4] }))
79
79
 
80
80
  const result2 = intersect(geometry1, geometry2)
81
- obs = geom3.toPoints(result2)
81
+ obs = geom3.toVertices(result2)
82
82
  t.notThrows(() => geom3.validate(result2))
83
83
  t.is(measureArea(result2), 0)
84
84
  t.is(measureVolume(result2), 0)
@@ -88,7 +88,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
88
88
  const geometry3 = cuboid({ size: [18, 18, 18] })
89
89
 
90
90
  const result3 = intersect(geometry2, geometry3)
91
- obs = geom3.toPoints(result3)
91
+ obs = geom3.toVertices(result3)
92
92
 
93
93
  // the order changes based on the best plane chosen in Node.js
94
94
  exp = [
@@ -108,7 +108,7 @@ test('intersect: intersect of one or more geom3 objects produces expected geomet
108
108
 
109
109
  // intersect of two completely overlapping objects
110
110
  const result4 = intersect(geometry1, geometry3)
111
- obs = geom3.toPoints(result4)
111
+ obs = geom3.toVertices(result4)
112
112
  t.notThrows.skip(() => geom3.validate(result4))
113
113
  t.is(measureArea(result4), 44.053756306589825)
114
114
  t.is(measureVolume(result4), 25.751611331979678)
@@ -23,23 +23,18 @@ export const compareEvents = (e1, e2) => {
23
23
  // Event with lower y-coordinate is processed first
24
24
  if (p1[1] !== p2[1]) return p1[1] > p2[1] ? 1 : -1
25
25
 
26
- return specialCases(e1, e2, p1, p2)
27
- }
28
-
29
- const specialCases = (e1, e2, p1, p2) => {
30
26
  // Same coordinates, but one is a left endpoint and the other is
31
27
  // a right endpoint. The right endpoint is processed first
32
28
  if (e1.left !== e2.left) { return e1.left ? 1 : -1 }
33
29
 
34
- // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point
35
- // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
36
30
  // Same coordinates, both events
37
31
  // are left endpoints or right endpoints.
38
32
  // not collinear
39
33
  if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
40
- // the event associate to the bottom segment is processed first
34
+ // the event associate to the lowest segment is processed first
41
35
  return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1
42
36
  }
43
37
 
38
+ // Same coordinates, subject events are processed first
44
39
  return (!e1.isSubject && e2.isSubject) ? 1 : -1
45
40
  }
@@ -12,45 +12,39 @@ import { Contour } from './contour.js'
12
12
  * @return {SweepEvent[]}
13
13
  */
14
14
  const orderEvents = (sortedEvents) => {
15
- let event, i, len, tmp
16
15
  const resultEvents = []
17
- for (i = 0, len = sortedEvents.length; i < len; i++) {
18
- event = sortedEvents[i]
19
- if ((event.left && event.inResult) ||
20
- (!event.left && event.otherEvent.inResult)) {
21
- resultEvents.push(event)
16
+ sortedEvents.forEach((e) => {
17
+ if ((e.left && e.inResult) || (!e.left && e.otherEvent.inResult)) {
18
+ resultEvents.push(e)
22
19
  }
23
- }
20
+ })
24
21
  // Due to overlapping edges the resultEvents array can be not wholly sorted
25
22
  let sorted = false
26
23
  while (!sorted) {
27
24
  sorted = true
28
- for (i = 0, len = resultEvents.length; i < len; i++) {
25
+ const len = resultEvents.length
26
+ for (let i = 0; i < len; i++) {
29
27
  if ((i + 1) < len &&
30
28
  compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
31
- tmp = resultEvents[i]
29
+ const tmp = resultEvents[i]
32
30
  resultEvents[i] = resultEvents[i + 1]
33
31
  resultEvents[i + 1] = tmp
34
32
  sorted = false
35
33
  }
36
34
  }
37
35
  }
38
-
39
- for (i = 0, len = resultEvents.length; i < len; i++) {
40
- event = resultEvents[i]
41
- event.otherPos = i
42
- }
36
+ // and index
37
+ resultEvents.forEach((e, i) => { e.otherPos = i })
43
38
 
44
39
  // imagine, the right event is found in the beginning of the queue,
45
40
  // when his left counterpart is not marked yet
46
- for (i = 0, len = resultEvents.length; i < len; i++) {
47
- event = resultEvents[i]
48
- if (!event.left) {
49
- tmp = event.otherPos
50
- event.otherPos = event.otherEvent.otherPos
51
- event.otherEvent.otherPos = tmp
41
+ resultEvents.forEach((e) => {
42
+ if (!e.left) {
43
+ const otherPos = e.otherPos
44
+ e.otherPos = e.otherEvent.otherPos
45
+ e.otherEvent.otherPos = otherPos
52
46
  }
53
- }
47
+ })
54
48
 
55
49
  return resultEvents
56
50
  }
@@ -62,14 +56,15 @@ const orderEvents = (sortedEvents) => {
62
56
  * @return {number}
63
57
  */
64
58
  const nextPos = (pos, resultEvents, processed, origPos) => {
65
- let newPos = pos + 1
66
- const p = resultEvents[pos].point
67
- let p1
68
59
  const length = resultEvents.length
69
60
 
61
+ const p0 = resultEvents[pos].point
62
+
63
+ let newPos = pos + 1
64
+ let p1
70
65
  if (newPos < length) { p1 = resultEvents[newPos].point }
71
66
 
72
- while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
67
+ while (newPos < length && p1[0] === p0[0] && p1[1] === p0[1]) {
73
68
  if (!processed[newPos]) {
74
69
  return newPos
75
70
  } else {
@@ -81,7 +76,6 @@ const nextPos = (pos, resultEvents, processed, origPos) => {
81
76
  }
82
77
 
83
78
  newPos = pos - 1
84
-
85
79
  while (processed[newPos] && newPos > origPos) {
86
80
  newPos--
87
81
  }
@@ -90,7 +84,8 @@ const nextPos = (pos, resultEvents, processed, origPos) => {
90
84
  }
91
85
 
92
86
  const initializeContourFromContext = (event, contours, contourId) => {
93
- const contour = new Contour()
87
+ const contour = new Contour() // default is exterior contour of depth 0
88
+
94
89
  if (event.prevInResult != null) {
95
90
  const prevInResult = event.prevInResult
96
91
  // Note that it is valid to query the "previous in result" for its output contour id,
@@ -99,6 +94,7 @@ const initializeContourFromContext = (event, contours, contourId) => {
99
94
  // result".
100
95
  const lowerContourId = prevInResult.outputContourId
101
96
  const lowerResultTransition = prevInResult.resultTransition
97
+
102
98
  if (lowerContourId < 0) {
103
99
  contour.holeOf = null
104
100
  contour.depth = 0
@@ -122,30 +118,24 @@ const initializeContourFromContext = (event, contours, contourId) => {
122
118
  }
123
119
  } else {
124
120
  // We are outside => this contour is an exterior contour of same depth.
125
- contour.holeOf = null
126
121
  contour.depth = contours[lowerContourId].depth
127
122
  }
128
- } else {
129
- // There is no lower/previous contour => this contour is an exterior contour of depth 0.
130
- contour.holeOf = null
131
- contour.depth = 0
132
123
  }
133
124
  return contour
134
125
  }
135
126
 
136
127
  /**
137
128
  * @param {Array.<SweepEvent>} sortedEvents
138
- * @return {Array.<*>} polygons
129
+ * @return array of Contour
139
130
  */
140
131
  export const connectEdges = (sortedEvents) => {
141
132
  const resultEvents = orderEvents(sortedEvents)
142
- const len = resultEvents.length
133
+ const evlen = resultEvents.length
143
134
 
144
- // "false"-filled array
145
- const processed = {}
135
+ const processed = []
146
136
  const contours = []
147
137
 
148
- for (let i = 0; i < len; i++) {
138
+ for (let i = 0; i < evlen; i++) {
149
139
  if (processed[i]) {
150
140
  continue
151
141
  }
@@ -156,7 +146,7 @@ export const connectEdges = (sortedEvents) => {
156
146
  // Helper function that combines marking an event as processed with assigning its output contour ID
157
147
  const markAsProcessed = (pos) => {
158
148
  processed[pos] = true
159
- if (pos < resultEvents.length && resultEvents[pos]) {
149
+ if (pos < evlen) {
160
150
  resultEvents[pos].outputContourId = contourId
161
151
  }
162
152
  }
@@ -164,8 +154,7 @@ export const connectEdges = (sortedEvents) => {
164
154
  let pos = i
165
155
  const origPos = i
166
156
 
167
- const initial = resultEvents[i].point
168
- contour.points.push(initial)
157
+ contour.points.push(resultEvents[pos].point)
169
158
 
170
159
  while (true) {
171
160
  markAsProcessed(pos)
@@ -177,7 +166,7 @@ export const connectEdges = (sortedEvents) => {
177
166
 
178
167
  pos = nextPos(pos, resultEvents, processed, origPos)
179
168
 
180
- if (pos === origPos || pos >= resultEvents.length || !resultEvents[pos]) {
169
+ if (pos === origPos || pos >= evlen) {
181
170
  break
182
171
  }
183
172
  }
@@ -3,7 +3,7 @@ export class Contour {
3
3
  this.points = []
4
4
  this.holeIds = []
5
5
  this.holeOf = null
6
- this.depth = null
6
+ this.depth = 0
7
7
  }
8
8
 
9
9
  isExterior () {
@@ -2,28 +2,29 @@ import { SweepEvent } from './sweepEvent.js'
2
2
  import { compareEvents } from './compareEvents.js'
3
3
 
4
4
  /**
5
- * @param {SweepEvent} se
6
- * @param {Array.<Number>} p
5
+ * Divide the given segment at the given point, push the parts on the given queue.
6
+ * @param {SweepEvent} segment
7
+ * @param {Array.<Number>} point
7
8
  * @param {Queue} queue
8
- * @return {Queue}
9
+ * @return {Queue} given queue
9
10
  */
10
- export const divideSegment = (se, p, queue) => {
11
- const r = new SweepEvent(p, false, se, se.isSubject)
12
- const l = new SweepEvent(p, true, se.otherEvent, se.isSubject)
11
+ export const divideSegment = (segment, point, queue) => {
12
+ const r = new SweepEvent(point, false, segment, segment.isSubject)
13
+ const l = new SweepEvent(point, true, segment.otherEvent, segment.isSubject)
13
14
 
14
- r.contourId = l.contourId = se.contourId
15
+ r.contourId = l.contourId = segment.contourId
15
16
 
16
17
  // avoid a rounding error. The left event would be processed after the right event
17
- if (compareEvents(l, se.otherEvent) > 0) {
18
- se.otherEvent.left = true
18
+ if (compareEvents(l, segment.otherEvent) > 0) {
19
+ segment.otherEvent.left = true
19
20
  l.left = false
20
21
  }
21
22
 
22
23
  // avoid a rounding error. The left event would be processed after the right event
23
24
  // if (compareEvents(se, r) > 0) {}
24
25
 
25
- se.otherEvent.otherEvent = l
26
- se.otherEvent = r
26
+ segment.otherEvent.otherEvent = l
27
+ segment.otherEvent = r
27
28
 
28
29
  queue.push(l)
29
30
  queue.push(r)
@@ -9,26 +9,23 @@ import { DIFFERENCE } from './operation.js'
9
9
  import { SweepEvent } from './sweepEvent.js'
10
10
  import { Queue } from './tinyqueue.js'
11
11
 
12
- const max = Math.max
13
- const min = Math.min
12
+ let externalRingId = 0
14
13
 
15
- let contourId = 0
16
-
17
- const processPolygon = (contourOrHole, isSubject, depth, queue, bbox, isExteriorRing) => {
14
+ const processPolygon = (contourOrHole, isSubject, ringId, queue, bbox, isExteriorRing) => {
18
15
  const len = contourOrHole.length - 1
19
- let s1, s2, e1, e2
20
16
  for (let i = 0; i < len; i++) {
21
- s1 = contourOrHole[i]
22
- s2 = contourOrHole[i + 1]
23
- e1 = new SweepEvent(s1, false, undefined, isSubject)
24
- e2 = new SweepEvent(s2, false, e1, isSubject)
17
+ const s1 = contourOrHole[i]
18
+ const s2 = contourOrHole[i + 1]
19
+ const e1 = new SweepEvent(s1, false, undefined, isSubject)
20
+ const e2 = new SweepEvent(s2, false, e1, isSubject)
21
+
25
22
  e1.otherEvent = e2
26
23
 
27
24
  if (s1[0] === s2[0] && s1[1] === s2[1]) {
28
25
  continue // skip collapsed edges, or it breaks
29
26
  }
30
27
 
31
- e1.contourId = e2.contourId = depth
28
+ e1.contourId = e2.contourId = ringId
32
29
  if (!isExteriorRing) {
33
30
  e1.isExteriorRing = false
34
31
  e2.isExteriorRing = false
@@ -41,10 +38,10 @@ const processPolygon = (contourOrHole, isSubject, depth, queue, bbox, isExterior
41
38
 
42
39
  const x = s1[0]
43
40
  const y = s1[1]
44
- bbox[0] = min(bbox[0], x)
45
- bbox[1] = min(bbox[1], y)
46
- bbox[2] = max(bbox[2], x)
47
- bbox[3] = max(bbox[3], y)
41
+ bbox[0] = Math.min(bbox[0], x)
42
+ bbox[1] = Math.min(bbox[1], y)
43
+ bbox[2] = Math.max(bbox[2], x)
44
+ bbox[3] = Math.max(bbox[3], y)
48
45
 
49
46
  // Pushing it so the queue is sorted from left to right,
50
47
  // with object on the left having the highest priority.
@@ -55,24 +52,23 @@ const processPolygon = (contourOrHole, isSubject, depth, queue, bbox, isExterior
55
52
 
56
53
  export const fillQueue = (subject, clipping, sbbox, cbbox, operation) => {
57
54
  const eventQueue = new Queue([], compareEvents)
58
- let polygonSet, isExteriorRing, i, ii, j, jj //, k, kk
59
55
 
60
- for (i = 0, ii = subject.length; i < ii; i++) {
61
- polygonSet = subject[i]
62
- for (j = 0, jj = polygonSet.length; j < jj; j++) {
63
- isExteriorRing = j === 0
64
- if (isExteriorRing) contourId++
65
- processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing)
56
+ for (let i = 0; i < subject.length; i++) {
57
+ const polygonSet = subject[i]
58
+ for (let j = 0; j < polygonSet.length; j++) {
59
+ const isExteriorRing = j === 0
60
+ if (isExteriorRing) externalRingId++
61
+ processPolygon(polygonSet[j], true, externalRingId, eventQueue, sbbox, isExteriorRing)
66
62
  }
67
63
  }
68
64
 
69
- for (i = 0, ii = clipping.length; i < ii; i++) {
70
- polygonSet = clipping[i]
71
- for (j = 0, jj = polygonSet.length; j < jj; j++) {
72
- isExteriorRing = j === 0
65
+ for (let i = 0; i < clipping.length; i++) {
66
+ const polygonSet = clipping[i]
67
+ for (let j = 0; j < polygonSet.length; j++) {
68
+ let isExteriorRing = j === 0
73
69
  if (operation === DIFFERENCE) isExteriorRing = false
74
- if (isExteriorRing) contourId++
75
- processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing)
70
+ if (isExteriorRing) externalRingId++
71
+ processPolygon(polygonSet[j], false, externalRingId, eventQueue, cbbox, isExteriorRing)
76
72
  }
77
73
  }
78
74
 
@@ -135,6 +135,7 @@ export const boolean = (subjectGeom, clippingGeom, operation) => {
135
135
  // Followed by holes if any
136
136
  for (let j = 0; j < contour.holeIds.length; j++) {
137
137
  const holeId = contour.holeIds[j]
138
+ // Reverse the order of points for holes
138
139
  const holePoints = contours[holeId].points
139
140
  const hole = []
140
141
  for (let k = holePoints.length - 2; k >= 0; k--) {
@@ -146,7 +147,7 @@ export const boolean = (subjectGeom, clippingGeom, operation) => {
146
147
  }
147
148
  }
148
149
 
149
- if (polygons) {
150
+ if (polygons.length) {
150
151
  return fromOutlines(polygons.flat())
151
152
  } else {
152
153
  return geom2.create()