@jscad/modeling 2.6.0 → 2.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/dist/jscad-modeling.min.js +97 -94
- package/package.json +2 -2
- package/src/colors/colorize.js +35 -1
- package/src/colors/colorize.test.js +19 -4
- package/src/colors/hslToRgb.js +1 -1
- package/src/colors/hueToColorComponent.js +1 -0
- package/src/curves/bezier/create.js +3 -8
- package/src/curves/bezier/tangentAt.js +2 -2
- package/src/curves/index.js +1 -1
- package/src/geometries/geom2/clone.js +2 -12
- package/src/geometries/geom2/toOutlines.js +6 -11
- package/src/geometries/geom2/transform.js +0 -2
- package/src/geometries/geom3/clone.js +2 -14
- package/src/geometries/geom3/clone.test.js +0 -2
- package/src/geometries/geom3/create.js +1 -3
- package/src/geometries/geom3/create.test.js +0 -2
- package/src/geometries/geom3/fromCompactBinary.js +4 -6
- package/src/geometries/geom3/fromToCompactBinary.test.js +0 -6
- package/src/geometries/geom3/invert.js +2 -2
- package/src/geometries/geom3/invert.test.js +0 -2
- package/src/geometries/geom3/toCompactBinary.js +8 -10
- package/src/geometries/geom3/toPoints.js +1 -0
- package/src/geometries/geom3/transform.js +0 -2
- package/src/geometries/geom3/transform.test.js +0 -1
- package/src/geometries/geom3/type.d.ts +0 -1
- package/src/geometries/path2/clone.js +2 -13
- package/src/geometries/path2/transform.js +0 -2
- package/src/geometries/poly3/isConvex.js +1 -1
- package/src/geometries/poly3/measureArea.js +12 -13
- package/src/geometries/poly3/measureArea.test.js +15 -0
- package/src/geometries/poly3/plane.js +1 -2
- package/src/maths/line3/create.js +2 -1
- package/src/maths/mat4/fromRotation.js +1 -1
- package/src/maths/mat4/isIdentity.test.js +0 -2
- package/src/maths/mat4/isOnlyTransformScale.js +5 -4
- package/src/maths/mat4/rotate.js +1 -1
- package/src/maths/plane/fromPoints.js +32 -10
- package/src/maths/plane/fromPoints.test.js +4 -0
- package/src/maths/vec2/length.test.js +10 -0
- package/src/maths/vec3/angle.js +2 -2
- package/src/maths/vec3/angle.test.js +17 -0
- package/src/maths/vec3/length.test.js +10 -0
- package/src/measurements/measureBoundingBox.js +37 -121
- package/src/measurements/measureBoundingBox.test.js +8 -0
- package/src/measurements/measureCenterOfMass.js +0 -1
- package/src/measurements/measureEpsilon.js +3 -9
- package/src/operations/booleans/reTesselateCoplanarPolygons.js +1 -1
- package/src/operations/booleans/retessellate.js +1 -3
- package/src/operations/booleans/trees/PolygonTreeNode.js +0 -1
- package/src/operations/expansions/expand.js +2 -0
- package/src/operations/expansions/expand.test.js +1 -1
- package/src/operations/expansions/offset.js +1 -0
- package/src/operations/extrusions/extrudeLinear.js +1 -1
- package/src/operations/extrusions/extrudeRectangular.js +2 -1
- package/src/operations/extrusions/extrudeRotate.test.js +18 -10
- package/src/operations/hulls/quickhull/QuickHull.js +2 -2
- package/src/operations/modifiers/edges.js +1 -3
- package/src/operations/modifiers/generalize.js +3 -7
- package/src/operations/modifiers/snapPolygons.js +2 -2
- package/src/operations/modifiers/snapPolygons.test.js +13 -5
- package/src/operations/transforms/mirror.js +1 -1
- package/src/primitives/ellipse.js +1 -1
- package/src/primitives/geodesicSphere.js +2 -2
- package/src/primitives/index.d.ts +1 -0
- package/src/primitives/index.js +2 -1
- package/src/primitives/roundedCylinder.js +1 -1
- package/src/primitives/triangle.d.ts +10 -0
- package/src/primitives/triangle.js +164 -0
- package/src/primitives/triangle.test.js +95 -0
- package/src/utils/insertSorted.js +1 -0
- package/test/helpers/comparePolygons.js +1 -3
- package/test/helpers/nearlyEqual.js +2 -6
|
@@ -34,29 +34,37 @@ 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
|
+
// inverted polygon
|
|
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
|
+
])
|
|
51
59
|
]
|
|
52
60
|
|
|
53
61
|
const results = snapPolygons(0.0001, polygons)
|
|
54
|
-
t.is(results.length,
|
|
62
|
+
t.is(results.length, 5)
|
|
55
63
|
|
|
56
64
|
const exp3 = poly3.fromPoints([
|
|
57
65
|
[-24.4451, 19.3468, 46.4757],
|
|
58
66
|
[-24.4445, 19.3468, 46.475100000000005],
|
|
59
67
|
[-23.7054, 18.7986, 39.5645]
|
|
60
68
|
])
|
|
61
|
-
t.deepEqual(results[3], exp3)
|
|
69
|
+
t.deepEqual(results[3].vertices, exp3.vertices)
|
|
62
70
|
})
|
|
@@ -64,7 +64,7 @@ const mirrorY = (...objects) => mirror({ normal: [0, 1, 0] }, objects)
|
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Mirror the given object(s) about the Z axis.
|
|
67
|
-
* @param {...Object}
|
|
67
|
+
* @param {...Object} geometries - the geometries to mirror
|
|
68
68
|
* @return {Object|Array} the mirrored geometry, or a list of mirrored geometry
|
|
69
69
|
* @alias module:modeling/transforms.mirrorZ
|
|
70
70
|
*/
|
|
@@ -7,7 +7,7 @@ const geom2 = require('../geometries/geom2')
|
|
|
7
7
|
const { isGTE, isNumberArray } = require('./commonChecks')
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Construct an axis-aligned
|
|
10
|
+
* Construct an axis-aligned ellipse in two dimensional space.
|
|
11
11
|
* @see https://en.wikipedia.org/wiki/Ellipse
|
|
12
12
|
* @param {Object} [options] - options for construction
|
|
13
13
|
* @param {Array} [options.center=[0,0]] - center of ellipse
|
|
@@ -79,7 +79,7 @@ const geodesicSphere = (options) => {
|
|
|
79
79
|
|
|
80
80
|
// -- normalize
|
|
81
81
|
for (let k = 0; k < 3; k++) {
|
|
82
|
-
const r = Math.
|
|
82
|
+
const r = Math.hypot(q[k][0], q[k][1], q[k][2])
|
|
83
83
|
for (let l = 0; l < 3; l++) {
|
|
84
84
|
q[k][l] /= r
|
|
85
85
|
}
|
|
@@ -95,7 +95,7 @@ const geodesicSphere = (options) => {
|
|
|
95
95
|
|
|
96
96
|
// -- normalize
|
|
97
97
|
for (let k = 0; k < 3; k++) {
|
|
98
|
-
const r = Math.
|
|
98
|
+
const r = Math.hypot(q[k][0], q[k][1], q[k][2])
|
|
99
99
|
for (let l = 0; l < 3; l++) {
|
|
100
100
|
q[k][l] /= r
|
|
101
101
|
}
|
|
@@ -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
|
package/src/primitives/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const { isGT, isGTE, isNumberArray } = require('./commonChecks')
|
|
|
11
11
|
* Construct a Z axis-aligned solid cylinder in three dimensional space with rounded ends.
|
|
12
12
|
* @param {Object} [options] - options for construction
|
|
13
13
|
* @param {Array} [options.center=[0,0,0]] - center of cylinder
|
|
14
|
-
* @param {
|
|
14
|
+
* @param {Number} [options.height=2] - height of cylinder
|
|
15
15
|
* @param {Number} [options.radius=1] - radius of cylinder
|
|
16
16
|
* @param {Number} [options.roundRadius=0.2] - radius of rounded edges
|
|
17
17
|
* @param {Number} [options.segments=32] - number of segments to create per full rotation
|
|
@@ -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
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
const vec2 = require('../maths/vec2')
|
|
2
|
+
|
|
3
|
+
const geom2 = require('../geometries/geom2')
|
|
4
|
+
|
|
5
|
+
const { isNumberArray } = require('./commonChecks')
|
|
6
|
+
|
|
7
|
+
const NEPS = 1e-13
|
|
8
|
+
|
|
9
|
+
// returns angle C
|
|
10
|
+
const solveAngleFromSSS = (a, b, c) => Math.acos(((a * a) + (b * b) - (c * c)) / (2 * a * b))
|
|
11
|
+
|
|
12
|
+
// returns side c
|
|
13
|
+
const solveSideFromSAS = (a, C, b) => {
|
|
14
|
+
if (C > NEPS) {
|
|
15
|
+
return Math.sqrt(a * a + b * b - 2 * a * b * Math.cos(C))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Explained in https://www.nayuki.io/page/numerically-stable-law-of-cosines
|
|
19
|
+
return Math.sqrt((a - b) * (a - b) + a * b * C * C * (1 - C * C / 12))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// AAA is when three angles of a triangle, but no sides
|
|
23
|
+
const solveAAA = (angles) => {
|
|
24
|
+
const eps = Math.abs(angles[0] + angles[1] + angles[2] - Math.PI)
|
|
25
|
+
if (eps > NEPS) throw new Error('AAA triangles require angles that sum to PI')
|
|
26
|
+
|
|
27
|
+
const A = angles[0]
|
|
28
|
+
const B = angles[1]
|
|
29
|
+
const C = Math.PI - A - B
|
|
30
|
+
|
|
31
|
+
// Note: This is not 100% proper but...
|
|
32
|
+
// default the side c length to 1
|
|
33
|
+
// solve the other lengths
|
|
34
|
+
const c = 1
|
|
35
|
+
const a = (c / Math.sin(C)) * Math.sin(A)
|
|
36
|
+
const b = (c / Math.sin(C)) * Math.sin(B)
|
|
37
|
+
return createTriangle(A, B, C, a, b, c)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// AAS is when two angles and one side are known, and the side is not between the angles
|
|
41
|
+
const solveAAS = (values) => {
|
|
42
|
+
const A = values[0]
|
|
43
|
+
const B = values[1]
|
|
44
|
+
const C = Math.PI + NEPS - A - B
|
|
45
|
+
|
|
46
|
+
if (C < NEPS) throw new Error('AAS triangles require angles that sum to PI')
|
|
47
|
+
|
|
48
|
+
const a = values[2]
|
|
49
|
+
const b = (a / Math.sin(A)) * Math.sin(B)
|
|
50
|
+
const c = (a / Math.sin(A)) * Math.sin(C)
|
|
51
|
+
return createTriangle(A, B, C, a, b, c)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ASA is when two angles and the side between the angles are known
|
|
55
|
+
const solveASA = (values) => {
|
|
56
|
+
const A = values[0]
|
|
57
|
+
const B = values[2]
|
|
58
|
+
const C = Math.PI + NEPS - A - B
|
|
59
|
+
|
|
60
|
+
if (C < NEPS) throw new Error('ASA triangles require angles that sum to PI')
|
|
61
|
+
|
|
62
|
+
const c = values[1]
|
|
63
|
+
const a = (c / Math.sin(C)) * Math.sin(A)
|
|
64
|
+
const b = (c / Math.sin(C)) * Math.sin(B)
|
|
65
|
+
return createTriangle(A, B, C, a, b, c)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// SAS is when two sides and the angle between them are known
|
|
69
|
+
const solveSAS = (values) => {
|
|
70
|
+
const c = values[0]
|
|
71
|
+
const B = values[1]
|
|
72
|
+
const a = values[2]
|
|
73
|
+
|
|
74
|
+
const b = solveSideFromSAS(c, B, a)
|
|
75
|
+
|
|
76
|
+
const A = solveAngleFromSSS(b, c, a) // solve for A
|
|
77
|
+
const C = Math.PI - A - B
|
|
78
|
+
return createTriangle(A, B, C, a, b, c)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// SSA is when two sides and an angle that is not the angle between the sides are known
|
|
82
|
+
const solveSSA = (values) => {
|
|
83
|
+
const c = values[0]
|
|
84
|
+
const a = values[1]
|
|
85
|
+
const C = values[2]
|
|
86
|
+
|
|
87
|
+
const A = Math.asin(a * Math.sin(C) / c)
|
|
88
|
+
const B = Math.PI - A - C
|
|
89
|
+
|
|
90
|
+
const b = (c / Math.sin(C)) * Math.sin(B)
|
|
91
|
+
return createTriangle(A, B, C, a, b, c)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// SSS is when we know three sides of the triangle
|
|
95
|
+
const solveSSS = (lengths) => {
|
|
96
|
+
const a = lengths[1]
|
|
97
|
+
const b = lengths[2]
|
|
98
|
+
const c = lengths[0]
|
|
99
|
+
if (((a + b) <= c) || ((b + c) <= a) || ((c + a) <= b)) {
|
|
100
|
+
throw new Error('SSS triangle is incorrect, as the longest side is longer than the sum of the other sides')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const A = solveAngleFromSSS(b, c, a) // solve for A
|
|
104
|
+
const B = solveAngleFromSSS(c, a, b) // solve for B
|
|
105
|
+
const C = Math.PI - A - B
|
|
106
|
+
return createTriangle(A, B, C, a, b, c)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const createTriangle = (A, B, C, a, b, c) => {
|
|
110
|
+
const p0 = vec2.fromValues(0, 0) // everything starts from 0, 0
|
|
111
|
+
const p1 = vec2.fromValues(c, 0)
|
|
112
|
+
const p2 = vec2.fromValues(a, 0)
|
|
113
|
+
vec2.add(p2, vec2.rotate(p2, p2, [0, 0], Math.PI - B), p1)
|
|
114
|
+
return geom2.fromPoints([p0, p1, p2])
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Construct a triangle in two dimensional space from the given options.
|
|
119
|
+
* The triangle is always constructed CCW from the origin, [0, 0, 0].
|
|
120
|
+
* @see https://www.mathsisfun.com/algebra/trig-solving-triangles.html
|
|
121
|
+
* @param {Object} [options] - options for construction
|
|
122
|
+
* @param {String} [options.type='SSS' - type of triangle to construct; A ~ angle, S ~ side
|
|
123
|
+
* @param {Array} [options.values=[1,1,1]] - angle (radians) of corners or length of sides
|
|
124
|
+
* @returns {geom2} new 2D geometry
|
|
125
|
+
* @alias module:modeling/primitives.triangle
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* let myshape = triangle({type: 'AAS', values: [degToRad(62), degToRad(35), 7]})
|
|
129
|
+
*/
|
|
130
|
+
const triangle = (options) => {
|
|
131
|
+
const defaults = {
|
|
132
|
+
type: 'SSS',
|
|
133
|
+
values: [1, 1, 1]
|
|
134
|
+
}
|
|
135
|
+
let { type, values } = Object.assign({}, defaults, options)
|
|
136
|
+
|
|
137
|
+
if (typeof (type) !== 'string') throw new Error('triangle type must be a string')
|
|
138
|
+
type = type.toUpperCase()
|
|
139
|
+
if (!((type[0] === 'A' || type[0] === 'S') &&
|
|
140
|
+
(type[1] === 'A' || type[1] === 'S') &&
|
|
141
|
+
(type[2] === 'A' || type[2] === 'S'))) throw new Error('triangle type must contain three letters; A or S')
|
|
142
|
+
|
|
143
|
+
if (!isNumberArray(values, 3)) throw new Error('triangle values must contain three values')
|
|
144
|
+
if (!values.every((n) => n > 0)) throw new Error('triangle values must be greater than zero')
|
|
145
|
+
|
|
146
|
+
switch (type) {
|
|
147
|
+
case 'AAA':
|
|
148
|
+
return solveAAA(values)
|
|
149
|
+
case 'AAS':
|
|
150
|
+
return solveAAS(values)
|
|
151
|
+
case 'ASA':
|
|
152
|
+
return solveASA(values)
|
|
153
|
+
case 'SAS':
|
|
154
|
+
return solveSAS(values)
|
|
155
|
+
case 'SSA':
|
|
156
|
+
return solveSSA(values)
|
|
157
|
+
case 'SSS':
|
|
158
|
+
return solveSSS(values)
|
|
159
|
+
default:
|
|
160
|
+
throw new Error('invalid triangle type, try again')
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = triangle
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const test = require('ava')
|
|
2
|
+
|
|
3
|
+
const { triangle } = require('./index')
|
|
4
|
+
|
|
5
|
+
const degToRad = require('../utils/degToRad')
|
|
6
|
+
const geom2 = require('../geometries/geom2')
|
|
7
|
+
|
|
8
|
+
const comparePoints = require('../../test/helpers/comparePoints')
|
|
9
|
+
|
|
10
|
+
test('triangle (defaults)', (t) => {
|
|
11
|
+
const geometry = triangle()
|
|
12
|
+
const obs = geom2.toPoints(geometry)
|
|
13
|
+
const exp = [
|
|
14
|
+
[0, 0],
|
|
15
|
+
[1, 0],
|
|
16
|
+
[0.5000000000000002, 0.8660254037844387]
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
t.deepEqual(obs.length, 3)
|
|
20
|
+
t.true(comparePoints(obs, exp))
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('triangle (options)', (t) => {
|
|
24
|
+
// test SSS
|
|
25
|
+
let geometry = triangle({ type: 'SSS', values: [7, 8, 6] })
|
|
26
|
+
let obs = geom2.toPoints(geometry)
|
|
27
|
+
let exp = [
|
|
28
|
+
[0, 0],
|
|
29
|
+
[7, 0],
|
|
30
|
+
[1.5, 5.809475019311125]
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
t.deepEqual(obs.length, 3)
|
|
34
|
+
t.true(comparePoints(obs, exp))
|
|
35
|
+
|
|
36
|
+
// test AAA
|
|
37
|
+
geometry = triangle({ type: 'AAA', values: [Math.PI / 2, Math.PI / 4, Math.PI / 4] })
|
|
38
|
+
obs = geom2.toPoints(geometry)
|
|
39
|
+
exp = [
|
|
40
|
+
[0, 0],
|
|
41
|
+
[1, 0],
|
|
42
|
+
[0, 1.0000000000000002]
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
t.deepEqual(obs.length, 3)
|
|
46
|
+
t.true(comparePoints(obs, exp))
|
|
47
|
+
|
|
48
|
+
// test AAS
|
|
49
|
+
geometry = triangle({ type: 'AAS', values: [degToRad(62), degToRad(35), 7] })
|
|
50
|
+
obs = geom2.toPoints(geometry)
|
|
51
|
+
exp = [
|
|
52
|
+
[0, 0],
|
|
53
|
+
[7.86889631692936, 0],
|
|
54
|
+
[2.1348320069064197, 4.015035054457325]
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
t.deepEqual(obs.length, 3)
|
|
58
|
+
t.true(comparePoints(obs, exp))
|
|
59
|
+
|
|
60
|
+
// test ASA
|
|
61
|
+
geometry = triangle({ type: 'ASA', values: [degToRad(76), 9, degToRad(34)] })
|
|
62
|
+
obs = geom2.toPoints(geometry)
|
|
63
|
+
exp = [
|
|
64
|
+
[0, 0],
|
|
65
|
+
[9, 0],
|
|
66
|
+
[1.295667368233083, 5.196637976713814]
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
t.deepEqual(obs.length, 3)
|
|
70
|
+
t.true(comparePoints(obs, exp))
|
|
71
|
+
|
|
72
|
+
// test SAS
|
|
73
|
+
geometry = triangle({ type: 'SAS', values: [5, degToRad(49), 7] })
|
|
74
|
+
obs = geom2.toPoints(geometry)
|
|
75
|
+
exp = [
|
|
76
|
+
[0, 0],
|
|
77
|
+
[5, 0],
|
|
78
|
+
[0.4075867970664495, 5.282967061559405]
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
t.deepEqual(obs.length, 3)
|
|
82
|
+
t.true(comparePoints(obs, exp))
|
|
83
|
+
|
|
84
|
+
// test SSA
|
|
85
|
+
geometry = triangle({ type: 'SSA', values: [8, 13, degToRad(31)] })
|
|
86
|
+
obs = geom2.toPoints(geometry)
|
|
87
|
+
exp = [
|
|
88
|
+
[0, 0],
|
|
89
|
+
[8, 0],
|
|
90
|
+
[8.494946725906148, 12.990574573070846]
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
t.deepEqual(obs.length, 3)
|
|
94
|
+
t.true(comparePoints(obs, exp))
|
|
95
|
+
})
|
|
@@ -8,9 +8,7 @@ const compareVectors = require('./compareVectors')
|
|
|
8
8
|
*/
|
|
9
9
|
const comparePolygons = (poly1, poly2) => {
|
|
10
10
|
if (poly1.vertices.length === poly2.vertices.length) {
|
|
11
|
-
return poly1.vertices.reduce((valid, vertex, index) =>
|
|
12
|
-
return valid && compareVectors(poly1.vertices[index], poly2.vertices[index])
|
|
13
|
-
}, true)
|
|
11
|
+
return poly1.vertices.reduce((valid, vertex, index) => valid && compareVectors(poly1.vertices[index], poly2.vertices[index]), true)
|
|
14
12
|
}
|
|
15
13
|
return false
|
|
16
14
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Compare two numeric values for near equality.
|
|
2
2
|
// the given test is fails if the numeric values are outside the given epsilon
|
|
3
3
|
const nearlyEqual = (t, a, b, epsilon, failMessage) => {
|
|
4
|
-
// console.log('nearEqual(t,'+a+','+b+','+epsilon+')')
|
|
5
4
|
if (a === b) { // shortcut, also handles infinities and NaNs
|
|
6
5
|
return true
|
|
7
6
|
}
|
|
@@ -10,11 +9,9 @@ const nearlyEqual = (t, a, b, epsilon, failMessage) => {
|
|
|
10
9
|
const absB = Math.abs(b)
|
|
11
10
|
const diff = Math.abs(a - b)
|
|
12
11
|
if (Number.isNaN(diff)) {
|
|
13
|
-
|
|
12
|
+
failMessage = failMessage === undefined ? 'difference is not a number' : failMessage
|
|
13
|
+
t.fail(failMessage + '(' + a + ',' + b + ')')
|
|
14
14
|
}
|
|
15
|
-
// console.log(absA)
|
|
16
|
-
// console.log(absB)
|
|
17
|
-
// console.log(diff)
|
|
18
15
|
if (a === 0 || b === 0 || diff < Number.EPSILON) {
|
|
19
16
|
// a or b is zero or both are extremely close to it
|
|
20
17
|
// relative error is less meaningful here
|
|
@@ -25,7 +22,6 @@ const nearlyEqual = (t, a, b, epsilon, failMessage) => {
|
|
|
25
22
|
}
|
|
26
23
|
// use relative error
|
|
27
24
|
const relative = (diff / Math.min((absA + absB), Number.MAX_VALUE))
|
|
28
|
-
// console.log(relative)
|
|
29
25
|
if (relative > epsilon) {
|
|
30
26
|
failMessage = failMessage === undefined ? 'Numbers outside of epsilon' : failMessage
|
|
31
27
|
t.fail(failMessage + '(' + a + ',' + b + ')')
|