@khanacademy/kmath 0.4.7 → 1.0.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/dist/index.js +102 -412
- package/dist/index.js.map +1 -1
- package/package.json +14 -7
- package/dist/es/index.js +0 -1122
- package/dist/es/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,40 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-utils';
|
|
2
|
+
import _ from 'underscore';
|
|
3
|
+
import { approximateEqual, approximateDeepEqual } from '@khanacademy/perseus-core';
|
|
4
|
+
import $ from 'jquery';
|
|
2
5
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var perseusUtils = require('@khanacademy/perseus-utils');
|
|
6
|
-
var _ = require('underscore');
|
|
7
|
-
var perseusCore = require('@khanacademy/perseus-core');
|
|
8
|
-
var $ = require('jquery');
|
|
9
|
-
|
|
10
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
11
|
-
|
|
12
|
-
var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
|
|
13
|
-
var $__default = /*#__PURE__*/_interopDefaultLegacy($);
|
|
14
|
-
|
|
15
|
-
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
16
6
|
const libName = "@khanacademy/kmath";
|
|
17
|
-
const libVersion = "0.
|
|
18
|
-
|
|
7
|
+
const libVersion = "1.0.0";
|
|
8
|
+
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
19
9
|
|
|
20
|
-
/**
|
|
21
|
-
* Number Utils
|
|
22
|
-
* A number is a js-number, e.g. 5.12
|
|
23
|
-
*/
|
|
24
10
|
const DEFAULT_TOLERANCE = 1e-9;
|
|
25
|
-
|
|
26
|
-
// TODO: Should this just be Number.Epsilon
|
|
27
11
|
const EPSILON = Math.pow(2, -42);
|
|
28
12
|
function is$2(x) {
|
|
29
|
-
return
|
|
13
|
+
return _.isNumber(x) && !_.isNaN(x);
|
|
30
14
|
}
|
|
31
15
|
function equal$4(x, y, tolerance) {
|
|
32
|
-
// Checking for undefined makes this function behave nicely
|
|
33
|
-
// with vectors of different lengths that are _.zip'd together
|
|
34
16
|
if (x == null || y == null) {
|
|
35
17
|
return x === y;
|
|
36
18
|
}
|
|
37
|
-
// We check === here so that +/-Infinity comparisons work correctly
|
|
38
19
|
if (x === y) {
|
|
39
20
|
return true;
|
|
40
21
|
}
|
|
@@ -43,21 +24,16 @@ function equal$4(x, y, tolerance) {
|
|
|
43
24
|
}
|
|
44
25
|
return Math.abs(x - y) < tolerance;
|
|
45
26
|
}
|
|
46
|
-
function sign$1(x, tolerance)
|
|
27
|
+
function sign$1(x, tolerance) {
|
|
47
28
|
return equal$4(x, 0, tolerance) ? 0 : Math.abs(x) / x;
|
|
48
29
|
}
|
|
49
30
|
function isInteger(num, tolerance) {
|
|
50
31
|
return equal$4(Math.round(num), num, tolerance);
|
|
51
32
|
}
|
|
52
|
-
|
|
53
|
-
// Round a number to a certain number of decimal places
|
|
54
33
|
function round$2(num, precision) {
|
|
55
34
|
const factor = Math.pow(10, precision);
|
|
56
35
|
return Math.round(num * factor) / factor;
|
|
57
36
|
}
|
|
58
|
-
|
|
59
|
-
// Round num to the nearest multiple of increment
|
|
60
|
-
// i.e. roundTo(83, 5) -> 85
|
|
61
37
|
function roundTo$2(num, increment) {
|
|
62
38
|
return Math.round(num / increment) * increment;
|
|
63
39
|
}
|
|
@@ -67,26 +43,7 @@ function floorTo$2(num, increment) {
|
|
|
67
43
|
function ceilTo$2(num, increment) {
|
|
68
44
|
return Math.ceil(num / increment) * increment;
|
|
69
45
|
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* toFraction
|
|
73
|
-
*
|
|
74
|
-
* Returns a [numerator, denominator] array rational representation
|
|
75
|
-
* of `decimal`
|
|
76
|
-
*
|
|
77
|
-
* See http://en.wikipedia.org/wiki/Continued_fraction for implementation
|
|
78
|
-
* details
|
|
79
|
-
*
|
|
80
|
-
* toFraction(4/8) => [1, 2]
|
|
81
|
-
* toFraction(0.66) => [33, 50]
|
|
82
|
-
* toFraction(0.66, 0.01) => [2/3]
|
|
83
|
-
* toFraction(283 + 1/3) => [850, 3]
|
|
84
|
-
*/
|
|
85
|
-
function toFraction(decimal) {
|
|
86
|
-
let tolerance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : EPSILON;
|
|
87
|
-
let maxDenominator = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1000;
|
|
88
|
-
// Initialize everything to compute successive terms of
|
|
89
|
-
// continued-fraction approximations via recurrence relation
|
|
46
|
+
function toFraction(decimal, tolerance = EPSILON, maxDenominator = 1000) {
|
|
90
47
|
let n = [1, 0];
|
|
91
48
|
let d = [0, 1];
|
|
92
49
|
let a = Math.floor(decimal);
|
|
@@ -100,9 +57,6 @@ function toFraction(decimal) {
|
|
|
100
57
|
a = Math.floor(1 / rem);
|
|
101
58
|
rem = 1 / rem - a;
|
|
102
59
|
}
|
|
103
|
-
|
|
104
|
-
// We failed to find a nice rational representation,
|
|
105
|
-
// so return an irrational "fraction"
|
|
106
60
|
return [decimal, 1];
|
|
107
61
|
}
|
|
108
62
|
|
|
@@ -110,31 +64,24 @@ var number = /*#__PURE__*/Object.freeze({
|
|
|
110
64
|
__proto__: null,
|
|
111
65
|
DEFAULT_TOLERANCE: DEFAULT_TOLERANCE,
|
|
112
66
|
EPSILON: EPSILON,
|
|
113
|
-
|
|
67
|
+
ceilTo: ceilTo$2,
|
|
114
68
|
equal: equal$4,
|
|
115
|
-
|
|
69
|
+
floorTo: floorTo$2,
|
|
70
|
+
is: is$2,
|
|
116
71
|
isInteger: isInteger,
|
|
117
72
|
round: round$2,
|
|
118
73
|
roundTo: roundTo$2,
|
|
119
|
-
|
|
120
|
-
ceilTo: ceilTo$2,
|
|
74
|
+
sign: sign$1,
|
|
121
75
|
toFraction: toFraction
|
|
122
76
|
});
|
|
123
77
|
|
|
124
|
-
/**
|
|
125
|
-
* Vector Utils
|
|
126
|
-
* A vector is an array of numbers e.g. [0, 3, 4].
|
|
127
|
-
*/
|
|
128
78
|
function arraySum(array) {
|
|
129
79
|
return array.reduce((memo, arg) => memo + arg, 0);
|
|
130
80
|
}
|
|
131
81
|
function arrayProduct(array) {
|
|
132
82
|
return array.reduce((memo, arg) => memo * arg, 1);
|
|
133
83
|
}
|
|
134
|
-
function zip() {
|
|
135
|
-
for (var _len = arguments.length, arrays = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
136
|
-
arrays[_key] = arguments[_key];
|
|
137
|
-
}
|
|
84
|
+
function zip(...arrays) {
|
|
138
85
|
const n = Math.min(...arrays.map(a => a.length));
|
|
139
86
|
const results = [];
|
|
140
87
|
for (let i = 0; i < n; i++) {
|
|
@@ -145,16 +92,6 @@ function zip() {
|
|
|
145
92
|
function map(pair, f) {
|
|
146
93
|
return [f(pair[0], 0), f(pair[1], 1)];
|
|
147
94
|
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Checks if the given vector contains only numbers and, optionally, is of the
|
|
151
|
-
* right dimension (length).
|
|
152
|
-
*
|
|
153
|
-
* is([1, 2, 3]) -> true
|
|
154
|
-
* is([1, "Hello", 3]) -> false
|
|
155
|
-
* is([1, 2, 3], 1) -> false
|
|
156
|
-
*/
|
|
157
|
-
|
|
158
95
|
function is$1(vec, dimension) {
|
|
159
96
|
if (!Array.isArray(vec)) {
|
|
160
97
|
return false;
|
|
@@ -164,46 +101,30 @@ function is$1(vec, dimension) {
|
|
|
164
101
|
}
|
|
165
102
|
return vec.every(is$2);
|
|
166
103
|
}
|
|
167
|
-
|
|
168
|
-
// Normalize to a unit vector
|
|
169
104
|
function normalize(v) {
|
|
170
105
|
return scale(v, 1 / length(v));
|
|
171
106
|
}
|
|
172
|
-
|
|
173
|
-
// Length/magnitude of a vector
|
|
174
107
|
function length(v) {
|
|
175
108
|
return Math.sqrt(dot(v, v));
|
|
176
109
|
}
|
|
177
|
-
// Dot product of two vectors
|
|
178
110
|
function dot(a, b) {
|
|
179
111
|
const zipped = zip(a, b);
|
|
180
112
|
const multiplied = zipped.map(arrayProduct);
|
|
181
113
|
return arraySum(multiplied);
|
|
182
114
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
*
|
|
186
|
-
* add([1, 2], [3, 4]) -> [4, 6]
|
|
187
|
-
*/
|
|
188
|
-
function add$1() {
|
|
189
|
-
const zipped = zip(...arguments);
|
|
190
|
-
// @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
|
|
115
|
+
function add$1(...vecs) {
|
|
116
|
+
const zipped = zip(...vecs);
|
|
191
117
|
return zipped.map(arraySum);
|
|
192
118
|
}
|
|
193
119
|
function subtract(v1, v2) {
|
|
194
|
-
// @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
|
|
195
120
|
return zip(v1, v2).map(dim => dim[0] - dim[1]);
|
|
196
121
|
}
|
|
197
122
|
function negate(v) {
|
|
198
|
-
// @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
|
|
199
123
|
return v.map(x => {
|
|
200
124
|
return -x;
|
|
201
125
|
});
|
|
202
126
|
}
|
|
203
|
-
|
|
204
|
-
// Scale a vector
|
|
205
127
|
function scale(v1, scalar) {
|
|
206
|
-
// @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
|
|
207
128
|
return v1.map(x => {
|
|
208
129
|
return x * scalar;
|
|
209
130
|
});
|
|
@@ -212,9 +133,6 @@ function equal$3(v1, v2, tolerance) {
|
|
|
212
133
|
return v1.length === v2.length && zip(v1, v2).every(pair => equal$4(pair[0], pair[1], tolerance));
|
|
213
134
|
}
|
|
214
135
|
function codirectional(v1, v2, tolerance) {
|
|
215
|
-
// The origin is trivially codirectional with all other vectors.
|
|
216
|
-
// This gives nice semantics for codirectionality between points when
|
|
217
|
-
// comparing their difference vectors.
|
|
218
136
|
if (equal$4(length(v1), 0, tolerance) || equal$4(length(v2), 0, tolerance)) {
|
|
219
137
|
return true;
|
|
220
138
|
}
|
|
@@ -225,48 +143,24 @@ function codirectional(v1, v2, tolerance) {
|
|
|
225
143
|
function collinear$1(v1, v2, tolerance) {
|
|
226
144
|
return codirectional(v1, v2, tolerance) || codirectional(v1, negate(v2), tolerance);
|
|
227
145
|
}
|
|
228
|
-
|
|
229
|
-
// TODO(jeremy) These coordinate conversion functions really only handle 2D points (ie. [number, number])
|
|
230
|
-
|
|
231
|
-
// Convert a cartesian coordinate into a radian polar coordinate
|
|
232
146
|
function polarRadFromCart$1(v) {
|
|
233
147
|
const radius = length(v);
|
|
234
148
|
let theta = Math.atan2(v[1], v[0]);
|
|
235
|
-
|
|
236
|
-
// Convert angle range from [-pi, pi] to [0, 2pi]
|
|
237
149
|
if (theta < 0) {
|
|
238
150
|
theta += 2 * Math.PI;
|
|
239
151
|
}
|
|
240
152
|
return [radius, theta];
|
|
241
153
|
}
|
|
242
|
-
|
|
243
|
-
// Converts a cartesian coordinate into a degree polar coordinate
|
|
244
|
-
function polarDegFromCart$1(v) /* TODO: convert to tuple/Point */{
|
|
154
|
+
function polarDegFromCart$1(v) {
|
|
245
155
|
const polar = polarRadFromCart$1(v);
|
|
246
156
|
return [polar[0], polar[1] * 180 / Math.PI];
|
|
247
157
|
}
|
|
248
|
-
|
|
249
|
-
/* Convert a polar coordinate into a cartesian coordinate
|
|
250
|
-
*
|
|
251
|
-
* Examples:
|
|
252
|
-
* cartFromPolarRad(5, Math.PI)
|
|
253
|
-
*/
|
|
254
|
-
function cartFromPolarRad$1(radius) /* TODO: convert to tuple/Point */{
|
|
255
|
-
let theta = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
158
|
+
function cartFromPolarRad$1(radius, theta = 0) {
|
|
256
159
|
return [radius * Math.cos(theta), radius * Math.sin(theta)];
|
|
257
160
|
}
|
|
258
|
-
|
|
259
|
-
/* Convert a polar coordinate into a cartesian coordinate
|
|
260
|
-
*
|
|
261
|
-
* Examples:
|
|
262
|
-
* cartFromPolarDeg(5, 30)
|
|
263
|
-
*/
|
|
264
|
-
function cartFromPolarDeg$1(radius) {
|
|
265
|
-
let theta = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
161
|
+
function cartFromPolarDeg$1(radius, theta = 0) {
|
|
266
162
|
return cartFromPolarRad$1(radius, theta * Math.PI / 180);
|
|
267
163
|
}
|
|
268
|
-
|
|
269
|
-
// Rotate vector
|
|
270
164
|
function rotateRad$1(v, theta) {
|
|
271
165
|
const polar = polarRadFromCart$1(v);
|
|
272
166
|
const angle = polar[1] + theta;
|
|
@@ -277,80 +171,59 @@ function rotateDeg$1(v, theta) {
|
|
|
277
171
|
const angle = polar[1] + theta;
|
|
278
172
|
return cartFromPolarDeg$1(polar[0], angle);
|
|
279
173
|
}
|
|
280
|
-
|
|
281
|
-
// Angle between two vectors
|
|
282
174
|
function angleRad(v1, v2) {
|
|
283
175
|
return Math.acos(dot(v1, v2) / (length(v1) * length(v2)));
|
|
284
176
|
}
|
|
285
177
|
function angleDeg(v1, v2) {
|
|
286
178
|
return angleRad(v1, v2) * 180 / Math.PI;
|
|
287
179
|
}
|
|
288
|
-
|
|
289
|
-
// Vector projection of v1 onto v2
|
|
290
180
|
function projection(v1, v2) {
|
|
291
181
|
const scalar = dot(v1, v2) / dot(v2, v2);
|
|
292
182
|
return scale(v2, scalar);
|
|
293
183
|
}
|
|
294
|
-
|
|
295
|
-
// Round each number to a certain number of decimal places
|
|
296
184
|
function round$1(vec, precision) {
|
|
297
|
-
// @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
|
|
298
185
|
return vec.map((elem, i) => round$2(elem, precision[i] || precision));
|
|
299
186
|
}
|
|
300
|
-
|
|
301
|
-
// Round each number to the nearest increment
|
|
302
|
-
|
|
303
187
|
function roundTo$1(vec, increment) {
|
|
304
|
-
// @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
|
|
305
188
|
return vec.map((elem, i) => roundTo$2(elem, increment[i] || increment));
|
|
306
189
|
}
|
|
307
190
|
function floorTo$1(vec, increment) {
|
|
308
|
-
// @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
|
|
309
191
|
return vec.map((elem, i) => floorTo$2(elem, increment[i] || increment));
|
|
310
192
|
}
|
|
311
193
|
function ceilTo$1(vec, increment) {
|
|
312
|
-
// @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
|
|
313
194
|
return vec.map((elem, i) => ceilTo$2(elem, increment[i] || increment));
|
|
314
195
|
}
|
|
315
196
|
|
|
316
197
|
var vector$1 = /*#__PURE__*/Object.freeze({
|
|
317
198
|
__proto__: null,
|
|
318
|
-
zip: zip,
|
|
319
|
-
map: map,
|
|
320
|
-
is: is$1,
|
|
321
|
-
normalize: normalize,
|
|
322
|
-
length: length,
|
|
323
|
-
dot: dot,
|
|
324
199
|
add: add$1,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
200
|
+
angleDeg: angleDeg,
|
|
201
|
+
angleRad: angleRad,
|
|
202
|
+
cartFromPolarDeg: cartFromPolarDeg$1,
|
|
203
|
+
cartFromPolarRad: cartFromPolarRad$1,
|
|
204
|
+
ceilTo: ceilTo$1,
|
|
329
205
|
codirectional: codirectional,
|
|
330
206
|
collinear: collinear$1,
|
|
331
|
-
|
|
207
|
+
dot: dot,
|
|
208
|
+
equal: equal$3,
|
|
209
|
+
floorTo: floorTo$1,
|
|
210
|
+
is: is$1,
|
|
211
|
+
length: length,
|
|
212
|
+
map: map,
|
|
213
|
+
negate: negate,
|
|
214
|
+
normalize: normalize,
|
|
332
215
|
polarDegFromCart: polarDegFromCart$1,
|
|
333
|
-
|
|
334
|
-
cartFromPolarDeg: cartFromPolarDeg$1,
|
|
335
|
-
rotateRad: rotateRad$1,
|
|
336
|
-
rotateDeg: rotateDeg$1,
|
|
337
|
-
angleRad: angleRad,
|
|
338
|
-
angleDeg: angleDeg,
|
|
216
|
+
polarRadFromCart: polarRadFromCart$1,
|
|
339
217
|
projection: projection,
|
|
218
|
+
rotateDeg: rotateDeg$1,
|
|
219
|
+
rotateRad: rotateRad$1,
|
|
340
220
|
round: round$1,
|
|
341
221
|
roundTo: roundTo$1,
|
|
342
|
-
|
|
343
|
-
|
|
222
|
+
scale: scale,
|
|
223
|
+
subtract: subtract,
|
|
224
|
+
zip: zip
|
|
344
225
|
});
|
|
345
226
|
|
|
346
|
-
/**
|
|
347
|
-
* Point Utils
|
|
348
|
-
* A point is an array of two numbers e.g. [0, 0].
|
|
349
|
-
*/
|
|
350
|
-
|
|
351
|
-
// A point, in 2D, 3D, or nD space.
|
|
352
|
-
|
|
353
|
-
// Rotate point (around origin unless a center is specified)
|
|
354
227
|
function rotateRad(point, theta, center) {
|
|
355
228
|
if (center === undefined) {
|
|
356
229
|
return rotateRad$1(point, theta);
|
|
@@ -365,13 +238,9 @@ function rotateDeg(point, theta, center) {
|
|
|
365
238
|
return add$1(center, rotateDeg$1(subtract(point, center), theta));
|
|
366
239
|
}
|
|
367
240
|
}
|
|
368
|
-
|
|
369
|
-
// Distance between two points
|
|
370
241
|
function distanceToPoint$1(point1, point2) {
|
|
371
242
|
return length(subtract(point1, point2));
|
|
372
243
|
}
|
|
373
|
-
|
|
374
|
-
// Distance between point and line
|
|
375
244
|
function distanceToLine(point, line) {
|
|
376
245
|
const lv = subtract(line[1], line[0]);
|
|
377
246
|
const pv = subtract(point, line[0]);
|
|
@@ -379,8 +248,6 @@ function distanceToLine(point, line) {
|
|
|
379
248
|
const distancePv = subtract(projectedPv, pv);
|
|
380
249
|
return length(distancePv);
|
|
381
250
|
}
|
|
382
|
-
|
|
383
|
-
// Reflect point over line
|
|
384
251
|
function reflectOverLine(point, line) {
|
|
385
252
|
const lv = subtract(line[1], line[0]);
|
|
386
253
|
const pv = subtract(point, line[0]);
|
|
@@ -388,17 +255,7 @@ function reflectOverLine(point, line) {
|
|
|
388
255
|
const reflectedPv = subtract(scale(projectedPv, 2), pv);
|
|
389
256
|
return add$1(line[0], reflectedPv);
|
|
390
257
|
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Compares two points, returning -1, 0, or 1, for use with
|
|
394
|
-
* Array.prototype.sort
|
|
395
|
-
*
|
|
396
|
-
* Note: This technically doesn't satisfy the total-ordering
|
|
397
|
-
* requirements of Array.prototype.sort unless equalityTolerance
|
|
398
|
-
* is 0. In some cases very close points that compare within a
|
|
399
|
-
* few equalityTolerances could appear in the wrong order.
|
|
400
|
-
*/
|
|
401
|
-
function compare(point1, point2, equalityTolerance) /* TODO: convert to -1 | 0 | 1 type */{
|
|
258
|
+
function compare(point1, point2, equalityTolerance) {
|
|
402
259
|
if (point1.length !== point2.length) {
|
|
403
260
|
return point1.length - point2.length;
|
|
404
261
|
}
|
|
@@ -409,23 +266,15 @@ function compare(point1, point2, equalityTolerance) /* TODO: convert to -1 | 0 |
|
|
|
409
266
|
}
|
|
410
267
|
return 0;
|
|
411
268
|
}
|
|
412
|
-
|
|
413
|
-
// Check if a value is a point
|
|
414
269
|
const is = is$1;
|
|
415
|
-
|
|
416
|
-
// Add and subtract vector(s)
|
|
417
270
|
const addVector = add$1;
|
|
418
271
|
const addVectors = add$1;
|
|
419
272
|
const subtractVector = subtract;
|
|
420
273
|
const equal$2 = equal$3;
|
|
421
|
-
|
|
422
|
-
// Convert from cartesian to polar and back
|
|
423
274
|
const polarRadFromCart = polarRadFromCart$1;
|
|
424
275
|
const polarDegFromCart = polarDegFromCart$1;
|
|
425
276
|
const cartFromPolarRad = cartFromPolarRad$1;
|
|
426
277
|
const cartFromPolarDeg = cartFromPolarDeg$1;
|
|
427
|
-
|
|
428
|
-
// Rounding
|
|
429
278
|
const round = round$1;
|
|
430
279
|
const roundTo = roundTo$1;
|
|
431
280
|
const floorTo = floorTo$1;
|
|
@@ -433,31 +282,27 @@ const ceilTo = ceilTo$1;
|
|
|
433
282
|
|
|
434
283
|
var point = /*#__PURE__*/Object.freeze({
|
|
435
284
|
__proto__: null,
|
|
436
|
-
rotateRad: rotateRad,
|
|
437
|
-
rotateDeg: rotateDeg,
|
|
438
|
-
distanceToPoint: distanceToPoint$1,
|
|
439
|
-
distanceToLine: distanceToLine,
|
|
440
|
-
reflectOverLine: reflectOverLine,
|
|
441
|
-
compare: compare,
|
|
442
|
-
is: is,
|
|
443
285
|
addVector: addVector,
|
|
444
286
|
addVectors: addVectors,
|
|
445
|
-
|
|
287
|
+
cartFromPolarDeg: cartFromPolarDeg,
|
|
288
|
+
cartFromPolarRad: cartFromPolarRad,
|
|
289
|
+
ceilTo: ceilTo,
|
|
290
|
+
compare: compare,
|
|
291
|
+
distanceToLine: distanceToLine,
|
|
292
|
+
distanceToPoint: distanceToPoint$1,
|
|
446
293
|
equal: equal$2,
|
|
447
|
-
|
|
294
|
+
floorTo: floorTo,
|
|
295
|
+
is: is,
|
|
448
296
|
polarDegFromCart: polarDegFromCart,
|
|
449
|
-
|
|
450
|
-
|
|
297
|
+
polarRadFromCart: polarRadFromCart,
|
|
298
|
+
reflectOverLine: reflectOverLine,
|
|
299
|
+
rotateDeg: rotateDeg,
|
|
300
|
+
rotateRad: rotateRad,
|
|
451
301
|
round: round,
|
|
452
302
|
roundTo: roundTo,
|
|
453
|
-
|
|
454
|
-
ceilTo: ceilTo
|
|
303
|
+
subtractVector: subtractVector
|
|
455
304
|
});
|
|
456
305
|
|
|
457
|
-
/**
|
|
458
|
-
* Line Utils
|
|
459
|
-
* A line is an array of two points e.g. [[-5, 0], [5, 0]].
|
|
460
|
-
*/
|
|
461
306
|
function distanceToPoint(line, point$1) {
|
|
462
307
|
return distanceToLine(point$1, line);
|
|
463
308
|
}
|
|
@@ -468,20 +313,14 @@ function midpoint(line) {
|
|
|
468
313
|
return [(line[0][0] + line[1][0]) / 2, (line[0][1] + line[1][1]) / 2];
|
|
469
314
|
}
|
|
470
315
|
function equal$1(line1, line2, tolerance) {
|
|
471
|
-
// TODO: A nicer implementation might just check collinearity of
|
|
472
|
-
// vectors using underscore magick
|
|
473
|
-
// Compare the directions of the lines
|
|
474
316
|
const v1 = subtract(line1[1], line1[0]);
|
|
475
317
|
const v2 = subtract(line2[1], line2[0]);
|
|
476
318
|
if (!collinear$1(v1, v2, tolerance)) {
|
|
477
319
|
return false;
|
|
478
320
|
}
|
|
479
|
-
// If the start point is the same for the two lines, then they are the same
|
|
480
321
|
if (equal$2(line1[0], line2[0])) {
|
|
481
322
|
return true;
|
|
482
323
|
}
|
|
483
|
-
// Make sure that the direction to get from line1 to
|
|
484
|
-
// line2 is the same as the direction of the lines
|
|
485
324
|
const line1ToLine2Vector = subtract(line2[0], line1[0]);
|
|
486
325
|
return collinear$1(v1, line1ToLine2Vector, tolerance);
|
|
487
326
|
}
|
|
@@ -489,19 +328,12 @@ function equal$1(line1, line2, tolerance) {
|
|
|
489
328
|
var line = /*#__PURE__*/Object.freeze({
|
|
490
329
|
__proto__: null,
|
|
491
330
|
distanceToPoint: distanceToPoint,
|
|
492
|
-
|
|
331
|
+
equal: equal$1,
|
|
493
332
|
midpoint: midpoint,
|
|
494
|
-
|
|
333
|
+
reflectPoint: reflectPoint
|
|
495
334
|
});
|
|
496
335
|
|
|
497
|
-
/**
|
|
498
|
-
* Ray Utils
|
|
499
|
-
* A ray (→) is an array of an endpoint and another point along the ray.
|
|
500
|
-
* For example, [[0, 0], [1, 0]] is the ray starting at the origin and
|
|
501
|
-
* traveling along the positive x-axis.
|
|
502
|
-
*/
|
|
503
336
|
function equal(ray1, ray2, tolerance) {
|
|
504
|
-
// Compare the directions of the rays
|
|
505
337
|
const v1 = subtract(ray1[1], ray1[0]);
|
|
506
338
|
const v2 = subtract(ray2[1], ray2[0]);
|
|
507
339
|
const sameOrigin = equal$2(ray1[0], ray2[0]);
|
|
@@ -515,11 +347,9 @@ var ray = /*#__PURE__*/Object.freeze({
|
|
|
515
347
|
});
|
|
516
348
|
|
|
517
349
|
const KhanMath = {
|
|
518
|
-
// Simplify formulas before display
|
|
519
350
|
cleanMath: function (expr) {
|
|
520
351
|
return typeof expr === "string" ? expr.replace(/\+\s*-/g, "- ").replace(/-\s*-/g, "+ ").replace(/\^1/g, "") : expr;
|
|
521
352
|
},
|
|
522
|
-
// Bound a number by 1e-6 and 1e20 to avoid exponents after toString
|
|
523
353
|
bound: function (num) {
|
|
524
354
|
if (num === 0) {
|
|
525
355
|
return num;
|
|
@@ -537,10 +367,7 @@ const KhanMath = {
|
|
|
537
367
|
},
|
|
538
368
|
getGCD: function (a, b) {
|
|
539
369
|
if (arguments.length > 2) {
|
|
540
|
-
// TODO(kevinb): rewrite using rest args instead of arguments
|
|
541
|
-
// eslint-disable-next-line prefer-rest-params
|
|
542
370
|
const rest = [].slice.call(arguments, 1);
|
|
543
|
-
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
544
371
|
return KhanMath.getGCD(a, KhanMath.getGCD(...rest));
|
|
545
372
|
}
|
|
546
373
|
let mod;
|
|
@@ -555,10 +382,7 @@ const KhanMath = {
|
|
|
555
382
|
},
|
|
556
383
|
getLCM: function (a, b) {
|
|
557
384
|
if (arguments.length > 2) {
|
|
558
|
-
// TODO(kevinb): rewrite using rest args instead of arguments
|
|
559
|
-
// eslint-disable-next-line prefer-rest-params
|
|
560
385
|
const rest = [].slice.call(arguments, 1);
|
|
561
|
-
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
562
386
|
return KhanMath.getLCM(a, KhanMath.getLCM(...rest));
|
|
563
387
|
}
|
|
564
388
|
return Math.abs(a * b) / KhanMath.getGCD(a, b);
|
|
@@ -569,7 +393,7 @@ const KhanMath = {
|
|
|
569
393
|
return false;
|
|
570
394
|
}
|
|
571
395
|
if (n < 101) {
|
|
572
|
-
return
|
|
396
|
+
return !!$.grep(KhanMath.primes, function (p, i) {
|
|
573
397
|
return Math.abs(p - n) <= 0.5;
|
|
574
398
|
}).length;
|
|
575
399
|
}
|
|
@@ -583,7 +407,6 @@ const KhanMath = {
|
|
|
583
407
|
}
|
|
584
408
|
return true;
|
|
585
409
|
},
|
|
586
|
-
// @ts-expect-error - TS2366 - Function lacks ending return statement and return type does not include 'undefined'.
|
|
587
410
|
getPrimeFactorization: function (number) {
|
|
588
411
|
if (number === 1) {
|
|
589
412
|
return [];
|
|
@@ -594,39 +417,24 @@ const KhanMath = {
|
|
|
594
417
|
const maxf = Math.sqrt(number);
|
|
595
418
|
for (let f = 2; f <= maxf; f++) {
|
|
596
419
|
if (number % f === 0) {
|
|
597
|
-
return
|
|
420
|
+
return $.merge(KhanMath.getPrimeFactorization(f), KhanMath.getPrimeFactorization(number / f));
|
|
598
421
|
}
|
|
599
422
|
}
|
|
600
423
|
},
|
|
601
|
-
// Round a number to the nearest increment
|
|
602
|
-
// E.g., if increment = 30 and num = 40, return 30. if increment = 30 and
|
|
603
|
-
// num = 45, return 60.
|
|
604
424
|
roundToNearest: function (increment, num) {
|
|
605
425
|
return Math.round(num / increment) * increment;
|
|
606
426
|
},
|
|
607
|
-
// Round a number to a certain number of decimal places
|
|
608
427
|
roundTo: function (precision, num) {
|
|
609
428
|
const factor = Math.pow(10, precision).toFixed(5);
|
|
610
|
-
// @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'number'. | TS2363 - The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. | TS2363 - The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
|
|
611
429
|
return Math.round((num * factor).toFixed(5)) / factor;
|
|
612
430
|
},
|
|
613
|
-
/**
|
|
614
|
-
* Return a string of num rounded to a fixed precision decimal places,
|
|
615
|
-
* with an approx symbol if num had to be rounded, and trailing 0s
|
|
616
|
-
*/
|
|
617
431
|
toFixedApprox: function (num, precision) {
|
|
618
|
-
// TODO(aria): Make this locale-dependent
|
|
619
432
|
const fixedStr = num.toFixed(precision);
|
|
620
433
|
if (equal$4(+fixedStr, num)) {
|
|
621
434
|
return fixedStr;
|
|
622
435
|
}
|
|
623
436
|
return "\\approx " + fixedStr;
|
|
624
437
|
},
|
|
625
|
-
/**
|
|
626
|
-
* Return a string of num rounded to precision decimal places, with an
|
|
627
|
-
* approx symbol if num had to be rounded, but no trailing 0s if it was
|
|
628
|
-
* not rounded.
|
|
629
|
-
*/
|
|
630
438
|
roundToApprox: function (num, precision) {
|
|
631
439
|
const fixed = KhanMath.roundTo(precision, num);
|
|
632
440
|
if (equal$4(fixed, num)) {
|
|
@@ -634,11 +442,6 @@ const KhanMath = {
|
|
|
634
442
|
}
|
|
635
443
|
return KhanMath.toFixedApprox(num, precision);
|
|
636
444
|
},
|
|
637
|
-
// toFraction(4/8) => [1, 2]
|
|
638
|
-
// toFraction(0.666) => [333, 500]
|
|
639
|
-
// toFraction(0.666, 0.001) => [2, 3]
|
|
640
|
-
//
|
|
641
|
-
// tolerance can't be bigger than 1, sorry
|
|
642
445
|
toFraction: function (decimal, tolerance) {
|
|
643
446
|
if (tolerance == null) {
|
|
644
447
|
tolerance = Math.pow(2, -46);
|
|
@@ -659,8 +462,6 @@ const KhanMath = {
|
|
|
659
462
|
let hiD = 1;
|
|
660
463
|
let midN = 1;
|
|
661
464
|
let midD = 2;
|
|
662
|
-
|
|
663
|
-
// eslint-disable-next-line no-constant-condition
|
|
664
465
|
while (true) {
|
|
665
466
|
if (Math.abs(Number(midN / midD) - decimal) <= tolerance) {
|
|
666
467
|
return [midN, midD];
|
|
@@ -676,11 +477,8 @@ const KhanMath = {
|
|
|
676
477
|
midD = loD + hiD;
|
|
677
478
|
}
|
|
678
479
|
},
|
|
679
|
-
// Returns the format (string) of a given numeric string
|
|
680
|
-
// Note: purposively more inclusive than answer-types' predicate.forms
|
|
681
|
-
// That is, it is not necessarily true that interpreted input are numeric
|
|
682
480
|
getNumericFormat: function (text) {
|
|
683
|
-
text =
|
|
481
|
+
text = $.trim(text);
|
|
684
482
|
text = text.replace(/\u2212/, "-").replace(/([+-])\s+/g, "$1");
|
|
685
483
|
if (text.match(/^[+-]?\d+$/)) {
|
|
686
484
|
return "integer";
|
|
@@ -700,13 +498,12 @@ const KhanMath = {
|
|
|
700
498
|
}
|
|
701
499
|
return null;
|
|
702
500
|
},
|
|
703
|
-
// Returns a string of the number in a specified format
|
|
704
501
|
toNumericString: function (number$1, format) {
|
|
705
502
|
if (number$1 == null) {
|
|
706
503
|
return "";
|
|
707
504
|
}
|
|
708
505
|
if (number$1 === 0) {
|
|
709
|
-
return "0";
|
|
506
|
+
return "0";
|
|
710
507
|
}
|
|
711
508
|
if (format === "percent") {
|
|
712
509
|
return number$1 * 100 + "%";
|
|
@@ -721,23 +518,21 @@ const KhanMath = {
|
|
|
721
518
|
return sign + (numerator === 1 ? "" : numerator) + pi + (denominator === 1 ? "" : "/" + denominator);
|
|
722
519
|
}
|
|
723
520
|
}
|
|
724
|
-
if (
|
|
521
|
+
if (_(["proper", "improper", "mixed", "fraction"]).contains(format)) {
|
|
725
522
|
const fraction = toFraction(number$1);
|
|
726
523
|
const numerator = Math.abs(fraction[0]);
|
|
727
524
|
const denominator = fraction[1];
|
|
728
525
|
const sign = number$1 < 0 ? "-" : "";
|
|
729
526
|
if (denominator === 1) {
|
|
730
|
-
return sign + numerator;
|
|
527
|
+
return sign + numerator;
|
|
731
528
|
}
|
|
732
529
|
if (format === "mixed") {
|
|
733
530
|
const modulus = numerator % denominator;
|
|
734
531
|
const integer = (numerator - modulus) / denominator;
|
|
735
532
|
return sign + (integer ? integer + " " : "") + modulus + "/" + denominator;
|
|
736
|
-
}
|
|
533
|
+
}
|
|
737
534
|
return sign + numerator + "/" + denominator;
|
|
738
535
|
}
|
|
739
|
-
|
|
740
|
-
// otherwise (decimal, float, long long)
|
|
741
536
|
return String(number$1);
|
|
742
537
|
}
|
|
743
538
|
};
|
|
@@ -748,39 +543,24 @@ function add(a, b) {
|
|
|
748
543
|
return a + b;
|
|
749
544
|
}
|
|
750
545
|
|
|
751
|
-
/**
|
|
752
|
-
* A collection of geomtry-related utility functions
|
|
753
|
-
*/
|
|
754
|
-
|
|
755
|
-
// This should really be a readonly tuple of [number, number]
|
|
756
|
-
|
|
757
|
-
// Given a number, return whether it is positive (1), negative (-1), or zero (0)
|
|
758
546
|
function sign(val) {
|
|
759
|
-
if (
|
|
547
|
+
if (approximateEqual(val, 0)) {
|
|
760
548
|
return 0;
|
|
761
549
|
}
|
|
762
550
|
return val > 0 ? 1 : -1;
|
|
763
551
|
}
|
|
764
|
-
|
|
765
|
-
// Determine whether three points are collinear (0), for a clockwise turn (negative),
|
|
766
|
-
// or counterclockwise turn (positive)
|
|
767
552
|
function ccw(a, b, c) {
|
|
768
553
|
return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
|
|
769
554
|
}
|
|
770
555
|
function collinear(a, b, c) {
|
|
771
|
-
return
|
|
556
|
+
return approximateEqual(ccw(a, b, c), 0);
|
|
772
557
|
}
|
|
773
|
-
|
|
774
|
-
// Given rect bounding points A and B, whether point C is inside the rect
|
|
775
558
|
function pointInRect(a, b, c) {
|
|
776
559
|
return c[0] <= Math.max(a[0], b[0]) && c[0] >= Math.min(a[0], b[0]) && c[1] <= Math.max(a[1], b[1]) && c[1] >= Math.min(a[1], b[1]);
|
|
777
560
|
}
|
|
778
|
-
|
|
779
|
-
// Whether line segment AB intersects line segment CD
|
|
780
|
-
// http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
|
|
781
561
|
function intersects(ab, cd) {
|
|
782
562
|
const triplets = [[ab[0], ab[1], cd[0]], [ab[0], ab[1], cd[1]], [cd[0], cd[1], ab[0]], [cd[0], cd[1], ab[1]]];
|
|
783
|
-
const orientations =
|
|
563
|
+
const orientations = _.map(triplets, function (triplet) {
|
|
784
564
|
return sign(ccw(...triplet));
|
|
785
565
|
});
|
|
786
566
|
if (orientations[0] !== orientations[1] && orientations[2] !== orientations[3]) {
|
|
@@ -793,21 +573,14 @@ function intersects(ab, cd) {
|
|
|
793
573
|
}
|
|
794
574
|
return false;
|
|
795
575
|
}
|
|
796
|
-
|
|
797
|
-
// Whether any two sides of a polygon intersect each other
|
|
798
576
|
function polygonSidesIntersect(vertices) {
|
|
799
577
|
for (let i = 0; i < vertices.length; i++) {
|
|
800
578
|
for (let k = i + 1; k < vertices.length; k++) {
|
|
801
|
-
// If any two vertices are the same point, sides overlap
|
|
802
579
|
if (equal$2(vertices[i], vertices[k])) {
|
|
803
580
|
return true;
|
|
804
581
|
}
|
|
805
|
-
|
|
806
|
-
// Find the other end of the sides starting at vertices i and k
|
|
807
582
|
const iNext = (i + 1) % vertices.length;
|
|
808
583
|
const kNext = (k + 1) % vertices.length;
|
|
809
|
-
|
|
810
|
-
// Adjacent sides always intersect (at the vertex); skip those
|
|
811
584
|
if (iNext === k || kNext === i) {
|
|
812
585
|
continue;
|
|
813
586
|
}
|
|
@@ -821,20 +594,16 @@ function polygonSidesIntersect(vertices) {
|
|
|
821
594
|
return false;
|
|
822
595
|
}
|
|
823
596
|
function vector(a, b) {
|
|
824
|
-
return
|
|
597
|
+
return _.map(_.zip(a, b), function (pair) {
|
|
825
598
|
return pair[0] - pair[1];
|
|
826
599
|
});
|
|
827
600
|
}
|
|
828
601
|
function reverseVector(vector) {
|
|
829
602
|
return [-vector[0], -vector[1]];
|
|
830
603
|
}
|
|
831
|
-
|
|
832
|
-
// Returns whether connecting the given sequence of `points` forms a clockwise
|
|
833
|
-
// path (assuming a closed loop, where the last point connects back to the
|
|
834
|
-
// first).
|
|
835
604
|
function clockwise(points) {
|
|
836
|
-
const segments =
|
|
837
|
-
const areas =
|
|
605
|
+
const segments = _.zip(points, points.slice(1).concat(points.slice(0, 1)));
|
|
606
|
+
const areas = _.map(segments, function (segment) {
|
|
838
607
|
const p1 = segment[0];
|
|
839
608
|
const p2 = segment[1];
|
|
840
609
|
return (p2[0] - p1[0]) * (p2[1] + p1[1]);
|
|
@@ -842,44 +611,36 @@ function clockwise(points) {
|
|
|
842
611
|
return sum(areas) > 0;
|
|
843
612
|
}
|
|
844
613
|
function magnitude(v) {
|
|
845
|
-
return Math.sqrt(
|
|
846
|
-
// @ts-expect-error - TS2345 - Argument of type 'Coord' is not assignable to parameter of type 'number'.
|
|
614
|
+
return Math.sqrt(_.reduce(v, function (memo, el) {
|
|
847
615
|
return memo + Math.pow(el, 2);
|
|
848
616
|
}, 0));
|
|
849
617
|
}
|
|
850
618
|
function dotProduct(a, b) {
|
|
851
|
-
return
|
|
619
|
+
return _.reduce(_.zip(a, b), function (memo, pair) {
|
|
852
620
|
return memo + pair[0] * pair[1];
|
|
853
621
|
}, 0);
|
|
854
622
|
}
|
|
855
623
|
function sideLengths(coords) {
|
|
856
|
-
const segments =
|
|
624
|
+
const segments = _.zip(coords, rotate(coords));
|
|
857
625
|
return segments.map(function (segment) {
|
|
858
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
859
626
|
return magnitude(vector(...segment));
|
|
860
627
|
});
|
|
861
628
|
}
|
|
862
|
-
|
|
863
|
-
// Based on http://math.stackexchange.com/a/151149
|
|
864
629
|
function angleMeasures(coords) {
|
|
865
|
-
const triplets =
|
|
866
|
-
const offsets =
|
|
630
|
+
const triplets = _.zip(rotate(coords, -1), coords, rotate(coords, 1));
|
|
631
|
+
const offsets = _.map(triplets, function (triplet) {
|
|
867
632
|
const p = vector(triplet[1], triplet[0]);
|
|
868
633
|
const q = vector(triplet[2], triplet[1]);
|
|
869
|
-
// @ts-expect-error - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'Coord'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
870
634
|
const raw = Math.acos(dotProduct(p, q) / (magnitude(p) * magnitude(q)));
|
|
871
|
-
// @ts-expect-error - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
872
635
|
return sign(ccw(...triplet)) > 0 ? raw : -raw;
|
|
873
636
|
});
|
|
874
|
-
const sum =
|
|
637
|
+
const sum = _.reduce(offsets, function (memo, arg) {
|
|
875
638
|
return memo + arg;
|
|
876
639
|
}, 0);
|
|
877
|
-
return
|
|
640
|
+
return _.map(offsets, function (offset) {
|
|
878
641
|
return sum > 0 ? Math.PI - offset : Math.PI + offset;
|
|
879
642
|
});
|
|
880
643
|
}
|
|
881
|
-
|
|
882
|
-
// Whether two polygons are similar (or if specified, congruent)
|
|
883
644
|
function similar(coords1, coords2, tolerance) {
|
|
884
645
|
if (coords1.length !== coords2.length) {
|
|
885
646
|
return false;
|
|
@@ -892,30 +653,22 @@ function similar(coords1, coords2, tolerance) {
|
|
|
892
653
|
for (let i = 0; i < 2 * n; i++) {
|
|
893
654
|
let angles = angles2.slice();
|
|
894
655
|
let sides = sides2.slice();
|
|
895
|
-
|
|
896
|
-
// Reverse angles and sides to allow matching reflected polygons
|
|
897
656
|
if (i >= n) {
|
|
898
657
|
angles.reverse();
|
|
899
658
|
sides.reverse();
|
|
900
|
-
// Since sides are calculated from two coordinates,
|
|
901
|
-
// simply reversing results in an off by one error
|
|
902
|
-
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
903
659
|
sides = rotate(sides, 1);
|
|
904
660
|
}
|
|
905
|
-
|
|
906
|
-
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
907
661
|
angles = rotate(angles, i);
|
|
908
|
-
// @ts-expect-error - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
909
662
|
sides = rotate(sides, i);
|
|
910
|
-
if (
|
|
911
|
-
const sidePairs =
|
|
912
|
-
const factors =
|
|
663
|
+
if (approximateDeepEqual(angles1, angles)) {
|
|
664
|
+
const sidePairs = _.zip(sides1, sides);
|
|
665
|
+
const factors = _.map(sidePairs, function (pair) {
|
|
913
666
|
return pair[0] / pair[1];
|
|
914
667
|
});
|
|
915
|
-
const same =
|
|
916
|
-
return
|
|
668
|
+
const same = _.all(factors, function (factor) {
|
|
669
|
+
return approximateEqual(factors[0], factor);
|
|
917
670
|
});
|
|
918
|
-
const congruentEnough =
|
|
671
|
+
const congruentEnough = _.all(sidePairs, function (pair) {
|
|
919
672
|
return equal$4(pair[0], pair[1], tolerance);
|
|
920
673
|
});
|
|
921
674
|
if (same && congruentEnough) {
|
|
@@ -925,32 +678,21 @@ function similar(coords1, coords2, tolerance) {
|
|
|
925
678
|
}
|
|
926
679
|
return false;
|
|
927
680
|
}
|
|
928
|
-
|
|
929
|
-
// Given triangle with sides ABC return angle opposite side C in degrees
|
|
930
681
|
function lawOfCosines(a, b, c) {
|
|
931
682
|
return Math.acos((a * a + b * b - c * c) / (2 * a * b)) * 180 / Math.PI;
|
|
932
683
|
}
|
|
933
|
-
function canonicalSineCoefficients(
|
|
934
|
-
let [amplitude, angularFrequency, phase, verticalOffset] = _ref;
|
|
935
|
-
// For a curve of the form f(x) = a * Sin(b * x - c) + d,
|
|
936
|
-
// this function ensures that a, b > 0, and c is its
|
|
937
|
-
// smallest possible positive value.
|
|
938
|
-
|
|
939
|
-
// Guarantee a > 0
|
|
684
|
+
function canonicalSineCoefficients([amplitude, angularFrequency, phase, verticalOffset]) {
|
|
940
685
|
if (amplitude < 0) {
|
|
941
686
|
amplitude *= -1;
|
|
942
687
|
angularFrequency *= -1;
|
|
943
688
|
phase *= -1;
|
|
944
689
|
}
|
|
945
690
|
const period = 2 * Math.PI;
|
|
946
|
-
// Guarantee b > 0
|
|
947
691
|
if (angularFrequency < 0) {
|
|
948
692
|
angularFrequency *= -1;
|
|
949
693
|
phase *= -1;
|
|
950
694
|
phase += period / 2;
|
|
951
695
|
}
|
|
952
|
-
|
|
953
|
-
// Guarantee c is smallest possible positive value
|
|
954
696
|
while (phase > 0) {
|
|
955
697
|
phase -= period;
|
|
956
698
|
}
|
|
@@ -959,26 +701,19 @@ function canonicalSineCoefficients(_ref) {
|
|
|
959
701
|
}
|
|
960
702
|
return [amplitude, angularFrequency, phase, verticalOffset];
|
|
961
703
|
}
|
|
962
|
-
|
|
963
|
-
// e.g. rotate([1, 2, 3]) -> [2, 3, 1]
|
|
964
704
|
function rotate(array, n) {
|
|
965
705
|
n = typeof n === "undefined" ? 1 : n % array.length;
|
|
966
706
|
return array.slice(n).concat(array.slice(0, n));
|
|
967
707
|
}
|
|
968
708
|
function getLineEquation(first, second) {
|
|
969
|
-
if (
|
|
709
|
+
if (approximateEqual(first[0], second[0])) {
|
|
970
710
|
return "x = " + first[0].toFixed(3);
|
|
971
711
|
}
|
|
972
712
|
const m = (second[1] - first[1]) / (second[0] - first[0]);
|
|
973
713
|
const b = first[1] - m * first[0];
|
|
974
714
|
return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
|
|
975
715
|
}
|
|
976
|
-
|
|
977
|
-
// Stolen from the wikipedia article
|
|
978
|
-
// http://en.wikipedia.org/wiki/Line-line_intersection
|
|
979
|
-
function getLineIntersection(
|
|
980
|
-
// TODO(LP-10725): update these to be 2-tuples
|
|
981
|
-
firstPoints, secondPoints) {
|
|
716
|
+
function getLineIntersection(firstPoints, secondPoints) {
|
|
982
717
|
const x1 = firstPoints[0][0];
|
|
983
718
|
const y1 = firstPoints[0][1];
|
|
984
719
|
const x2 = firstPoints[1][0];
|
|
@@ -989,7 +724,6 @@ firstPoints, secondPoints) {
|
|
|
989
724
|
const y4 = secondPoints[1][1];
|
|
990
725
|
const determinant = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
991
726
|
if (Math.abs(determinant) < 1e-9) {
|
|
992
|
-
// Lines are parallel
|
|
993
727
|
return null;
|
|
994
728
|
}
|
|
995
729
|
const x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / determinant;
|
|
@@ -1007,43 +741,35 @@ function getLineIntersectionString(firstPoints, secondPoints) {
|
|
|
1007
741
|
|
|
1008
742
|
var geometry = /*#__PURE__*/Object.freeze({
|
|
1009
743
|
__proto__: null,
|
|
1010
|
-
|
|
744
|
+
angleMeasures: angleMeasures,
|
|
745
|
+
canonicalSineCoefficients: canonicalSineCoefficients,
|
|
1011
746
|
ccw: ccw,
|
|
747
|
+
clockwise: clockwise,
|
|
1012
748
|
collinear: collinear,
|
|
749
|
+
getLineEquation: getLineEquation,
|
|
750
|
+
getLineIntersection: getLineIntersection,
|
|
751
|
+
getLineIntersectionString: getLineIntersectionString,
|
|
1013
752
|
intersects: intersects,
|
|
753
|
+
lawOfCosines: lawOfCosines,
|
|
754
|
+
magnitude: magnitude,
|
|
1014
755
|
polygonSidesIntersect: polygonSidesIntersect,
|
|
1015
|
-
vector: vector,
|
|
1016
756
|
reverseVector: reverseVector,
|
|
1017
|
-
clockwise: clockwise,
|
|
1018
|
-
magnitude: magnitude,
|
|
1019
|
-
angleMeasures: angleMeasures,
|
|
1020
|
-
similar: similar,
|
|
1021
|
-
lawOfCosines: lawOfCosines,
|
|
1022
|
-
canonicalSineCoefficients: canonicalSineCoefficients,
|
|
1023
757
|
rotate: rotate,
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
758
|
+
sign: sign,
|
|
759
|
+
similar: similar,
|
|
760
|
+
vector: vector
|
|
1027
761
|
});
|
|
1028
762
|
|
|
1029
|
-
// This file contains helper functions for working with angles.
|
|
1030
763
|
function convertDegreesToRadians(degrees) {
|
|
1031
764
|
return degrees / 180 * Math.PI;
|
|
1032
765
|
}
|
|
1033
766
|
function convertRadiansToDegrees(radians) {
|
|
1034
767
|
const degree = radians / Math.PI * 180;
|
|
1035
|
-
// Account for floating point errors.
|
|
1036
768
|
return Number(degree.toPrecision(15));
|
|
1037
769
|
}
|
|
1038
|
-
|
|
1039
|
-
// Returns a value between -180 and 180, inclusive. The angle is measured
|
|
1040
|
-
// between the positive x-axis and the given vector.
|
|
1041
|
-
function calculateAngleInDegrees(_ref) {
|
|
1042
|
-
let [x, y] = _ref;
|
|
770
|
+
function calculateAngleInDegrees([x, y]) {
|
|
1043
771
|
return Math.atan2(y, x) * 180 / Math.PI;
|
|
1044
772
|
}
|
|
1045
|
-
|
|
1046
|
-
// Converts polar coordinates to cartesian. The th(eta) parameter is in degrees.
|
|
1047
773
|
function polar(r, th) {
|
|
1048
774
|
if (typeof r === "number") {
|
|
1049
775
|
r = [r, r];
|
|
@@ -1051,9 +777,6 @@ function polar(r, th) {
|
|
|
1051
777
|
th = th * Math.PI / 180;
|
|
1052
778
|
return [r[0] * Math.cos(th), r[1] * Math.sin(th)];
|
|
1053
779
|
}
|
|
1054
|
-
// This function calculates the angle between two points and an optional vertex.
|
|
1055
|
-
// If the vertex is not provided, the angle is measured between the two points.
|
|
1056
|
-
// This does not account for reflex angles or clockwise position.
|
|
1057
780
|
const getAngleFromVertex = (point, vertex) => {
|
|
1058
781
|
const x = point[0] - vertex[0];
|
|
1059
782
|
const y = point[1] - vertex[1];
|
|
@@ -1062,25 +785,11 @@ const getAngleFromVertex = (point, vertex) => {
|
|
|
1062
785
|
}
|
|
1063
786
|
return (180 + Math.atan2(-y, -x) * 180 / Math.PI + 360) % 360;
|
|
1064
787
|
};
|
|
1065
|
-
|
|
1066
|
-
// This function calculates the clockwise angle between three points,
|
|
1067
|
-
// and is used to generate the labels and equation strings of the
|
|
1068
|
-
// current angle for the interactive graph.
|
|
1069
|
-
const getClockwiseAngle = function (coords) {
|
|
1070
|
-
let allowReflexAngles = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
788
|
+
const getClockwiseAngle = (coords, allowReflexAngles = false) => {
|
|
1071
789
|
const coordsCopy = [...coords];
|
|
1072
|
-
// The coords are saved as [point1, vertex, point2] in the interactive graph
|
|
1073
790
|
const areClockwise = clockwise([coordsCopy[0], coordsCopy[2], coordsCopy[1]]);
|
|
1074
|
-
|
|
1075
|
-
// We may need to reverse the coordinates if we allow
|
|
1076
|
-
// reflex angles and the points are not in clockwise order.
|
|
1077
791
|
const shouldReverseCoords = !areClockwise || allowReflexAngles;
|
|
1078
|
-
|
|
1079
|
-
// Reverse the coordinates accordingly to ensure the angle is calculated correctly
|
|
1080
792
|
const clockwiseCoords = shouldReverseCoords ? coordsCopy.reverse() : coordsCopy;
|
|
1081
|
-
|
|
1082
|
-
// Calculate the angles between the two points and get the difference
|
|
1083
|
-
// between the two angles to get the clockwise angle.
|
|
1084
793
|
const startAngle = getAngleFromVertex(clockwiseCoords[0], clockwiseCoords[1]);
|
|
1085
794
|
const endAngle = getAngleFromVertex(clockwiseCoords[2], clockwiseCoords[1]);
|
|
1086
795
|
const angle = (startAngle + 360 - endAngle) % 360;
|
|
@@ -1089,38 +798,29 @@ const getClockwiseAngle = function (coords) {
|
|
|
1089
798
|
|
|
1090
799
|
var angles = /*#__PURE__*/Object.freeze({
|
|
1091
800
|
__proto__: null,
|
|
801
|
+
calculateAngleInDegrees: calculateAngleInDegrees,
|
|
1092
802
|
convertDegreesToRadians: convertDegreesToRadians,
|
|
1093
803
|
convertRadiansToDegrees: convertRadiansToDegrees,
|
|
1094
|
-
calculateAngleInDegrees: calculateAngleInDegrees,
|
|
1095
|
-
polar: polar,
|
|
1096
804
|
getAngleFromVertex: getAngleFromVertex,
|
|
1097
|
-
getClockwiseAngle: getClockwiseAngle
|
|
805
|
+
getClockwiseAngle: getClockwiseAngle,
|
|
806
|
+
polar: polar
|
|
1098
807
|
});
|
|
1099
808
|
|
|
1100
|
-
// TODO: there's another, very similar getSinusoidCoefficients function
|
|
1101
|
-
// they should probably be merged
|
|
1102
809
|
function getSinusoidCoefficients(coords) {
|
|
1103
|
-
// It's assumed that p1 is the root and p2 is the first peak
|
|
1104
810
|
const p1 = coords[0];
|
|
1105
811
|
const p2 = coords[1];
|
|
1106
|
-
|
|
1107
|
-
// Resulting coefficients are canonical for this sine curve
|
|
1108
812
|
const amplitude = p2[1] - p1[1];
|
|
1109
813
|
const angularFrequency = Math.PI / (2 * (p2[0] - p1[0]));
|
|
1110
814
|
const phase = p1[0] * angularFrequency;
|
|
1111
815
|
const verticalOffset = p1[1];
|
|
1112
816
|
return [amplitude, angularFrequency, phase, verticalOffset];
|
|
1113
817
|
}
|
|
1114
|
-
// TODO: there's another, very similar getQuadraticCoefficients function
|
|
1115
|
-
// they should probably be merged
|
|
1116
818
|
function getQuadraticCoefficients(coords) {
|
|
1117
819
|
const p1 = coords[0];
|
|
1118
820
|
const p2 = coords[1];
|
|
1119
821
|
const p3 = coords[2];
|
|
1120
822
|
const denom = (p1[0] - p2[0]) * (p1[0] - p3[0]) * (p2[0] - p3[0]);
|
|
1121
823
|
if (denom === 0) {
|
|
1122
|
-
// Many of the callers assume that the return value is always defined.
|
|
1123
|
-
// @ts-expect-error - TS2322 - Type 'undefined' is not assignable to type 'QuadraticCoefficient'.
|
|
1124
824
|
return;
|
|
1125
825
|
}
|
|
1126
826
|
const a = (p3[0] * (p2[1] - p1[1]) + p2[0] * (p1[1] - p3[1]) + p1[0] * (p3[1] - p2[1])) / denom;
|
|
@@ -1131,19 +831,9 @@ function getQuadraticCoefficients(coords) {
|
|
|
1131
831
|
|
|
1132
832
|
var coefficients = /*#__PURE__*/Object.freeze({
|
|
1133
833
|
__proto__: null,
|
|
1134
|
-
|
|
1135
|
-
|
|
834
|
+
getQuadraticCoefficients: getQuadraticCoefficients,
|
|
835
|
+
getSinusoidCoefficients: getSinusoidCoefficients
|
|
1136
836
|
});
|
|
1137
837
|
|
|
1138
|
-
|
|
1139
|
-
exports.angles = angles;
|
|
1140
|
-
exports.coefficients = coefficients;
|
|
1141
|
-
exports.geometry = geometry;
|
|
1142
|
-
exports.libVersion = libVersion;
|
|
1143
|
-
exports.line = line;
|
|
1144
|
-
exports.number = number;
|
|
1145
|
-
exports.point = point;
|
|
1146
|
-
exports.ray = ray;
|
|
1147
|
-
exports.sum = sum;
|
|
1148
|
-
exports.vector = vector$1;
|
|
838
|
+
export { KhanMath, angles, coefficients, geometry, libVersion, line, number, point, ray, sum, vector$1 as vector };
|
|
1149
839
|
//# sourceMappingURL=index.js.map
|