pts 0.12.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -80
- package/dist/index.d.mts +6208 -1254
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +6208 -1254
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9314 -10680
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +9266 -10611
- package/dist/index.mjs.map +1 -0
- package/dist/pts.js +9446 -10777
- package/dist/pts.js.map +1 -0
- package/dist/pts.min.js +3 -5
- package/dist/pts.min.js.map +1 -0
- package/package.json +91 -31
- package/src/Canvas.ts +1642 -0
- package/src/Color.ts +1109 -0
- package/src/Create.ts +1547 -0
- package/src/Dom.ts +940 -0
- package/src/Form.ts +312 -0
- package/src/Image.ts +722 -0
- package/src/LinearAlgebra.ts +530 -0
- package/src/Num.ts +1091 -0
- package/src/Op.ts +2127 -0
- package/src/Physics.ts +1233 -0
- package/src/Play.ts +861 -0
- package/src/Pt.ts +1303 -0
- package/src/Space.ts +898 -0
- package/src/Svg.ts +1573 -0
- package/src/Types.ts +301 -0
- package/src/Typography.ts +228 -0
- package/src/UI.ts +757 -0
- package/src/Util.ts +454 -0
- package/src/_module.ts +18 -0
- package/src/_script.ts +94 -0
- package/src/_triangulate.ts +884 -0
- package/src/uheprng.ts +153 -0
package/src/Util.ts
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/*! Pts.js is licensed under Apache License 2.0. Copyright © 2017-current William Ngan and contributors. (https://github.com/williamngan/pts) */
|
|
2
|
+
|
|
3
|
+
import type { CanvasSpace } from "./Canvas";
|
|
4
|
+
import { Num } from "./Num";
|
|
5
|
+
import { Group, Pt } from "./Pt";
|
|
6
|
+
import { type WarningType, type PtLike, type PtLikeIterable } from "./Types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Various constant values for enumerations and calculations.
|
|
10
|
+
*/
|
|
11
|
+
export const Const = {
|
|
12
|
+
/** A string to indicate xy plane. */
|
|
13
|
+
xy: "xy",
|
|
14
|
+
|
|
15
|
+
/** A string to indicate yz plane. */
|
|
16
|
+
yz: "yz",
|
|
17
|
+
|
|
18
|
+
/** A string to indicate xz plane. */
|
|
19
|
+
xz: "xz",
|
|
20
|
+
|
|
21
|
+
/** A string to indicate xyz space. */
|
|
22
|
+
xyz: "xyz",
|
|
23
|
+
|
|
24
|
+
/** Represents horizontal direction. */
|
|
25
|
+
horizontal: 0,
|
|
26
|
+
|
|
27
|
+
/** Represents vertical direction. */
|
|
28
|
+
vertical: 1,
|
|
29
|
+
|
|
30
|
+
/** Represents identical point or value */
|
|
31
|
+
identical: 0,
|
|
32
|
+
|
|
33
|
+
/** Represents right position or direction */
|
|
34
|
+
right: 4,
|
|
35
|
+
|
|
36
|
+
/** Represents bottom right position or direction */
|
|
37
|
+
bottom_right: 5,
|
|
38
|
+
|
|
39
|
+
/** Represents bottom position or direction */
|
|
40
|
+
bottom: 6,
|
|
41
|
+
|
|
42
|
+
/** Represents bottom left position or direction */
|
|
43
|
+
bottom_left: 7,
|
|
44
|
+
|
|
45
|
+
/** Represents left position or direction */
|
|
46
|
+
left: 8,
|
|
47
|
+
|
|
48
|
+
/** Represents top left position or direction */
|
|
49
|
+
top_left: 1,
|
|
50
|
+
|
|
51
|
+
/** Represents top position or direction */
|
|
52
|
+
top: 2,
|
|
53
|
+
|
|
54
|
+
/** Represents top right position or direction */
|
|
55
|
+
top_right: 3,
|
|
56
|
+
|
|
57
|
+
/** Represents an arbitrary very small number. It is set as 0.0001 here. */
|
|
58
|
+
epsilon: 0.0001,
|
|
59
|
+
|
|
60
|
+
/** Represents Number.MAX_VALUE. Note: as a Float32 Pt value this overflows to Infinity. */
|
|
61
|
+
max: Number.MAX_VALUE,
|
|
62
|
+
|
|
63
|
+
/** Represents Number.MIN_VALUE, the smallest *positive* number (5e-324) — not the most negative number. Do not use it to initialize a running maximum; use -Infinity instead. As a Float32 Pt value this flushes to 0. */
|
|
64
|
+
min: Number.MIN_VALUE,
|
|
65
|
+
|
|
66
|
+
/** π radian (180 deg) */
|
|
67
|
+
pi: Math.PI,
|
|
68
|
+
|
|
69
|
+
/** Two π radian (360deg) */
|
|
70
|
+
two_pi: 6.283185307179586,
|
|
71
|
+
|
|
72
|
+
/** Half π radian (90deg) */
|
|
73
|
+
half_pi: 1.5707963267948966,
|
|
74
|
+
|
|
75
|
+
/** π/4 radian (45deg) */
|
|
76
|
+
quarter_pi: 0.7853981633974483,
|
|
77
|
+
|
|
78
|
+
/** π/180 or 1 degree in radian */
|
|
79
|
+
one_degree: 0.017453292519943295,
|
|
80
|
+
|
|
81
|
+
/** Multiply this constant with a radian to get a degree */
|
|
82
|
+
rad_to_deg: 57.29577951308232,
|
|
83
|
+
|
|
84
|
+
/** Multiply this constant with a degree to get a radian */
|
|
85
|
+
deg_to_rad: 0.017453292519943295,
|
|
86
|
+
|
|
87
|
+
/** Gravity acceleration (unit: m/s^2) and gravity force (unit: Newton) on 1kg of mass. */
|
|
88
|
+
gravity: 9.81,
|
|
89
|
+
|
|
90
|
+
/** 1 Newton: 0.10197 Kilogram-force */
|
|
91
|
+
newton: 0.10197,
|
|
92
|
+
|
|
93
|
+
/** Gaussian constant (1 / Math.sqrt(2 * Math.PI)) */
|
|
94
|
+
gaussian: 0.3989422804014327,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Util class provides static helper functions.
|
|
99
|
+
*/
|
|
100
|
+
export class Util {
|
|
101
|
+
static _warnLevel: WarningType = "mute";
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Set a global warning level setting. If no parameter is passed, this will return the current warn-level. See [`Util.warn`](#link).
|
|
105
|
+
* @param lv a [`WarningType`](#link) option, where "error" will throw an error, "warn" will log in console, and "mute" will ignore the error.
|
|
106
|
+
*/
|
|
107
|
+
static warnLevel(lv?: WarningType): WarningType {
|
|
108
|
+
if (lv) {
|
|
109
|
+
Util._warnLevel = lv;
|
|
110
|
+
}
|
|
111
|
+
return Util._warnLevel;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Convert different kinds of parameters (arguments, array, object) into an array of numbers.
|
|
116
|
+
* @param args can be either a list of numbers, an array, a Pt, or an object with {x,y,z,w} properties
|
|
117
|
+
*/
|
|
118
|
+
static getArgs(args: any[]): Array<number> {
|
|
119
|
+
if (args.length < 1) return [];
|
|
120
|
+
|
|
121
|
+
let pos = [];
|
|
122
|
+
|
|
123
|
+
let isArray = Array.isArray(args[0]) || ArrayBuffer.isView(args[0]);
|
|
124
|
+
|
|
125
|
+
// positional arguments: x,y,z,w,...
|
|
126
|
+
if (typeof args[0] === "number") {
|
|
127
|
+
pos = Array.prototype.slice.call(args);
|
|
128
|
+
|
|
129
|
+
// as an object of {x, y?, z?, w?}
|
|
130
|
+
} else if (typeof args[0] === "object" && !isArray) {
|
|
131
|
+
let a = ["x", "y", "z", "w"];
|
|
132
|
+
let p = args[0];
|
|
133
|
+
for (let i = 0; i < a.length; i++) {
|
|
134
|
+
if ((p.length && i >= p.length) || !(a[i] in p)) break; // check for length and key exist
|
|
135
|
+
pos.push(p[a[i]]);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// as an array of values
|
|
139
|
+
} else if (isArray) {
|
|
140
|
+
pos = Util.toNumericArray(args[0]);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return pos;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Copy an array or typed array of numbers into a fresh plain array.
|
|
148
|
+
* @param a an array or typed array
|
|
149
|
+
*/
|
|
150
|
+
static toNumericArray(a: ArrayLike<number>): number[] {
|
|
151
|
+
// native slice is a fast path for plain arrays, but `[].slice.call` on a
|
|
152
|
+
// typed array falls back to the generic per-element protocol
|
|
153
|
+
if (Array.isArray(a)) return a.slice();
|
|
154
|
+
const out = [];
|
|
155
|
+
for (let i = 0, len = a.length; i < len; i++) {
|
|
156
|
+
out.push(a[i]);
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Like [`Util.getArgs`](#link), but avoids copying when the arguments are already
|
|
163
|
+
* a numeric array, a typed array (eg, a Pt), or a list of numbers. The result may be
|
|
164
|
+
* the caller's own object, so it must be treated as read-only.
|
|
165
|
+
* @param args can be either a list of numbers, an array, a Pt, or an object with {x,y,z,w} properties
|
|
166
|
+
*/
|
|
167
|
+
static getPtLike(args: any[]): PtLike {
|
|
168
|
+
const a0 = args[0];
|
|
169
|
+
if (typeof a0 === "number") return args; // rest args are a fresh, private array
|
|
170
|
+
if (args.length === 1 && (Array.isArray(a0) || ArrayBuffer.isView(a0))) {
|
|
171
|
+
return a0 as PtLike;
|
|
172
|
+
}
|
|
173
|
+
return Util.getArgs(args);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Send a warning message based on [`Util.warnLevel`](#link) global setting. This allows you to dynamically set whether minor errors should be thrown or printed in console or muted.
|
|
178
|
+
* @param message any error or warning message
|
|
179
|
+
* @param defaultReturn optional return value
|
|
180
|
+
*/
|
|
181
|
+
static warn(message: string = "error", defaultReturn: any = undefined): any {
|
|
182
|
+
if (Util.warnLevel() == "error") {
|
|
183
|
+
throw new Error(message);
|
|
184
|
+
} else if (Util.warnLevel() == "warn") {
|
|
185
|
+
console.warn(message);
|
|
186
|
+
}
|
|
187
|
+
return defaultReturn;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get a random integer. This can be useful for selecting a random index in an array.
|
|
192
|
+
* @deprecated Use [`Num.randomRange`](#link) instead, for example `Math.floor( Num.randomRange( start, start + range ) )`.
|
|
193
|
+
* @param range value range
|
|
194
|
+
* @param start Optional starting value
|
|
195
|
+
*/
|
|
196
|
+
static randomInt(range: number, start: number = 0) {
|
|
197
|
+
Util.warn("Util.randomInt is deprecated. Please use `Num.randomRange`");
|
|
198
|
+
return Math.floor(Num.random() * range) + start;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Split an array into chunks of sub-array.
|
|
203
|
+
* @param pts an array
|
|
204
|
+
* @param size chunk size, ie, number of items in a chunk
|
|
205
|
+
* @param stride optional parameter to "walk through" the array in steps
|
|
206
|
+
* @param loopBack if `true`, always go through the array till the end and loop back to the beginning to complete the segments if needed.
|
|
207
|
+
* @param matchSize if `true`, all chunks's length must match `size`.
|
|
208
|
+
*/
|
|
209
|
+
static split(
|
|
210
|
+
pts: any[],
|
|
211
|
+
size: number,
|
|
212
|
+
stride?: number,
|
|
213
|
+
loopBack: boolean = false,
|
|
214
|
+
matchSize = true,
|
|
215
|
+
): any[][] {
|
|
216
|
+
let chunks: any[] = [];
|
|
217
|
+
let part: any[] = [];
|
|
218
|
+
let st: number = stride || size;
|
|
219
|
+
let index: number = 0;
|
|
220
|
+
if (pts.length <= 0 || st <= 0) return [];
|
|
221
|
+
|
|
222
|
+
while (index < pts.length) {
|
|
223
|
+
part = [];
|
|
224
|
+
for (let k = 0; k < size; k++) {
|
|
225
|
+
if (loopBack) {
|
|
226
|
+
part.push(pts[(index + k) % pts.length]);
|
|
227
|
+
} else {
|
|
228
|
+
if (index + k >= pts.length) break;
|
|
229
|
+
part.push(pts[index + k]);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
index += st;
|
|
233
|
+
if (!matchSize || (matchSize && part.length === size)) chunks.push(part);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return chunks;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Flatten an array of arrays such as Group[] to a flat Array or Group.
|
|
241
|
+
* @param pts an array, usually an array of Groups
|
|
242
|
+
* @param flattenAsGroup a boolean to specify whether the return type should be a Group or Array. Default is `true` which returns a Group.
|
|
243
|
+
*/
|
|
244
|
+
static flatten(pts: any[], flattenAsGroup: boolean = true): any {
|
|
245
|
+
// loop instead of concat.apply: spreading `pts` as arguments overflows
|
|
246
|
+
// the JS argument-count limit for very large inputs
|
|
247
|
+
const arr: unknown[] = flattenAsGroup ? new Group() : [];
|
|
248
|
+
for (let i = 0, len = pts.length; i < len; i++) {
|
|
249
|
+
const p = pts[i];
|
|
250
|
+
if (Array.isArray(p)) {
|
|
251
|
+
for (let k = 0, lenP = p.length; k < lenP; k++) arr.push(p[k]);
|
|
252
|
+
} else {
|
|
253
|
+
arr.push(p);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return arr;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Given two arrays of objects, and a function that operate on two objects, return an array. Objects must be of same type.
|
|
261
|
+
* @param a an array of object, eg `[Group, Group, ...]`
|
|
262
|
+
* @param b another array of object
|
|
263
|
+
* @param op a function that takes two parameters (a, b) and returns an object.
|
|
264
|
+
*/
|
|
265
|
+
static combine<T>(a: T[], b: T[], op: (a: T, b: T) => T): T[] {
|
|
266
|
+
let result = [];
|
|
267
|
+
for (let i = 0, len = a.length; i < len; i++) {
|
|
268
|
+
for (let k = 0, lenB = b.length; k < lenB; k++) {
|
|
269
|
+
result.push(op(a[i], b[k]));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Zip arrays. eg, `[[1,2],[3,4],[5,6]] => [[1,3,5],[2,4,6]]`.
|
|
277
|
+
* @param arrays an array of arrays
|
|
278
|
+
*/
|
|
279
|
+
static zip(arrays: Array<any>[]) {
|
|
280
|
+
let z = [];
|
|
281
|
+
for (let i = 0, len = arrays[0].length; i < len; i++) {
|
|
282
|
+
let p = [];
|
|
283
|
+
for (let k = 0; k < arrays.length; k++) {
|
|
284
|
+
p.push(arrays[k][i]);
|
|
285
|
+
}
|
|
286
|
+
z.push(p);
|
|
287
|
+
}
|
|
288
|
+
return z;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Create a convenient stepper. This returns a function which you can call repeatedly to step a counter.
|
|
293
|
+
* @param max Maximum of the stepper range. The resulting stepper will return values within [min, max). Note that the first call returns `min + stride`, not `min`.
|
|
294
|
+
* @param min Minimum of the stepper range. Default is 0.
|
|
295
|
+
* @param stride Stride of the step. Default is 1.
|
|
296
|
+
* @param callback An optional callback function `fn( step )`, which will be called each time when stepper function is called.
|
|
297
|
+
* @example `let counter = stepper(100); let c = counter(); c = counter(); ...`
|
|
298
|
+
* @returns a function which will increment the stepper and return its value at each call.
|
|
299
|
+
*/
|
|
300
|
+
static stepper(
|
|
301
|
+
max: number,
|
|
302
|
+
min: number = 0,
|
|
303
|
+
stride: number = 1,
|
|
304
|
+
callback?: (n: number) => void,
|
|
305
|
+
): () => number {
|
|
306
|
+
let c = min;
|
|
307
|
+
return function () {
|
|
308
|
+
c += stride;
|
|
309
|
+
if (c >= max) {
|
|
310
|
+
// anchored modulo keeps c in [min, max) even when stride > max - min
|
|
311
|
+
c = min + ((c - min) % (max - min));
|
|
312
|
+
}
|
|
313
|
+
if (callback) callback(c);
|
|
314
|
+
return c;
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* A convenient way to step through a range. Same as `for (i=0; i<range; i++)`, except this also stores the resulting return values at each step and return them as an array.
|
|
320
|
+
* @param range a range to step through
|
|
321
|
+
* @param fn a callback function `fn(index)`. If this function returns a value, it will be stored at each step
|
|
322
|
+
* @returns an index-aligned array of returned values: entries sit at their step index, so with a non-zero `start` the positions below `start` are empty holes
|
|
323
|
+
*/
|
|
324
|
+
static forRange(
|
|
325
|
+
fn: (index: number) => any,
|
|
326
|
+
range: number,
|
|
327
|
+
start: number = 0,
|
|
328
|
+
step: number = 1,
|
|
329
|
+
): any[] {
|
|
330
|
+
let temp = [];
|
|
331
|
+
for (let i = start, len = range; i < len; i += step) {
|
|
332
|
+
temp[i] = fn(i);
|
|
333
|
+
}
|
|
334
|
+
return temp;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* A helper function to load data from a url via XMLHttpRequest GET. Since the response passed into callback is a string, if you're loading json data, you may use standard `JSON.parse(response)` to get a JSON object. For csv, try using a javascript csv library like papaparse or vega/datalib.
|
|
339
|
+
* @param url the request url
|
|
340
|
+
* @param callback a function to capture the data. It receives two parameters: a `response` as string, and a `success` status as boolean.
|
|
341
|
+
*/
|
|
342
|
+
static load(
|
|
343
|
+
url: string,
|
|
344
|
+
callback: (response: string, success: boolean) => void,
|
|
345
|
+
) {
|
|
346
|
+
let request = new XMLHttpRequest();
|
|
347
|
+
request.open("GET", url, true);
|
|
348
|
+
|
|
349
|
+
request.onload = function () {
|
|
350
|
+
if (request.status >= 200 && request.status < 400) {
|
|
351
|
+
callback(request.responseText, true);
|
|
352
|
+
} else {
|
|
353
|
+
callback(
|
|
354
|
+
`Server error (${request.status}) when loading "${url}"`,
|
|
355
|
+
false,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
request.onerror = function () {
|
|
361
|
+
callback(`Unknown network error`, false);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
request.send();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Download the current `CanvasSpace` as an image (jpg/png/webp). Calling this function will automatically trigger a download.
|
|
369
|
+
* @param space an instance of `CanvasSpace`
|
|
370
|
+
* @param filename the name of the file, without the extension name.
|
|
371
|
+
* @param filetype the image type (jpg/png/webp)
|
|
372
|
+
* @param quality a value between 0 to 1, if filetype is either "jpg" or "png"
|
|
373
|
+
*/
|
|
374
|
+
static download(
|
|
375
|
+
space: CanvasSpace,
|
|
376
|
+
filename: string = "pts_canvas_image",
|
|
377
|
+
filetype: "jpeg" | "jpg" | "png" | "webp" = "png",
|
|
378
|
+
quality: number = 1,
|
|
379
|
+
) {
|
|
380
|
+
const ftype = filetype === "jpg" ? "jpeg" : filetype;
|
|
381
|
+
space.element.toBlob(
|
|
382
|
+
function (blob) {
|
|
383
|
+
const link = document.createElement("a");
|
|
384
|
+
// toBlob yields null on encoding failure; createObjectURL then throws
|
|
385
|
+
// a TypeError, same as it always has — kept, not silently swallowed
|
|
386
|
+
const url = URL.createObjectURL(blob!);
|
|
387
|
+
link.href = url;
|
|
388
|
+
link.download = `${filename}.${filetype}`;
|
|
389
|
+
document.body.appendChild(link);
|
|
390
|
+
link.click();
|
|
391
|
+
document.body.removeChild(link);
|
|
392
|
+
URL.revokeObjectURL(url);
|
|
393
|
+
},
|
|
394
|
+
`image/${ftype}`,
|
|
395
|
+
quality,
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Estimate performance by checking how long it takes to render a frame
|
|
401
|
+
* @param avgFrames The number of frames used calculate to average
|
|
402
|
+
* @example `let perf = Util.performance(); perf();`
|
|
403
|
+
* @returns milliseconds per frame
|
|
404
|
+
*/
|
|
405
|
+
static performance(avgFrames: number = 10): () => number {
|
|
406
|
+
let last = Date.now();
|
|
407
|
+
let avg = [];
|
|
408
|
+
return function () {
|
|
409
|
+
const now = Date.now();
|
|
410
|
+
avg.push(now - last);
|
|
411
|
+
if (avg.length > avgFrames) avg.shift();
|
|
412
|
+
last = now;
|
|
413
|
+
return Math.floor(avg.reduce((a, b) => a + b, 0) / avg.length);
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Check number of items in a Group against a required number
|
|
419
|
+
* @param pts a Group or an Iterable<PtLike>
|
|
420
|
+
* @param minRequired minimum number of items required
|
|
421
|
+
*/
|
|
422
|
+
static arrayCheck(pts: PtLikeIterable, minRequired: number = 2): boolean {
|
|
423
|
+
if (Array.isArray(pts) && pts.length < minRequired) {
|
|
424
|
+
Util.warn(`Requires ${minRequired} or more Pts in this Group.`);
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Convert an iterable into an array
|
|
432
|
+
* @param it an iterable
|
|
433
|
+
*/
|
|
434
|
+
static iterToArray(it: Iterable<any>): any[] {
|
|
435
|
+
return !Array.isArray(it) ? [...it] : it;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Check if accessing from a mobile device. Can be useful since some experimental features may not be availble in mobile browsers.
|
|
440
|
+
*/
|
|
441
|
+
static isMobile() {
|
|
442
|
+
return /iPhone|iPad|Android/i.test(navigator.userAgent);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Generate a time-based unique ID or a crypto-based ID.
|
|
447
|
+
* @returns
|
|
448
|
+
*/
|
|
449
|
+
static uniqueId(useCrypto = false) {
|
|
450
|
+
return useCrypto && typeof crypto !== "undefined" && crypto?.randomUUID
|
|
451
|
+
? crypto.randomUUID()
|
|
452
|
+
: Date.now().toString(36) + Math.random().toString(36).substring(2);
|
|
453
|
+
}
|
|
454
|
+
}
|
package/src/_module.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from "./Canvas";
|
|
2
|
+
export * from "./Create";
|
|
3
|
+
export * from "./Form";
|
|
4
|
+
export * from "./LinearAlgebra";
|
|
5
|
+
export * from "./Num";
|
|
6
|
+
export * from "./Op";
|
|
7
|
+
export * from "./Pt";
|
|
8
|
+
export * from "./Space";
|
|
9
|
+
export * from "./Color";
|
|
10
|
+
export * from "./Util";
|
|
11
|
+
export * from "./Dom";
|
|
12
|
+
export * from "./Svg";
|
|
13
|
+
export * from "./Typography";
|
|
14
|
+
export * from "./Physics";
|
|
15
|
+
export * from "./Play";
|
|
16
|
+
export * from "./UI";
|
|
17
|
+
export * from "./Image";
|
|
18
|
+
export * from "./Types";
|
package/src/_script.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as Canvas from "./Canvas";
|
|
2
|
+
import * as Create from "./Create";
|
|
3
|
+
import * as Form from "./Form";
|
|
4
|
+
import * as LinearAlgebra from "./LinearAlgebra";
|
|
5
|
+
import * as Num from "./Num";
|
|
6
|
+
import * as Op from "./Op";
|
|
7
|
+
import * as Pt from "./Pt";
|
|
8
|
+
import * as Space from "./Space";
|
|
9
|
+
import * as Color from "./Color";
|
|
10
|
+
import * as Util from "./Util";
|
|
11
|
+
import * as Dom from "./Dom";
|
|
12
|
+
import * as Svg from "./Svg";
|
|
13
|
+
import * as Typography from "./Typography";
|
|
14
|
+
import * as Physics from "./Physics";
|
|
15
|
+
import * as UI from "./UI";
|
|
16
|
+
import * as Play from "./Play";
|
|
17
|
+
import * as Image from "./Image";
|
|
18
|
+
import * as Types from "./Types";
|
|
19
|
+
|
|
20
|
+
declare global {
|
|
21
|
+
// the browser bundle mounts the whole library on globalThis.Pts
|
|
22
|
+
|
|
23
|
+
var Pts: Record<string, any>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
globalThis.Pts = {
|
|
27
|
+
...Canvas,
|
|
28
|
+
...Create,
|
|
29
|
+
...Form,
|
|
30
|
+
...LinearAlgebra,
|
|
31
|
+
...Num,
|
|
32
|
+
...Op,
|
|
33
|
+
...Pt,
|
|
34
|
+
...Space,
|
|
35
|
+
...Color,
|
|
36
|
+
...Util,
|
|
37
|
+
...Dom,
|
|
38
|
+
...Svg,
|
|
39
|
+
...Typography,
|
|
40
|
+
...Physics,
|
|
41
|
+
...UI,
|
|
42
|
+
...Play,
|
|
43
|
+
...Image,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// A function to switch scope for Pts library. eg, Pts.namespace( window );
|
|
47
|
+
globalThis.Pts.namespace = (scope: any) => {
|
|
48
|
+
let lib = globalThis.Pts;
|
|
49
|
+
for (let k in lib) {
|
|
50
|
+
if (k != "namespace") {
|
|
51
|
+
scope[k] = lib[k];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
globalThis.Pts.quickStart = (id: string | Element, bg: string = "#9ab") => {
|
|
57
|
+
if (!window) return;
|
|
58
|
+
|
|
59
|
+
let s: any = globalThis;
|
|
60
|
+
globalThis.Pts.namespace(s);
|
|
61
|
+
|
|
62
|
+
// pick the rendering backend from the mount element: an <svg> element (or a
|
|
63
|
+
// container holding one) gets an SVGSpace, anything else a CanvasSpace —
|
|
64
|
+
// so a sketch can swap renderers by changing only its HTML
|
|
65
|
+
// Preserve CanvasSpace's bare-ID shorthand for both rendering backends.
|
|
66
|
+
const mount =
|
|
67
|
+
typeof id === "string" && id[0] !== "#" && id[0] !== "." ? `#${id}` : id;
|
|
68
|
+
const elem =
|
|
69
|
+
typeof mount === "string" ? document.querySelector(mount) : mount;
|
|
70
|
+
const isSVG =
|
|
71
|
+
elem &&
|
|
72
|
+
((elem as Element).nodeName.toLowerCase() === "svg" ||
|
|
73
|
+
!!(elem as Element).querySelector(":scope > svg"));
|
|
74
|
+
|
|
75
|
+
s.space = isSVG
|
|
76
|
+
? new Svg.SVGSpace(mount).setup({ bgcolor: bg, resize: true })
|
|
77
|
+
: new Canvas.CanvasSpace(mount).setup({
|
|
78
|
+
bgcolor: bg,
|
|
79
|
+
resize: true,
|
|
80
|
+
retina: true,
|
|
81
|
+
});
|
|
82
|
+
s.form = s.space.getForm();
|
|
83
|
+
|
|
84
|
+
return function (animate = null, start = null, action = null, resize = null) {
|
|
85
|
+
s.space.add({
|
|
86
|
+
start: start,
|
|
87
|
+
animate: animate,
|
|
88
|
+
resize: resize,
|
|
89
|
+
action: action,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
s.space.bindMouse().bindTouch().play();
|
|
93
|
+
};
|
|
94
|
+
};
|