@zushah/chalkboard 3.0.0 → 3.0.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 +26 -1
- package/README.md +12 -14
- package/dist/Chalkboard.d.ts +4 -4
- package/dist/Chalkboard.d.ts.map +1 -1
- package/dist/Chalkboard.js +762 -654
- package/dist/Chalkboard.js.map +1 -1
- package/dist/Chalkboard.min.js +1 -1
- package/package.json +4 -3
package/dist/Chalkboard.js
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Chalkboard
|
|
3
|
+
Version 3.0.2 Euler
|
|
4
|
+
Released April 13th, 2026
|
|
5
|
+
Authored by Zushah: https://www.github.com/Zushah
|
|
6
|
+
Licensed under MPL-2.0: https://opensource.org/license/mpl-2-0
|
|
7
|
+
Repository: https://www.github.com/Zushah/Chalkboard
|
|
8
|
+
Website: https://zushah.github.io/Chalkboard
|
|
9
|
+
*/
|
|
10
|
+
/*
|
|
11
|
+
This Source Code Form is subject to the terms of the
|
|
12
|
+
Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
|
|
13
|
+
with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
14
|
+
*/
|
|
1
15
|
"use strict";
|
|
2
16
|
var Chalkboard;
|
|
3
17
|
(function (Chalkboard) {
|
|
@@ -83,7 +97,15 @@ var Chalkboard;
|
|
|
83
97
|
};
|
|
84
98
|
Chalkboard.CONTEXT = typeof window !== "undefined" ? "ctx" : "0";
|
|
85
99
|
Chalkboard.E = (exponent = 1) => {
|
|
86
|
-
|
|
100
|
+
if (exponent === 0)
|
|
101
|
+
return 1;
|
|
102
|
+
if (exponent === 1)
|
|
103
|
+
return 2.718281828459045;
|
|
104
|
+
const LN2 = 0.6931471805599453, INV_LN2 = 1.4426950408889634;
|
|
105
|
+
const k = Math.round(exponent * INV_LN2);
|
|
106
|
+
const r = exponent - k * LN2, r2 = r * r, r3 = r2 * r, r4 = r3 * r, r5 = r4 * r, r6 = r5 * r, r7 = r6 * r, r8 = r7 * r, r9 = r8 * r, r10 = r9 * r;
|
|
107
|
+
const exp_r = 1 + r + r2 / 2 + r3 / 6 + r4 / 24 + r5 / 120 + r6 / 720 + r7 / 5040 + r8 / 40320 + r9 / 362880 + r10 / 3628800;
|
|
108
|
+
return exp_r * (2 ** k);
|
|
87
109
|
};
|
|
88
110
|
Chalkboard.I = (exponent = 1) => {
|
|
89
111
|
if (exponent % 4 === 0)
|
|
@@ -97,13 +119,36 @@ var Chalkboard;
|
|
|
97
119
|
return Chalkboard.comp.init(0, 0);
|
|
98
120
|
};
|
|
99
121
|
Chalkboard.PI = (coefficient = 1) => {
|
|
100
|
-
|
|
122
|
+
let a = 1.0, b = Math.sqrt(0.5), t = 0.25, p = 1.0;
|
|
123
|
+
let aNext = (a + b) * 0.5, bNext = Math.sqrt(a * b);
|
|
124
|
+
t -= p * (a - aNext) * (a - aNext);
|
|
125
|
+
a = aNext;
|
|
126
|
+
b = bNext;
|
|
127
|
+
p *= 2.0;
|
|
128
|
+
aNext = (a + b) * 0.5;
|
|
129
|
+
bNext = Math.sqrt(a * b);
|
|
130
|
+
t -= p * (a - aNext) * (a - aNext);
|
|
131
|
+
a = aNext;
|
|
132
|
+
b = bNext;
|
|
133
|
+
p *= 2.0;
|
|
134
|
+
aNext = (a + b) * 0.5;
|
|
135
|
+
bNext = Math.sqrt(a * b);
|
|
136
|
+
t -= p * (a - aNext) * (a - aNext);
|
|
137
|
+
a = aNext;
|
|
138
|
+
b = bNext;
|
|
139
|
+
p *= 2.0;
|
|
140
|
+
aNext = (a + b) * 0.5;
|
|
141
|
+
bNext = Math.sqrt(a * b);
|
|
142
|
+
t -= p * (a - aNext) * (a - aNext);
|
|
143
|
+
a = aNext;
|
|
144
|
+
b = bNext;
|
|
145
|
+
return coefficient * (((a + b) * (a + b)) / (4.0 * t));
|
|
101
146
|
};
|
|
102
147
|
Chalkboard.REGISTER = (name, func) => {
|
|
103
148
|
Chalkboard.REGISTRY[name] = func;
|
|
104
149
|
};
|
|
105
150
|
Chalkboard.REGISTRY = {};
|
|
106
|
-
Chalkboard.VERSION = "3.0.
|
|
151
|
+
Chalkboard.VERSION = "3.0.1";
|
|
107
152
|
Chalkboard.VERSIONALIAS = "Euler";
|
|
108
153
|
})(Chalkboard || (Chalkboard = {}));
|
|
109
154
|
if (typeof window === "undefined")
|
|
@@ -3057,38 +3102,34 @@ var Chalkboard;
|
|
|
3057
3102
|
calc.fxdx = (func, inf, sup) => {
|
|
3058
3103
|
if (func.field !== "real")
|
|
3059
3104
|
throw new TypeError("Chalkboard.calc.fxdx: Property 'field' of 'func' must be 'real'.");
|
|
3105
|
+
const integrate = (f, a, b, eps = 1e-6) => {
|
|
3106
|
+
const asq = (a, b, fa, fm, fb, whole, eps, depth) => {
|
|
3107
|
+
const m = (a + b) / 2, h = (b - a) / 2;
|
|
3108
|
+
const lm = (a + m) / 2, rm = (m + b) / 2;
|
|
3109
|
+
const flm = f(lm), frm = f(rm);
|
|
3110
|
+
const left = (h / 6) * (fa + 4 * flm + fm);
|
|
3111
|
+
const right = (h / 6) * (fm + 4 * frm + fb);
|
|
3112
|
+
const delta = left + right - whole;
|
|
3113
|
+
if (depth >= 50 || Math.abs(delta) <= 15 * eps)
|
|
3114
|
+
return left + right + delta / 15;
|
|
3115
|
+
return asq(a, m, fa, flm, fm, left, eps / 2, depth + 1) + asq(m, b, fm, frm, fb, right, eps / 2, depth + 1);
|
|
3116
|
+
};
|
|
3117
|
+
const m = (a + b) / 2;
|
|
3118
|
+
const fa = f(a), fm = f(m), fb = f(b);
|
|
3119
|
+
const whole = ((b - a) / 6) * (fa + 4 * fm + fb);
|
|
3120
|
+
return asq(a, b, fa, fm, fb, whole, eps, 0);
|
|
3121
|
+
};
|
|
3060
3122
|
if (func.type === "scalar2d") {
|
|
3061
3123
|
const f = func.rule;
|
|
3062
|
-
|
|
3063
|
-
const dx = (sup - inf) / 1000000;
|
|
3064
|
-
for (let i = 1; i < 1000000; i++) {
|
|
3065
|
-
fx += i % 2 === 0 ? 2 * f(inf + i * dx) : 4 * f(inf + i * dx);
|
|
3066
|
-
}
|
|
3067
|
-
return (fx * dx) / 3;
|
|
3124
|
+
return integrate(f, inf, sup);
|
|
3068
3125
|
}
|
|
3069
3126
|
else if (func.type === "curve2d") {
|
|
3070
3127
|
const f = func.rule;
|
|
3071
|
-
|
|
3072
|
-
let fy = f[1](inf) + f[1](sup);
|
|
3073
|
-
const dt = (sup - inf) / 1000000;
|
|
3074
|
-
for (let i = 1; i < 1000000; i++) {
|
|
3075
|
-
fx += i % 2 === 0 ? 2 * f[0](inf + i * dt) : 4 * f[0](inf + i * dt);
|
|
3076
|
-
fy += i % 2 === 0 ? 2 * f[1](inf + i * dt) : 4 * f[1](inf + i * dt);
|
|
3077
|
-
}
|
|
3078
|
-
return Chalkboard.vect.init((fx * dt) / 3, (fy * dt) / 3);
|
|
3128
|
+
return Chalkboard.vect.init(integrate(f[0], inf, sup), integrate(f[1], inf, sup));
|
|
3079
3129
|
}
|
|
3080
3130
|
else if (func.type === "curve3d") {
|
|
3081
3131
|
const f = func.rule;
|
|
3082
|
-
|
|
3083
|
-
let fy = f[1](inf) + f[1](sup);
|
|
3084
|
-
let fz = f[2](inf) + f[2](sup);
|
|
3085
|
-
const dt = (sup - inf) / 1000000;
|
|
3086
|
-
for (let i = 1; i < 1000000; i++) {
|
|
3087
|
-
fx += i % 2 === 0 ? 2 * f[0](inf + i * dt) : 4 * f[0](inf + i * dt);
|
|
3088
|
-
fy += i % 2 === 0 ? 2 * f[1](inf + i * dt) : 4 * f[1](inf + i * dt);
|
|
3089
|
-
fz += i % 2 === 0 ? 2 * f[2](inf + i * dt) : 4 * f[2](inf + i * dt);
|
|
3090
|
-
}
|
|
3091
|
-
return Chalkboard.vect.init((fx * dt) / 3, (fy * dt) / 3, (fz * dt) / 3);
|
|
3132
|
+
return Chalkboard.vect.init(integrate(f[0], inf, sup), integrate(f[1], inf, sup), integrate(f[2], inf, sup));
|
|
3092
3133
|
}
|
|
3093
3134
|
throw new TypeError("Chalkboard.calc.fxdx: Property 'type' of 'func' must be 'scalar2d', 'curve2d', or 'curve3d'.");
|
|
3094
3135
|
};
|
|
@@ -3097,15 +3138,25 @@ var Chalkboard;
|
|
|
3097
3138
|
throw new TypeError("Chalkboard.calc.fxydxdy: Property 'field' of 'func' must be 'real'.");
|
|
3098
3139
|
if (func.type === "scalar3d") {
|
|
3099
3140
|
const f = func.rule;
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3141
|
+
const integrate = (g, a, b, eps) => {
|
|
3142
|
+
const asq = (a, b, fa, fm, fb, whole, eps, depth) => {
|
|
3143
|
+
const m = (a + b) / 2, h = (b - a) / 2;
|
|
3144
|
+
const lm = (a + m) / 2, rm = (m + b) / 2;
|
|
3145
|
+
const flm = g(lm), frm = g(rm);
|
|
3146
|
+
const left = (h / 6) * (fa + 4 * flm + fm);
|
|
3147
|
+
const right = (h / 6) * (fm + 4 * frm + fb);
|
|
3148
|
+
const delta = left + right - whole;
|
|
3149
|
+
if (depth >= 50 || Math.abs(delta) <= 15 * eps)
|
|
3150
|
+
return left + right + delta / 15;
|
|
3151
|
+
return asq(a, m, fa, flm, fm, left, eps / 2, depth + 1) + asq(m, b, fm, frm, fb, right, eps / 2, depth + 1);
|
|
3152
|
+
};
|
|
3153
|
+
const m = (a + b) / 2;
|
|
3154
|
+
const fa = g(a), fm = g(m), fb = g(b);
|
|
3155
|
+
const whole = ((b - a) / 6) * (fa + 4 * fm + fb);
|
|
3156
|
+
return asq(a, b, fa, fm, fb, whole, eps, 0);
|
|
3157
|
+
};
|
|
3158
|
+
const g = (x) => integrate((y) => f(x, y), yinf, ysup, 1e-5);
|
|
3159
|
+
return integrate(g, xinf, xsup, 1e-5);
|
|
3109
3160
|
}
|
|
3110
3161
|
throw new TypeError("Chalkboard.calc.fxydxdy: Property 'type' of 'func' must be 'scalar3d'.");
|
|
3111
3162
|
};
|
|
@@ -7517,16 +7568,24 @@ var Chalkboard;
|
|
|
7517
7568
|
numb.combination = (n, r) => {
|
|
7518
7569
|
if (!Number.isInteger(n) || !Number.isInteger(r) || n < 0 || r < 0 || r > n)
|
|
7519
7570
|
throw new Error(`Chalkboard.numb.combination: Parameters "n" and "r" must be integers with 0 <= r <= n.`);
|
|
7520
|
-
return Chalkboard.numb.
|
|
7571
|
+
return Chalkboard.numb.binomial(n, r);
|
|
7521
7572
|
};
|
|
7522
7573
|
numb.compositeArr = (inf, sup) => {
|
|
7523
7574
|
if (!Number.isInteger(inf) || !Number.isInteger(sup))
|
|
7524
7575
|
throw new Error(`Chalkboard.numb.compositeArr: Parameters "inf" and "sup" must be integers.`);
|
|
7525
7576
|
if (inf > sup)
|
|
7526
7577
|
throw new Error(`Chalkboard.numb.compositeArr: Parameter "inf" must be less than or equal to "sup".`);
|
|
7578
|
+
if (sup < 4)
|
|
7579
|
+
return [];
|
|
7580
|
+
const sieve = new Uint8Array(sup + 1);
|
|
7581
|
+
for (let p = 2; p * p <= sup; p++)
|
|
7582
|
+
if (sieve[p] === 0)
|
|
7583
|
+
for (let i = p * p; i <= sup; i += p)
|
|
7584
|
+
sieve[i] = 1;
|
|
7527
7585
|
const result = [];
|
|
7528
|
-
|
|
7529
|
-
|
|
7586
|
+
const start = Math.max(4, inf);
|
|
7587
|
+
for (let i = start; i <= sup; i++)
|
|
7588
|
+
if (sieve[i] === 1)
|
|
7530
7589
|
result.push(i);
|
|
7531
7590
|
return result;
|
|
7532
7591
|
};
|
|
@@ -7741,10 +7800,15 @@ var Chalkboard;
|
|
|
7741
7800
|
if (!Number.isInteger(num) || num <= 0)
|
|
7742
7801
|
throw new Error(`Chalkboard.numb.divisors: Parameter "num" must be a positive integer.`);
|
|
7743
7802
|
const result = [];
|
|
7744
|
-
|
|
7745
|
-
|
|
7803
|
+
const upper = Math.floor(Math.sqrt(num));
|
|
7804
|
+
for (let i = 1; i <= upper; i++) {
|
|
7805
|
+
if (num % i === 0) {
|
|
7746
7806
|
result.push(i);
|
|
7747
|
-
|
|
7807
|
+
if (i !== num / i)
|
|
7808
|
+
result.push(num / i);
|
|
7809
|
+
}
|
|
7810
|
+
}
|
|
7811
|
+
return result.sort((a, b) => a - b);
|
|
7748
7812
|
};
|
|
7749
7813
|
numb.Euler = (num) => {
|
|
7750
7814
|
if (!Number.isInteger(num) || num <= 0)
|
|
@@ -7985,7 +8049,10 @@ var Chalkboard;
|
|
|
7985
8049
|
numb.permutation = (n, r) => {
|
|
7986
8050
|
if (!Number.isInteger(n) || !Number.isInteger(r) || n < 0 || r < 0 || r > n)
|
|
7987
8051
|
throw new Error(`Chalkboard.numb.permutation: Parameters "n" and "r" must be integers with 0 <= r <= n.`);
|
|
7988
|
-
|
|
8052
|
+
let result = 1;
|
|
8053
|
+
for (let i = n; i > n - r; i--)
|
|
8054
|
+
result *= i;
|
|
8055
|
+
return Math.round(result);
|
|
7989
8056
|
};
|
|
7990
8057
|
numb.Poissonian = (l = 1) => {
|
|
7991
8058
|
if (typeof l !== "number" || !Number.isFinite(l))
|
|
@@ -8019,9 +8086,19 @@ var Chalkboard;
|
|
|
8019
8086
|
throw new Error(`Chalkboard.numb.primeArr: Parameters "inf" and "sup" must be integers.`);
|
|
8020
8087
|
if (inf > sup)
|
|
8021
8088
|
throw new Error(`Chalkboard.numb.primeArr: Parameter "inf" must be less than or equal to "sup".`);
|
|
8089
|
+
if (sup < 2)
|
|
8090
|
+
return [];
|
|
8091
|
+
const sieve = new Uint8Array(sup + 1);
|
|
8092
|
+
sieve[0] = 1;
|
|
8093
|
+
sieve[1] = 1;
|
|
8094
|
+
for (let p = 2; p * p <= sup; p++)
|
|
8095
|
+
if (sieve[p] === 0)
|
|
8096
|
+
for (let i = p * p; i <= sup; i += p)
|
|
8097
|
+
sieve[i] = 1;
|
|
8022
8098
|
const result = [];
|
|
8023
|
-
|
|
8024
|
-
|
|
8099
|
+
const start = Math.max(2, inf);
|
|
8100
|
+
for (let i = start; i <= sup; i++)
|
|
8101
|
+
if (sieve[i] === 0)
|
|
8025
8102
|
result.push(i);
|
|
8026
8103
|
return result;
|
|
8027
8104
|
};
|
|
@@ -8195,205 +8272,218 @@ var Chalkboard;
|
|
|
8195
8272
|
throw new Error("Cannot initialize canvas context. Make sure an HTML <canvas> element exists in the webpage before using Chalkboard.plot functions.");
|
|
8196
8273
|
}
|
|
8197
8274
|
};
|
|
8275
|
+
const $ = (config, defaults) => {
|
|
8276
|
+
const ctx = (config?.context ?? defaults?.context ?? getContext());
|
|
8277
|
+
const configMap = (config ?? {});
|
|
8278
|
+
const defaultsMap = { x: ctx.canvas.width / 2, y: ctx.canvas.height / 2, size: 1, strokeStyle: "black", lineWidth: 2, context: ctx, ...(defaults ?? {}) };
|
|
8279
|
+
const merged = {};
|
|
8280
|
+
const keys = new Set([...Object.keys(defaultsMap), ...Object.keys(configMap)]);
|
|
8281
|
+
for (const key of keys)
|
|
8282
|
+
merged[key] = configMap[key] ?? defaultsMap[key];
|
|
8283
|
+
merged.context = ctx;
|
|
8284
|
+
merged.x = (merged.x ?? ctx.canvas.width / 2);
|
|
8285
|
+
merged.y = (merged.y ?? ctx.canvas.height / 2);
|
|
8286
|
+
merged.size = (merged.size ?? 1) / 100;
|
|
8287
|
+
return merged;
|
|
8288
|
+
};
|
|
8198
8289
|
plot.autocorrelation = (func, config) => {
|
|
8199
|
-
(config
|
|
8200
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8201
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8202
|
-
size: config.size || 1,
|
|
8203
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8204
|
-
lineWidth: config.lineWidth || 2,
|
|
8205
|
-
domain: config.domain || [-10, 10],
|
|
8206
|
-
res: config.res || 25,
|
|
8207
|
-
context: config.context || getContext()
|
|
8208
|
-
}).size /= 100;
|
|
8290
|
+
const _config = $(config, { domain: [-10, 10], res: 25 });
|
|
8209
8291
|
const data = [];
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8292
|
+
_config.context.save();
|
|
8293
|
+
_config.context.translate(_config.x, _config.y);
|
|
8294
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8295
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8296
|
+
_config.context.beginPath();
|
|
8297
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8298
|
+
let previousY = null;
|
|
8299
|
+
for (let i = _config.domain[0] / _config.size; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8300
|
+
const currentY = -Chalkboard.calc.autocorrelation(func, i * _config.size) / _config.size;
|
|
8301
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8302
|
+
_config.context.moveTo(i, currentY);
|
|
8303
|
+
}
|
|
8304
|
+
else {
|
|
8305
|
+
_config.context.lineTo(i, currentY);
|
|
8306
|
+
}
|
|
8307
|
+
previousY = currentY;
|
|
8217
8308
|
data.push([i, Chalkboard.calc.autocorrelation(func, i)]);
|
|
8218
8309
|
}
|
|
8219
|
-
|
|
8220
|
-
|
|
8310
|
+
_config.context.stroke();
|
|
8311
|
+
_config.context.restore();
|
|
8221
8312
|
return data;
|
|
8222
8313
|
};
|
|
8223
8314
|
plot.barplot = (arr, bins, config) => {
|
|
8224
|
-
(config
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
lineWidth: config.lineWidth || 2,
|
|
8231
|
-
context: config.context || getContext()
|
|
8232
|
-
}).size /= 100;
|
|
8233
|
-
config.context.save();
|
|
8234
|
-
config.context.translate(config.x, config.y);
|
|
8235
|
-
config.context.lineWidth = config.lineWidth;
|
|
8236
|
-
config.context.strokeStyle = config.strokeStyle;
|
|
8237
|
-
config.context.fillStyle = config.fillStyle;
|
|
8315
|
+
const _config = $(config, { fillStyle: "white" });
|
|
8316
|
+
_config.context.save();
|
|
8317
|
+
_config.context.translate(_config.x, _config.y);
|
|
8318
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8319
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8320
|
+
_config.context.fillStyle = _config.fillStyle;
|
|
8238
8321
|
const bars = [];
|
|
8239
|
-
for (let i =
|
|
8240
|
-
|
|
8241
|
-
bars.push(Chalkboard.stat.lt(arr, bins[0], true));
|
|
8242
|
-
}
|
|
8243
|
-
else if (i === bins.length) {
|
|
8244
|
-
bars.push(Chalkboard.stat.gt(arr, bins[bins.length - 1], true));
|
|
8245
|
-
}
|
|
8246
|
-
else {
|
|
8247
|
-
bars.push(Chalkboard.stat.ineq(arr, bins[i - 1], bins[i], false, true));
|
|
8248
|
-
}
|
|
8322
|
+
for (let i = 1; i < bins.length; i++) {
|
|
8323
|
+
bars.push(Chalkboard.stat.ineq(arr, bins[i - 1], bins[i], i === 1, true));
|
|
8249
8324
|
}
|
|
8250
8325
|
const counts = [];
|
|
8251
8326
|
for (let i = 0; i < bars.length; i++) {
|
|
8252
8327
|
counts.push(bars[i].length);
|
|
8253
8328
|
}
|
|
8254
|
-
let x = 0;
|
|
8255
|
-
const width = counts.length / (2 * config.size);
|
|
8256
8329
|
for (let i = 0; i < counts.length; i++) {
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8330
|
+
const xStart = bins[i] / _config.size;
|
|
8331
|
+
const xEnd = bins[i + 1] / _config.size;
|
|
8332
|
+
const dynamicWidth = xEnd - xStart;
|
|
8333
|
+
_config.context.fillRect(xStart, 0, dynamicWidth, -counts[i] / _config.size);
|
|
8334
|
+
_config.context.strokeRect(xStart, 0, dynamicWidth, -counts[i] / _config.size);
|
|
8260
8335
|
}
|
|
8261
|
-
|
|
8336
|
+
_config.context.restore();
|
|
8262
8337
|
return bars;
|
|
8263
8338
|
};
|
|
8264
8339
|
plot.comp = (comp, config) => {
|
|
8265
|
-
(config
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
config.context.fillStyle = config.fillStyle;
|
|
8274
|
-
config.context.save();
|
|
8275
|
-
config.context.translate(config.x, config.y);
|
|
8276
|
-
config.context.beginPath();
|
|
8277
|
-
config.context.ellipse(comp.a / config.size, -comp.b / config.size, config.lineWidth, config.lineWidth, 0, 0, Chalkboard.PI(2));
|
|
8278
|
-
config.context.fill();
|
|
8279
|
-
config.context.restore();
|
|
8340
|
+
const _config = $(config, { fillStyle: "black", lineWidth: 5 });
|
|
8341
|
+
_config.context.fillStyle = _config.fillStyle;
|
|
8342
|
+
_config.context.save();
|
|
8343
|
+
_config.context.translate(_config.x, _config.y);
|
|
8344
|
+
_config.context.beginPath();
|
|
8345
|
+
_config.context.ellipse(comp.a / _config.size, -comp.b / _config.size, _config.lineWidth, _config.lineWidth, 0, 0, Chalkboard.PI(2));
|
|
8346
|
+
_config.context.fill();
|
|
8347
|
+
_config.context.restore();
|
|
8280
8348
|
return [[comp.a], [comp.b]];
|
|
8281
8349
|
};
|
|
8282
8350
|
plot.convolution = (func1, func2, config) => {
|
|
8283
|
-
(config
|
|
8284
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8285
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8286
|
-
size: config.size || 1,
|
|
8287
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8288
|
-
lineWidth: config.lineWidth || 2,
|
|
8289
|
-
domain: config.domain || [-10, 10],
|
|
8290
|
-
res: config.res || 25,
|
|
8291
|
-
context: config.context || getContext()
|
|
8292
|
-
}).size /= 100;
|
|
8351
|
+
const _config = $(config, { domain: [-10, 10], res: 25 });
|
|
8293
8352
|
const data = [];
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8353
|
+
_config.context.save();
|
|
8354
|
+
_config.context.translate(_config.x, _config.y);
|
|
8355
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8356
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8357
|
+
_config.context.beginPath();
|
|
8358
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8359
|
+
let previousY = null;
|
|
8360
|
+
for (let i = _config.domain[0] / _config.size; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8361
|
+
const currentY = -Chalkboard.calc.convolution(func1, func2, i * _config.size) / _config.size;
|
|
8362
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8363
|
+
_config.context.moveTo(i, currentY);
|
|
8364
|
+
}
|
|
8365
|
+
else {
|
|
8366
|
+
_config.context.lineTo(i, currentY);
|
|
8367
|
+
}
|
|
8368
|
+
previousY = currentY;
|
|
8301
8369
|
data.push([i, Chalkboard.calc.convolution(func1, func2, i)]);
|
|
8302
8370
|
}
|
|
8303
|
-
|
|
8304
|
-
|
|
8371
|
+
_config.context.stroke();
|
|
8372
|
+
_config.context.restore();
|
|
8305
8373
|
return data;
|
|
8306
8374
|
};
|
|
8307
8375
|
plot.correlation = (func1, func2, config) => {
|
|
8308
|
-
(config
|
|
8309
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8310
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8311
|
-
size: config.size || 1,
|
|
8312
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8313
|
-
lineWidth: config.lineWidth || 2,
|
|
8314
|
-
domain: config.domain || [-10, 10],
|
|
8315
|
-
res: config.res || 25,
|
|
8316
|
-
context: config.context || getContext()
|
|
8317
|
-
}).size /= 100;
|
|
8376
|
+
const _config = $(config, { domain: [-10, 10], res: 25 });
|
|
8318
8377
|
const data = [];
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8378
|
+
_config.context.save();
|
|
8379
|
+
_config.context.translate(_config.x, _config.y);
|
|
8380
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8381
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8382
|
+
_config.context.beginPath();
|
|
8383
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8384
|
+
let previousY = null;
|
|
8385
|
+
for (let i = _config.domain[0] / _config.size; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8386
|
+
const currentY = -Chalkboard.calc.correlation(func1, func2, i * _config.size) / _config.size;
|
|
8387
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8388
|
+
_config.context.moveTo(i, currentY);
|
|
8389
|
+
}
|
|
8390
|
+
else {
|
|
8391
|
+
_config.context.lineTo(i, currentY);
|
|
8392
|
+
}
|
|
8393
|
+
previousY = currentY;
|
|
8326
8394
|
data.push([i, Chalkboard.calc.correlation(func1, func2, i)]);
|
|
8327
8395
|
}
|
|
8328
|
-
|
|
8329
|
-
|
|
8396
|
+
_config.context.stroke();
|
|
8397
|
+
_config.context.restore();
|
|
8330
8398
|
return data;
|
|
8331
8399
|
};
|
|
8332
8400
|
plot.definition = (func, config) => {
|
|
8333
|
-
(config
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
size: config.size || 1,
|
|
8337
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8338
|
-
lineWidth: config.lineWidth || 2,
|
|
8339
|
-
domain: config.domain || (func.field === "comp" ? [[-10, 10], [-10, 10]] : [-10, 10]),
|
|
8340
|
-
res: config.res || (func.field === "comp" ? 5 : 1),
|
|
8341
|
-
isInverse: config.isInverse || false,
|
|
8342
|
-
isPolar: config.isPolar || false,
|
|
8343
|
-
context: config.context || getContext()
|
|
8344
|
-
}).size /= 100;
|
|
8345
|
-
const xdomain = config.domain;
|
|
8346
|
-
const xydomain = config.domain;
|
|
8401
|
+
const _config = $(config, { domain: (func.field === "comp" ? [[-10, 10], [-10, 10]] : [-10, 10]), res: (func.field === "comp" ? 5 : 1), isInverse: false, isPolar: false });
|
|
8402
|
+
const xdomain = _config.domain;
|
|
8403
|
+
const xydomain = _config.domain;
|
|
8347
8404
|
const data = [];
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8405
|
+
_config.context.save();
|
|
8406
|
+
_config.context.translate(_config.x, _config.y);
|
|
8407
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8408
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8409
|
+
_config.context.beginPath();
|
|
8410
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8411
|
+
let previousY = null;
|
|
8412
|
+
let previousX = null;
|
|
8413
|
+
if (func.type === "scalar2d" && !_config.isInverse && !_config.isPolar) {
|
|
8354
8414
|
const f = func.rule;
|
|
8355
|
-
for (let i = xdomain[0] /
|
|
8356
|
-
|
|
8415
|
+
for (let i = xdomain[0] / _config.size; i <= xdomain[1] / _config.size; i += _config.res) {
|
|
8416
|
+
const currentY = -f(i * _config.size) / _config.size;
|
|
8417
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8418
|
+
_config.context.moveTo(i, currentY);
|
|
8419
|
+
}
|
|
8420
|
+
else {
|
|
8421
|
+
_config.context.lineTo(i, currentY);
|
|
8422
|
+
}
|
|
8423
|
+
previousY = currentY;
|
|
8357
8424
|
data.push([i, f(i)]);
|
|
8358
8425
|
}
|
|
8359
8426
|
}
|
|
8360
|
-
else if (func.type === "scalar2d" &&
|
|
8427
|
+
else if (func.type === "scalar2d" && _config.isInverse && !_config.isPolar) {
|
|
8361
8428
|
const f = func.rule;
|
|
8362
|
-
for (let i = xdomain[0] /
|
|
8363
|
-
|
|
8429
|
+
for (let i = xdomain[0] / _config.size; i <= xdomain[1] / _config.size; i += _config.res) {
|
|
8430
|
+
const currentX = f(i * _config.size) / _config.size;
|
|
8431
|
+
const currentY = -i;
|
|
8432
|
+
if (previousX === null || Math.abs(currentX - previousX) > discontinuityThreshold) {
|
|
8433
|
+
_config.context.moveTo(currentX, currentY);
|
|
8434
|
+
}
|
|
8435
|
+
else {
|
|
8436
|
+
_config.context.lineTo(currentX, currentY);
|
|
8437
|
+
}
|
|
8438
|
+
previousX = currentX;
|
|
8364
8439
|
data.push([f(i), i]);
|
|
8365
8440
|
}
|
|
8366
8441
|
}
|
|
8367
|
-
else if (func.type === "scalar2d" && !
|
|
8442
|
+
else if (func.type === "scalar2d" && !_config.isInverse && _config.isPolar) {
|
|
8368
8443
|
const r = func.rule;
|
|
8369
|
-
for (let i = xdomain[0] /
|
|
8370
|
-
|
|
8444
|
+
for (let i = xdomain[0] / _config.size; i <= xdomain[1] / _config.size; i += _config.res) {
|
|
8445
|
+
const currentX = (r(i * _config.size) / _config.size) * Chalkboard.trig.cos(i * _config.size);
|
|
8446
|
+
const currentY = (-r(i * _config.size) / _config.size) * Chalkboard.trig.sin(i * _config.size);
|
|
8447
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8448
|
+
_config.context.moveTo(currentX, currentY);
|
|
8449
|
+
}
|
|
8450
|
+
else {
|
|
8451
|
+
_config.context.lineTo(currentX, currentY);
|
|
8452
|
+
}
|
|
8453
|
+
previousY = currentY;
|
|
8371
8454
|
data.push([i, r(i)]);
|
|
8372
8455
|
}
|
|
8373
8456
|
}
|
|
8374
8457
|
else if (func.type === "curve2d") {
|
|
8375
8458
|
const f = func.rule;
|
|
8376
|
-
for (let i = xdomain[0] /
|
|
8377
|
-
|
|
8459
|
+
for (let i = xdomain[0] / _config.size; i <= xdomain[1] / _config.size; i += _config.res) {
|
|
8460
|
+
const currentX = f[0](i * _config.size) / _config.size;
|
|
8461
|
+
const currentY = -f[1](i * _config.size) / _config.size;
|
|
8462
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8463
|
+
_config.context.moveTo(currentX, currentY);
|
|
8464
|
+
}
|
|
8465
|
+
else {
|
|
8466
|
+
_config.context.lineTo(currentX, currentY);
|
|
8467
|
+
}
|
|
8468
|
+
previousY = currentY;
|
|
8378
8469
|
data.push([f[0](i), f[1](i)]);
|
|
8379
8470
|
}
|
|
8380
8471
|
}
|
|
8381
8472
|
else if (func.field === "comp") {
|
|
8382
8473
|
const f = func.rule;
|
|
8383
|
-
for (let i = xydomain[0][0] /
|
|
8384
|
-
for (let j = xydomain[1][0] /
|
|
8385
|
-
const z = Chalkboard.comp.init(f[0](i *
|
|
8474
|
+
for (let i = xydomain[0][0] / _config.size; i <= xydomain[0][1] / _config.size; i += _config.res) {
|
|
8475
|
+
for (let j = xydomain[1][0] / _config.size; j <= xydomain[1][1] / _config.size; j += _config.res) {
|
|
8476
|
+
const z = Chalkboard.comp.init(f[0](i * _config.size, j * _config.size) / _config.size, f[1](i * _config.size, j * _config.size) / _config.size);
|
|
8386
8477
|
if (z.a === 0 && z.b === 0) {
|
|
8387
|
-
|
|
8478
|
+
_config.context.fillStyle = "rgb(0, 0, 0)";
|
|
8388
8479
|
}
|
|
8389
8480
|
else if (z.a === Infinity && z.b === Infinity) {
|
|
8390
|
-
|
|
8481
|
+
_config.context.fillStyle = "rgb(255, 255, 255)";
|
|
8391
8482
|
}
|
|
8392
8483
|
else {
|
|
8393
|
-
|
|
8394
|
-
"hsl(" + Chalkboard.trig.toDeg(Chalkboard.comp.arg(z)) + ", 100%, " + (Chalkboard.trig.tanh(Chalkboard.comp.mag(z) / Chalkboard.real.pow(10, 20)) + 0.5) * 100 + "%)";
|
|
8484
|
+
_config.context.fillStyle = "hsl(" + Chalkboard.trig.toDeg(Chalkboard.comp.arg(z)) + ", 100%, " + (Chalkboard.trig.tanh(Chalkboard.comp.mag(z) / Chalkboard.real.pow(10, 20)) + 0.5) * 100 + "%)";
|
|
8395
8485
|
}
|
|
8396
|
-
|
|
8486
|
+
_config.context.fillRect(i, j, 5, 5);
|
|
8397
8487
|
data.push([f[0](i, j), f[1](i, j)]);
|
|
8398
8488
|
}
|
|
8399
8489
|
}
|
|
@@ -8401,475 +8491,441 @@ var Chalkboard;
|
|
|
8401
8491
|
else {
|
|
8402
8492
|
throw new TypeError('Parameter "func" must be of type "ChalkboardFunction" with a property "type" of "expl", "inve", "pola", "curv", or "comp".');
|
|
8403
8493
|
}
|
|
8404
|
-
|
|
8405
|
-
|
|
8494
|
+
_config.context.stroke();
|
|
8495
|
+
_config.context.restore();
|
|
8406
8496
|
return data;
|
|
8407
8497
|
};
|
|
8408
8498
|
plot.dfdx = (func, config) => {
|
|
8409
|
-
(config
|
|
8410
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8411
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8412
|
-
size: config.size || 1,
|
|
8413
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8414
|
-
lineWidth: config.lineWidth || 2,
|
|
8415
|
-
domain: config.domain || [-10, 10],
|
|
8416
|
-
res: config.res || 25,
|
|
8417
|
-
isInverse: config.isInverse || false,
|
|
8418
|
-
context: config.context || getContext()
|
|
8419
|
-
}).size /= 100;
|
|
8499
|
+
const _config = $(config, { domain: [-10, 10], res: 25, isInverse: false });
|
|
8420
8500
|
const data = [];
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
|
|
8428
|
-
|
|
8501
|
+
_config.context.save();
|
|
8502
|
+
_config.context.translate(_config.x, _config.y);
|
|
8503
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8504
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8505
|
+
_config.context.beginPath();
|
|
8506
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8507
|
+
let previousY = null;
|
|
8508
|
+
let previousX = null;
|
|
8509
|
+
for (let i = _config.domain[0] / _config.size; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8510
|
+
if (func.type === "scalar2d" && !_config.isInverse) {
|
|
8511
|
+
const currentY = -Chalkboard.calc.dfdx(func, i * _config.size) / _config.size;
|
|
8512
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8513
|
+
_config.context.moveTo(i, currentY);
|
|
8514
|
+
}
|
|
8515
|
+
else {
|
|
8516
|
+
_config.context.lineTo(i, currentY);
|
|
8517
|
+
}
|
|
8518
|
+
previousY = currentY;
|
|
8429
8519
|
data.push([i, Chalkboard.calc.dfdx(func, i)]);
|
|
8430
8520
|
}
|
|
8431
|
-
else if (func.type === "scalar2d" &&
|
|
8432
|
-
|
|
8521
|
+
else if (func.type === "scalar2d" && _config.isInverse) {
|
|
8522
|
+
const currentX = Chalkboard.calc.dfdx(func, i * _config.size) / _config.size;
|
|
8523
|
+
const currentY = -i;
|
|
8524
|
+
if (previousX === null || Math.abs(currentX - previousX) > discontinuityThreshold) {
|
|
8525
|
+
_config.context.moveTo(currentX, currentY);
|
|
8526
|
+
}
|
|
8527
|
+
else {
|
|
8528
|
+
_config.context.lineTo(currentX, currentY);
|
|
8529
|
+
}
|
|
8530
|
+
previousX = currentX;
|
|
8433
8531
|
data.push([Chalkboard.calc.dfdx(func, i), i]);
|
|
8434
8532
|
}
|
|
8435
8533
|
}
|
|
8436
|
-
|
|
8437
|
-
|
|
8534
|
+
_config.context.stroke();
|
|
8535
|
+
_config.context.restore();
|
|
8438
8536
|
return data;
|
|
8439
8537
|
};
|
|
8440
8538
|
plot.d2fdx2 = (func, config) => {
|
|
8441
|
-
(config
|
|
8442
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8443
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8444
|
-
size: config.size || 1,
|
|
8445
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8446
|
-
lineWidth: config.lineWidth || 2,
|
|
8447
|
-
domain: config.domain || [-10, 10],
|
|
8448
|
-
res: config.res || 25,
|
|
8449
|
-
isInverse: config.isInverse || false,
|
|
8450
|
-
context: config.context || getContext()
|
|
8451
|
-
}).size /= 100;
|
|
8539
|
+
const _config = $(config, { domain: [-10, 10], res: 25, isInverse: false });
|
|
8452
8540
|
const data = [];
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8541
|
+
_config.context.save();
|
|
8542
|
+
_config.context.translate(_config.x, _config.y);
|
|
8543
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8544
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8545
|
+
_config.context.beginPath();
|
|
8546
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8547
|
+
let previousY = null;
|
|
8548
|
+
let previousX = null;
|
|
8549
|
+
for (let i = _config.domain[0] / _config.size; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8550
|
+
if (func.type === "scalar2d" && !_config.isInverse) {
|
|
8551
|
+
const currentY = -Chalkboard.calc.d2fdx2(func, i * _config.size) / _config.size;
|
|
8552
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8553
|
+
_config.context.moveTo(i, currentY);
|
|
8554
|
+
}
|
|
8555
|
+
else {
|
|
8556
|
+
_config.context.lineTo(i, currentY);
|
|
8557
|
+
}
|
|
8558
|
+
previousY = currentY;
|
|
8461
8559
|
data.push([i, Chalkboard.calc.d2fdx2(func, i)]);
|
|
8462
8560
|
}
|
|
8463
|
-
else if (func.type === "scalar2d" &&
|
|
8464
|
-
|
|
8561
|
+
else if (func.type === "scalar2d" && _config.isInverse) {
|
|
8562
|
+
const currentX = Chalkboard.calc.d2fdx2(func, i * _config.size) / _config.size;
|
|
8563
|
+
const currentY = -i;
|
|
8564
|
+
if (previousX === null || Math.abs(currentX - previousX) > discontinuityThreshold) {
|
|
8565
|
+
_config.context.moveTo(currentX, currentY);
|
|
8566
|
+
}
|
|
8567
|
+
else {
|
|
8568
|
+
_config.context.lineTo(currentX, currentY);
|
|
8569
|
+
}
|
|
8570
|
+
previousX = currentX;
|
|
8465
8571
|
data.push([Chalkboard.calc.d2fdx2(func, i), i]);
|
|
8466
8572
|
}
|
|
8467
8573
|
}
|
|
8468
|
-
|
|
8469
|
-
|
|
8574
|
+
_config.context.stroke();
|
|
8575
|
+
_config.context.restore();
|
|
8470
8576
|
return data;
|
|
8471
8577
|
};
|
|
8472
8578
|
plot.field = (vectfield, config) => {
|
|
8473
|
-
(config
|
|
8474
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8475
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8476
|
-
size: config.size || 1,
|
|
8477
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8478
|
-
lineWidth: config.lineWidth || 2,
|
|
8479
|
-
domain: config.domain || [[-10, 10], [-10, 10]],
|
|
8480
|
-
res: config.res || 25,
|
|
8481
|
-
context: config.context || getContext()
|
|
8482
|
-
}).size /= 100;
|
|
8579
|
+
const _config = $(config, { domain: [[-10, 10], [-10, 10]], res: 25 });
|
|
8483
8580
|
const data = [];
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
for (let i =
|
|
8489
|
-
for (let j =
|
|
8581
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8582
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8583
|
+
_config.context.save();
|
|
8584
|
+
_config.context.translate(_config.x, _config.y);
|
|
8585
|
+
for (let i = _config.domain[0][0] / _config.size; i <= _config.domain[0][1] / _config.size; i += _config.res) {
|
|
8586
|
+
for (let j = _config.domain[1][0] / _config.size; j <= _config.domain[1][1] / _config.size; j += _config.res) {
|
|
8490
8587
|
const v = Chalkboard.vect.fromField(vectfield, Chalkboard.vect.init(i, j));
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8588
|
+
_config.context.beginPath();
|
|
8589
|
+
_config.context.moveTo(i, j);
|
|
8590
|
+
_config.context.lineTo(i + v.x, j + v.y);
|
|
8591
|
+
_config.context.stroke();
|
|
8495
8592
|
data.push([i + v.x, j + v.y]);
|
|
8496
8593
|
}
|
|
8497
8594
|
}
|
|
8498
|
-
|
|
8595
|
+
_config.context.restore();
|
|
8499
8596
|
return data;
|
|
8500
8597
|
};
|
|
8501
8598
|
plot.Fourier = (func, config) => {
|
|
8502
|
-
(config
|
|
8503
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8504
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8505
|
-
size: config.size || 1,
|
|
8506
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8507
|
-
lineWidth: config.lineWidth || 2,
|
|
8508
|
-
domain: config.domain || [-10, 10],
|
|
8509
|
-
res: config.res || 25,
|
|
8510
|
-
context: config.context || getContext()
|
|
8511
|
-
}).size /= 100;
|
|
8599
|
+
const _config = $(config, { domain: [-10, 10], res: 25 });
|
|
8512
8600
|
const data = [];
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8601
|
+
_config.context.save();
|
|
8602
|
+
_config.context.translate(_config.x, _config.y);
|
|
8603
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8604
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8605
|
+
_config.context.beginPath();
|
|
8606
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8607
|
+
let previousY = null;
|
|
8608
|
+
for (let i = _config.domain[0] / _config.size; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8609
|
+
const currentY = -Chalkboard.calc.Fourier(func, i * _config.size) / _config.size;
|
|
8610
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8611
|
+
_config.context.moveTo(i, currentY);
|
|
8612
|
+
}
|
|
8613
|
+
else {
|
|
8614
|
+
_config.context.lineTo(i, currentY);
|
|
8615
|
+
}
|
|
8616
|
+
previousY = currentY;
|
|
8520
8617
|
data.push([i, Chalkboard.calc.Fourier(func, i)]);
|
|
8521
8618
|
}
|
|
8522
|
-
|
|
8523
|
-
|
|
8619
|
+
_config.context.stroke();
|
|
8620
|
+
_config.context.restore();
|
|
8524
8621
|
return data;
|
|
8525
8622
|
};
|
|
8526
8623
|
plot.fxdx = (func, config) => {
|
|
8527
|
-
(config
|
|
8528
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8529
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8530
|
-
size: config.size || 1,
|
|
8531
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8532
|
-
lineWidth: config.lineWidth || 2,
|
|
8533
|
-
domain: config.domain || [-10, 10],
|
|
8534
|
-
res: config.res || 25,
|
|
8535
|
-
isInverse: config.isInverse || false,
|
|
8536
|
-
context: config.context || getContext()
|
|
8537
|
-
}).size /= 100;
|
|
8624
|
+
const _config = $(config, { domain: [-10, 10], res: 25, isInverse: false });
|
|
8538
8625
|
const data = [];
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8626
|
+
_config.context.save();
|
|
8627
|
+
_config.context.translate(_config.x, _config.y);
|
|
8628
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8629
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8630
|
+
_config.context.beginPath();
|
|
8631
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8632
|
+
let previousY = null;
|
|
8633
|
+
let previousX = null;
|
|
8634
|
+
for (let i = _config.domain[0] / _config.size; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8635
|
+
if (func.type === "scalar2d" && !_config.isInverse) {
|
|
8636
|
+
const currentY = -Chalkboard.calc.fxdx(func, 0, i * _config.size) / _config.size;
|
|
8637
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8638
|
+
_config.context.moveTo(i, currentY);
|
|
8639
|
+
}
|
|
8640
|
+
else {
|
|
8641
|
+
_config.context.lineTo(i, currentY);
|
|
8642
|
+
}
|
|
8643
|
+
previousY = currentY;
|
|
8547
8644
|
data.push([i, Chalkboard.calc.fxdx(func, 0, i)]);
|
|
8548
8645
|
}
|
|
8549
|
-
else if (func.type === "scalar2d" &&
|
|
8550
|
-
|
|
8646
|
+
else if (func.type === "scalar2d" && _config.isInverse) {
|
|
8647
|
+
const currentX = Chalkboard.calc.fxdx(func, 0, i * _config.size) / _config.size;
|
|
8648
|
+
const currentY = -i;
|
|
8649
|
+
if (previousX === null || Math.abs(currentX - previousX) > discontinuityThreshold) {
|
|
8650
|
+
_config.context.moveTo(currentX, currentY);
|
|
8651
|
+
}
|
|
8652
|
+
else {
|
|
8653
|
+
_config.context.lineTo(currentX, currentY);
|
|
8654
|
+
}
|
|
8655
|
+
previousX = currentX;
|
|
8551
8656
|
data.push([Chalkboard.calc.fxdx(func, 0, i), i]);
|
|
8552
8657
|
}
|
|
8553
8658
|
}
|
|
8554
|
-
|
|
8555
|
-
|
|
8659
|
+
_config.context.stroke();
|
|
8660
|
+
_config.context.restore();
|
|
8556
8661
|
return data;
|
|
8557
8662
|
};
|
|
8558
8663
|
plot.Laplace = (func, config) => {
|
|
8559
|
-
(config
|
|
8560
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8561
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8562
|
-
size: config.size || 1,
|
|
8563
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8564
|
-
lineWidth: config.lineWidth || 2,
|
|
8565
|
-
domain: config.domain || [-10, 10],
|
|
8566
|
-
res: config.res || 25,
|
|
8567
|
-
context: config.context || getContext()
|
|
8568
|
-
}).size /= 100;
|
|
8664
|
+
const _config = $(config, { domain: [-10, 10], res: 25 });
|
|
8569
8665
|
const data = [];
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8666
|
+
_config.context.save();
|
|
8667
|
+
_config.context.translate(_config.x, _config.y);
|
|
8668
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8669
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8670
|
+
_config.context.beginPath();
|
|
8671
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8672
|
+
let previousY = null;
|
|
8673
|
+
if (_config.domain[0] >= 0) {
|
|
8674
|
+
for (let i = _config.domain[0] / _config.size; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8675
|
+
const currentY = -Chalkboard.calc.Laplace(func, i * _config.size) / _config.size;
|
|
8676
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8677
|
+
_config.context.moveTo(i, currentY);
|
|
8678
|
+
}
|
|
8679
|
+
else {
|
|
8680
|
+
_config.context.lineTo(i, currentY);
|
|
8681
|
+
}
|
|
8682
|
+
previousY = currentY;
|
|
8578
8683
|
data.push([i, Chalkboard.calc.Laplace(func, i)]);
|
|
8579
8684
|
}
|
|
8580
8685
|
}
|
|
8581
8686
|
else {
|
|
8582
|
-
for (let i = 0; i <=
|
|
8583
|
-
|
|
8687
|
+
for (let i = 0; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8688
|
+
const currentY = -Chalkboard.calc.Laplace(func, i * _config.size) / _config.size;
|
|
8689
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8690
|
+
_config.context.moveTo(i, currentY);
|
|
8691
|
+
}
|
|
8692
|
+
else {
|
|
8693
|
+
_config.context.lineTo(i, currentY);
|
|
8694
|
+
}
|
|
8695
|
+
previousY = currentY;
|
|
8584
8696
|
data.push([i, Chalkboard.calc.Laplace(func, i)]);
|
|
8585
8697
|
}
|
|
8586
8698
|
}
|
|
8587
|
-
|
|
8588
|
-
|
|
8699
|
+
_config.context.stroke();
|
|
8700
|
+
_config.context.restore();
|
|
8589
8701
|
return data;
|
|
8590
8702
|
};
|
|
8591
8703
|
plot.lineplot = (arr, bins, config) => {
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
lineWidth: config.lineWidth || 2,
|
|
8598
|
-
context: config.context || getContext()
|
|
8599
|
-
}).size /= 100;
|
|
8600
|
-
config.context.save();
|
|
8601
|
-
config.context.translate(config.x, config.y);
|
|
8602
|
-
config.context.lineWidth = config.lineWidth;
|
|
8603
|
-
config.context.strokeStyle = config.strokeStyle;
|
|
8704
|
+
const _config = $(config);
|
|
8705
|
+
_config.context.save();
|
|
8706
|
+
_config.context.translate(_config.x, _config.y);
|
|
8707
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8708
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8604
8709
|
const verts = [];
|
|
8605
|
-
for (let i =
|
|
8606
|
-
|
|
8607
|
-
verts.push(Chalkboard.stat.lt(arr, bins[0], true));
|
|
8608
|
-
}
|
|
8609
|
-
else if (i === bins.length) {
|
|
8610
|
-
verts.push(Chalkboard.stat.gt(arr, bins[bins.length - 1], true));
|
|
8611
|
-
}
|
|
8612
|
-
else {
|
|
8613
|
-
verts.push(Chalkboard.stat.ineq(arr, bins[i - 1], bins[i], false, true));
|
|
8614
|
-
}
|
|
8710
|
+
for (let i = 1; i < bins.length; i++) {
|
|
8711
|
+
verts.push(Chalkboard.stat.ineq(arr, bins[i - 1], bins[i], i === 1, true));
|
|
8615
8712
|
}
|
|
8616
8713
|
const counts = [];
|
|
8617
8714
|
for (let i = 0; i < verts.length; i++) {
|
|
8618
8715
|
counts.push(verts[i].length);
|
|
8619
8716
|
}
|
|
8620
|
-
|
|
8717
|
+
_config.context.beginPath();
|
|
8718
|
+
_config.context.moveTo(bins[0] / _config.size, 0);
|
|
8621
8719
|
for (let i = 0; i < counts.length; i++) {
|
|
8622
|
-
|
|
8720
|
+
const midX = (bins[i] + bins[i + 1]) / 2 / _config.size;
|
|
8721
|
+
_config.context.lineTo(midX, -counts[i] / _config.size);
|
|
8623
8722
|
}
|
|
8624
|
-
|
|
8625
|
-
|
|
8723
|
+
_config.context.lineTo(bins[bins.length - 1] / _config.size, 0);
|
|
8724
|
+
_config.context.stroke();
|
|
8725
|
+
_config.context.restore();
|
|
8626
8726
|
return verts;
|
|
8627
8727
|
};
|
|
8628
8728
|
plot.matr = (matr, config) => {
|
|
8629
|
-
(config
|
|
8630
|
-
|
|
8631
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8632
|
-
size: config.size || 1,
|
|
8633
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8634
|
-
lineWidth: config.lineWidth || 2,
|
|
8635
|
-
domain: config.domain || [-10, 10],
|
|
8636
|
-
context: config.context || getContext()
|
|
8637
|
-
}).size /= 100;
|
|
8638
|
-
for (let i = config.domain[0]; i <= config.domain[1]; i++) {
|
|
8729
|
+
const _config = $(config, { domain: [-10, 10] });
|
|
8730
|
+
for (let i = _config.domain[0]; i <= _config.domain[1]; i++) {
|
|
8639
8731
|
Chalkboard.plot.vect(Chalkboard.vect.init(matr[0][0], matr[1][0]), {
|
|
8640
|
-
x:
|
|
8641
|
-
y:
|
|
8642
|
-
size:
|
|
8643
|
-
strokeStyle:
|
|
8644
|
-
lineWidth:
|
|
8645
|
-
context:
|
|
8732
|
+
x: _config.x,
|
|
8733
|
+
y: _config.y + (i / _config.size) * matr[1][1],
|
|
8734
|
+
size: _config.size,
|
|
8735
|
+
strokeStyle: _config.strokeStyle,
|
|
8736
|
+
lineWidth: _config.lineWidth / 4,
|
|
8737
|
+
context: _config.context
|
|
8646
8738
|
});
|
|
8647
8739
|
Chalkboard.plot.vect(Chalkboard.vect.init(-matr[0][0], -matr[1][0]), {
|
|
8648
|
-
x:
|
|
8649
|
-
y:
|
|
8650
|
-
size:
|
|
8651
|
-
strokeStyle:
|
|
8652
|
-
lineWidth:
|
|
8653
|
-
context:
|
|
8740
|
+
x: _config.x,
|
|
8741
|
+
y: _config.y + (i / _config.size) * matr[1][1],
|
|
8742
|
+
size: _config.size,
|
|
8743
|
+
strokeStyle: _config.strokeStyle,
|
|
8744
|
+
lineWidth: _config.lineWidth / 4,
|
|
8745
|
+
context: _config.context
|
|
8654
8746
|
});
|
|
8655
8747
|
Chalkboard.plot.vect(Chalkboard.vect.init(matr[0][1], matr[1][1]), {
|
|
8656
|
-
x:
|
|
8657
|
-
y:
|
|
8658
|
-
size:
|
|
8659
|
-
strokeStyle:
|
|
8660
|
-
lineWidth:
|
|
8661
|
-
context:
|
|
8748
|
+
x: _config.x + (i / _config.size) * matr[0][0],
|
|
8749
|
+
y: _config.y,
|
|
8750
|
+
size: _config.size,
|
|
8751
|
+
strokeStyle: _config.strokeStyle,
|
|
8752
|
+
lineWidth: _config.lineWidth / 4,
|
|
8753
|
+
context: _config.context
|
|
8662
8754
|
});
|
|
8663
8755
|
Chalkboard.plot.vect(Chalkboard.vect.init(-matr[0][1], -matr[1][1]), {
|
|
8664
|
-
x:
|
|
8665
|
-
y:
|
|
8666
|
-
size:
|
|
8667
|
-
strokeStyle:
|
|
8668
|
-
lineWidth:
|
|
8669
|
-
context:
|
|
8756
|
+
x: _config.x + (i / _config.size) * matr[0][0],
|
|
8757
|
+
y: _config.y,
|
|
8758
|
+
size: _config.size,
|
|
8759
|
+
strokeStyle: _config.strokeStyle,
|
|
8760
|
+
lineWidth: _config.lineWidth / 4,
|
|
8761
|
+
context: _config.context
|
|
8670
8762
|
});
|
|
8671
8763
|
}
|
|
8672
|
-
Chalkboard.plot.vect(Chalkboard.vect.init(matr[0][0], matr[1][0]),
|
|
8673
|
-
Chalkboard.plot.vect(Chalkboard.vect.init(-matr[0][0], -matr[1][0]),
|
|
8674
|
-
Chalkboard.plot.vect(Chalkboard.vect.init(matr[0][1], matr[1][1]),
|
|
8675
|
-
Chalkboard.plot.vect(Chalkboard.vect.init(-matr[0][1], -matr[1][1]),
|
|
8764
|
+
Chalkboard.plot.vect(Chalkboard.vect.init(matr[0][0], matr[1][0]), _config);
|
|
8765
|
+
Chalkboard.plot.vect(Chalkboard.vect.init(-matr[0][0], -matr[1][0]), _config);
|
|
8766
|
+
Chalkboard.plot.vect(Chalkboard.vect.init(matr[0][1], matr[1][1]), _config);
|
|
8767
|
+
Chalkboard.plot.vect(Chalkboard.vect.init(-matr[0][1], -matr[1][1]), _config);
|
|
8676
8768
|
return matr;
|
|
8677
8769
|
};
|
|
8678
8770
|
plot.ode = (sol, config = {}) => {
|
|
8771
|
+
const _config = $(config, { phase: false, i: 0, j: 1 });
|
|
8679
8772
|
if (!sol || !Array.isArray(sol.t) || !Array.isArray(sol.y))
|
|
8680
8773
|
throw new Error(`Chalkboard.plot.ode: Parameter "sol" must have properties "t" and "y" as arrays.`);
|
|
8681
8774
|
if (sol.t.length !== sol.y.length || sol.t.length === 0)
|
|
8682
8775
|
throw new Error(`Chalkboard.plot.ode: Invalid solution object (length mismatch or empty).`);
|
|
8683
|
-
const ctx = config.context || getContext();
|
|
8684
|
-
const x0 = (config.x ?? ctx.canvas.width / 2);
|
|
8685
|
-
const y0 = (config.y ?? ctx.canvas.height / 2);
|
|
8686
|
-
const strokeStyle = config.strokeStyle ?? "black";
|
|
8687
|
-
const lineWidth = config.lineWidth ?? 2;
|
|
8688
|
-
const size = ((config.size ?? 1) / 100);
|
|
8689
|
-
const phase = config.phase ?? false;
|
|
8690
|
-
const i = config.i ?? 0;
|
|
8691
|
-
const j = config.j ?? 1;
|
|
8692
8776
|
const dim = sol.y[0].length;
|
|
8693
|
-
if (!Number.isInteger(i) || i < 0)
|
|
8777
|
+
if (!Number.isInteger(_config.i) || _config.i < 0)
|
|
8694
8778
|
throw new Error(`Chalkboard.plot.ode: "i" must be an integer >= 0.`);
|
|
8695
|
-
if (i >= dim)
|
|
8779
|
+
if (_config.i >= dim)
|
|
8696
8780
|
throw new Error(`Chalkboard.plot.ode: "i" is out of range for solution dimension.`);
|
|
8697
|
-
if (phase) {
|
|
8698
|
-
if (!Number.isInteger(j) || j < 0)
|
|
8781
|
+
if (_config.phase) {
|
|
8782
|
+
if (!Number.isInteger(_config.j) || _config.j < 0)
|
|
8699
8783
|
throw new Error(`Chalkboard.plot.ode: "j" must be an integer >= 0.`);
|
|
8700
|
-
if (j >= dim)
|
|
8784
|
+
if (_config.j >= dim)
|
|
8701
8785
|
throw new Error(`Chalkboard.plot.ode: "j" is out of range for solution dimension.`);
|
|
8702
|
-
if (i === j)
|
|
8786
|
+
if (_config.i === _config.j)
|
|
8703
8787
|
throw new Error(`Chalkboard.plot.ode: For phase plots, "i" and "j" must be different.`);
|
|
8704
8788
|
}
|
|
8705
8789
|
const data = [];
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
if (!phase) {
|
|
8790
|
+
_config.context.save();
|
|
8791
|
+
_config.context.translate(_config.x, _config.y);
|
|
8792
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8793
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8794
|
+
_config.context.beginPath();
|
|
8795
|
+
if (!_config.phase) {
|
|
8712
8796
|
for (let k = 0; k < sol.t.length; k++) {
|
|
8713
|
-
const X = sol.t[k] / size;
|
|
8714
|
-
const Y = -sol.y[k][i] / size;
|
|
8797
|
+
const X = sol.t[k] / _config.size;
|
|
8798
|
+
const Y = -sol.y[k][_config.i] / _config.size;
|
|
8715
8799
|
if (k === 0)
|
|
8716
|
-
|
|
8800
|
+
_config.context.moveTo(X, Y);
|
|
8717
8801
|
else
|
|
8718
|
-
|
|
8719
|
-
data.push([sol.t[k], sol.y[k][i]]);
|
|
8802
|
+
_config.context.lineTo(X, Y);
|
|
8803
|
+
data.push([sol.t[k], sol.y[k][_config.i]]);
|
|
8720
8804
|
}
|
|
8721
8805
|
}
|
|
8722
8806
|
else {
|
|
8723
8807
|
for (let k = 0; k < sol.y.length; k++) {
|
|
8724
|
-
const X = sol.y[k][i] / size;
|
|
8725
|
-
const Y = -sol.y[k][j] / size;
|
|
8808
|
+
const X = sol.y[k][_config.i] / _config.size;
|
|
8809
|
+
const Y = -sol.y[k][_config.j] / _config.size;
|
|
8726
8810
|
if (k === 0)
|
|
8727
|
-
|
|
8811
|
+
_config.context.moveTo(X, Y);
|
|
8728
8812
|
else
|
|
8729
|
-
|
|
8730
|
-
data.push([sol.y[k][i], sol.y[k][j]]);
|
|
8813
|
+
_config.context.lineTo(X, Y);
|
|
8814
|
+
data.push([sol.y[k][_config.i], sol.y[k][_config.j]]);
|
|
8731
8815
|
}
|
|
8732
8816
|
}
|
|
8733
|
-
|
|
8734
|
-
|
|
8817
|
+
_config.context.stroke();
|
|
8818
|
+
_config.context.restore();
|
|
8735
8819
|
return data;
|
|
8736
8820
|
};
|
|
8737
8821
|
plot.rOplane = (config) => {
|
|
8738
|
-
|
|
8739
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8740
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8741
|
-
size: config.size || 1,
|
|
8742
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8743
|
-
lineWidth: config.lineWidth || 2,
|
|
8744
|
-
context: config.context || getContext()
|
|
8745
|
-
}).size /= 100;
|
|
8822
|
+
const _config = $(config);
|
|
8746
8823
|
const cw = getContext().canvas.width;
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
for (let i = 0; i <= (
|
|
8753
|
-
|
|
8754
|
-
}
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8824
|
+
_config.context.save();
|
|
8825
|
+
_config.context.translate(_config.x, _config.y);
|
|
8826
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8827
|
+
_config.context.lineWidth = _config.lineWidth / 4;
|
|
8828
|
+
_config.context.beginPath();
|
|
8829
|
+
for (let i = 0; i <= (_config.size * cw) / 2; i++) {
|
|
8830
|
+
_config.context.ellipse(0, 0, i / _config.size, i / _config.size, 0, 0, Chalkboard.PI(2));
|
|
8831
|
+
}
|
|
8832
|
+
_config.context.stroke();
|
|
8833
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8834
|
+
_config.context.beginPath();
|
|
8835
|
+
_config.context.moveTo(-_config.x, 0);
|
|
8836
|
+
_config.context.lineTo(cw - _config.x, 0);
|
|
8837
|
+
_config.context.stroke();
|
|
8838
|
+
_config.context.beginPath();
|
|
8839
|
+
_config.context.moveTo(0, -_config.y);
|
|
8840
|
+
_config.context.lineTo(0, cw - _config.y);
|
|
8841
|
+
_config.context.stroke();
|
|
8842
|
+
_config.context.restore();
|
|
8766
8843
|
};
|
|
8767
8844
|
plot.scatterplot = (arr1, arr2, config) => {
|
|
8768
|
-
(config
|
|
8769
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8770
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8771
|
-
size: config.size || 1,
|
|
8772
|
-
fillStyle: config.fillStyle || "black",
|
|
8773
|
-
lineWidth: config.lineWidth || 5,
|
|
8774
|
-
context: config.context || getContext()
|
|
8775
|
-
}).size /= 100;
|
|
8845
|
+
const _config = $(config, { fillStyle: "black", lineWidth: 5 });
|
|
8776
8846
|
const data = [];
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8847
|
+
_config.context.save();
|
|
8848
|
+
_config.context.translate(_config.x, _config.y);
|
|
8849
|
+
_config.context.fillStyle = _config.fillStyle;
|
|
8780
8850
|
if (arr1.length === arr2.length) {
|
|
8781
8851
|
for (let i = 0; i < arr1.length; i++) {
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8852
|
+
_config.context.beginPath();
|
|
8853
|
+
_config.context.ellipse(arr1[i] / _config.size, -arr2[i] / _config.size, _config.lineWidth, _config.lineWidth, 0, 0, Chalkboard.PI(2));
|
|
8854
|
+
_config.context.fill();
|
|
8785
8855
|
data.push([arr1[i], arr2[i]]);
|
|
8786
8856
|
}
|
|
8787
8857
|
}
|
|
8788
|
-
|
|
8858
|
+
_config.context.restore();
|
|
8789
8859
|
return data;
|
|
8790
8860
|
};
|
|
8791
8861
|
plot.Taylor = (func, n, a, config) => {
|
|
8792
|
-
(config
|
|
8793
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8794
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8795
|
-
size: config.size || 1,
|
|
8796
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8797
|
-
lineWidth: config.lineWidth || 2,
|
|
8798
|
-
domain: config.domain || [-10, 10],
|
|
8799
|
-
res: config.res || 25,
|
|
8800
|
-
context: config.context || getContext()
|
|
8801
|
-
}).size /= 100;
|
|
8862
|
+
const _config = $(config, { domain: [-10, 10], res: 25 });
|
|
8802
8863
|
const data = [];
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8864
|
+
_config.context.save();
|
|
8865
|
+
_config.context.translate(_config.x, _config.y);
|
|
8866
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8867
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8868
|
+
_config.context.beginPath();
|
|
8869
|
+
const discontinuityThreshold = (_config.context.canvas.height / _config.size) * 1.5;
|
|
8870
|
+
let previousY = null;
|
|
8871
|
+
for (let i = _config.domain[0] / _config.size; i <= _config.domain[1] / _config.size; i += _config.res) {
|
|
8872
|
+
const currentY = -Chalkboard.calc.Taylor(func, i * _config.size, n, a) / _config.size;
|
|
8873
|
+
if (previousY === null || Math.abs(currentY - previousY) > discontinuityThreshold) {
|
|
8874
|
+
_config.context.moveTo(i, currentY);
|
|
8875
|
+
}
|
|
8876
|
+
else {
|
|
8877
|
+
_config.context.lineTo(i, currentY);
|
|
8878
|
+
}
|
|
8879
|
+
previousY = currentY;
|
|
8810
8880
|
data.push([i, Chalkboard.calc.Taylor(func, i, n, a)]);
|
|
8811
8881
|
}
|
|
8812
|
-
|
|
8813
|
-
|
|
8882
|
+
_config.context.stroke();
|
|
8883
|
+
_config.context.restore();
|
|
8814
8884
|
return data;
|
|
8815
8885
|
};
|
|
8816
8886
|
plot.vect = (vect, config) => {
|
|
8817
|
-
(config
|
|
8818
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8819
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8820
|
-
size: config.size || 1,
|
|
8821
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8822
|
-
lineWidth: config.lineWidth || 5,
|
|
8823
|
-
context: config.context || getContext()
|
|
8824
|
-
}).size /= 100;
|
|
8887
|
+
const _config = $(config, { lineWidth: 5 });
|
|
8825
8888
|
vect = vect;
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8889
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8890
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8891
|
+
_config.context.save();
|
|
8892
|
+
_config.context.translate(_config.x, _config.y);
|
|
8893
|
+
_config.context.beginPath();
|
|
8894
|
+
_config.context.moveTo(0, 0);
|
|
8895
|
+
_config.context.lineTo(vect.x / _config.size, -vect.y / _config.size);
|
|
8896
|
+
_config.context.stroke();
|
|
8897
|
+
_config.context.restore();
|
|
8835
8898
|
return [[vect.x], [vect.y]];
|
|
8836
8899
|
};
|
|
8837
8900
|
plot.xyplane = (config) => {
|
|
8838
|
-
|
|
8839
|
-
x: (config = config || {}).x || getContext().canvas.width / 2,
|
|
8840
|
-
y: config.y || getContext().canvas.height / 2,
|
|
8841
|
-
size: config.size || 1,
|
|
8842
|
-
strokeStyle: config.strokeStyle || "black",
|
|
8843
|
-
lineWidth: config.lineWidth || 2,
|
|
8844
|
-
context: config.context || getContext()
|
|
8845
|
-
}).size /= 100;
|
|
8901
|
+
const _config = $(config);
|
|
8846
8902
|
const cw = getContext().canvas.width;
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
for (let i = Math.floor(-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
}
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
for (let i = Math.floor(-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
}
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8903
|
+
_config.context.save();
|
|
8904
|
+
_config.context.translate(_config.x, _config.y);
|
|
8905
|
+
_config.context.strokeStyle = _config.strokeStyle;
|
|
8906
|
+
_config.context.lineWidth = _config.lineWidth / 4;
|
|
8907
|
+
_config.context.beginPath();
|
|
8908
|
+
for (let i = Math.floor(-_config.x / _config.size); i <= (cw - _config.x) / _config.size; i++) {
|
|
8909
|
+
_config.context.moveTo(i / _config.size, -_config.y);
|
|
8910
|
+
_config.context.lineTo(i / _config.size, cw - _config.y);
|
|
8911
|
+
}
|
|
8912
|
+
_config.context.stroke();
|
|
8913
|
+
_config.context.beginPath();
|
|
8914
|
+
for (let i = Math.floor(-_config.y / _config.size); i <= (cw - _config.y) / _config.size; i++) {
|
|
8915
|
+
_config.context.moveTo(-_config.x, i / _config.size);
|
|
8916
|
+
_config.context.lineTo(cw - _config.x, i / _config.size);
|
|
8917
|
+
}
|
|
8918
|
+
_config.context.stroke();
|
|
8919
|
+
_config.context.lineWidth = _config.lineWidth;
|
|
8920
|
+
_config.context.beginPath();
|
|
8921
|
+
_config.context.moveTo(-_config.x, 0);
|
|
8922
|
+
_config.context.lineTo(cw - _config.x, 0);
|
|
8923
|
+
_config.context.stroke();
|
|
8924
|
+
_config.context.beginPath();
|
|
8925
|
+
_config.context.moveTo(0, -_config.y);
|
|
8926
|
+
_config.context.lineTo(0, cw - _config.y);
|
|
8927
|
+
_config.context.stroke();
|
|
8928
|
+
_config.context.restore();
|
|
8873
8929
|
};
|
|
8874
8930
|
})(plot = Chalkboard.plot || (Chalkboard.plot = {}));
|
|
8875
8931
|
})(Chalkboard || (Chalkboard = {}));
|
|
@@ -9240,15 +9296,15 @@ var Chalkboard;
|
|
|
9240
9296
|
return 0;
|
|
9241
9297
|
}
|
|
9242
9298
|
};
|
|
9243
|
-
real.discriminant = (a, b, c, form = "
|
|
9244
|
-
if (form === "
|
|
9299
|
+
real.discriminant = (a, b, c, form = "standard") => {
|
|
9300
|
+
if (form === "standard") {
|
|
9245
9301
|
return b * b - 4 * a * c;
|
|
9246
9302
|
}
|
|
9247
|
-
else if (form === "
|
|
9303
|
+
else if (form === "vertex") {
|
|
9248
9304
|
return 2 * a * b * (2 * a * b) - 4 * a * c;
|
|
9249
9305
|
}
|
|
9250
9306
|
else {
|
|
9251
|
-
throw new TypeError("Chalkboard.real.discriminant: String 'form' must be '
|
|
9307
|
+
throw new TypeError("Chalkboard.real.discriminant: String 'form' must be 'standard' or 'vertex'.");
|
|
9252
9308
|
}
|
|
9253
9309
|
};
|
|
9254
9310
|
real.div = (func1, func2) => {
|
|
@@ -9303,12 +9359,12 @@ var Chalkboard;
|
|
|
9303
9359
|
const a4 = -1.453152027;
|
|
9304
9360
|
const a5 = 1.061405429;
|
|
9305
9361
|
const t = 1 / (1 + p * x);
|
|
9306
|
-
const y = 1 - (((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t) *
|
|
9362
|
+
const y = 1 - (((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t) * Chalkboard.E(-x * x);
|
|
9307
9363
|
return sign * y;
|
|
9308
9364
|
};
|
|
9309
9365
|
real.Gamma = (num) => {
|
|
9310
9366
|
if (typeof num !== "number" || !Number.isFinite(num))
|
|
9311
|
-
throw new TypeError("Chalkboard.real.
|
|
9367
|
+
throw new TypeError("Chalkboard.real.Gamma: Parameter 'num' must be a finite number.");
|
|
9312
9368
|
if (Number.isInteger(num) && num <= 0)
|
|
9313
9369
|
return NaN;
|
|
9314
9370
|
const p0 = 0.99999999999980993;
|
|
@@ -9321,7 +9377,7 @@ var Chalkboard;
|
|
|
9321
9377
|
const p7 = 9.9843695780195716e-6;
|
|
9322
9378
|
const p8 = 1.5056327351493116e-7;
|
|
9323
9379
|
if (num < 0.5)
|
|
9324
|
-
return
|
|
9380
|
+
return Chalkboard.PI() / (Chalkboard.trig.sin(Chalkboard.PI(num)) * Chalkboard.real.Gamma(1 - num));
|
|
9325
9381
|
const g = 7;
|
|
9326
9382
|
let x = num - 1;
|
|
9327
9383
|
let a = p0;
|
|
@@ -9334,7 +9390,7 @@ var Chalkboard;
|
|
|
9334
9390
|
a += p7 / (x + 7);
|
|
9335
9391
|
a += p8 / (x + 8);
|
|
9336
9392
|
const t = x + g + 0.5;
|
|
9337
|
-
return
|
|
9393
|
+
return Chalkboard.real.sqrt(Chalkboard.PI(2)) * Chalkboard.real.pow(t, x + 0.5) * Chalkboard.E(-t) * a;
|
|
9338
9394
|
};
|
|
9339
9395
|
real.Heaviside = (num, edge = 0, scl = 1) => {
|
|
9340
9396
|
if (num >= edge) {
|
|
@@ -9362,7 +9418,25 @@ var Chalkboard;
|
|
|
9362
9418
|
}
|
|
9363
9419
|
};
|
|
9364
9420
|
real.ln = (num) => {
|
|
9365
|
-
|
|
9421
|
+
if (num <= 0)
|
|
9422
|
+
return NaN;
|
|
9423
|
+
if (num === 1)
|
|
9424
|
+
return 0;
|
|
9425
|
+
if (num === Infinity)
|
|
9426
|
+
return Infinity;
|
|
9427
|
+
const LN2 = 0.6931471805599453;
|
|
9428
|
+
let E = 0, m = num;
|
|
9429
|
+
while (m > 1.414213562373095) {
|
|
9430
|
+
m *= 0.5;
|
|
9431
|
+
E++;
|
|
9432
|
+
}
|
|
9433
|
+
while (m < 0.7071067811865475) {
|
|
9434
|
+
m *= 2.0;
|
|
9435
|
+
E--;
|
|
9436
|
+
}
|
|
9437
|
+
const y = (m - 1) / (m + 1), y2 = y * y, y3 = y2 * y, y5 = y3 * y2, y7 = y5 * y2, y9 = y7 * y2, y11 = y9 * y2, y13 = y11 * y2, y15 = y13 * y2, y17 = y15 * y2, y19 = y17 * y2;
|
|
9438
|
+
const series = y + y3 / 3 + y5 / 5 + y7 / 7 + y9 / 9 + y11 / 11 + y13 / 13 + y15 / 15 + y17 / 17 + y19 / 19;
|
|
9439
|
+
return 2 * series + E * LN2;
|
|
9366
9440
|
};
|
|
9367
9441
|
real.log = (base, num) => {
|
|
9368
9442
|
return Chalkboard.real.ln(num) / Chalkboard.real.ln(base);
|
|
@@ -10293,11 +10367,30 @@ var Chalkboard;
|
|
|
10293
10367
|
};
|
|
10294
10368
|
real.pow = (base, num) => {
|
|
10295
10369
|
if (typeof base === "number") {
|
|
10296
|
-
if (base === 0 && num === 0)
|
|
10370
|
+
if (base === 0 && num === 0)
|
|
10371
|
+
return 1;
|
|
10372
|
+
if (base === 0)
|
|
10373
|
+
return 0;
|
|
10374
|
+
if (num === 0)
|
|
10297
10375
|
return 1;
|
|
10376
|
+
if (num === 1)
|
|
10377
|
+
return base;
|
|
10378
|
+
if (Number.isInteger(num)) {
|
|
10379
|
+
let res = 1;
|
|
10380
|
+
let b = base;
|
|
10381
|
+
let n = Math.abs(num);
|
|
10382
|
+
while (n > 0) {
|
|
10383
|
+
if (n % 2 === 1)
|
|
10384
|
+
res *= b;
|
|
10385
|
+
b *= b;
|
|
10386
|
+
n = Math.floor(n / 2);
|
|
10387
|
+
}
|
|
10388
|
+
return num < 0 ? 1 / res : res;
|
|
10298
10389
|
}
|
|
10299
10390
|
else {
|
|
10300
|
-
|
|
10391
|
+
if (base < 0)
|
|
10392
|
+
return NaN;
|
|
10393
|
+
return Chalkboard.E(num * Chalkboard.real.ln(base));
|
|
10301
10394
|
}
|
|
10302
10395
|
}
|
|
10303
10396
|
else {
|
|
@@ -10346,26 +10439,26 @@ var Chalkboard;
|
|
|
10346
10439
|
(p3[1] * p1[0] * p2[0]) / ((p3[0] - p1[0]) * (p3[0] - p2[0]));
|
|
10347
10440
|
return a * t * t + b * t + c;
|
|
10348
10441
|
};
|
|
10349
|
-
real.quadratic = (a, b, c, form = "
|
|
10350
|
-
if (form === "
|
|
10442
|
+
real.quadratic = (a, b, c, form = "standard") => {
|
|
10443
|
+
if (form === "standard") {
|
|
10351
10444
|
return Chalkboard.real.define((x) => a * x * x + b * x + c);
|
|
10352
10445
|
}
|
|
10353
|
-
else if (form === "
|
|
10446
|
+
else if (form === "vertex") {
|
|
10354
10447
|
return Chalkboard.real.define((x) => a * (x - b) * (x - b) + c);
|
|
10355
10448
|
}
|
|
10356
10449
|
else {
|
|
10357
|
-
throw new TypeError("Chalkboard.real.quadratic: String 'form' must be '
|
|
10450
|
+
throw new TypeError("Chalkboard.real.quadratic: String 'form' must be 'standard' or 'vertex'.");
|
|
10358
10451
|
}
|
|
10359
10452
|
};
|
|
10360
|
-
real.quadraticFormula = (a, b, c, form = "
|
|
10361
|
-
if (form === "
|
|
10362
|
-
return [(-b + Chalkboard.real.sqrt(Chalkboard.real.discriminant(a, b, c, "
|
|
10453
|
+
real.quadraticFormula = (a, b, c, form = "standard") => {
|
|
10454
|
+
if (form === "standard") {
|
|
10455
|
+
return [(-b + Chalkboard.real.sqrt(Chalkboard.real.discriminant(a, b, c, "standard"))) / (2 * a), (-b - Chalkboard.real.sqrt(Chalkboard.real.discriminant(a, b, c, "standard"))) / (2 * a)];
|
|
10363
10456
|
}
|
|
10364
|
-
else if (form === "
|
|
10457
|
+
else if (form === "vertex") {
|
|
10365
10458
|
return [b + Chalkboard.real.sqrt(-c / a), b - Chalkboard.real.sqrt(-c / a)];
|
|
10366
10459
|
}
|
|
10367
10460
|
else {
|
|
10368
|
-
throw new TypeError("Chalkboard.real.quadraticFormula: String 'form' must be '
|
|
10461
|
+
throw new TypeError("Chalkboard.real.quadraticFormula: String 'form' must be 'standard' or 'vertex'.");
|
|
10369
10462
|
}
|
|
10370
10463
|
};
|
|
10371
10464
|
real.ramp = (num, edge = 0, scl = 1) => {
|
|
@@ -10422,7 +10515,14 @@ var Chalkboard;
|
|
|
10422
10515
|
}
|
|
10423
10516
|
};
|
|
10424
10517
|
real.root = (num, index = 3) => {
|
|
10425
|
-
|
|
10518
|
+
if (num === 0)
|
|
10519
|
+
return 0;
|
|
10520
|
+
if (num < 0) {
|
|
10521
|
+
if (Number.isInteger(index) && Math.abs(index) % 2 === 1)
|
|
10522
|
+
return -Chalkboard.E(Chalkboard.real.ln(-num) / index);
|
|
10523
|
+
return NaN;
|
|
10524
|
+
}
|
|
10525
|
+
return Chalkboard.E(Chalkboard.real.ln(num) / index);
|
|
10426
10526
|
};
|
|
10427
10527
|
real.scl = (func, num) => {
|
|
10428
10528
|
if (func.field !== "real")
|
|
@@ -10462,12 +10562,26 @@ var Chalkboard;
|
|
|
10462
10562
|
return (y2 - y1) / (x2 - x1);
|
|
10463
10563
|
};
|
|
10464
10564
|
real.sqrt = (num) => {
|
|
10465
|
-
if (num
|
|
10466
|
-
return Math.exp(Math.log(num) / 2);
|
|
10467
|
-
}
|
|
10468
|
-
else {
|
|
10565
|
+
if (num < 0)
|
|
10469
10566
|
return NaN;
|
|
10470
|
-
|
|
10567
|
+
if (num === 0 || num === 1 || num === Infinity)
|
|
10568
|
+
return num;
|
|
10569
|
+
let S = num, E = 0;
|
|
10570
|
+
while (S >= 1.0) {
|
|
10571
|
+
S *= 0.25;
|
|
10572
|
+
E++;
|
|
10573
|
+
}
|
|
10574
|
+
while (S < 0.25) {
|
|
10575
|
+
S *= 4.0;
|
|
10576
|
+
E--;
|
|
10577
|
+
}
|
|
10578
|
+
let x = (S + 1.0) * 0.5;
|
|
10579
|
+
x = 0.5 * (x + S / x);
|
|
10580
|
+
x = 0.5 * (x + S / x);
|
|
10581
|
+
x = 0.5 * (x + S / x);
|
|
10582
|
+
x = 0.5 * (x + S / x);
|
|
10583
|
+
x = 0.5 * (x + S / x);
|
|
10584
|
+
return x * (2 ** E);
|
|
10471
10585
|
};
|
|
10472
10586
|
real.sub = (func1, func2) => {
|
|
10473
10587
|
if (func1.field !== "real" || func2.field !== "real")
|
|
@@ -10510,12 +10624,19 @@ var Chalkboard;
|
|
|
10510
10624
|
throw new TypeError("Chalkboard.real.sub: Properties 'type' of 'func1' and 'func2' must be 'scalar2d', 'scalar3d', 'scalar4d', 'vector2d', 'vector3d', 'vector4d', 'curve2d', 'curve3d', 'curve4d', or 'surface3d'.");
|
|
10511
10625
|
};
|
|
10512
10626
|
real.tetration = (base, num) => {
|
|
10513
|
-
if (num
|
|
10627
|
+
if (!Number.isInteger(num) || num < 0)
|
|
10628
|
+
return NaN;
|
|
10629
|
+
if (num === 0)
|
|
10514
10630
|
return 1;
|
|
10631
|
+
if (num === 1)
|
|
10632
|
+
return base;
|
|
10633
|
+
let result = base;
|
|
10634
|
+
for (let i = 1; i < num; i++) {
|
|
10635
|
+
result = Chalkboard.real.pow(base, result);
|
|
10636
|
+
if (result === Infinity)
|
|
10637
|
+
return Infinity;
|
|
10515
10638
|
}
|
|
10516
|
-
|
|
10517
|
-
return Math.pow(base, Chalkboard.real.tetration(base, num - 1));
|
|
10518
|
-
}
|
|
10639
|
+
return result;
|
|
10519
10640
|
};
|
|
10520
10641
|
real.translate = (func, h = 0, v = 0) => {
|
|
10521
10642
|
if (func.field !== "real")
|
|
@@ -10621,6 +10742,34 @@ var Chalkboard;
|
|
|
10621
10742
|
(function (Chalkboard) {
|
|
10622
10743
|
let stat;
|
|
10623
10744
|
(function (stat) {
|
|
10745
|
+
const $quickselect = (arr, k) => {
|
|
10746
|
+
const select = (left, right, k) => {
|
|
10747
|
+
if (left === right)
|
|
10748
|
+
return arr[left];
|
|
10749
|
+
let pivotIndex = Math.floor(Math.random() * (right - left + 1)) + left;
|
|
10750
|
+
const pivotValue = arr[pivotIndex];
|
|
10751
|
+
arr[pivotIndex] = arr[right];
|
|
10752
|
+
arr[right] = pivotValue;
|
|
10753
|
+
let storeIndex = left;
|
|
10754
|
+
for (let i = left; i < right; i++) {
|
|
10755
|
+
if (arr[i] < pivotValue) {
|
|
10756
|
+
const temp = arr[storeIndex];
|
|
10757
|
+
arr[storeIndex] = arr[i];
|
|
10758
|
+
arr[i] = temp;
|
|
10759
|
+
storeIndex++;
|
|
10760
|
+
}
|
|
10761
|
+
}
|
|
10762
|
+
arr[right] = arr[storeIndex];
|
|
10763
|
+
arr[storeIndex] = pivotValue;
|
|
10764
|
+
if (k === storeIndex)
|
|
10765
|
+
return arr[k];
|
|
10766
|
+
else if (k < storeIndex)
|
|
10767
|
+
return select(left, storeIndex - 1, k);
|
|
10768
|
+
else
|
|
10769
|
+
return select(storeIndex + 1, right, k);
|
|
10770
|
+
};
|
|
10771
|
+
return select(0, arr.length - 1, k);
|
|
10772
|
+
};
|
|
10624
10773
|
stat.absolute = (arr) => {
|
|
10625
10774
|
const result = [];
|
|
10626
10775
|
for (let i = 0; i < arr.length; i++) {
|
|
@@ -11081,14 +11230,15 @@ var Chalkboard;
|
|
|
11081
11230
|
return sum / weightSum;
|
|
11082
11231
|
};
|
|
11083
11232
|
stat.median = (arr) => {
|
|
11084
|
-
|
|
11085
|
-
return
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11233
|
+
if (arr.length === 0)
|
|
11234
|
+
return NaN;
|
|
11235
|
+
const copy = arr.slice();
|
|
11236
|
+
const mid = Math.floor(copy.length / 2);
|
|
11237
|
+
if (copy.length % 2 === 1) {
|
|
11238
|
+
return $quickselect(copy, mid);
|
|
11089
11239
|
}
|
|
11090
11240
|
else {
|
|
11091
|
-
return (
|
|
11241
|
+
return ($quickselect(copy, mid - 1) + $quickselect(copy, mid)) / 2.0;
|
|
11092
11242
|
}
|
|
11093
11243
|
};
|
|
11094
11244
|
stat.min = (arr) => {
|
|
@@ -11101,30 +11251,21 @@ var Chalkboard;
|
|
|
11101
11251
|
return min;
|
|
11102
11252
|
};
|
|
11103
11253
|
stat.mode = (arr) => {
|
|
11104
|
-
|
|
11105
|
-
return
|
|
11106
|
-
|
|
11107
|
-
let
|
|
11108
|
-
let
|
|
11109
|
-
let
|
|
11110
|
-
|
|
11111
|
-
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
}
|
|
11117
|
-
currStr = 0;
|
|
11118
|
-
currElm = temp[i];
|
|
11254
|
+
if (arr.length === 0)
|
|
11255
|
+
return NaN;
|
|
11256
|
+
const frequency = new Map();
|
|
11257
|
+
let maxFreq = 0;
|
|
11258
|
+
let result = arr[0];
|
|
11259
|
+
for (let i = 0; i < arr.length; i++) {
|
|
11260
|
+
const val = arr[i];
|
|
11261
|
+
const count = (frequency.get(val) || 0) + 1;
|
|
11262
|
+
frequency.set(val, count);
|
|
11263
|
+
if (count > maxFreq) {
|
|
11264
|
+
maxFreq = count;
|
|
11265
|
+
result = val;
|
|
11119
11266
|
}
|
|
11120
|
-
currStr++;
|
|
11121
|
-
}
|
|
11122
|
-
if (currStr > bestStr) {
|
|
11123
|
-
return currElm;
|
|
11124
|
-
}
|
|
11125
|
-
else {
|
|
11126
|
-
return bestElm;
|
|
11127
11267
|
}
|
|
11268
|
+
return result;
|
|
11128
11269
|
};
|
|
11129
11270
|
stat.mul = (arr) => {
|
|
11130
11271
|
let result = 1;
|
|
@@ -11231,19 +11372,26 @@ var Chalkboard;
|
|
|
11231
11372
|
console.log(Chalkboard.stat.toString(arr));
|
|
11232
11373
|
};
|
|
11233
11374
|
stat.quartile = (arr, type) => {
|
|
11234
|
-
|
|
11235
|
-
return
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11375
|
+
if (arr.length === 0)
|
|
11376
|
+
return NaN;
|
|
11377
|
+
const copy = arr.slice();
|
|
11378
|
+
if (type === "Q2")
|
|
11379
|
+
return Chalkboard.stat.median(copy);
|
|
11380
|
+
const mid = Math.floor(copy.length / 2);
|
|
11239
11381
|
if (type === "Q1") {
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
|
|
11243
|
-
|
|
11382
|
+
const q1Mid = Math.floor(mid / 2);
|
|
11383
|
+
if (mid % 2 === 1)
|
|
11384
|
+
return $quickselect(copy, q1Mid);
|
|
11385
|
+
else
|
|
11386
|
+
return ($quickselect(copy, q1Mid - 1) + $quickselect(copy, q1Mid)) / 2.0;
|
|
11244
11387
|
}
|
|
11245
11388
|
else if (type === "Q3") {
|
|
11246
|
-
|
|
11389
|
+
const offset = copy.length % 2 === 0 ? mid : mid + 1;
|
|
11390
|
+
const q3Mid = offset + Math.floor(mid / 2);
|
|
11391
|
+
if (mid % 2 === 1)
|
|
11392
|
+
return $quickselect(copy, q3Mid);
|
|
11393
|
+
else
|
|
11394
|
+
return ($quickselect(copy, q3Mid - 1) + $quickselect(copy, q3Mid)) / 2.0;
|
|
11247
11395
|
}
|
|
11248
11396
|
else {
|
|
11249
11397
|
throw new TypeError('Parameter "type" must be "Q1", "Q2", or "Q3".');
|
|
@@ -11499,6 +11647,11 @@ var Chalkboard;
|
|
|
11499
11647
|
}
|
|
11500
11648
|
};
|
|
11501
11649
|
stat.unique = (arr) => {
|
|
11650
|
+
if (arr.length === 0)
|
|
11651
|
+
return [];
|
|
11652
|
+
const firstType = typeof arr[0];
|
|
11653
|
+
if (firstType === "number" || firstType === "string" || firstType === "boolean")
|
|
11654
|
+
return Array.from(new Set(arr));
|
|
11502
11655
|
const stableStringify = (obj) => {
|
|
11503
11656
|
const replacer = (key, value) => {
|
|
11504
11657
|
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
@@ -11512,52 +11665,13 @@ var Chalkboard;
|
|
|
11512
11665
|
};
|
|
11513
11666
|
return JSON.stringify(obj, replacer);
|
|
11514
11667
|
};
|
|
11515
|
-
const getKey = (item) => {
|
|
11516
|
-
let typePrefix;
|
|
11517
|
-
let valuePart;
|
|
11518
|
-
if (item === null) {
|
|
11519
|
-
typePrefix = "null";
|
|
11520
|
-
valuePart = stableStringify(item);
|
|
11521
|
-
}
|
|
11522
|
-
else if (Array.isArray(item)) {
|
|
11523
|
-
typePrefix = "array";
|
|
11524
|
-
valuePart = stableStringify(item);
|
|
11525
|
-
}
|
|
11526
|
-
else if (typeof item === "object") {
|
|
11527
|
-
typePrefix = "object";
|
|
11528
|
-
valuePart = stableStringify(item);
|
|
11529
|
-
}
|
|
11530
|
-
else if (typeof item === "number") {
|
|
11531
|
-
typePrefix = "number";
|
|
11532
|
-
if (Number.isNaN(item)) {
|
|
11533
|
-
valuePart = "NaN";
|
|
11534
|
-
}
|
|
11535
|
-
else if (item === Infinity) {
|
|
11536
|
-
valuePart = "Infinity";
|
|
11537
|
-
}
|
|
11538
|
-
else if (item === -Infinity) {
|
|
11539
|
-
valuePart = "-Infinity";
|
|
11540
|
-
}
|
|
11541
|
-
else {
|
|
11542
|
-
valuePart = JSON.stringify(item);
|
|
11543
|
-
}
|
|
11544
|
-
}
|
|
11545
|
-
else if (typeof item === "undefined") {
|
|
11546
|
-
typePrefix = "undefined";
|
|
11547
|
-
valuePart = "";
|
|
11548
|
-
}
|
|
11549
|
-
else {
|
|
11550
|
-
typePrefix = typeof item;
|
|
11551
|
-
valuePart = stableStringify(item);
|
|
11552
|
-
}
|
|
11553
|
-
return `${typePrefix}:${valuePart}`;
|
|
11554
|
-
};
|
|
11555
11668
|
const seen = new Map();
|
|
11556
11669
|
for (const item of arr) {
|
|
11557
|
-
const
|
|
11558
|
-
|
|
11670
|
+
const typePrefix = item === null ? "null" : typeof item;
|
|
11671
|
+
const valuePart = (typePrefix === "undefined" || Number.isNaN(item)) ? "" : stableStringify(item);
|
|
11672
|
+
const key = `${typePrefix}:${valuePart}`;
|
|
11673
|
+
if (!seen.has(key))
|
|
11559
11674
|
seen.set(key, item);
|
|
11560
|
-
}
|
|
11561
11675
|
}
|
|
11562
11676
|
return Array.from(seen.values());
|
|
11563
11677
|
};
|
|
@@ -12175,32 +12289,26 @@ var Chalkboard;
|
|
|
12175
12289
|
};
|
|
12176
12290
|
trig.arctan = (rad) => {
|
|
12177
12291
|
const series = (x) => {
|
|
12178
|
-
|
|
12179
|
-
|
|
12180
|
-
const xx = x * x;
|
|
12181
|
-
for (let n = 1; n < 20; n++) {
|
|
12182
|
-
term *= -xx;
|
|
12183
|
-
sum += term / (2 * n + 1);
|
|
12184
|
-
}
|
|
12185
|
-
return sum;
|
|
12292
|
+
const x2 = x * x, x3 = x2 * x, x5 = x3 * x2, x7 = x5 * x2, x9 = x7 * x2, x11 = x9 * x2, x13 = x11 * x2, x15 = x13 * x2, x17 = x15 * x2, x19 = x17 * x2, x21 = x19 * x2, x23 = x21 * x2, x25 = x23 * x2, x27 = x25 * x2, x29 = x27 * x2, x31 = x29 * x2, x33 = x31 * x2, x35 = x33 * x2, x37 = x35 * x2, x39 = x37 * x2;
|
|
12293
|
+
return x - x3 / 3 + x5 / 5 - x7 / 7 + x9 / 9 - x11 / 11 + x13 / 13 - x15 / 15 + x17 / 17 - x19 / 19 + x21 / 21 - x23 / 23 + x25 / 25 - x27 / 27 + x29 / 29 - x31 / 31 + x33 / 33 - x35 / 35 + x37 / 37 - x39 / 39;
|
|
12186
12294
|
};
|
|
12187
|
-
if (rad === 0)
|
|
12295
|
+
if (rad === 0)
|
|
12188
12296
|
return 0;
|
|
12189
|
-
|
|
12190
|
-
if (!Number.isFinite(rad)) {
|
|
12297
|
+
if (!Number.isFinite(rad))
|
|
12191
12298
|
return rad > 0 ? Chalkboard.PI(0.5) : Chalkboard.PI(-0.5);
|
|
12192
|
-
}
|
|
12193
12299
|
const sign = rad < 0 ? -1 : 1;
|
|
12194
12300
|
const x = Math.abs(rad);
|
|
12301
|
+
const SQRT2_MINUS_1 = Math.SQRT2 - 1, SQRT2_PLUS_1 = Math.SQRT2 + 1;
|
|
12302
|
+
const PI_FOURTH = Math.PI * 0.25, PI_HALF = Math.PI * 0.5;
|
|
12195
12303
|
let result;
|
|
12196
|
-
if (x <=
|
|
12304
|
+
if (x <= SQRT2_MINUS_1) {
|
|
12197
12305
|
result = series(x);
|
|
12198
12306
|
}
|
|
12199
|
-
else if (x <=
|
|
12200
|
-
result =
|
|
12307
|
+
else if (x <= SQRT2_PLUS_1) {
|
|
12308
|
+
result = PI_FOURTH + series((x - 1) / (x + 1));
|
|
12201
12309
|
}
|
|
12202
12310
|
else {
|
|
12203
|
-
result =
|
|
12311
|
+
result = PI_HALF - series(1 / x);
|
|
12204
12312
|
}
|
|
12205
12313
|
return sign * result;
|
|
12206
12314
|
};
|