@jscad/modeling 2.6.1 → 2.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jscad/modeling",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "Constructive Solid Geometry (CSG) Library for JSCAD",
5
5
  "repository": "https://github.com/jscad/OpenJSCAD.org",
6
6
  "main": "src/index.js",
@@ -60,5 +60,5 @@
60
60
  "nyc": "15.1.0",
61
61
  "uglifyify": "5.0.2"
62
62
  },
63
- "gitHead": "d716497da91f951ddf7b0e0096eb7343846bc4bd"
63
+ "gitHead": "fbc9b90f0ae02bea4e6e7b974c65e751c3858269"
64
64
  }
@@ -58,8 +58,8 @@ test('color (rgba on geometry)', (t) => {
58
58
 
59
59
  test('color (returns new object)', (t) => {
60
60
  const obj0 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
61
- const obj1 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
62
- const obj2 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
61
+ // const obj1 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
62
+ // const obj2 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
63
63
  const obj3 = geom2.fromPoints([[0, 0], [1, 0], [0, 1]])
64
64
 
65
65
  const obs = colorize([1, 1, 1, 0.8], obj0, obj3)
@@ -1,7 +1,5 @@
1
1
  const mat4 = require('../../maths/mat4')
2
2
 
3
- const create = require('./create')
4
-
5
3
  /**
6
4
  * Transform the given geometry using the given matrix.
7
5
  * This is a lazy transform of the sides, as this function only adjusts the transforms.
@@ -1,7 +1,5 @@
1
1
  const mat4 = require('../../maths/mat4')
2
2
 
3
- const create = require('./create')
4
-
5
3
  /**
6
4
  * Transform the given geometry using the given matrix.
7
5
  * This is a lazy transform of the polygons, as this function only adjusts the transforms.
@@ -1,7 +1,5 @@
1
1
  const mat4 = require('../../maths/mat4')
2
2
 
3
- const create = require('./create')
4
-
5
3
  /**
6
4
  * Transform the given geometry using the given matrix.
7
5
  * This is a lazy transform of the points, as this function only adjusts the transforms.
@@ -13,7 +13,7 @@ const areVerticesConvex = (vertices) => {
13
13
  const numvertices = vertices.length
14
14
  if (numvertices > 2) {
15
15
  // note: plane ~= normal point
16
- const normal = plane.fromPoints(plane.create(), vertices[0], vertices[1], vertices[2])
16
+ const normal = plane.fromPoints(plane.create(), ...vertices)
17
17
  let prevprevpos = vertices[numvertices - 2]
18
18
  let prevpos = vertices[numvertices - 1]
19
19
  for (let i = 0; i < numvertices; i++) {
@@ -1,4 +1,4 @@
1
- const vec3 = require('../../maths/vec3')
1
+ const plane = require('./plane')
2
2
 
3
3
  /**
4
4
  * Measure the area of the given polygon.
@@ -14,19 +14,18 @@ const measureArea = (poly3) => {
14
14
  }
15
15
  const vertices = poly3.vertices
16
16
 
17
- // calculate a real normal
18
- const a = vertices[0]
19
- const b = vertices[1]
20
- const c = vertices[2]
21
- const ba = vec3.subtract(vec3.create(), b, a)
22
- const ca = vec3.subtract(vec3.create(), c, a)
23
- const normal = vec3.cross(ba, ba, ca)
17
+ // calculate a normal vector
18
+ const normal = plane(poly3)
24
19
 
25
- // determin direction of projection
20
+ // determine direction of projection
26
21
  const ax = Math.abs(normal[0])
27
22
  const ay = Math.abs(normal[1])
28
23
  const az = Math.abs(normal[2])
29
- const an = Math.sqrt((ax * ax) + (ay * ay) + (az * az)) // length of normal
24
+
25
+ if (ax + ay + az === 0) {
26
+ // normal does not exist
27
+ return 0
28
+ }
30
29
 
31
30
  let coord = 3 // ignore Z coordinates
32
31
  if ((ax > ay) && (ax > az)) {
@@ -50,7 +49,7 @@ const measureArea = (poly3) => {
50
49
  }
51
50
  area += (vertices[0][1] * (vertices[1][2] - vertices[n - 1][2]))
52
51
  // scale to get area
53
- area *= (an / (2 * normal[0]))
52
+ area /= (2 * normal[0])
54
53
  break
55
54
 
56
55
  case 2: // ignore Y coordinates
@@ -62,7 +61,7 @@ const measureArea = (poly3) => {
62
61
  }
63
62
  area += (vertices[0][2] * (vertices[1][0] - vertices[n - 1][0]))
64
63
  // scale to get area
65
- area *= (an / (2 * normal[1]))
64
+ area /= (2 * normal[1])
66
65
  break
67
66
 
68
67
  case 3: // ignore Z coordinates
@@ -75,7 +74,7 @@ const measureArea = (poly3) => {
75
74
  }
76
75
  area += (vertices[0][0] * (vertices[1][1] - vertices[n - 1][1]))
77
76
  // scale to get area
78
- area *= (an / (2 * normal[2]))
77
+ area /= (2 * normal[2])
79
78
  break
80
79
  }
81
80
  return area
@@ -37,6 +37,21 @@ test('poly3: measureArea() should return correct values', (t) => {
37
37
  let ret4 = measureArea(ply4)
38
38
  t.is(ret4, 19.5)
39
39
 
40
+ // colinear vertices non-zero area
41
+ const ply5 = fromPoints([[0, 0, 0], [1, 0, 0], [2, 0, 0], [0, 1, 0]])
42
+ const ret5 = measureArea(ply5)
43
+ t.is(ret5, 1)
44
+
45
+ // colinear vertices empty area
46
+ const ply6 = fromPoints([[0, 0, 0], [1, 0, 0], [2, 0, 0]])
47
+ const ret6 = measureArea(ply6)
48
+ t.is(ret6, 0)
49
+
50
+ // duplicate vertices empty area
51
+ const ply7 = fromPoints([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
52
+ const ret7 = measureArea(ply7)
53
+ t.is(ret7, 0)
54
+
40
55
  // rotated to various angles
41
56
  let rotation = mat4.fromZRotation(mat4.create(), (45 * 0.017453292519943295))
42
57
  ply1 = transform(rotation, ply1)
@@ -2,8 +2,7 @@ const mplane = require('../../maths/plane/')
2
2
 
3
3
  const plane = (polygon) => {
4
4
  if (!polygon.plane) {
5
- const vertices = polygon.vertices
6
- polygon.plane = mplane.fromPoints(mplane.create(), vertices[0], vertices[1], vertices[2])
5
+ polygon.plane = mplane.fromPoints(mplane.create(), ...polygon.vertices)
7
6
  }
8
7
  return polygon.plane
9
8
  }
@@ -10,17 +10,39 @@ const vec3 = require('../vec3')
10
10
  * @returns {plane} out
11
11
  * @alias module:modeling/maths/plane.fromPoints
12
12
  */
13
- const fromPoints = (out, a, b, c) => {
14
- const ba = vec3.subtract(vec3.create(), b, a)
15
- const ca = vec3.subtract(vec3.create(), c, a)
16
- vec3.cross(ba, ba, ca)
17
- vec3.normalize(ba, ba) // normal part
18
- const w = vec3.dot(ba, a)
13
+ const fromPoints = (out, ...vertices) => {
14
+ const len = vertices.length
19
15
 
20
- out[0] = ba[0]
21
- out[1] = ba[1]
22
- out[2] = ba[2]
23
- out[3] = w
16
+ // Calculate normal vector for a single vertex
17
+ // Inline to avoid allocations
18
+ const ba = vec3.create()
19
+ const ca = vec3.create()
20
+ const vertexNormal = (index) => {
21
+ const a = vertices[index]
22
+ const b = vertices[(index + 1) % len]
23
+ const c = vertices[(index + 2) % len]
24
+ vec3.subtract(ba, b, a) // ba = b - a
25
+ vec3.subtract(ca, c, a) // ca = c - a
26
+ vec3.cross(ba, ba, ca) // ba = ba x ca
27
+ vec3.normalize(ba, ba)
28
+ return ba
29
+ }
30
+
31
+ out[0] = 0
32
+ out[1] = 0
33
+ out[2] = 0
34
+ if (len === 3) {
35
+ // optimization for triangles, which are always coplanar
36
+ vec3.copy(out, vertexNormal(0))
37
+ } else {
38
+ // sum of vertex normals
39
+ vertices.forEach((v, i) => {
40
+ vec3.add(out, out, vertexNormal(i))
41
+ })
42
+ // renormalize normal vector
43
+ vec3.normalize(out, out)
44
+ }
45
+ out[3] = vec3.dot(out, vertices[0])
24
46
  return out
25
47
  }
26
48
 
@@ -13,4 +13,8 @@ test('plane: fromPoints() should return a new plane with correct values', (t) =>
13
13
  // planes created from the same points results in an invalid plane
14
14
  const obs3 = fromPoints(obs1, [0, 6, 0], [0, 6, 0], [0, 6, 0])
15
15
  t.true(compareVectors(obs3, [0 / 0, 0 / 0, 0 / 0, 0 / 0]))
16
+
17
+ // polygon with co-linear vertices
18
+ const obs4 = fromPoints(obs1, [0, 0, 0], [1, 0, 0], [2, 0, 0], [0, 1, 0])
19
+ t.true(compareVectors(obs4, [0, 0, 1, 0]))
16
20
  })
@@ -31,10 +31,9 @@ const measureBoundingBoxOfPath2Points = (points) => {
31
31
  vec2.min(minpoint, minpoint, point)
32
32
  vec2.max(maxpoint, maxpoint, point)
33
33
  })
34
-
35
34
  boundingBox = [[minpoint[0], minpoint[1], 0], [maxpoint[0], maxpoint[1], 0]]
36
- cache.set(points, boundingBox)
37
35
 
36
+ cache.set(points, boundingBox)
38
37
  return boundingBox
39
38
  }
40
39
 
@@ -55,7 +54,6 @@ const measureBoundingBoxOfPath2 = (geometry) => {
55
54
  }
56
55
 
57
56
  cache.set(geometry, boundingBox)
58
-
59
57
  return boundingBox
60
58
  }
61
59
 
@@ -65,6 +63,7 @@ const measureBoundingBoxOfPath2 = (geometry) => {
65
63
  */
66
64
  const measureBoundingBoxOfGeom2Points = ({ points, sides }) => {
67
65
  const cacheKey = points || sides
66
+
68
67
  let boundingBox = cache.get(cacheKey)
69
68
  if (boundingBox) return boundingBox
70
69
 
@@ -96,11 +95,9 @@ const measureBoundingBoxOfGeom2Points = ({ points, sides }) => {
96
95
  vec2.max(maxpoint, maxpoint, side[0])
97
96
  })
98
97
  }
99
-
100
98
  boundingBox = [[minpoint[0], minpoint[1], 0], [maxpoint[0], maxpoint[1], 0]]
101
99
 
102
100
  cache.set(cacheKey, boundingBox)
103
-
104
101
  return boundingBox
105
102
  }
106
103
 
@@ -121,7 +118,6 @@ const measureBoundingBoxOfGeom2 = (geometry) => {
121
118
  }
122
119
 
123
120
  cache.set(geometry, boundingBox)
124
-
125
121
  return boundingBox
126
122
  }
127
123
 
@@ -150,7 +146,6 @@ const measureBoundingBoxOfGeom3Polygons = (polygons) => {
150
146
  boundingBox = [[minpoint[0], minpoint[1], minpoint[2]], [maxpoint[0], maxpoint[1], maxpoint[2]]]
151
147
 
152
148
  cache.set(polygons, boundingBox)
153
-
154
149
  return boundingBox
155
150
  }
156
151
 
@@ -171,13 +166,13 @@ const measureBoundingBoxOfGeom3 = (geometry) => {
171
166
  }
172
167
 
173
168
  cache.set(geometry, boundingBox)
174
-
175
169
  return boundingBox
176
170
  }
177
171
 
178
- /* swap values if specific axis value in the second vector has lower value than the axis value in first vector */
172
+ /*
173
+ * swap values if specific axis value in the second vector has lower value than the axis value in first vector
174
+ */
179
175
  const fixBound = (i, v1, v2) => {
180
- // this function will likely be inlined by the JS optimization compiler
181
176
  if (v1[i] > v2[i]) {
182
177
  const tmp = v1[i]
183
178
  v1[i] = v2[i]
@@ -185,18 +180,21 @@ const fixBound = (i, v1, v2) => {
185
180
  }
186
181
  }
187
182
 
183
+ /*
184
+ * Transform the given bounding box
185
+ */
188
186
  const transformBoundingBox = (boundingBox, transforms) => {
189
- if (transforms && !mat4.isIdentity(transforms)) {
190
- const out = [vec3.transform(vec3.create(), boundingBox[0], transforms), vec3.transform(vec3.create(), boundingBox[1], transforms)]
187
+ if (!mat4.isIdentity(transforms)) {
188
+ vec3.transform(boundingBox[0], boundingBox[0], transforms)
189
+ vec3.transform(boundingBox[1], boundingBox[1], transforms)
191
190
 
192
191
  // we now have a new 2 vectors: [v1,v2] => [ [x1,y1,z1], [x2,y2,z2] ]
193
192
  // transform can move bounding box corner in such way that it is no longer true that
194
193
  // - v1 = [min(x1,x2),min(y1,y2),min(z1,z2)]
195
194
  // - v2 = [max(x1,x2),max(y1,y2),max(z1,z2)]
196
- fixBound(0, ...out)// swap x, if higher value is in first vector
197
- fixBound(1, ...out)// swap y, if higher value is in first vector
198
- fixBound(2, ...out)// swap z, if higher value is in first vector
199
- return out
195
+ fixBound(0, ...boundingBox) // swap x, if higher value is in first vector
196
+ fixBound(1, ...boundingBox) // swap y, if higher value is in first vector
197
+ fixBound(2, ...boundingBox) // swap z, if higher value is in first vector
200
198
  }
201
199
  return boundingBox
202
200
  }
@@ -223,15 +221,4 @@ const measureBoundingBox = (...geometries) => {
223
221
  return results.length === 1 ? results[0] : results
224
222
  }
225
223
 
226
- /**
227
- * Shortcut for geometries that are complex but have a fast way to calculate bounding box.
228
- * Ellipsoid, or cylinder can provide boundingBox that pre-calculated based on parameters without traversing points.
229
- *
230
- * Another option is to calculate boundingBox durint toPoints (so the boundingBox could be calculated during transform)
231
- *
232
- * NOTE: It seems that measureBoundingBox is used all over the place, and it would be wise to allow
233
- * shortcuts for calculating it, as default implementation goes through all points in geometry which is bound to be slow.
234
- */
235
- measureBoundingBox.setCache = (geometry, boundingBox) => cache.set(geometry, boundingBox)
236
-
237
224
  module.exports = measureBoundingBox
@@ -4,7 +4,6 @@ const vec3 = require('../maths/vec3')
4
4
 
5
5
  const geom2 = require('../geometries/geom2')
6
6
  const geom3 = require('../geometries/geom3')
7
- const path2 = require('../geometries/path2')
8
7
 
9
8
  const cacheOfCenterOfMass = new WeakMap()
10
9
 
@@ -8,25 +8,19 @@ const measureBoundingBox = require('./measureBoundingBox')
8
8
  * Measure the epsilon of the given (path2) geometry.
9
9
  * @return {Number} the epsilon (precision) of the geometry
10
10
  */
11
- const measureEpsilonOfPath2 = (geometry) => {
12
- return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
13
- }
11
+ const measureEpsilonOfPath2 = (geometry) => calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
14
12
 
15
13
  /*
16
14
  * Measure the epsilon of the given (geom2) geometry.
17
15
  * @return {Number} the epsilon (precision) of the geometry
18
16
  */
19
- const measureEpsilonOfGeom2 = (geometry) => {
20
- return calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
21
- }
17
+ const measureEpsilonOfGeom2 = (geometry) => calculateEpsilonFromBounds(measureBoundingBox(geometry), 2)
22
18
 
23
19
  /*
24
20
  * Measure the epsilon of the given (geom3) geometry.
25
21
  * @return {Float} the epsilon (precision) of the geometry
26
22
  */
27
- const measureEpsilonOfGeom3 = (geometry) => {
28
- return calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
29
- }
23
+ const measureEpsilonOfGeom3 = (geometry) => calculateEpsilonFromBounds(measureBoundingBox(geometry), 3)
30
24
 
31
25
  /**
32
26
  * Measure the epsilon of the given geometries.
@@ -309,7 +309,7 @@ const reTesselateCoplanarPolygons = (sourcepolygons) => {
309
309
  const polygon = poly3.fromPointsAndPlane(vertices3d, plane) // TODO support shared
310
310
 
311
311
  // if we let empty polygon out, next retesselate will crash
312
- if(polygon.vertices.length) destpolygons.push(polygon)
312
+ if (polygon.vertices.length) destpolygons.push(polygon)
313
313
  }
314
314
  }
315
315
  } // if(yindex > 0)
@@ -20,7 +20,6 @@ const splitPolygonByPlane = require('./splitPolygonByPlane')
20
20
  // remove() removes a polygon from the tree. Once a polygon is removed, the parent polygons are invalidated
21
21
  // since they are no longer intact.
22
22
  class PolygonTreeNode {
23
-
24
23
  // constructor creates the root node
25
24
  constructor () {
26
25
  this.parent = null
@@ -120,7 +120,7 @@ test('expand: expanding of a geom3 produces expected changes to polygons', (t) =
120
120
  const geometry2 = sphere({ radius: 5, segments: 8 })
121
121
  const obs2 = expand({ delta: 5 }, geometry2)
122
122
  const pts2 = geom3.toPoints(obs2)
123
- t.is(pts2.length, 2059)
123
+ t.is(pts2.length, 2065)
124
124
  })
125
125
 
126
126
  test('expand (options): offsetting of a complex geom2 produces expected offset geom2', (t) => {
@@ -107,10 +107,14 @@ test('extrudeRotate: (overlap +/-) extruding of a geom2 produces an expected geo
107
107
  [[7, -4.898587196589413e-16, -8], [4.898587196589413e-16, -2.999519565323715e-32, -8], [9.184850993605148e-16, 7, -8]],
108
108
  [[7, 4.898587196589413e-16, 8], [7, -4.898587196589413e-16, -8], [9.184850993605148e-16, 7, -8]],
109
109
  [[7, 4.898587196589413e-16, 8], [9.184850993605148e-16, 7, -8], [-6.123233995736767e-17, 7, 8]],
110
- [[-4.898587196589414e-16, 0, 8], [-6.123233995736777e-17, 6.999999999999999, 8],
111
- [9.18485099360515e-16, 7.000000000000001, -8], [4.898587196589413e-16, 0, -8]],
112
- [[7, 4.898587196589413e-16, 8], [0, 4.898587196589413e-16, 8],
113
- [0, -4.898587196589413e-16, -8], [7, -4.898587196589413e-16, -8]]
110
+ [
111
+ [-4.898587196589414e-16, 0, 8], [-6.123233995736777e-17, 6.999999999999999, 8],
112
+ [9.18485099360515e-16, 7.000000000000001, -8], [4.898587196589413e-16, 0, -8]
113
+ ],
114
+ [
115
+ [7, 4.898587196589413e-16, 8], [0, 4.898587196589413e-16, 8],
116
+ [0, -4.898587196589413e-16, -8], [7, -4.898587196589413e-16, -8]
117
+ ]
114
118
  ]
115
119
  t.is(pts.length, 6)
116
120
  t.true(comparePolygonsAsPoints(pts, exp))
@@ -133,12 +137,16 @@ test('extrudeRotate: (overlap +/-) extruding of a geom2 produces an expected geo
133
137
  [[0.7071067811865472, 0.7071067811865478, 8], [1.414213562373095, 1.4142135623730951, 4], [-1.2246467991473532e-16, 2, 4]],
134
138
  [[0.7071067811865472, 0.7071067811865478, 8], [-1.2246467991473532e-16, 2, 4], [-4.286263797015736e-16, 1, 8]],
135
139
  [[-3.4638242249419727e-16, 3.4638242249419736e-16, 8], [0.7071067811865472, 0.7071067811865478, 8], [-4.286263797015736e-16, 1, 8]],
136
- [[-4.898587196589412e-16, 0, 8], [-4.2862637970157346e-16, 0.9999999999999998, 8],
137
- [-1.2246467991473475e-16, 2.0000000000000004, 3.9999999999999964], [5.510910596163092e-16, 1.0000000000000004, -8],
138
- [ 4.898587196589414e-16, 0, -8]],
139
- [[0, -4.898587196589413e-16, -8.000000000000002], [1.0000000000000027, -4.898587196589413e-16, -8.000000000000002],
140
- [2.000000000000001, 2.449293598294702e-16, 3.9999999999999964], [1.0000000000000004, 4.898587196589411e-16, 8],
141
- [0, 4.898587196589411e-16, 8]]
140
+ [
141
+ [-4.898587196589412e-16, 0, 8], [-4.2862637970157346e-16, 0.9999999999999998, 8],
142
+ [-1.2246467991473475e-16, 2.0000000000000004, 3.9999999999999964], [5.510910596163092e-16, 1.0000000000000004, -8],
143
+ [4.898587196589414e-16, 0, -8]
144
+ ],
145
+ [
146
+ [0, -4.898587196589413e-16, -8.000000000000002], [1.0000000000000027, -4.898587196589413e-16, -8.000000000000002],
147
+ [2.000000000000001, 2.449293598294702e-16, 3.9999999999999964], [1.0000000000000004, 4.898587196589411e-16, 8],
148
+ [0, 4.898587196589411e-16, 8]
149
+ ]
142
150
  ]
143
151
  t.is(pts.length, 14)
144
152
  t.true(comparePolygonsAsPoints(pts, exp))
@@ -19,7 +19,6 @@ const generalizePath2 = (options, geometry) => {
19
19
  return geometry
20
20
  }
21
21
 
22
-
23
22
  /*
24
23
  */
25
24
  const generalizeGeom2 = (options, geometry) => {
@@ -17,7 +17,7 @@ const snapPolygons = (epsilon, polygons) => {
17
17
  const newvertices = []
18
18
  for (let i = 0; i < snapvertices.length; i++) {
19
19
  const j = (i + 1) % snapvertices.length
20
- if (! vec3.equals(snapvertices[i], snapvertices[j])) newvertices.push(snapvertices[i])
20
+ if (!vec3.equals(snapvertices[i], snapvertices[j])) newvertices.push(snapvertices[i])
21
21
  }
22
22
  const newpolygon = poly3.create(newvertices)
23
23
  if (polygon.color) newpolygon.color = polygon.color
@@ -34,28 +34,28 @@ test('snapPolygons: snap of polygons produces expected results', (t) => {
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
- [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234],
37
+ [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
38
38
  ]), // OK
39
39
  poly3.fromPoints([
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
- [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234],
43
+ [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
44
44
  ]),
45
45
  poly3.fromPoints([
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
- [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234],
49
+ [-23.70540266666678 - 0.00001234, 18.79864266666676 + 0.000001234, 39.56448800000019 + 0.00001234]
50
50
  ]),
51
51
  // inverted polygon
52
52
  poly3.fromPoints([
53
- [ 20.109133333333336, -4.894033333333335, -1.0001266666666668 ],
54
- [ 20.021120000000003, -5.1802133333333344, -1.0001266666666668 ],
55
- [ 20.020300000000002, -5.182946666666668, -1.0001266666666668 ],
56
- [ 10.097753333333335, -5.182946666666668, -1.0001266666666668 ],
57
- [ 10.287720000000002, -4.894033333333335, -1.0001266666666668 ]
58
- ]),
53
+ [20.109133333333336, -4.894033333333335, -1.0001266666666668],
54
+ [20.021120000000003, -5.1802133333333344, -1.0001266666666668],
55
+ [20.020300000000002, -5.182946666666668, -1.0001266666666668],
56
+ [10.097753333333335, -5.182946666666668, -1.0001266666666668],
57
+ [10.287720000000002, -4.894033333333335, -1.0001266666666668]
58
+ ])
59
59
  ]
60
60
 
61
61
  const results = snapPolygons(0.0001, polygons)
@@ -66,5 +66,5 @@ test('snapPolygons: snap of polygons produces expected results', (t) => {
66
66
  [-24.4445, 19.3468, 46.475100000000005],
67
67
  [-23.7054, 18.7986, 39.5645]
68
68
  ])
69
- t.deepEqual(results[3], exp3)
69
+ t.deepEqual(results[3].vertices, exp3.vertices)
70
70
  })
@@ -18,5 +18,6 @@ export { default as sphere, SphereOptions } from './sphere'
18
18
  export { default as square, SquareOptions } from './square'
19
19
  export { default as star, StarOptions } from './star'
20
20
  export { default as torus, TorusOptions } from './torus'
21
+ export { default as triangle, TriangleOptions } from './triangle'
21
22
 
22
23
  export as namespace primitives
@@ -26,5 +26,6 @@ module.exports = {
26
26
  sphere: require('./sphere'),
27
27
  square: require('./square'),
28
28
  star: require('./star'),
29
- torus: require('./torus')
29
+ torus: require('./torus'),
30
+ triangle: require('./triangle')
30
31
  }
@@ -0,0 +1,10 @@
1
+ import Geom2 from '../geometries/geom2/type'
2
+
3
+ export default triangle
4
+
5
+ export interface TriangleOptions {
6
+ type?: 'AAA' | 'AAS' | 'ASA' | 'SAS' | 'SSA' | 'SSS'
7
+ values?: [number, number, number]
8
+ }
9
+
10
+ declare function triangle(options?: TriangleOptions): Geom2