@jscad/modeling 2.9.1 → 2.9.4

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 (72) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +4 -4
  3. package/dist/jscad-modeling.min.js +414 -420
  4. package/package.json +3 -2
  5. package/src/colors/colorize.test.js +1 -1
  6. package/src/geometries/geom2/toOutlines.js +66 -52
  7. package/src/geometries/geom3/applyTransforms.js +1 -2
  8. package/src/geometries/geom3/create.js +1 -1
  9. package/src/geometries/geom3/create.test.js +1 -1
  10. package/src/geometries/geom3/fromPoints.js +1 -1
  11. package/src/geometries/path2/index.d.ts +0 -1
  12. package/src/geometries/path2/index.js +0 -1
  13. package/src/geometries/poly3/create.js +1 -1
  14. package/src/geometries/poly3/validate.js +14 -0
  15. package/src/maths/constants.d.ts +1 -0
  16. package/src/maths/constants.js +11 -0
  17. package/src/maths/mat4/isMirroring.js +11 -11
  18. package/src/maths/utils/aboutEqualNormals.js +1 -5
  19. package/src/operations/booleans/intersectGeom2.test.js +69 -0
  20. package/src/operations/booleans/intersectGeom3.js +2 -1
  21. package/src/operations/booleans/{intersect.test.js → intersectGeom3.test.js} +3 -71
  22. package/src/operations/booleans/subtractGeom2.test.js +72 -0
  23. package/src/operations/booleans/subtractGeom3.js +2 -1
  24. package/src/operations/booleans/{subtract.test.js → subtractGeom3.test.js} +3 -74
  25. package/src/operations/booleans/to3DWalls.js +1 -1
  26. package/src/operations/booleans/unionGeom2.test.js +166 -0
  27. package/src/operations/booleans/unionGeom3.js +2 -1
  28. package/src/operations/booleans/{union.test.js → unionGeom3.test.js} +3 -168
  29. package/src/operations/expansions/expandGeom3.test.js +14 -14
  30. package/src/operations/expansions/expandShell.js +5 -4
  31. package/src/operations/expansions/extrudePolygon.js +7 -7
  32. package/src/operations/extrusions/extrudeFromSlices.js +3 -2
  33. package/src/operations/extrusions/extrudeFromSlices.test.js +2 -2
  34. package/src/operations/extrusions/extrudeRotate.js +5 -1
  35. package/src/operations/extrusions/extrudeRotate.test.js +5 -5
  36. package/src/operations/extrusions/extrudeWalls.js +2 -2
  37. package/src/operations/extrusions/project.js +11 -14
  38. package/src/operations/extrusions/project.test.js +50 -50
  39. package/src/operations/extrusions/slice/repair.js +62 -0
  40. package/src/operations/hulls/hullChain.test.js +4 -4
  41. package/src/operations/hulls/hullGeom2.js +6 -18
  42. package/src/operations/hulls/hullGeom3.js +5 -18
  43. package/src/operations/hulls/hullPath2.js +4 -14
  44. package/src/operations/hulls/hullPoints2.js +43 -92
  45. package/src/operations/hulls/toUniquePoints.js +34 -0
  46. package/src/operations/modifiers/generalize.js +2 -13
  47. package/src/operations/modifiers/generalize.test.js +0 -32
  48. package/src/operations/modifiers/insertTjunctions.js +1 -1
  49. package/src/operations/modifiers/insertTjunctions.test.js +21 -21
  50. package/src/operations/modifiers/mergePolygons.js +11 -14
  51. package/src/operations/{booleans → modifiers}/reTesselateCoplanarPolygons.js +33 -32
  52. package/src/operations/{booleans → modifiers}/reTesselateCoplanarPolygons.test.js +5 -5
  53. package/src/operations/{booleans → modifiers}/retessellate.js +2 -9
  54. package/src/operations/{booleans → modifiers}/retessellate.test.js +0 -0
  55. package/src/operations/modifiers/snapPolygons.test.js +12 -12
  56. package/src/operations/modifiers/triangulatePolygons.js +3 -3
  57. package/src/operations/transforms/center.js +1 -1
  58. package/src/primitives/cuboid.js +1 -1
  59. package/src/primitives/cylinderElliptic.js +1 -1
  60. package/src/primitives/ellipsoid.js +2 -2
  61. package/src/primitives/polyhedron.js +1 -1
  62. package/src/primitives/roundedCuboid.js +6 -6
  63. package/src/primitives/roundedCylinder.js +1 -1
  64. package/src/primitives/torus.test.js +6 -6
  65. package/src/primitives/triangle.js +1 -2
  66. package/src/utils/trigonometry.js +1 -2
  67. package/src/geometries/path2/eachPoint.d.ts +0 -9
  68. package/src/geometries/path2/eachPoint.js +0 -17
  69. package/src/geometries/path2/eachPoint.test.js +0 -11
  70. package/src/operations/extrusions/slice/repairSlice.js +0 -47
  71. package/src/operations/modifiers/edges.js +0 -195
  72. package/src/operations/modifiers/repairTjunctions.js +0 -44
@@ -104,38 +104,6 @@ test('generalize: generalize of a geom3 produces an expected geom3', (t) => {
104
104
  ]
105
105
  t.notThrows(() => geom3.validate(result))
106
106
  t.true(comparePolygonsAsPoints(pts, exp))
107
-
108
- // apply repairs only (triangles)
109
- result = generalize({ repair: true }, geometry2)
110
- pts = geom3.toPoints(result)
111
- exp = [
112
- [[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, -0.7853981633974483, 3.141592653589793],
113
- [-1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
114
- [[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, 0.7853981633974483, 3.141592653589793],
115
- [-1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
116
- [[1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, -3.141592653589793],
117
- [1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
118
- [[1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
119
- [1.5707963267948966, -0.7853981633974483, 3.141592653589793]],
120
- [[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, -0.7853981633974483, -3.141592653589793],
121
- [1.5707963267948966, -0.7853981633974483, 3.141592653589793]],
122
- [[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, -0.7853981633974483, 3.141592653589793],
123
- [-1.5707963267948966, -0.7853981633974483, 3.141592653589793]],
124
- [[-1.5707963267948966, 0.7853981633974483, -3.141592653589793], [-1.5707963267948966, 0.7853981633974483, 3.141592653589793],
125
- [1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
126
- [[-1.5707963267948966, 0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
127
- [1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
128
- [[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [-1.5707963267948966, 0.7853981633974483, -3.141592653589793],
129
- [1.5707963267948966, 0.7853981633974483, -3.141592653589793]],
130
- [[-1.5707963267948966, -0.7853981633974483, -3.141592653589793], [1.5707963267948966, 0.7853981633974483, -3.141592653589793],
131
- [1.5707963267948966, -0.7853981633974483, -3.141592653589793]],
132
- [[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, -0.7853981633974483, 3.141592653589793],
133
- [1.5707963267948966, 0.7853981633974483, 3.141592653589793]],
134
- [[-1.5707963267948966, -0.7853981633974483, 3.141592653589793], [1.5707963267948966, 0.7853981633974483, 3.141592653589793],
135
- [-1.5707963267948966, 0.7853981633974483, 3.141592653589793]]
136
- ]
137
- t.notThrows(() => geom3.validate(result))
138
- t.true(comparePolygonsAsPoints(pts, exp))
139
107
  })
140
108
 
141
109
  test('generalize: generalize of a geom3 with T junctions produces an expected geom3', (t) => {
@@ -259,7 +259,7 @@ const insertTjunctions = (polygons) => {
259
259
  // split the side by inserting the vertex:
260
260
  const newvertices = polygon.vertices.slice(0)
261
261
  newvertices.splice(insertionvertextagindex, 0, endvertex)
262
- const newpolygon = poly3.fromPoints(newvertices)
262
+ const newpolygon = poly3.create(newvertices)
263
263
 
264
264
  newpolygons[polygonindex] = newpolygon
265
265
 
@@ -56,33 +56,33 @@ test('insertTjunctions: insertTjunctions produces expected polygons', (t) => {
56
56
 
57
57
  const result3 = insertTjunctions(geom3.toPolygons(geometry3))
58
58
  let exp = [
59
- poly3.fromPoints([[-1, -1, -1], [-1, -1, 1], [-1, 1, 1], [-1, 1, -1]]),
60
- poly3.fromPoints([[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, -1, 1]]),
61
- poly3.fromPoints([[-1, -1, -1], [1, -1, -1], [1, -1, 1], [-1, -1, 1]]),
62
- poly3.fromPoints([[-1, 1, -1], [-1, 1, 1], [1, 1, 1], [1, 1, -1]]),
63
- poly3.fromPoints([[-1, -1, -1], [-1, 1, -1], [1, 1, -1], [1, -1, -1]]),
64
- poly3.fromPoints([[0, 0, 1], [-1, -1, 1], [1, -1, 1], [1, 1, 1]]),
65
- poly3.fromPoints([[1, 1, 1], [-1, 1, 1], [0, 0, 1]]),
66
- poly3.fromPoints([[-1, 1, 1], [-1, -1, 1], [0, 0, 1]])
59
+ poly3.create([[-1, -1, -1], [-1, -1, 1], [-1, 1, 1], [-1, 1, -1]]),
60
+ poly3.create([[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, -1, 1]]),
61
+ poly3.create([[-1, -1, -1], [1, -1, -1], [1, -1, 1], [-1, -1, 1]]),
62
+ poly3.create([[-1, 1, -1], [-1, 1, 1], [1, 1, 1], [1, 1, -1]]),
63
+ poly3.create([[-1, -1, -1], [-1, 1, -1], [1, 1, -1], [1, -1, -1]]),
64
+ poly3.create([[0, 0, 1], [-1, -1, 1], [1, -1, 1], [1, 1, 1]]),
65
+ poly3.create([[1, 1, 1], [-1, 1, 1], [0, 0, 1]]),
66
+ poly3.create([[-1, 1, 1], [-1, -1, 1], [0, 0, 1]])
67
67
  ]
68
68
  t.not(result3, geom3.toPolygons(geometry3))
69
69
  t.true(comparePolygonLists(result3, exp))
70
70
 
71
71
  const result4 = insertTjunctions(geom3.toPolygons(geometry4))
72
72
  exp = [
73
- poly3.fromPoints([[-1, -1, -1], [-1, -1, 1], [-1, 0, 1], [-1, 1, 1], [-1, 1, -1]]),
74
- poly3.fromPoints([[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, 0, 1], [1, -1, 1]]),
75
- poly3.fromPoints([[-1, -1, -1], [1, -1, -1], [1, -1, 1], [0, -1, 1], [-1, -1, 1]]),
76
- poly3.fromPoints([[-1, 1, -1], [-1, 1, 1], [0, 1, 1], [1, 1, 1], [1, 1, -1]]),
77
- poly3.fromPoints([[-1, -1, -1], [-1, 1, -1], [1, 1, -1], [1, -1, -1]]),
78
- poly3.fromPoints([[-1, -1, 1], [0, -1, 1], [0, 0, 1]]),
79
- poly3.fromPoints([[-1, 0, 1], [-1, -1, 1], [0, 0, 1]]),
80
- poly3.fromPoints([[0, -1, 1], [1, -1, 1], [0, 0, 1]]),
81
- poly3.fromPoints([[1, -1, 1], [1, 0, 1], [0, 0, 1]]),
82
- poly3.fromPoints([[1, 0, 1], [1, 1, 1], [0, 0, 1]]),
83
- poly3.fromPoints([[1, 1, 1], [0, 1, 1], [0, 0, 1]]),
84
- poly3.fromPoints([[0, 1, 1], [-1, 1, 1], [0, 0, 1]]),
85
- poly3.fromPoints([[-1, 1, 1], [-1, 0, 1], [0, 0, 1]])
73
+ poly3.create([[-1, -1, -1], [-1, -1, 1], [-1, 0, 1], [-1, 1, 1], [-1, 1, -1]]),
74
+ poly3.create([[1, -1, -1], [1, 1, -1], [1, 1, 1], [1, 0, 1], [1, -1, 1]]),
75
+ poly3.create([[-1, -1, -1], [1, -1, -1], [1, -1, 1], [0, -1, 1], [-1, -1, 1]]),
76
+ poly3.create([[-1, 1, -1], [-1, 1, 1], [0, 1, 1], [1, 1, 1], [1, 1, -1]]),
77
+ poly3.create([[-1, -1, -1], [-1, 1, -1], [1, 1, -1], [1, -1, -1]]),
78
+ poly3.create([[-1, -1, 1], [0, -1, 1], [0, 0, 1]]),
79
+ poly3.create([[-1, 0, 1], [-1, -1, 1], [0, 0, 1]]),
80
+ poly3.create([[0, -1, 1], [1, -1, 1], [0, 0, 1]]),
81
+ poly3.create([[1, -1, 1], [1, 0, 1], [0, 0, 1]]),
82
+ poly3.create([[1, 0, 1], [1, 1, 1], [0, 0, 1]]),
83
+ poly3.create([[1, 1, 1], [0, 1, 1], [0, 0, 1]]),
84
+ poly3.create([[0, 1, 1], [-1, 1, 1], [0, 0, 1]]),
85
+ poly3.create([[-1, 1, 1], [-1, 0, 1], [0, 0, 1]])
86
86
  ]
87
87
  t.not(result4, geometry4)
88
88
  t.true(comparePolygonLists(result4, exp))
@@ -1,3 +1,4 @@
1
+ const aboutEqualNormals = require('../../maths/utils/aboutEqualNormals')
1
2
  const vec3 = require('../../maths/vec3')
2
3
 
3
4
  const poly3 = require('../../geometries/poly3')
@@ -53,9 +54,12 @@ const calculateAnglesBetween = (current, opposite, normal) => {
53
54
  return [angle1, angle2]
54
55
  }
55
56
 
57
+ const v1 = vec3.create()
58
+ const v2 = vec3.create()
59
+
56
60
  const calculateAngle = (prevpoint, point, nextpoint, normal) => {
57
- const d0 = vec3.subtract(vec3.create(), point, prevpoint)
58
- const d1 = vec3.subtract(vec3.create(), nextpoint, point)
61
+ const d0 = vec3.subtract(v1, point, prevpoint)
62
+ const d1 = vec3.subtract(v2, nextpoint, point)
59
63
  vec3.cross(d0, d0, d1)
60
64
  return vec3.dot(d0, normal)
61
65
  }
@@ -76,7 +80,7 @@ const createPolygonAnd = (edge) => {
76
80
 
77
81
  edge = next
78
82
  }
79
- if (points.length > 0) polygon = poly3.fromPoints(points)
83
+ if (points.length > 0) polygon = poly3.create(points)
80
84
  return polygon
81
85
  }
82
86
 
@@ -85,7 +89,7 @@ const createPolygonAnd = (edge) => {
85
89
  * @param {poly3[]} sourcepolygons - list of polygons
86
90
  * @returns {poly3[]} new set of polygons
87
91
  */
88
- const mergeCoplanarPolygons = (epsilon, sourcepolygons) => {
92
+ const mergeCoplanarPolygons = (sourcepolygons) => {
89
93
  if (sourcepolygons.length < 2) return sourcepolygons
90
94
 
91
95
  const normal = sourcepolygons[0].plane
@@ -167,18 +171,11 @@ const mergeCoplanarPolygons = (epsilon, sourcepolygons) => {
167
171
  if (polygon) destpolygons.push(polygon)
168
172
  })
169
173
 
174
+ edgeList.clear()
175
+
170
176
  return destpolygons
171
177
  }
172
178
 
173
- // Normals are directional vectors with component values from 0 to 1.0, requiring specialized comparision
174
- // This EPS is derived from a serieas of tests to determine the optimal precision for comparing coplanar polygons,
175
- // as provided by the sphere primitive at high segmentation
176
- // This EPS is for 64 bit Number values
177
- const NEPS = 1e-13
178
-
179
- // Compare two normals (unit vectors) for equality.
180
- const aboutEqualNormals = (a, b) => (Math.abs(a[0] - b[0]) <= NEPS && Math.abs(a[1] - b[1]) <= NEPS && Math.abs(a[2] - b[2]) <= NEPS)
181
-
182
179
  const coplanar = (plane1, plane2) => {
183
180
  // expect the same distance from the origin, within tolerance
184
181
  if (Math.abs(plane1[3] - plane2[3]) < 0.00000015) {
@@ -202,7 +199,7 @@ const mergePolygons = (epsilon, polygons) => {
202
199
  let destpolygons = []
203
200
  polygonsPerPlane.forEach((mapping) => {
204
201
  const sourcepolygons = mapping[1]
205
- const retesselayedpolygons = mergeCoplanarPolygons(epsilon, sourcepolygons)
202
+ const retesselayedpolygons = mergeCoplanarPolygons(sourcepolygons)
206
203
  destpolygons = destpolygons.concat(retesselayedpolygons)
207
204
  })
208
205
  return destpolygons
@@ -23,15 +23,14 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
23
23
  const orthobasis = new OrthoNormalBasis(plane)
24
24
  const polygonvertices2d = [] // array of array of Vector2D
25
25
  const polygontopvertexindexes = [] // array of indexes of topmost vertex per polygon
26
- const topy2polygonindexes = {}
27
- const ycoordinatetopolygonindexes = {}
28
-
29
- const ycoordinatebins = {}
26
+ const topy2polygonindexes = new Map()
27
+ const ycoordinatetopolygonindexes = new Map()
30
28
 
31
29
  // convert all polygon vertices to 2D
32
30
  // Make a list of all encountered y coordinates
33
31
  // And build a map of all polygons that have a vertex at a certain y coordinate:
34
- const ycoordinateBinningFactor = 1.0 / EPS * 10
32
+ const ycoordinatebins = new Map()
33
+ const ycoordinateBinningFactor = 10 / EPS
35
34
  for (let polygonindex = 0; polygonindex < numpolygons; polygonindex++) {
36
35
  const poly3d = sourcepolygons[polygonindex]
37
36
  let vertices2d = []
@@ -46,15 +45,15 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
46
45
  // close to each other, give them the same y coordinate:
47
46
  const ycoordinatebin = Math.floor(pos2d[1] * ycoordinateBinningFactor)
48
47
  let newy
49
- if (ycoordinatebin in ycoordinatebins) {
50
- newy = ycoordinatebins[ycoordinatebin]
51
- } else if (ycoordinatebin + 1 in ycoordinatebins) {
52
- newy = ycoordinatebins[ycoordinatebin + 1]
53
- } else if (ycoordinatebin - 1 in ycoordinatebins) {
54
- newy = ycoordinatebins[ycoordinatebin - 1]
48
+ if (ycoordinatebins.has(ycoordinatebin)) {
49
+ newy = ycoordinatebins.get(ycoordinatebin)
50
+ } else if (ycoordinatebins.has(ycoordinatebin + 1)) {
51
+ newy = ycoordinatebins.get(ycoordinatebin + 1)
52
+ } else if (ycoordinatebins.has(ycoordinatebin - 1)) {
53
+ newy = ycoordinatebins.get(ycoordinatebin - 1)
55
54
  } else {
56
55
  newy = pos2d[1]
57
- ycoordinatebins[ycoordinatebin] = pos2d[1]
56
+ ycoordinatebins.set(ycoordinatebin, pos2d[1])
58
57
  }
59
58
  pos2d = vec2.fromValues(pos2d[0], newy)
60
59
  vertices2d.push(pos2d)
@@ -66,10 +65,12 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
66
65
  if ((i === 0) || (y > maxy)) {
67
66
  maxy = y
68
67
  }
69
- if (!(y in ycoordinatetopolygonindexes)) {
70
- ycoordinatetopolygonindexes[y] = {}
68
+ let polygonindexes = ycoordinatetopolygonindexes.get(y)
69
+ if (!polygonindexes) {
70
+ polygonindexes = {} // PERF
71
+ ycoordinatetopolygonindexes.set(y, polygonindexes)
71
72
  }
72
- ycoordinatetopolygonindexes[y][polygonindex] = true
73
+ polygonindexes[polygonindex] = true
73
74
  }
74
75
  if (miny >= maxy) {
75
76
  // degenerate polygon, all vertices have same y coordinate. Just ignore it from now:
@@ -77,10 +78,12 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
77
78
  numvertices = 0
78
79
  minindex = -1
79
80
  } else {
80
- if (!(miny in topy2polygonindexes)) {
81
- topy2polygonindexes[miny] = []
81
+ let polygonindexes = topy2polygonindexes.get(miny)
82
+ if (!polygonindexes) {
83
+ polygonindexes = []
84
+ topy2polygonindexes.set(miny, polygonindexes)
82
85
  }
83
- topy2polygonindexes[miny].push(polygonindex)
86
+ polygonindexes.push(polygonindex)
84
87
  }
85
88
  } // if(numvertices > 0)
86
89
  // reverse the vertex order:
@@ -89,8 +92,9 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
89
92
  polygonvertices2d.push(vertices2d)
90
93
  polygontopvertexindexes.push(minindex)
91
94
  }
95
+
92
96
  const ycoordinates = []
93
- for (const ycoordinate in ycoordinatetopolygonindexes) ycoordinates.push(ycoordinate)
97
+ ycoordinatetopolygonindexes.forEach((polylist, y) => ycoordinates.push(y))
94
98
  ycoordinates.sort(fnNumberSort)
95
99
 
96
100
  // Now we will iterate over all y coordinates, from lowest to highest y coordinate
@@ -108,15 +112,14 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
108
112
  let prevoutpolygonrow = []
109
113
  for (let yindex = 0; yindex < ycoordinates.length; yindex++) {
110
114
  const newoutpolygonrow = []
111
- const ycoordinateasstring = ycoordinates[yindex]
112
- const ycoordinate = Number(ycoordinateasstring)
115
+ const ycoordinate = ycoordinates[yindex]
113
116
 
114
117
  // update activepolygons for this y coordinate:
115
118
  // - Remove any polygons that end at this y coordinate
116
119
  // - update leftvertexindex and rightvertexindex (which point to the current vertex index
117
120
  // at the the left and right side of the polygon
118
121
  // Iterate over all polygons that have a corner at this y coordinate:
119
- const polygonindexeswithcorner = ycoordinatetopolygonindexes[ycoordinateasstring]
122
+ const polygonindexeswithcorner = ycoordinatetopolygonindexes.get(ycoordinate)
120
123
  for (let activepolygonindex = 0; activepolygonindex < activepolygons.length; ++activepolygonindex) {
121
124
  const activepolygon = activepolygons[activepolygonindex]
122
125
  const polygonindex = activepolygon.polygonindex
@@ -166,7 +169,7 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
166
169
  nextycoordinate = Number(ycoordinates[yindex + 1])
167
170
  const middleycoordinate = 0.5 * (ycoordinate + nextycoordinate)
168
171
  // update activepolygons by adding any polygons that start here:
169
- const startingpolygonindexes = topy2polygonindexes[ycoordinateasstring]
172
+ const startingpolygonindexes = topy2polygonindexes.get(ycoordinate)
170
173
  for (const polygonindexKey in startingpolygonindexes) {
171
174
  const polygonindex = startingpolygonindexes[polygonindexKey]
172
175
  const vertices2d = polygonvertices2d[polygonindex]
@@ -212,8 +215,6 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
212
215
  })
213
216
  } // for(let polygonindex in startingpolygonindexes)
214
217
  } // yindex < ycoordinates.length-1
215
- // if( (yindex === ycoordinates.length-1) || (nextycoordinate - ycoordinate > EPS) )
216
- // FIXME : what ???
217
218
 
218
219
  // Now activepolygons is up to date
219
220
  // Build the output polygons for the next row in newoutpolygonrow:
@@ -252,19 +253,19 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
252
253
  } // for(activepolygon in activepolygons)
253
254
  if (yindex > 0) {
254
255
  // try to match the new polygons against the previous row:
255
- const prevcontinuedindexes = {}
256
- const matchedindexes = {}
256
+ const prevcontinuedindexes = new Set()
257
+ const matchedindexes = new Set()
257
258
  for (let i = 0; i < newoutpolygonrow.length; i++) {
258
259
  const thispolygon = newoutpolygonrow[i]
259
260
  for (let ii = 0; ii < prevoutpolygonrow.length; ii++) {
260
- if (!matchedindexes[ii]) { // not already processed?
261
+ if (!matchedindexes.has(ii)) { // not already processed?
261
262
  // We have a match if the sidelines are equal or if the top coordinates
262
263
  // are on the sidelines of the previous polygon
263
264
  const prevpolygon = prevoutpolygonrow[ii]
264
265
  if (vec2.distance(prevpolygon.bottomleft, thispolygon.topleft) < EPS) {
265
266
  if (vec2.distance(prevpolygon.bottomright, thispolygon.topright) < EPS) {
266
267
  // Yes, the top of this polygon matches the bottom of the previous:
267
- matchedindexes[ii] = true
268
+ matchedindexes.add(ii)
268
269
  // Now check if the joined polygon would remain convex:
269
270
  const v1 = line2.direction(thispolygon.leftline)
270
271
  const v2 = line2.direction(prevpolygon.leftline)
@@ -284,16 +285,16 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
284
285
  thispolygon.outpolygon = prevpolygon.outpolygon
285
286
  thispolygon.leftlinecontinues = leftlinecontinues
286
287
  thispolygon.rightlinecontinues = rightlinecontinues
287
- prevcontinuedindexes[ii] = true
288
+ prevcontinuedindexes.add(ii)
288
289
  }
289
290
  break
290
291
  }
291
292
  }
292
- } // if(!prevcontinuedindexes[ii])
293
+ } // if(!prevcontinuedindexes.has(ii))
293
294
  } // for ii
294
295
  } // for i
295
296
  for (let ii = 0; ii < prevoutpolygonrow.length; ii++) {
296
- if (!prevcontinuedindexes[ii]) {
297
+ if (!prevcontinuedindexes.has(ii)) {
297
298
  // polygon ends here
298
299
  // Finish the polygon with the last point(s):
299
300
  const prevpolygon = prevoutpolygonrow[ii]
@@ -17,11 +17,11 @@ const rotatePoly3 = (angles, polygon) => {
17
17
  }
18
18
 
19
19
  test.only('retessellateCoplanarPolygons: should merge coplanar polygons', (t) => {
20
- const polyA = poly3.fromPoints([[-5, -5, 0], [5, -5, 0], [5, 5, 0], [-5, 5, 0]])
21
- const polyB = poly3.fromPoints([[5, -5, 0], [8, 0, 0], [5, 5, 0]])
22
- const polyC = poly3.fromPoints([[-5, 5, 0], [-8, 0, 0], [-5, -5, 0]])
23
- const polyD = poly3.fromPoints([[-5, 5, 0], [5, 5, 0], [0, 8, 0]])
24
- const polyE = poly3.fromPoints([[5, -5, 0], [-5, -5, 0], [0, -8, 0]])
20
+ const polyA = poly3.create([[-5, -5, 0], [5, -5, 0], [5, 5, 0], [-5, 5, 0]])
21
+ const polyB = poly3.create([[5, -5, 0], [8, 0, 0], [5, 5, 0]])
22
+ const polyC = poly3.create([[-5, 5, 0], [-8, 0, 0], [-5, -5, 0]])
23
+ const polyD = poly3.create([[-5, 5, 0], [5, 5, 0], [0, 8, 0]])
24
+ const polyE = poly3.create([[5, -5, 0], [-5, -5, 0], [0, -8, 0]])
25
25
 
26
26
  // combine polygons in each direction
27
27
  let obs = reTesselateCoplanarPolygons([polyA, polyB])
@@ -1,16 +1,9 @@
1
1
  const geom3 = require('../../geometries/geom3')
2
2
  const poly3 = require('../../geometries/poly3')
3
3
 
4
- const reTesselateCoplanarPolygons = require('./reTesselateCoplanarPolygons')
5
-
6
- // Normals are directional vectors with component values from 0 to 1.0, requiring specialized comparison
7
- // This EPS is derived from a series of tests to determine the optimal precision for comparing coplanar polygons,
8
- // as provided by the sphere primitive at high segmentation
9
- // This EPS is for 64 bit Number values
10
- const NEPS = 1e-13
4
+ const aboutEqualNormals = require('../../maths/utils/aboutEqualNormals')
11
5
 
12
- // Compare two normals (unit vectors) for equality.
13
- const aboutEqualNormals = (a, b) => (Math.abs(a[0] - b[0]) <= NEPS && Math.abs(a[1] - b[1]) <= NEPS && Math.abs(a[2] - b[2]) <= NEPS)
6
+ const reTesselateCoplanarPolygons = require('./reTesselateCoplanarPolygons')
14
7
 
15
8
  const coplanar = (plane1, plane2) => {
16
9
  // expect the same distance from the origin, within tolerance
@@ -7,49 +7,49 @@ const snapPolygons = require('./snapPolygons')
7
7
  test('snapPolygons: snap of polygons produces expected results', (t) => {
8
8
  const polygons = [
9
9
  // valid polygons
10
- poly3.fromPoints([[0, 0, 0], [0, 10, 0], [0, 10, 10]]), // OK
11
- poly3.fromPoints([[0, 0, 0], [0, 10, 0], [0, 10, 10], [0, 0, 10]]), // OK
10
+ poly3.create([[0, 0, 0], [0, 10, 0], [0, 10, 10]]), // OK
11
+ poly3.create([[0, 0, 0], [0, 10, 0], [0, 10, 10], [0, 0, 10]]), // OK
12
12
  // invalid polygons
13
13
  poly3.create(),
14
- poly3.fromPoints([[0, 0, 0]]),
15
- poly3.fromPoints([[0, 0, 0], [0, 10, 0]]),
14
+ poly3.create([[0, 0, 0]]),
15
+ poly3.create([[0, 0, 0], [0, 10, 0]]),
16
16
  // duplicated vertices
17
- poly3.fromPoints([
17
+ poly3.create([
18
18
  [-24.445112000000115, 19.346837333333426, 46.47572533333356],
19
19
  [-24.44446933333345, 19.346837333333426, 46.47508266666689],
20
20
  [-23.70540266666678, 18.79864266666676, 39.56448800000019],
21
21
  [-23.70540266666678, 18.79864266666676, 39.56448800000019]
22
22
  ]), // OK
23
- poly3.fromPoints([
23
+ poly3.create([
24
24
  [-24.445112000000115, 19.346837333333426, 46.47572533333356],
25
25
  [-23.70540266666678, 18.79864266666676, 39.56448800000019],
26
26
  [-23.70540266666678, 18.79864266666676, 39.56448800000019]
27
27
  ]),
28
- poly3.fromPoints([
28
+ poly3.create([
29
29
  [-23.70540266666678, 18.79864266666676, 39.56448800000019],
30
30
  [-23.70540266666678, 18.79864266666676, 39.56448800000019]
31
31
  ]),
32
32
  // duplicate vertices after snap
33
- poly3.fromPoints([
33
+ poly3.create([
34
34
  [-24.445112000000115, 19.346837333333426, 46.47572533333356],
35
35
  [-24.44446933333345, 19.346837333333426, 46.47508266666689],
36
36
  [-23.70540266666678, 18.79864266666676, 39.56448800000019],
37
37
  [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
38
38
  ]), // OK
39
- poly3.fromPoints([
39
+ poly3.create([
40
40
  [-24.445112000000115, 19.346837333333426, 46.47572533333356],
41
41
  [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234],
42
42
  [-23.70540266666678, 18.79864266666676, 39.56448800000019],
43
43
  [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
44
44
  ]),
45
- poly3.fromPoints([
45
+ poly3.create([
46
46
  [-23.70540266666678, 18.79864266666676, 39.56448800000019],
47
47
  [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234],
48
48
  [-23.70540266666678, 18.79864266666676, 39.56448800000019],
49
49
  [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
50
50
  ]),
51
51
  // inverted polygon
52
- poly3.fromPoints([
52
+ poly3.create([
53
53
  [20.109133333333336, -4.894033333333335, -1.0001266666666668],
54
54
  [20.021120000000003, -5.1802133333333344, -1.0001266666666668],
55
55
  [20.020300000000002, -5.182946666666668, -1.0001266666666668],
@@ -61,7 +61,7 @@ test('snapPolygons: snap of polygons produces expected results', (t) => {
61
61
  const results = snapPolygons(0.0001, polygons)
62
62
  t.is(results.length, 5)
63
63
 
64
- const exp3 = poly3.fromPoints([
64
+ const exp3 = poly3.create([
65
65
  [-24.4451, 19.3468, 46.4757],
66
66
  [-24.4445, 19.3468, 46.475100000000005],
67
67
  [-23.7054, 18.7986, 39.5645]
@@ -10,15 +10,15 @@ const triangulatePolygon = (epsilon, polygon, triangles) => {
10
10
  polygon.vertices.forEach((vertice) => vec3.add(midpoint, midpoint, vertice))
11
11
  vec3.snap(midpoint, vec3.divide(midpoint, midpoint, [nv, nv, nv]), epsilon)
12
12
  for (let i = 0; i < nv; i++) {
13
- const poly = poly3.fromPoints([midpoint, polygon.vertices[i], polygon.vertices[(i + 1) % nv]])
13
+ const poly = poly3.create([midpoint, polygon.vertices[i], polygon.vertices[(i + 1) % nv]])
14
14
  if (polygon.color) poly.color = polygon.color
15
15
  triangles.push(poly)
16
16
  }
17
17
  return
18
18
  }
19
19
  // exactly 4 vertices, use simple triangulation
20
- const poly0 = poly3.fromPoints([polygon.vertices[0], polygon.vertices[1], polygon.vertices[2]])
21
- const poly1 = poly3.fromPoints([polygon.vertices[0], polygon.vertices[2], polygon.vertices[3]])
20
+ const poly0 = poly3.create([polygon.vertices[0], polygon.vertices[1], polygon.vertices[2]])
21
+ const poly1 = poly3.create([polygon.vertices[0], polygon.vertices[2], polygon.vertices[3]])
22
22
  if (polygon.color) {
23
23
  poly0.color = polygon.color
24
24
  poly1.color = polygon.color
@@ -39,7 +39,7 @@ const center = (options, ...objects) => {
39
39
  const defaults = {
40
40
  axes: [true, true, true],
41
41
  relativeTo: [0, 0, 0]
42
- // TODO : Add addition 'methods' of centering; midpoint, centeriod
42
+ // TODO: Add additional 'methods' of centering: midpoint, centroid
43
43
  }
44
44
  const { axes, relativeTo } = Object.assign({}, defaults, options)
45
45
 
@@ -43,7 +43,7 @@ const cuboid = (options) => {
43
43
  ]
44
44
  return pos
45
45
  })
46
- return poly3.fromPoints(points)
46
+ return poly3.create(points)
47
47
  })
48
48
  )
49
49
  return result
@@ -90,7 +90,7 @@ const cylinderElliptic = (options) => {
90
90
  // adjust the points to center
91
91
  const fromPoints = (...points) => {
92
92
  const newpoints = points.map((point) => vec3.add(vec3.create(), point, center))
93
- return poly3.fromPoints(newpoints)
93
+ return poly3.create(newpoints)
94
94
  }
95
95
 
96
96
  const polygons = []
@@ -66,7 +66,7 @@ const ellipsoid = (options) => {
66
66
  point = vec3.subtract(vec3.create(), vec3.scale(p1, prevcylinderpoint, cospitch), vec3.scale(p2, zvector, sinpitch))
67
67
  points.push(vec3.add(point, point, center))
68
68
 
69
- polygons.push(poly3.fromPoints(points))
69
+ polygons.push(poly3.create(points))
70
70
 
71
71
  points = []
72
72
  point = vec3.add(vec3.create(), vec3.scale(p1, prevcylinderpoint, prevcospitch), vec3.scale(p2, zvector, prevsinpitch))
@@ -81,7 +81,7 @@ const ellipsoid = (options) => {
81
81
  points.push(vec3.add(vec3.create(), center, point))
82
82
  points.reverse()
83
83
 
84
- polygons.push(poly3.fromPoints(points))
84
+ polygons.push(poly3.create(points))
85
85
  }
86
86
  prevcospitch = cospitch
87
87
  prevsinpitch = sinpitch
@@ -60,7 +60,7 @@ const polyhedron = (options) => {
60
60
  }
61
61
 
62
62
  const polygons = faces.map((face, findex) => {
63
- const polygon = poly3.fromPoints(face.map((pindex) => points[pindex]))
63
+ const polygon = poly3.create(face.map((pindex) => points[pindex]))
64
64
  if (colors && colors[findex]) polygon.color = colors[findex]
65
65
  return polygon
66
66
  })
@@ -57,10 +57,10 @@ const stitchCorners = (previousCorners, currentCorners) => {
57
57
  const previous = previousCorners[i]
58
58
  const current = currentCorners[i]
59
59
  for (let j = 0; j < (previous.length - 1); j++) {
60
- polygons.push(poly3.fromPoints([previous[j], previous[j + 1], current[j]]))
60
+ polygons.push(poly3.create([previous[j], previous[j + 1], current[j]]))
61
61
 
62
62
  if (j < (current.length - 1)) {
63
- polygons.push(poly3.fromPoints([current[j], previous[j + 1], current[j + 1]]))
63
+ polygons.push(poly3.create([current[j], previous[j + 1], current[j + 1]]))
64
64
  }
65
65
  }
66
66
  }
@@ -81,7 +81,7 @@ const stitchWalls = (previousCorners, currentCorners) => {
81
81
  const p1 = previous[0]
82
82
  const c1 = current[0]
83
83
 
84
- polygons.push(poly3.fromPoints([p0, p1, c1, c0]))
84
+ polygons.push(poly3.create([p0, p1, c1, c0]))
85
85
  }
86
86
  return polygons
87
87
  }
@@ -104,7 +104,7 @@ const stitchSides = (bottomCorners, topCorners) => {
104
104
  const polygons = []
105
105
  for (let i = 0; i < topPoints.length; i++) {
106
106
  const j = (i + 1) % topPoints.length
107
- polygons.push(poly3.fromPoints([bottomPoints[i], bottomPoints[j], topPoints[j], topPoints[i]]))
107
+ polygons.push(poly3.create([bottomPoints[i], bottomPoints[j], topPoints[j], topPoints[i]]))
108
108
  }
109
109
  return polygons
110
110
  }
@@ -168,10 +168,10 @@ const roundedCuboid = (options) => {
168
168
  if (slice === segments) {
169
169
  // add the top
170
170
  let points = cornersPos.map((corner) => corner[0])
171
- polygons.push(poly3.fromPoints(points))
171
+ polygons.push(poly3.create(points))
172
172
  // add the bottom
173
173
  points = cornersNeg.map((corner) => corner[0])
174
- polygons.push(poly3.fromPoints(points))
174
+ polygons.push(poly3.create(points))
175
175
  }
176
176
 
177
177
  prevCornersPos = cornersPos
@@ -66,7 +66,7 @@ const roundedCylinder = (options) => {
66
66
  const fromPoints = (points) => {
67
67
  // adjust the points to center
68
68
  const newpoints = points.map((point) => vec3.add(point, point, center))
69
- return poly3.fromPoints(newpoints)
69
+ return poly3.create(newpoints)
70
70
  }
71
71
 
72
72
  const polygons = []
@@ -10,24 +10,24 @@ test('torus (defaults)', (t) => {
10
10
  const obs = torus()
11
11
  const pts = geom3.toPoints(obs)
12
12
 
13
- t.notThrows.skip(() => geom3.validate(obs))
13
+ t.notThrows(() => geom3.validate(obs))
14
14
  t.is(pts.length, 2048) // 32 * 32 * 2 (polys/segment) = 2048
15
15
 
16
16
  const bounds = measureBoundingBox(obs)
17
17
  const expectedBounds = [[-5, -5, -1], [5, 5, 1]]
18
- t.notThrows.skip(() => geom3.validate(obs))
18
+ t.notThrows(() => geom3.validate(obs))
19
19
  t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected: ' + JSON.stringify(bounds))
20
20
  })
21
21
 
22
- test('torus (Simple options)', (t) => {
22
+ test('torus (simple options)', (t) => {
23
23
  const obs = torus({ innerRadius: 0.5, innerSegments: 4, outerRadius: 5, outerSegments: 8 })
24
24
  const pts = geom3.toPoints(obs)
25
- t.notThrows.skip(() => geom3.validate(obs))
25
+ t.notThrows(() => geom3.validate(obs))
26
26
  t.is(pts.length, 64) // 4 * 8 * 2 (polys/segment) = 64
27
27
 
28
28
  const bounds = measureBoundingBox(obs)
29
29
  const expectedBounds = [[-5.5, -5.5, -0.5], [5.5, 5.5, 0.5]]
30
- t.notThrows.skip(() => geom3.validate(obs))
30
+ t.notThrows(() => geom3.validate(obs))
31
31
  t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected: ' + JSON.stringify(bounds))
32
32
  })
33
33
 
@@ -48,6 +48,6 @@ test('torus (square by square)', (t) => {
48
48
 
49
49
  const bounds = measureBoundingBox(obs)
50
50
  const expectedBounds = [[-5, -5, -1], [5, 5, 1]]
51
- t.notThrows.skip(() => geom3.validate(obs))
51
+ t.notThrows(() => geom3.validate(obs))
52
52
  t.true(comparePoints(bounds, expectedBounds), 'Bounding box was not as expected: ' + JSON.stringify(bounds))
53
53
  })
@@ -1,11 +1,10 @@
1
+ const { NEPS } = require('../maths/constants')
1
2
  const vec2 = require('../maths/vec2')
2
3
 
3
4
  const geom2 = require('../geometries/geom2')
4
5
 
5
6
  const { isNumberArray } = require('./commonChecks')
6
7
 
7
- const NEPS = 1e-13
8
-
9
8
  // returns angle C
10
9
  const solveAngleFromSSS = (a, b, c) => Math.acos(((a * a) + (b * b) - (c * c)) / (2 * a * b))
11
10
 
@@ -1,5 +1,4 @@
1
-
2
- const NEPS = 1e-13
1
+ const { NEPS } = require('../maths/constants')
3
2
 
4
3
  /*
5
4
  * Returns zero if n is within epsilon of zero, otherwise return n