@khanacademy/kmath 0.1.11 → 0.1.13

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/src/logo.ts DELETED
@@ -1,39 +0,0 @@
1
- /* eslint-disable import/no-default-export */
2
- // This file describes the graphie source code of the kmath logo
3
- // currently used on khan.github.io.
4
- //
5
- // Also located at http://ka-perseus-graphie.s3.amazonaws.com/42ef3cbadc3e6464124533191728c3c5c55c7355.svg
6
-
7
- declare let init: any;
8
- declare let ellipse: any;
9
- declare let line: any;
10
-
11
- const GREEN = "#28AE7B";
12
-
13
- export default () => {
14
- init({
15
- range: [
16
- [0, 10],
17
- [0, 10],
18
- ],
19
- scale: 40,
20
- });
21
-
22
- ellipse(5, 5, 5, {
23
- stroke: null,
24
- fill: GREEN,
25
- });
26
-
27
- line([2, 5], [8.5, 5], {
28
- stroke: "WHITE",
29
- fill: "WHITE",
30
- strokeWidth: 25,
31
- arrows: "->",
32
- });
33
-
34
- line([5, 2], [5, 8], {
35
- stroke: "WHITE",
36
- fill: "WHITE",
37
- strokeWidth: 25,
38
- });
39
- };
package/src/number.ts DELETED
@@ -1,104 +0,0 @@
1
- /**
2
- * Number Utils
3
- * A number is a js-number, e.g. 5.12
4
- */
5
-
6
- import _ from "underscore";
7
-
8
- export const DEFAULT_TOLERANCE = 1e-9;
9
-
10
- // TODO: Should this just be Number.Epsilon
11
- export const EPSILON: number = Math.pow(2, -42);
12
-
13
- export function is(x: any): boolean {
14
- return _.isNumber(x) && !_.isNaN(x);
15
- }
16
-
17
- export function equal(x: number, y: number, tolerance?: number): boolean {
18
- // Checking for undefined makes this function behave nicely
19
- // with vectors of different lengths that are _.zip'd together
20
- if (x == null || y == null) {
21
- return x === y;
22
- }
23
- // We check === here so that +/-Infinity comparisons work correctly
24
- if (x === y) {
25
- return true;
26
- }
27
- if (tolerance == null) {
28
- tolerance = DEFAULT_TOLERANCE;
29
- }
30
- return Math.abs(x - y) < tolerance;
31
- }
32
-
33
- export function sign(
34
- x: number,
35
- tolerance?: number,
36
- ): number /* Should be: 0 | 1 | -1 */ {
37
- return equal(x, 0, tolerance) ? 0 : Math.abs(x) / x;
38
- }
39
-
40
- export function isInteger(num: number, tolerance?: number): boolean {
41
- return equal(Math.round(num), num, tolerance);
42
- }
43
-
44
- // Round a number to a certain number of decimal places
45
- export function round(num: number, precision: number): number {
46
- const factor = Math.pow(10, precision);
47
- return Math.round(num * factor) / factor;
48
- }
49
-
50
- // Round num to the nearest multiple of increment
51
- // i.e. roundTo(83, 5) -> 85
52
- export function roundTo(num: number, increment: number): number {
53
- return Math.round(num / increment) * increment;
54
- }
55
-
56
- export function floorTo(num: number, increment: number): number {
57
- return Math.floor(num / increment) * increment;
58
- }
59
-
60
- export function ceilTo(num: number, increment: number): number {
61
- return Math.ceil(num / increment) * increment;
62
- }
63
-
64
- /**
65
- * toFraction
66
- *
67
- * Returns a [numerator, denominator] array rational representation
68
- * of `decimal`
69
- *
70
- * See http://en.wikipedia.org/wiki/Continued_fraction for implementation
71
- * details
72
- *
73
- * toFraction(4/8) => [1, 2]
74
- * toFraction(0.66) => [33, 50]
75
- * toFraction(0.66, 0.01) => [2/3]
76
- * toFraction(283 + 1/3) => [850, 3]
77
- */
78
- export function toFraction(
79
- decimal: number,
80
- // can't be 0
81
- tolerance: number = EPSILON,
82
- maxDenominator = 1000,
83
- ): [number, number] {
84
- // Initialize everything to compute successive terms of
85
- // continued-fraction approximations via recurrence relation
86
- let n = [1, 0];
87
- let d = [0, 1];
88
- let a = Math.floor(decimal);
89
- let rem = decimal - a;
90
-
91
- while (d[0] <= maxDenominator) {
92
- if (equal(n[0] / d[0], decimal, tolerance)) {
93
- return [n[0], d[0]];
94
- }
95
- n = [a * n[0] + n[1], n[0]];
96
- d = [a * d[0] + d[1], d[0]];
97
- a = Math.floor(1 / rem);
98
- rem = 1 / rem - a;
99
- }
100
-
101
- // We failed to find a nice rational representation,
102
- // so return an irrational "fraction"
103
- return [decimal, 1];
104
- }
package/src/point.ts DELETED
@@ -1,102 +0,0 @@
1
- /**
2
- * Point Utils
3
- * A point is an array of two numbers e.g. [0, 0].
4
- */
5
-
6
- import * as knumber from "./number";
7
- import * as kvector from "./vector";
8
-
9
- // A point, in 2D, 3D, or nD space.
10
- export type Point = ReadonlyArray<number>;
11
-
12
- // Rotate point (around origin unless a center is specified)
13
- export function rotateRad(point: Point, theta: number, center?: Point): Point {
14
- if (center === undefined) {
15
- return kvector.rotateRad(point, theta);
16
- } else {
17
- return kvector.add(
18
- center,
19
- kvector.rotateRad(kvector.subtract(point, center), theta),
20
- );
21
- }
22
- }
23
-
24
- export function rotateDeg(point: Point, theta: number, center?: Point): Point {
25
- if (center === undefined) {
26
- return kvector.rotateDeg(point, theta);
27
- } else {
28
- return kvector.add(
29
- center,
30
- kvector.rotateDeg(kvector.subtract(point, center), theta),
31
- );
32
- }
33
- }
34
-
35
- // Distance between two points
36
- export function distanceToPoint(point1: Point, point2: Point): number {
37
- return kvector.length(kvector.subtract(point1, point2));
38
- }
39
-
40
- // Distance between point and line
41
- export function distanceToLine(point: Point, line: [Point, Point]): number {
42
- const lv = kvector.subtract(line[1], line[0]);
43
- const pv = kvector.subtract(point, line[0]);
44
- const projectedPv = kvector.projection(pv, lv);
45
- const distancePv = kvector.subtract(projectedPv, pv);
46
- return kvector.length(distancePv);
47
- }
48
-
49
- // Reflect point over line
50
- export function reflectOverLine<P extends Point>(point: P, line: [P, P]): P {
51
- const lv = kvector.subtract(line[1], line[0]);
52
- const pv = kvector.subtract(point, line[0]);
53
- const projectedPv = kvector.projection(pv, lv);
54
- const reflectedPv = kvector.subtract(kvector.scale(projectedPv, 2), pv);
55
- return kvector.add(line[0], reflectedPv);
56
- }
57
-
58
- /**
59
- * Compares two points, returning -1, 0, or 1, for use with
60
- * Array.prototype.sort
61
- *
62
- * Note: This technically doesn't satisfy the total-ordering
63
- * requirements of Array.prototype.sort unless equalityTolerance
64
- * is 0. In some cases very close points that compare within a
65
- * few equalityTolerances could appear in the wrong order.
66
- */
67
- export function compare(
68
- point1: Point,
69
- point2: Point,
70
- equalityTolerance?: number,
71
- ): number /* TODO: convert to -1 | 0 | 1 type */ {
72
- if (point1.length !== point2.length) {
73
- return point1.length - point2.length;
74
- }
75
- for (let i = 0; i < point1.length; i++) {
76
- if (!knumber.equal(point1[i], point2[i], equalityTolerance)) {
77
- return point1[i] - point2[i];
78
- }
79
- }
80
- return 0;
81
- }
82
-
83
- // Check if a value is a point
84
- export const is = kvector.is;
85
-
86
- // Add and subtract vector(s)
87
- export const addVector = kvector.add;
88
- export const addVectors = kvector.add;
89
- export const subtractVector = kvector.subtract;
90
- export const equal = kvector.equal;
91
-
92
- // Convert from cartesian to polar and back
93
- export const polarRadFromCart = kvector.polarRadFromCart;
94
- export const polarDegFromCart = kvector.polarDegFromCart;
95
- export const cartFromPolarRad = kvector.cartFromPolarRad;
96
- export const cartFromPolarDeg = kvector.cartFromPolarDeg;
97
-
98
- // Rounding
99
- export const round = kvector.round;
100
- export const roundTo = kvector.roundTo;
101
- export const floorTo = kvector.floorTo;
102
- export const ceilTo = kvector.ceilTo;
package/src/ray.ts DELETED
@@ -1,24 +0,0 @@
1
- /**
2
- * Ray Utils
3
- * A ray (→) is an array of an endpoint and another point along the ray.
4
- * For example, [[0, 0], [1, 0]] is the ray starting at the origin and
5
- * traveling along the positive x-axis.
6
- */
7
-
8
- import * as kpoint from "./point";
9
- import * as kvector from "./vector";
10
-
11
- import type {Point} from "./point";
12
-
13
- export type Ray = [Point, Point];
14
-
15
- export function equal(ray1: Ray, ray2: Ray, tolerance?: number): boolean {
16
- // Compare the directions of the rays
17
- const v1 = kvector.subtract(ray1[1], ray1[0]);
18
- const v2 = kvector.subtract(ray2[1], ray2[0]);
19
-
20
- const sameOrigin = kpoint.equal(ray1[0], ray2[0]);
21
- const codirectional = kvector.codirectional(v1, v2, tolerance);
22
-
23
- return sameOrigin && codirectional;
24
- }
package/src/vector.ts DELETED
@@ -1,248 +0,0 @@
1
- /**
2
- * Vector Utils
3
- * A vector is an array of numbers e.g. [0, 3, 4].
4
- */
5
-
6
- import * as knumber from "./number";
7
-
8
- type Vector = ReadonlyArray<number>;
9
-
10
- function arraySum(array: ReadonlyArray<number>): number {
11
- return array.reduce((memo, arg) => memo + arg, 0);
12
- }
13
-
14
- function arrayProduct(array: ReadonlyArray<number>): number {
15
- return array.reduce((memo, arg) => memo * arg, 1);
16
- }
17
-
18
- export function zip<T>(xs: ReadonlyArray<T>, ys: ReadonlyArray<T>): [T, T][];
19
- export function zip<T>(...arrays: ReadonlyArray<T>[]): T[][];
20
- export function zip<T>(...arrays: ReadonlyArray<T>[]): T[][] {
21
- const n = Math.min(...arrays.map((a) => a.length));
22
- const results: T[][] = [];
23
- for (let i = 0; i < n; i++) {
24
- results.push(arrays.map((a) => a[i]));
25
- }
26
- return results;
27
- }
28
-
29
- export function map<T, U>(pair: [T, T], f: (a: T, i: number) => U): [U, U] {
30
- return [f(pair[0], 0), f(pair[1], 1)];
31
- }
32
-
33
- /**
34
- * Checks if the given vector contains only numbers and, optionally, is of the
35
- * right dimension (length).
36
- *
37
- * is([1, 2, 3]) -> true
38
- * is([1, "Hello", 3]) -> false
39
- * is([1, 2, 3], 1) -> false
40
- */
41
- export function is(vec: unknown, dimension: 2): vec is [number, number];
42
- export function is(vec: unknown, dimension?: number): vec is Vector;
43
- export function is(vec: unknown, dimension?: number) {
44
- if (!Array.isArray(vec)) {
45
- return false;
46
- }
47
- if (dimension !== undefined && vec.length !== dimension) {
48
- return false;
49
- }
50
- return vec.every(knumber.is);
51
- }
52
-
53
- // Normalize to a unit vector
54
- export function normalize<V extends Vector>(v: V): V {
55
- return scale(v, 1 / length(v));
56
- }
57
-
58
- // Length/magnitude of a vector
59
- export function length(v: Vector): number {
60
- return Math.sqrt(dot(v, v));
61
- }
62
- // Dot product of two vectors
63
- export function dot(a: Vector, b: Vector): number {
64
- const zipped = zip(a, b);
65
- const multiplied = zipped.map(arrayProduct);
66
- return arraySum(multiplied);
67
- }
68
-
69
- /* vector-add multiple [x, y] coords/vectors
70
- *
71
- * add([1, 2], [3, 4]) -> [4, 6]
72
- */
73
- export function add<V extends Vector>(...vecs: ReadonlyArray<V>): V {
74
- const zipped = zip(...vecs);
75
- // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
76
- return zipped.map(arraySum);
77
- }
78
-
79
- export function subtract<V extends Vector>(v1: V, v2: V): V {
80
- // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
81
- return zip(v1, v2).map((dim) => dim[0] - dim[1]);
82
- }
83
-
84
- export function negate<V extends Vector>(v: V): V {
85
- // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
86
- return v.map((x) => {
87
- return -x;
88
- });
89
- }
90
-
91
- // Scale a vector
92
- export function scale<V extends Vector>(v1: V, scalar: number): V {
93
- // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
94
- return v1.map((x) => {
95
- return x * scalar;
96
- });
97
- }
98
-
99
- export function equal(v1: Vector, v2: Vector, tolerance?: number): boolean {
100
- return (
101
- v1.length === v2.length &&
102
- zip(v1, v2).every((pair) => knumber.equal(pair[0], pair[1], tolerance))
103
- );
104
- }
105
-
106
- export function codirectional(
107
- v1: Vector,
108
- v2: Vector,
109
- tolerance?: number,
110
- ): boolean {
111
- // The origin is trivially codirectional with all other vectors.
112
- // This gives nice semantics for codirectionality between points when
113
- // comparing their difference vectors.
114
- if (
115
- knumber.equal(length(v1), 0, tolerance) ||
116
- knumber.equal(length(v2), 0, tolerance)
117
- ) {
118
- return true;
119
- }
120
-
121
- v1 = normalize(v1);
122
- v2 = normalize(v2);
123
-
124
- return equal(v1, v2, tolerance);
125
- }
126
-
127
- export function collinear(v1: Vector, v2: Vector, tolerance?: number): boolean {
128
- return (
129
- codirectional(v1, v2, tolerance) ||
130
- codirectional(v1, negate(v2), tolerance)
131
- );
132
- }
133
-
134
- // TODO(jeremy) These coordinate conversion functions really only handle 2D points (ie. [number, number])
135
-
136
- // Convert a cartesian coordinate into a radian polar coordinate
137
- export function polarRadFromCart(
138
- v: ReadonlyArray<number>,
139
- ): ReadonlyArray<number> {
140
- const radius = length(v);
141
- let theta = Math.atan2(v[1], v[0]);
142
-
143
- // Convert angle range from [-pi, pi] to [0, 2pi]
144
- if (theta < 0) {
145
- theta += 2 * Math.PI;
146
- }
147
-
148
- return [radius, theta];
149
- }
150
-
151
- // Converts a cartesian coordinate into a degree polar coordinate
152
- export function polarDegFromCart(
153
- v: ReadonlyArray<number>,
154
- ): ReadonlyArray<number> /* TODO: convert to tuple/Point */ {
155
- const polar = polarRadFromCart(v);
156
- return [polar[0], (polar[1] * 180) / Math.PI];
157
- }
158
-
159
- /* Convert a polar coordinate into a cartesian coordinate
160
- *
161
- * Examples:
162
- * cartFromPolarRad(5, Math.PI)
163
- */
164
- export function cartFromPolarRad(
165
- radius: number,
166
- theta = 0,
167
- ): ReadonlyArray<number> /* TODO: convert to tuple/Point */ {
168
- return [radius * Math.cos(theta), radius * Math.sin(theta)];
169
- }
170
-
171
- /* Convert a polar coordinate into a cartesian coordinate
172
- *
173
- * Examples:
174
- * cartFromPolarDeg(5, 30)
175
- */
176
- export function cartFromPolarDeg(
177
- radius: number,
178
- theta = 0,
179
- ): ReadonlyArray<number> {
180
- return cartFromPolarRad(radius, (theta * Math.PI) / 180);
181
- }
182
-
183
- // Rotate vector
184
- export function rotateRad(
185
- v: ReadonlyArray<number>,
186
- theta: number,
187
- ): ReadonlyArray<number> {
188
- const polar = polarRadFromCart(v);
189
- const angle = polar[1] + theta;
190
- return cartFromPolarRad(polar[0], angle);
191
- }
192
-
193
- export function rotateDeg(
194
- v: ReadonlyArray<number>,
195
- theta: number,
196
- ): ReadonlyArray<number> {
197
- const polar = polarDegFromCart(v);
198
- const angle = polar[1] + theta;
199
- return cartFromPolarDeg(polar[0], angle);
200
- }
201
-
202
- // Angle between two vectors
203
- export function angleRad(v1: Vector, v2: Vector): number {
204
- return Math.acos(dot(v1, v2) / (length(v1) * length(v2)));
205
- }
206
-
207
- export function angleDeg(v1: Vector, v2: Vector): number {
208
- return (angleRad(v1, v2) * 180) / Math.PI;
209
- }
210
-
211
- // Vector projection of v1 onto v2
212
- export function projection<V extends Vector>(v1: V, v2: V): V {
213
- const scalar = dot(v1, v2) / dot(v2, v2);
214
- return scale(v2, scalar);
215
- }
216
-
217
- // Round each number to a certain number of decimal places
218
- export function round<V extends Vector>(vec: V, precision: V | number): V {
219
- // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
220
- return vec.map((elem, i) => knumber.round(elem, precision[i] || precision));
221
- }
222
-
223
- // Round each number to the nearest increment
224
- export function roundTo(
225
- coord: [number, number],
226
- increment: [number, number] | number,
227
- ): [number, number];
228
- export function roundTo<V extends Vector>(vec: V, increment: V | number): V;
229
- export function roundTo<V extends Vector>(vec: V, increment: V | number): V {
230
- // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
231
- return vec.map((elem, i) =>
232
- knumber.roundTo(elem, increment[i] || increment),
233
- );
234
- }
235
-
236
- export function floorTo<V extends Vector>(vec: V, increment: V | number): V {
237
- // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
238
- return vec.map((elem, i) =>
239
- knumber.floorTo(elem, increment[i] || increment),
240
- );
241
- }
242
-
243
- export function ceilTo<V extends Vector>(vec: V, increment: V | number): V {
244
- // @ts-expect-error - TS2322 - Type 'number[]' is not assignable to type 'V'.
245
- return vec.map((elem, i) =>
246
- knumber.ceilTo(elem, increment[i] || increment),
247
- );
248
- }
package/src/version.ts DELETED
@@ -1,10 +0,0 @@
1
- // This file is processed by a Rollup plugin (replace) to inject the production
2
- // version number during the release build.
3
- // In dev, you'll never see the version number.
4
-
5
- import {addLibraryVersionToPerseusDebug} from "@khanacademy/perseus-core";
6
-
7
- const libName = "@khanacademy/kmath";
8
- export const libVersion = "__lib_version__";
9
-
10
- addLibraryVersionToPerseusDebug(libName, libVersion);
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../tsconfig-shared.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "src",
6
- },
7
- "references": [
8
- {"path": "../perseus-core/tsconfig-build.json"}
9
- ]
10
- }