@jscad/modeling 3.0.4-alpha.0 → 3.0.6-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 (116) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/jscad-modeling.es.js +2 -2
  3. package/dist/jscad-modeling.min.js +2 -2
  4. package/package.json +4 -4
  5. package/rollup.config.js +1 -1
  6. package/src/colors/colorize.js +17 -1
  7. package/src/colors/hsvToRgb.js +1 -0
  8. package/src/colors/rgbToHsl.js +3 -1
  9. package/src/colors/rgbToHsv.js +1 -0
  10. package/src/curves/bezier/create.js +1 -1
  11. package/src/geometries/geom2/fromPoints.d.ts +4 -0
  12. package/src/geometries/geom2/fromPoints.js +28 -0
  13. package/src/geometries/geom2/fromPoints.test.js +22 -0
  14. package/src/geometries/geom2/index.d.ts +1 -0
  15. package/src/geometries/geom2/index.js +1 -0
  16. package/src/geometries/geom2/transform.js +1 -0
  17. package/src/geometries/geom3/transform.js +1 -0
  18. package/src/geometries/geom3/validate.js +11 -1
  19. package/src/geometries/path2/appendArc.js +19 -25
  20. package/src/geometries/path2/appendBezier.js +8 -11
  21. package/src/geometries/path2/appendPoints.js +21 -3
  22. package/src/geometries/path2/appendPoints.test.js +16 -4
  23. package/src/geometries/path2/close.js +7 -10
  24. package/src/geometries/path2/concat.js +2 -2
  25. package/src/geometries/path2/fromPoints.js +7 -7
  26. package/src/geometries/path2/reverse.js +3 -3
  27. package/src/geometries/path2/transform.js +1 -0
  28. package/src/geometries/path2/validate.js +2 -2
  29. package/src/geometries/path3/clone.d.ts +3 -0
  30. package/src/geometries/path3/clone.js +11 -0
  31. package/src/geometries/path3/close.js +7 -8
  32. package/src/geometries/path3/index.d.ts +1 -0
  33. package/src/geometries/path3/index.js +1 -0
  34. package/src/geometries/path3/transform.js +1 -0
  35. package/src/geometries/poly2/type.d.ts +1 -5
  36. package/src/geometries/slice/earcut/index.js +4 -2
  37. package/src/geometries/slice/earcut/linkedPolygon.js +4 -2
  38. package/src/geometries/slice/fromOutlines.d.ts +5 -0
  39. package/src/geometries/slice/fromOutlines.js +16 -0
  40. package/src/geometries/slice/fromOutlines.test.js +17 -0
  41. package/src/geometries/slice/index.d.ts +1 -1
  42. package/src/geometries/slice/index.js +1 -1
  43. package/src/geometries/slice/transform.js +2 -1
  44. package/src/maths/line2/intersectPointOfLines.js +1 -4
  45. package/src/maths/plane/fromNormalAndPoint.js +4 -6
  46. package/src/maths/plane/fromPoints.js +8 -7
  47. package/src/maths/plane/fromPointsRandom.js +13 -13
  48. package/src/measurements/measureAggregateEpsilon.js +3 -1
  49. package/src/measurements/measureAggregateEpsilon.test.js +1 -1
  50. package/src/measurements/measureArea.js +6 -4
  51. package/src/measurements/measureArea.test.js +4 -1
  52. package/src/measurements/measureBoundingBox.js +16 -2
  53. package/src/measurements/measureBoundingBox.test.js +4 -1
  54. package/src/measurements/measureBoundingSphere.js +38 -29
  55. package/src/measurements/measureBoundingSphere.test.js +4 -1
  56. package/src/measurements/measureCenterOfMass.js +3 -2
  57. package/src/measurements/measureEpsilon.js +4 -2
  58. package/src/operations/booleans/martinez/computeFields.js +6 -0
  59. package/src/operations/booleans/martinez/divideSegment.js +2 -1
  60. package/src/operations/booleans/martinez/fillQueue.js +3 -1
  61. package/src/operations/booleans/martinez/subdivideSegments.js +5 -4
  62. package/src/operations/booleans/subtractGeom3Sub.js +1 -1
  63. package/src/operations/booleans/trees/PolygonTreeNode.js +3 -0
  64. package/src/operations/booleans/trees/splitPolygonByPlane.d.ts +1 -3
  65. package/src/operations/booleans/union.js +1 -1
  66. package/src/operations/extrusions/extrudeFromSlices.js +1 -1
  67. package/src/operations/extrusions/extrudeFromSlices.test.js +1 -1
  68. package/src/operations/extrusions/extrudeHelical.js +2 -1
  69. package/src/operations/extrusions/extrudeLinear.js +1 -1
  70. package/src/operations/extrusions/extrudeLinearGeom2.js +12 -8
  71. package/src/operations/extrusions/extrudeRotate.js +1 -1
  72. package/src/operations/hulls/hull.js +3 -2
  73. package/src/operations/hulls/quickhull/Face.js +8 -3
  74. package/src/operations/hulls/quickhull/QuickHull.js +10 -5
  75. package/src/operations/hulls/quickhull/VertexList.js +2 -1
  76. package/src/operations/hulls/toUniquePoints.js +3 -0
  77. package/src/operations/modifiers/generalize.js +9 -2
  78. package/src/operations/modifiers/reTesselateCoplanarPolygons.js +2 -2
  79. package/src/operations/modifiers/reTesselateCoplanarPolygons.test.js +29 -3
  80. package/src/operations/modifiers/retessellate.js +1 -0
  81. package/src/operations/modifiers/snap.js +22 -3
  82. package/src/operations/modifiers/snap.test.js +12 -2
  83. package/src/operations/offsets/offsetGeom3.test.js +2 -2
  84. package/src/operations/transforms/align.js +2 -1
  85. package/src/operations/transforms/align.test.js +1 -1
  86. package/src/operations/transforms/mirror.js +6 -2
  87. package/src/operations/transforms/rotate.js +6 -2
  88. package/src/operations/transforms/scale.js +6 -2
  89. package/src/operations/transforms/transform.js +6 -2
  90. package/src/operations/transforms/transform.test.js +16 -5
  91. package/src/operations/transforms/translate.js +12 -4
  92. package/src/primitives/arc.js +14 -14
  93. package/src/primitives/circle.js +10 -9
  94. package/src/primitives/cube.js +5 -6
  95. package/src/primitives/cuboid.js +6 -6
  96. package/src/primitives/cylinder.js +8 -8
  97. package/src/primitives/cylinderElliptic.js +11 -11
  98. package/src/primitives/ellipse.js +12 -12
  99. package/src/primitives/ellipsoid.js +8 -8
  100. package/src/primitives/geodesicSphere.js +6 -6
  101. package/src/primitives/line.js +2 -0
  102. package/src/primitives/polygon.js +6 -7
  103. package/src/primitives/polyhedron.js +7 -8
  104. package/src/primitives/rectangle.js +6 -6
  105. package/src/primitives/roundedCuboid.js +8 -8
  106. package/src/primitives/roundedCylinder.js +9 -9
  107. package/src/primitives/roundedRectangle.js +8 -8
  108. package/src/primitives/sphere.js +7 -8
  109. package/src/primitives/square.js +6 -6
  110. package/src/primitives/star.js +12 -14
  111. package/src/primitives/torus.js +11 -11
  112. package/src/primitives/triangle.js +7 -6
  113. package/src/utils/areAllShapesTheSameType.js +4 -0
  114. package/test/helpers/nearlyEqual.js +1 -0
  115. package/src/geometries/slice/fromGeom2.d.ts +0 -5
  116. package/src/geometries/slice/fromGeom2.js +0 -17
@@ -1,7 +1,3 @@
1
1
  import type { Vec2 } from '../../maths/vec2/type.d.ts'
2
2
 
3
- export declare interface Poly2 {
4
- points: Array<Vec2>
5
- }
6
-
7
- export default Poly2
3
+ export declare interface Poly2 { points: Array<Vec2> }
@@ -28,8 +28,10 @@ export const triangulate = (data, holeIndices, dim = 2) => {
28
28
 
29
29
  // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
30
30
  if (data.length > 80 * dim) {
31
- minX = maxX = data[0]
32
- minY = maxY = data[1]
31
+ minX = data[0]
32
+ maxX = data[0]
33
+ minY = data[1]
34
+ maxY = data[1]
33
35
 
34
36
  for (let i = dim; i < outerLen; i += dim) {
35
37
  const x = data[i]
@@ -39,7 +39,8 @@ export const filterPoints = (start, end) => {
39
39
 
40
40
  if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
41
41
  removeNode(p)
42
- p = end = p.prev
42
+ p = p.prev
43
+ end = p.prev
43
44
  if (p === p.next) break
44
45
  again = true
45
46
  } else {
@@ -68,7 +69,8 @@ export const cureLocalIntersections = (start, triangles, dim) => {
68
69
  removeNode(p)
69
70
  removeNode(p.next)
70
71
 
71
- p = start = b
72
+ p = b
73
+ start = b
72
74
  }
73
75
 
74
76
  p = p.next
@@ -0,0 +1,5 @@
1
+ import type { Vec2 } from '../../maths/types.d.ts'
2
+
3
+ import type { Slice } from './type.d.ts'
4
+
5
+ export function fromOutlines(outlines: Array<Array<Vec2>>): Slice
@@ -0,0 +1,16 @@
1
+ import * as vec3 from '../../maths/vec3/index.js'
2
+
3
+ import { create } from './create.js'
4
+
5
+ /**
6
+ * Create a slice from the given outlines.
7
+ *
8
+ * @param {Array} outlines - where each outline is an array of ordered points
9
+ * @returns {Slice} a new slice
10
+ * @alias module:modeling/slice.fromOutlines
11
+ */
12
+ export const fromOutlines = (outlines) => {
13
+ // Convert from 2D points to 3D vertices
14
+ const contours = outlines.map((outline) => outline.map((point) => vec3.fromVec2(vec3.create(), point)))
15
+ return create(contours)
16
+ }
@@ -0,0 +1,17 @@
1
+ import test from 'ava'
2
+
3
+ import { fromOutlines } from './index.js'
4
+
5
+ test('slice: fromOutlines() should return a new slice with correct values', (t) => {
6
+ const exp1 = {
7
+ contours: [
8
+ [[0, 0, 0], [1, 0, 0], [1, 1, 0]],
9
+ [[2, 2, 0], [3, 1, 0], [3, 3, 0]]
10
+ ]
11
+ }
12
+ const obs1 = fromOutlines([
13
+ [[0, 0], [1, 0], [1, 1]],
14
+ [[2, 2], [3, 1], [3, 3]]
15
+ ])
16
+ t.deepEqual(obs1, exp1)
17
+ })
@@ -2,7 +2,7 @@ export { calculatePlane } from './calculatePlane.js'
2
2
  export { clone } from './clone.js'
3
3
  export { create } from './create.js'
4
4
  export { equals } from './equals.js'
5
- export { fromGeom2 } from './fromGeom2.js'
5
+ export { fromOutlines } from './fromOutlines.js'
6
6
  export { fromVertices } from './fromVertices.js'
7
7
  export { isA } from './isA.js'
8
8
  export { reverse } from './reverse.js'
@@ -26,7 +26,7 @@ export { calculatePlane } from './calculatePlane.js'
26
26
  export { clone } from './clone.js'
27
27
  export { create } from './create.js'
28
28
  export { equals } from './equals.js'
29
- export { fromGeom2 } from './fromGeom2.js'
29
+ export { fromOutlines } from './fromOutlines.js'
30
30
  export { fromVertices } from './fromVertices.js'
31
31
  export { isA } from './isA.js'
32
32
  export { reverse } from './reverse.js'
@@ -16,5 +16,6 @@ import { create } from './create.js'
16
16
  */
17
17
  export const transform = (matrix, slice) => {
18
18
  const contours = slice.contours.map((contour) => contour.map((vertex) => vec3.transform(vec3.create(), vertex, matrix)))
19
- return create(contours)
19
+ // use Object.assign in order to assign EXTRA attributes like color, name, etc
20
+ return Object.assign({}, slice, { contours })
20
21
  }
@@ -14,7 +14,4 @@ import { solve2Linear } from '../utils/solve2Linear.js'
14
14
  * @return {Vec2} the point of intersection
15
15
  * @alias module:modeling/maths/line2.intersectPointOfLines
16
16
  */
17
- export const intersectPointOfLines = (line1, line2) => {
18
- const point = solve2Linear(line1[0], line1[1], line2[0], line2[1], line1[2], line2[2])
19
- return vec2.clone(point)
20
- }
17
+ export const intersectPointOfLines = (line1, line2) => solve2Linear(line1[0], line1[1], line2[0], line2[1], line1[2], line2[2])
@@ -18,12 +18,10 @@ import * as vec3 from '../vec3/index.js'
18
18
  * @alias module:modeling/maths/plane.fromNormalAndPoint
19
19
  */
20
20
  export const fromNormalAndPoint = (out, normal, point) => {
21
- const u = vec3.normalize(vec3.create(), normal)
22
- const w = vec3.dot(point, u)
21
+ // normalize to out
22
+ vec3.normalize(out, normal)
23
+ // calculate distance
24
+ out[3] = vec3.dot(point, out)
23
25
 
24
- out[0] = u[0]
25
- out[1] = u[1]
26
- out[2] = u[2]
27
- out[3] = w
28
26
  return out
29
27
  }
@@ -1,10 +1,13 @@
1
1
  import * as vec3 from '../vec3/index.js'
2
2
 
3
+ const ba = vec3.create()
4
+ const ca = vec3.create()
5
+
3
6
  /**
4
7
  * Create a plane from the given points.
5
8
  *
6
9
  * @param {Plane} out - receiving plane
7
- * @param {Array} vertices - points on the plane
10
+ * @param {Array} vertices - list of points on the plane
8
11
  * @returns {Plane} out
9
12
  * @alias module:modeling/maths/plane.fromPoints
10
13
  */
@@ -13,8 +16,6 @@ export const fromPoints = (out, ...vertices) => {
13
16
 
14
17
  // Calculate normal vector for a single vertex
15
18
  // Inline to avoid allocations
16
- const ba = vec3.create()
17
- const ca = vec3.create()
18
19
  const vertexNormal = (index) => {
19
20
  const a = vertices[index]
20
21
  const b = vertices[(index + 1) % len]
@@ -26,18 +27,18 @@ export const fromPoints = (out, ...vertices) => {
26
27
  return ba
27
28
  }
28
29
 
29
- out[0] = 0
30
- out[1] = 0
31
- out[2] = 0
32
30
  if (len === 3) {
33
31
  // optimization for triangles, which are always coplanar
34
32
  vec3.copy(out, vertexNormal(0))
35
33
  } else {
36
34
  // sum of vertex normals
35
+ out[0] = 0
36
+ out[1] = 0
37
+ out[2] = 0
37
38
  vertices.forEach((v, i) => {
38
39
  vec3.add(out, out, vertexNormal(i))
39
40
  })
40
- // renormalize normal vector
41
+ // normalize sum
41
42
  vec3.normalize(out, out)
42
43
  }
43
44
  out[3] = vec3.dot(out, vertices[0])
@@ -15,26 +15,26 @@ import * as vec3 from '../vec3/index.js'
15
15
  * @alias module:modeling/maths/plane.fromPointsRandom
16
16
  */
17
17
  export const fromPointsRandom = (out, a, b, c) => {
18
- let ba = vec3.subtract(vec3.create(), b, a)
19
- let ca = vec3.subtract(vec3.create(), c, a)
18
+ const ba = vec3.subtract(vec3.create(), b, a)
19
+ const ca = vec3.subtract(vec3.create(), c, a)
20
20
  if (vec3.length(ba) < EPS) {
21
- ba = vec3.orthogonal(ba, ca)
21
+ vec3.orthogonal(ba, ca)
22
22
  }
23
23
  if (vec3.length(ca) < EPS) {
24
- ca = vec3.orthogonal(ca, ba)
24
+ vec3.orthogonal(ca, ba)
25
25
  }
26
- let normal = vec3.cross(vec3.create(), ba, ca)
26
+
27
+ // calculate plane normal
28
+ const normal = vec3.cross(out, ba, ca)
27
29
  if (vec3.length(normal) < EPS) {
28
30
  // this would mean that ba == ca.negated()
29
- ca = vec3.orthogonal(ca, ba)
30
- normal = vec3.cross(normal, ba, ca)
31
+ vec3.orthogonal(ca, ba)
32
+ vec3.cross(normal, ba, ca)
31
33
  }
32
- normal = vec3.normalize(normal, normal)
33
- const w = vec3.dot(normal, a)
34
+ vec3.normalize(normal, normal)
35
+
36
+ // and distance
37
+ out[3] = vec3.dot(normal, a)
34
38
 
35
- out[0] = normal[0]
36
- out[1] = normal[1]
37
- out[2] = normal[2]
38
- out[3] = w
39
39
  return out
40
40
  }
@@ -3,6 +3,8 @@ import { flatten } from '../utils/flatten.js'
3
3
  import * as geom2 from '../geometries/geom2/index.js'
4
4
  import * as geom3 from '../geometries/geom3/index.js'
5
5
  import * as path2 from '../geometries/path2/index.js'
6
+ import * as path3 from '../geometries/path3/index.js'
7
+ import * as slice from '../geometries/slice/index.js'
6
8
 
7
9
  import { measureAggregateBoundingBox } from './measureAggregateBoundingBox.js'
8
10
  import { calculateEpsilonFromBounds } from './calculateEpsilonFromBounds.js'
@@ -23,7 +25,7 @@ export const measureAggregateEpsilon = (...geometries) => {
23
25
  let dimensions = 0
24
26
  dimensions = geometries.reduce((dimensions, geometry) => {
25
27
  if (path2.isA(geometry) || geom2.isA(geometry)) return Math.max(dimensions, 2)
26
- if (geom3.isA(geometry)) return Math.max(dimensions, 3)
28
+ if (geom3.isA(geometry || path3.isA(geometry) || slice.isA(geometry))) return Math.max(dimensions, 3)
27
29
  return 0
28
30
  }, dimensions)
29
31
  return calculateEpsilonFromBounds(bounds, dimensions)
@@ -13,7 +13,7 @@ test('measureAggregateEpsilon (single objects)', (t) => {
13
13
  t.is(calculatedEpsilon, expectedEpsilon)
14
14
  })
15
15
 
16
- test('measureAggregateEpsilon (multiple objects)', (t) => {
16
+ test('measureAggregateEpsilon (multiple 3D objects)', (t) => {
17
17
  const highCube = cube({ size: 4, center: [-40, 100, 20] })
18
18
  const lowCube = cube({ size: 60, center: [20, -10, 20] })
19
19
  const calculatedEpsilon = measureAggregateEpsilon(highCube, lowCube)
@@ -3,6 +3,7 @@ import { flatten } from '../utils/flatten.js'
3
3
  import * as geom2 from '../geometries/geom2/index.js'
4
4
  import * as geom3 from '../geometries/geom3/index.js'
5
5
  import * as path2 from '../geometries/path2/index.js'
6
+ import * as path3 from '../geometries/path3/index.js'
6
7
  import * as poly3 from '../geometries/poly3/index.js'
7
8
  import * as slice from '../geometries/slice/index.js'
8
9
 
@@ -12,10 +13,10 @@ const cache = new WeakMap()
12
13
  * Measure the area of the given geometry.
13
14
  * NOTE: paths are infinitely narrow and do not have an area
14
15
  *
15
- * @param {Path2} geometry - geometry to measure
16
+ * @param {Path2|Path3} geometry - geometry to measure
16
17
  * @returns {number} area of the geometry
17
18
  */
18
- const measureAreaOfPath2 = () => 0
19
+ const measureAreaOfPath = () => 0
19
20
 
20
21
  /*
21
22
  * Measure the area of the given geometry.
@@ -87,9 +88,10 @@ export const measureArea = (...geometries) => {
87
88
  geometries = flatten(geometries)
88
89
 
89
90
  const results = geometries.map((geometry) => {
90
- if (path2.isA(geometry)) return measureAreaOfPath2(geometry)
91
- if (geom2.isA(geometry)) return measureAreaOfGeom2(geometry)
92
91
  if (geom3.isA(geometry)) return measureAreaOfGeom3(geometry)
92
+ if (geom2.isA(geometry)) return measureAreaOfGeom2(geometry)
93
+ if (path2.isA(geometry)) return measureAreaOfPath(geometry)
94
+ if (path3.isA(geometry)) return measureAreaOfPath(geometry)
93
95
  if (slice.isA(geometry)) return measureAreaOfSlice(geometry)
94
96
  return 0
95
97
  })
@@ -1,6 +1,6 @@
1
1
  import test from 'ava'
2
2
 
3
- import { geom2, geom3, path2, slice } from '../geometries/index.js'
3
+ import { geom2, geom3, path2, path3, slice } from '../geometries/index.js'
4
4
 
5
5
  import { line, rectangle, cuboid } from '../primitives/index.js'
6
6
 
@@ -12,6 +12,7 @@ test('measureArea: single objects', (t) => {
12
12
  const acube = cuboid()
13
13
 
14
14
  const apath2 = path2.create()
15
+ const apath3 = path3.create()
15
16
  const ageom2 = geom2.create()
16
17
  const ageom3 = geom3.create()
17
18
  const aslice = slice.create()
@@ -25,6 +26,7 @@ test('measureArea: single objects', (t) => {
25
26
  const carea = measureArea(acube)
26
27
 
27
28
  const p2area = measureArea(apath2)
29
+ const p3area = measureArea(apath3)
28
30
  const g2area = measureArea(ageom2)
29
31
  const g3area = measureArea(ageom3)
30
32
  const slarea = measureArea(aslice)
@@ -38,6 +40,7 @@ test('measureArea: single objects', (t) => {
38
40
  t.is(carea, 24) // 2x2x6
39
41
 
40
42
  t.is(p2area, 0)
43
+ t.is(p3area, 0)
41
44
  t.is(g2area, 0)
42
45
  t.is(g3area, 0)
43
46
  t.is(slarea, 0)
@@ -6,6 +6,7 @@ import * as vec3 from '../maths/vec3/index.js'
6
6
  import * as geom2 from '../geometries/geom2/index.js'
7
7
  import * as geom3 from '../geometries/geom3/index.js'
8
8
  import * as path2 from '../geometries/path2/index.js'
9
+ import * as path3 from '../geometries/path3/index.js'
9
10
  import * as poly3 from '../geometries/poly3/index.js'
10
11
  import * as slice from '../geometries/slice/index.js'
11
12
 
@@ -70,6 +71,18 @@ const measureBoundingBoxOfPath2 = (geometry) => {
70
71
  return boundingBox
71
72
  }
72
73
 
74
+ /*
75
+ * Measure the min and max bounds of the given (path3) geometry.
76
+ * @return {Array[]} the min and max bounds for the geometry
77
+ */
78
+ const measureBoundingBoxOfPath3 = (geometry) => {
79
+ const boundingBox = []
80
+ path3.toVertices(geometry).forEach((vertice) => {
81
+ expand3(boundingBox, vertice)
82
+ })
83
+ return boundingBox
84
+ }
85
+
73
86
  /*
74
87
  * Measure the min and max bounds of the given (geom2) geometry.
75
88
  * @return {Array[]} the min and max bounds for the geometry
@@ -123,9 +136,10 @@ export const measureBoundingBox = (...geometries) => {
123
136
  geometries = flatten(geometries)
124
137
 
125
138
  const results = geometries.map((geometry) => {
126
- if (path2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfPath2)
127
- if (geom2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfGeom2)
128
139
  if (geom3.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfGeom3)
140
+ if (geom2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfGeom2)
141
+ if (path2.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfPath2)
142
+ if (path3.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfPath3)
129
143
  if (slice.isA(geometry)) return measureCached(geometry, measureBoundingBoxOfSlice)
130
144
  return [[0, 0, 0], [0, 0, 0]]
131
145
  })
@@ -1,6 +1,6 @@
1
1
  import test from 'ava'
2
2
 
3
- import { geom2, geom3, path2, slice } from '../geometries/index.js'
3
+ import { geom2, geom3, path2, path3, slice } from '../geometries/index.js'
4
4
 
5
5
  import { line, rectangle, cuboid } from '../primitives/index.js'
6
6
 
@@ -14,6 +14,7 @@ test('measureBoundingBox (single objects)', (t) => {
14
14
  const acube = cuboid()
15
15
 
16
16
  const apath2 = path2.create()
17
+ const apath3 = path3.create()
17
18
  const ageom2 = geom2.create()
18
19
  const ageom3 = geom3.create()
19
20
  const aslice = slice.create()
@@ -27,6 +28,7 @@ test('measureBoundingBox (single objects)', (t) => {
27
28
  const cbounds = measureBoundingBox(acube)
28
29
 
29
30
  const p2bounds = measureBoundingBox(apath2)
31
+ const p3bounds = measureBoundingBox(apath3)
30
32
  const g2bounds = measureBoundingBox(ageom2)
31
33
  const g3bounds = measureBoundingBox(ageom3)
32
34
  const slbounds = measureBoundingBox(aslice)
@@ -40,6 +42,7 @@ test('measureBoundingBox (single objects)', (t) => {
40
42
  t.deepEqual(cbounds, [[-1, -1, -1], [1, 1, 1]])
41
43
 
42
44
  t.deepEqual(p2bounds, [[0, 0, 0], [0, 0, 0]])
45
+ t.deepEqual(p3bounds, [[0, 0, 0], [0, 0, 0]])
43
46
  t.deepEqual(g2bounds, [[0, 0, 0], [0, 0, 0]])
44
47
  t.deepEqual(g3bounds, [[0, 0, 0], [0, 0, 0]])
45
48
  t.deepEqual(slbounds, [[0, 0, 0], [0, 0, 0]])
@@ -6,6 +6,7 @@ import * as vec3 from '../maths/vec3/index.js'
6
6
  import * as geom2 from '../geometries/geom2/index.js'
7
7
  import * as geom3 from '../geometries/geom3/index.js'
8
8
  import * as path2 from '../geometries/path2/index.js'
9
+ import * as path3 from '../geometries/path3/index.js'
9
10
  import * as poly3 from '../geometries/poly3/index.js'
10
11
  import * as slice from '../geometries/slice/index.js'
11
12
 
@@ -60,6 +61,33 @@ const measureBoundingSphereOfPoints = (points) => {
60
61
  return [centroid, radius]
61
62
  }
62
63
 
64
+ /*
65
+ * Measure the bounding sphere of the given 2D points.
66
+ * @return {[[x, y, z], radius]} the bounding sphere for the points
67
+ */
68
+ const measureBoundingSphereOfVertices = (vertices) => {
69
+ const centroid = vec3.create()
70
+ let radius = 0
71
+
72
+ if (vertices.length > 0) {
73
+ // calculate the centroid of the vertices
74
+ let numVertices = 0
75
+ vertices.forEach((vertex) => {
76
+ vec3.add(centroid, centroid, vertex)
77
+ numVertices++
78
+ })
79
+ vec3.scale(centroid, centroid, 1 / numVertices)
80
+
81
+ // find the farthest vertex from the centroid
82
+ vertices.forEach((vertex) => {
83
+ radius = Math.max(radius, vec3.squaredDistance(centroid, vertex))
84
+ })
85
+ radius = Math.sqrt(radius)
86
+ }
87
+
88
+ return [centroid, radius]
89
+ }
90
+
63
91
  /*
64
92
  * Measure the bounding sphere of the given (path2) geometry.
65
93
  * @return {[[x, y, z], radius]} the bounding sphere for the geometry
@@ -106,36 +134,16 @@ const measureBoundingSphereOfGeom3 = (geometry) => {
106
134
  }
107
135
 
108
136
  /*
109
- * Measure the bounding sphere of the given (geom3) geometry.
137
+ * Measure the bounding sphere of the given (slice) geometry.
110
138
  * @return {[[x, y, z], radius]} the bounding sphere for the geometry
111
139
  */
112
- const measureBoundingSphereOfSlice = (geometry) => {
113
- const centroid = vec3.create()
114
- let radius = 0
115
- let numVertices = 0
140
+ const measureBoundingSphereOfPath3 = (geometry) => measureBoundingSphereOfVertices(path3.toVertices(geometry))
116
141
 
117
- // calculate the centroid of the geometry
118
- geometry.contours.forEach((contour) => {
119
- contour.forEach((vertex) => {
120
- vec3.add(centroid, centroid, vertex)
121
- numVertices++
122
- })
123
- })
124
-
125
- if (numVertices > 0) {
126
- vec3.scale(centroid, centroid, 1 / numVertices)
127
-
128
- // find the farthest vertex from the centroid
129
- geometry.contours.forEach((contour) => {
130
- contour.forEach((vertex) => {
131
- radius = Math.max(radius, vec3.squaredDistance(centroid, vertex))
132
- })
133
- })
134
- radius = Math.sqrt(radius)
135
- }
136
-
137
- return [centroid, radius]
138
- }
142
+ /*
143
+ * Measure the bounding sphere of the given (slice) geometry.
144
+ * @return {[[x, y, z], radius]} the bounding sphere for the geometry
145
+ */
146
+ const measureBoundingSphereOfSlice = (geometry) => measureBoundingSphereOfVertices(slice.toVertices(geometry))
139
147
 
140
148
  /**
141
149
  * Measure the (approximate) bounding sphere of the given geometries.
@@ -151,9 +159,10 @@ export const measureBoundingSphere = (...geometries) => {
151
159
  geometries = flatten(geometries)
152
160
 
153
161
  const results = geometries.map((geometry) => {
154
- if (path2.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfPath2)
155
- if (geom2.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfGeom2)
156
162
  if (geom3.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfGeom3)
163
+ if (geom2.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfGeom2)
164
+ if (path2.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfPath2)
165
+ if (path3.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfPath3)
157
166
  if (slice.isA(geometry)) return measureCached(geometry, measureBoundingSphereOfSlice)
158
167
  return [[0, 0, 0], 0]
159
168
  })
@@ -1,6 +1,6 @@
1
1
  import test from 'ava'
2
2
 
3
- import { geom2, geom3, path2, slice } from '../geometries/index.js'
3
+ import { geom2, geom3, path2, path3, slice } from '../geometries/index.js'
4
4
 
5
5
  import { line, rectangle, ellipsoid } from '../primitives/index.js'
6
6
 
@@ -12,6 +12,7 @@ test('measureBoundingSphere (single objects)', (t) => {
12
12
  const aellipsoid = ellipsoid({ radius: [5, 10, 15], center: [5, 5, 5] })
13
13
 
14
14
  const apath2 = path2.create()
15
+ const apath3 = path3.create()
15
16
  const ageom2 = geom2.create()
16
17
  const ageom3 = geom3.create()
17
18
  const aslice = slice.create()
@@ -25,6 +26,7 @@ test('measureBoundingSphere (single objects)', (t) => {
25
26
  const cbounds = measureBoundingSphere(aellipsoid)
26
27
 
27
28
  const p2bounds = measureBoundingSphere(apath2)
29
+ const p3bounds = measureBoundingSphere(apath3)
28
30
  const g2bounds = measureBoundingSphere(ageom2)
29
31
  const g3bounds = measureBoundingSphere(ageom3)
30
32
  const slbounds = measureBoundingSphere(aslice)
@@ -38,6 +40,7 @@ test('measureBoundingSphere (single objects)', (t) => {
38
40
  t.deepEqual(cbounds, [[5.000000000000018, 4.999999999999983, 5.000000000000001], 15])
39
41
 
40
42
  t.deepEqual(p2bounds, [[0, 0, 0], 0])
43
+ t.deepEqual(p3bounds, [[0, 0, 0], 0])
41
44
  t.deepEqual(g2bounds, [[0, 0, 0], 0])
42
45
  t.deepEqual(g3bounds, [[0, 0, 0], 0])
43
46
  t.deepEqual(slbounds, [[0, 0, 0], 0])
@@ -95,9 +95,10 @@ export const measureCenterOfMass = (...geometries) => {
95
95
  geometries = flatten(geometries)
96
96
 
97
97
  const results = geometries.map((geometry) => {
98
- // NOTE: center of mass for geometry path2 is not possible
99
- if (geom2.isA(geometry)) return measureCenterOfMassGeom2(geometry)
100
98
  if (geom3.isA(geometry)) return measureCenterOfMassGeom3(geometry)
99
+ if (geom2.isA(geometry)) return measureCenterOfMassGeom2(geometry)
100
+ // TODO if (slice.isA(geometry)) return measureCenterOfMassSlice(geometry)
101
+ // NOTE: center of mass for geometry path2 and path3 is not possible
101
102
  return [0, 0, 0]
102
103
  })
103
104
  return results.length === 1 ? results[0] : results
@@ -3,6 +3,7 @@ import { flatten } from '../utils/flatten.js'
3
3
  import * as geom2 from '../geometries/geom2/index.js'
4
4
  import * as geom3 from '../geometries/geom3/index.js'
5
5
  import * as path2 from '../geometries/path2/index.js'
6
+ import * as path3 from '../geometries/path3/index.js'
6
7
  import * as slice from '../geometries/slice/index.js'
7
8
 
8
9
  import { calculateEpsilonFromBounds } from './calculateEpsilonFromBounds.js'
@@ -22,9 +23,10 @@ export const measureEpsilon = (...geometries) => {
22
23
  geometries = flatten(geometries)
23
24
 
24
25
  const results = geometries.map((geometry) => {
25
- if (path2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
26
- if (geom2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
27
26
  if (geom3.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
27
+ if (geom2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
28
+ if (path2.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
29
+ if (path3.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
28
30
  if (slice.isA(geometry)) return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
29
31
  return 0
30
32
  })
@@ -70,6 +70,8 @@ const inResult = (event, operation) => {
70
70
  (!event.isSubject && !event.otherInOut)
71
71
  case XOR:
72
72
  return true
73
+ default:
74
+ throw new Error('computeFields01')
73
75
  }
74
76
  break
75
77
  case SAME_TRANSITION:
@@ -78,6 +80,8 @@ const inResult = (event, operation) => {
78
80
  return operation === DIFFERENCE
79
81
  case NON_CONTRIBUTING:
80
82
  return false
83
+ default:
84
+ throw new Error('computeFields02')
81
85
  }
82
86
  return false
83
87
  }
@@ -104,6 +108,8 @@ const determineResultTransition = (event, operation) => {
104
108
  isIn = thatIn && !thisIn
105
109
  }
106
110
  break
111
+ default:
112
+ throw new Error('computeFields03')
107
113
  }
108
114
  return isIn ? +1 : -1
109
115
  }
@@ -12,7 +12,8 @@ export const divideSegment = (segment, point, queue) => {
12
12
  const r = new SweepEvent(point, false, segment, segment.isSubject)
13
13
  const l = new SweepEvent(point, true, segment.otherEvent, segment.isSubject)
14
14
 
15
- r.contourId = l.contourId = segment.contourId
15
+ r.contourId = segment.contourId
16
+ l.contourId = segment.contourId
16
17
 
17
18
  // avoid a rounding error. The left event would be processed after the right event
18
19
  if (compareEvents(l, segment.otherEvent) > 0) {
@@ -25,7 +25,9 @@ const processPolygon = (contourOrHole, isSubject, ringId, queue, bbox, isExterio
25
25
  continue // skip collapsed edges, or it breaks
26
26
  }
27
27
 
28
- e1.contourId = e2.contourId = ringId
28
+ e1.contourId = ringId
29
+ e2.contourId = ringId
30
+
29
31
  if (!isExteriorRing) {
30
32
  e1.isExteriorRing = false
31
33
  e2.isExteriorRing = false
@@ -32,14 +32,14 @@ export const subdivideSegments = (eventQueue, subject, clipping, sbbox, cbbox, o
32
32
  }
33
33
 
34
34
  if (event.left) {
35
- next = prev = sweepLine.insert(event)
35
+ prev = sweepLine.insert(event)
36
36
  begin = sweepLine.minNode()
37
37
 
38
+ next = sweepLine.next(prev)
39
+
38
40
  if (prev !== begin) prev = sweepLine.prev(prev)
39
41
  else prev = null
40
42
 
41
- next = sweepLine.next(next)
42
-
43
43
  const prevEvent = prev ? prev.key : null
44
44
  let prevprevEvent
45
45
  computeFields(event, prevEvent, operation)
@@ -62,7 +62,8 @@ export const subdivideSegments = (eventQueue, subject, clipping, sbbox, cbbox, o
62
62
  }
63
63
  }
64
64
  } else {
65
- next = prev = sweepLine.find(event.otherEvent)
65
+ prev = sweepLine.find(event.otherEvent)
66
+ next = prev
66
67
 
67
68
  if (prev && next) {
68
69
  // FIXME is this correct? begin is assigned if event.left, not every iterration
@@ -13,7 +13,7 @@ import { mayOverlap } from './mayOverlap.js'
13
13
  */
14
14
  export const subtractGeom3Sub = (geometry1, geometry2) => {
15
15
  if (!mayOverlap(geometry1, geometry2)) {
16
- return geom3.clone(geometry1)
16
+ return geometry1
17
17
  }
18
18
 
19
19
  const a = new Tree(geom3.toPolygons(geometry1))
@@ -215,6 +215,9 @@ export class PolygonTreeNode {
215
215
  backNodes.push(backNode)
216
216
  }
217
217
  break
218
+
219
+ default:
220
+ throw new Error('PolygonTreeNode05')
218
221
  }
219
222
  }
220
223