pts 1.0.1 → 1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pts",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "A library for visualization and creative coding.",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",
package/src/Color.ts CHANGED
@@ -5,6 +5,32 @@ import { Util } from "./Util";
5
5
  import { Num, Geom } from "./Num";
6
6
  import { type PtLike, type ColorType } from "./Types";
7
7
 
8
+ // Module state behind Color's static accessors: a static field would be
9
+ // emitted as an assignment after the class, which bundlers keep, and with it
10
+ // the class and everything it references (see Util.ts). The initializers are
11
+ // marked pure so a bundle without Color drops them. Internal code reads
12
+ // `Color.ranges`, not `_colorRanges`, so redefining the static still reaches
13
+ // the conversions.
14
+
15
+ // XYZ property for Standard Observer 2deg, Daylight/sRGB illuminant D65
16
+ const D65 = /* @__PURE__ */ new Pt(95.047, 100, 108.883, 1);
17
+
18
+ let _colorRanges: { [name: string]: Group } = /* @__PURE__ */ colorRanges();
19
+
20
+ function colorRanges(): { [name: string]: Group } {
21
+ return {
22
+ rgb: new Group(new Pt(0, 255), new Pt(0, 255), new Pt(0, 255)),
23
+ hsl: new Group(new Pt(0, 360), new Pt(0, 1), new Pt(0, 1)),
24
+ hsb: new Group(new Pt(0, 360), new Pt(0, 1), new Pt(0, 1)),
25
+ lab: new Group(new Pt(0, 100), new Pt(-128, 127), new Pt(-128, 127)),
26
+ lch: new Group(new Pt(0, 100), new Pt(0, 100), new Pt(0, 360)),
27
+ luv: new Group(new Pt(0, 100), new Pt(-134, 220), new Pt(-140, 122)),
28
+ xyz: new Group(new Pt(0, 100), new Pt(0, 100), new Pt(0, 100)),
29
+ oklab: new Group(new Pt(0, 1), new Pt(-0.4, 0.4), new Pt(-0.4, 0.4)),
30
+ oklch: new Group(new Pt(0, 1), new Pt(0, 0.4), new Pt(0, 360)),
31
+ };
32
+ }
33
+
8
34
  /**
9
35
  * Color is a subclass of Pt. Since a color in a color space is analogous to a point or vector in a space, you can apply all Pt operations to colors too. The Color class provides support for many color spaces like HSL and LAB.
10
36
  * Convert non-RGB colors to RGB before using `.hex`, `.rgb`, or `.rgba` for rendering. These getters format the channels; they don't convert between color spaces.
@@ -15,26 +41,28 @@ import { type PtLike, type ColorType } from "./Types";
15
41
  * ```
16
42
  */
17
43
  export class Color extends Pt {
18
- // XYZ property for Standard Observer 2deg, Daylight/sRGB illuminant D65
19
- private static D65: PtLike = new Pt(95.047, 100, 108.883, 1);
20
-
21
44
  protected _mode: ColorType = "rgb";
22
45
  private _isNorm: boolean = false;
23
46
 
24
47
  /**
25
48
  * Value range for each color space
26
49
  */
27
- static ranges: { [name: string]: Group } = {
28
- rgb: new Group(new Pt(0, 255), new Pt(0, 255), new Pt(0, 255)),
29
- hsl: new Group(new Pt(0, 360), new Pt(0, 1), new Pt(0, 1)),
30
- hsb: new Group(new Pt(0, 360), new Pt(0, 1), new Pt(0, 1)),
31
- lab: new Group(new Pt(0, 100), new Pt(-128, 127), new Pt(-128, 127)),
32
- lch: new Group(new Pt(0, 100), new Pt(0, 100), new Pt(0, 360)),
33
- luv: new Group(new Pt(0, 100), new Pt(-134, 220), new Pt(-140, 122)),
34
- xyz: new Group(new Pt(0, 100), new Pt(0, 100), new Pt(0, 100)),
35
- oklab: new Group(new Pt(0, 1), new Pt(-0.4, 0.4), new Pt(-0.4, 0.4)),
36
- oklch: new Group(new Pt(0, 1), new Pt(0, 0.4), new Pt(0, 360)),
37
- };
50
+ static get ranges(): { [name: string]: Group } {
51
+ return _colorRanges;
52
+ }
53
+ static set ranges(value: { [name: string]: Group }) {
54
+ // as with a static field, an assignment on a subclass stays on the subclass
55
+ if (this === Color) {
56
+ _colorRanges = value;
57
+ } else {
58
+ Object.defineProperty(this, "ranges", {
59
+ value,
60
+ writable: true,
61
+ enumerable: true,
62
+ configurable: true,
63
+ });
64
+ }
65
+ }
38
66
 
39
67
  /**
40
68
  * Create a Color. Same as creating a Pt. Optionally you may use [`Color.from`](#link) to create a color.
@@ -779,7 +807,7 @@ export class Color extends Pt {
779
807
  const kap = 24389 / 27;
780
808
 
781
809
  // adjust for D65
782
- c.divide(Color.D65);
810
+ c.divide(D65);
783
811
 
784
812
  const fn = (n: number) => (n > eps ? Math.cbrt(n) : (kap * n + 16) / 116);
785
813
  const cy = fn(c[1]);
@@ -812,7 +840,7 @@ export class Color extends Pt {
812
840
  const eps = 216 / 24389;
813
841
  const kap = 24389 / 27;
814
842
 
815
- const d = Color.D65;
843
+ const d = D65;
816
844
  const xxx = Math.pow(x, 3);
817
845
  const zzz = Math.pow(z, 3);
818
846
 
@@ -850,12 +878,8 @@ export class Color extends Pt {
850
878
  y = y / 100;
851
879
  const L = y > eps ? 116 * Math.cbrt(y) - 16 : kap * y;
852
880
 
853
- const refU =
854
- (4 * Color.D65[0]) /
855
- (Color.D65[0] + 15 * Color.D65[1] + 3 * Color.D65[2]);
856
- const refV =
857
- (9 * Color.D65[1]) /
858
- (Color.D65[0] + 15 * Color.D65[1] + 3 * Color.D65[2]);
881
+ const refU = (4 * D65[0]) / (D65[0] + 15 * D65[1] + 3 * D65[2]);
882
+ const refV = (9 * D65[1]) / (D65[0] + 15 * D65[1] + 3 * D65[2]);
859
883
 
860
884
  const cc = Color.luv(
861
885
  L,
@@ -891,12 +915,8 @@ export class Color extends Pt {
891
915
  const fy = (l + 16) / 116;
892
916
  let y = l > kap * eps ? fy * fy * fy : l / kap;
893
917
 
894
- const refU =
895
- (4 * Color.D65[0]) /
896
- (Color.D65[0] + 15 * Color.D65[1] + 3 * Color.D65[2]);
897
- const refV =
898
- (9 * Color.D65[1]) /
899
- (Color.D65[0] + 15 * Color.D65[1] + 3 * Color.D65[2]);
918
+ const refU = (4 * D65[0]) / (D65[0] + 15 * D65[1] + 3 * D65[2]);
919
+ const refV = (9 * D65[1]) / (D65[0] + 15 * D65[1] + 3 * D65[2]);
900
920
 
901
921
  u = u / (13 * l) + refU;
902
922
  v = v / (13 * l) + refV;
package/src/Create.ts CHANGED
@@ -261,7 +261,8 @@ const __noise_permTable = [
261
261
  // The doubled base permutation table, built once and shared by every unseeded
262
262
  // Noise instance (a per-instance copy would allocate 512 entries per point in
263
263
  // `Create.noisePts`). `seed()` swaps in a seeded table instead of mutating.
264
- const __noise_permDoubled = __noise_permTable.concat(__noise_permTable);
264
+ const __noise_permDoubled =
265
+ /* @__PURE__ */ __noise_permTable.concat(__noise_permTable);
265
266
 
266
267
  // Memoize the last seeded table: `Create.noisePts` seeds every point with the
267
268
  // same value, so all its Noise Pts share one table.
@@ -1,7 +1,6 @@
1
1
  /*! Pts.js is licensed under Apache License 2.0. Copyright © 2017-current William Ngan and contributors. (https://github.com/williamngan/pts) */
2
2
 
3
3
  import { Pt, Group } from "./Pt";
4
- import { Line } from "./Op";
5
4
  import { type PtLike, type GroupLike } from "./Types";
6
5
 
7
6
  /**
@@ -506,17 +505,19 @@ export class Mat {
506
505
  * @param p1 second end point to define the reflection line
507
506
  */
508
507
  static reflectAt2DMatrix(p1: PtLike, p2: PtLike) {
509
- const intercept = Line.intercept(p1, p2);
510
-
511
- if (intercept == undefined) {
508
+ // The slope and y-intercept as `Line.intercept` computes them, inlined so
509
+ // this module does not import Op: a Pt-only bundle would otherwise keep
510
+ // every geometry class.
511
+ if (p2[0] - p1[0] === 0) {
512
512
  return [
513
513
  new Pt([-1, 0, 0]),
514
514
  new Pt([0, 1, 0]),
515
515
  new Pt([p1[0] + p2[0], 0, 1]),
516
516
  ];
517
517
  } else {
518
- const yi = intercept.yi;
519
- const ang2 = Math.atan(intercept.slope) * 2;
518
+ const slope = (p2[1] - p1[1]) / (p2[0] - p1[0]);
519
+ const yi = p1[1] - slope * p1[0];
520
+ const ang2 = Math.atan(slope) * 2;
520
521
  const cosA = Math.cos(ang2);
521
522
  const sinA = Math.sin(ang2);
522
523
 
package/src/Num.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  /*! Pts.js is licensed under Apache License 2.0. Copyright © 2017-current William Ngan and contributors. (https://github.com/williamngan/pts) */
2
2
 
3
3
  import { Const, Util } from "./Util";
4
- import { Curve } from "./Op";
5
4
  import { Pt, Group } from "./Pt";
6
5
  import { Vec, Mat } from "./LinearAlgebra";
7
6
  import {
@@ -890,7 +889,7 @@ export class Shaping {
890
889
  }
891
890
 
892
891
  /**
893
- * Cubic bezier curve. This reuses the bezier functions in Curve class. Note that `t` is the curve parameter, not the x position: unlike CSS `cubic-bezier(...)`, this returns the curve's y value at parameter `t` rather than solving y at x = t.
892
+ * Cubic bezier curve from (0, 0) to (1, 1) with two control points. Note that `t` is the curve parameter, not the x position: unlike CSS `cubic-bezier(...)`, this returns the curve's y value at parameter `t` rather than solving y at x = t.
894
893
  * @param t a value between 0 to 1
895
894
  * @param c the value to shape, default is 1
896
895
  * @param p1` a Pt object specifying the first control Pt. Default is `Pt(0.1, 0.7).
@@ -902,14 +901,20 @@ export class Shaping {
902
901
  p1: PtLike = [0.1, 0.7],
903
902
  p2: PtLike = [0.9, 0.2],
904
903
  ): number {
905
- const curve = new Group(new Pt(0, 0), new Pt(p1), new Pt(p2), new Pt(1, 1));
906
- return (
907
- c *
908
- Curve.bezierStep(
909
- new Pt(t * t * t, t * t, t, 1),
910
- Curve.controlPoints(curve),
911
- ).y
912
- );
904
+ // `Curve.bezierStep` on the controls (0, 0), p1, p2, (1, 1), inlined so
905
+ // this module does not import Op (a Pt-only bundle would otherwise keep
906
+ // every geometry class). The float32 rounding of that Pt-based evaluation
907
+ // is kept: the powers of t, the control values and the result are float32,
908
+ // and the weighted sum has the same terms in the same order.
909
+ const t3 = Math.fround(t * t * t);
910
+ const t2 = Math.fround(t * t);
911
+ const t1 = Math.fround(t);
912
+ const y =
913
+ (-t3 + 3 * t2 - 3 * t1 + 1) * 0 +
914
+ (3 * t3 - 6 * t2 + 3 * t1) * new Pt(p1)[1] +
915
+ (-3 * t3 + 3 * t2) * new Pt(p2)[1] +
916
+ t3 * 1;
917
+ return c * Math.fround(y);
913
918
  }
914
919
 
915
920
  /**
package/src/Svg.ts CHANGED
@@ -963,6 +963,7 @@ export class SVGSpace extends DOMSpace {
963
963
 
964
964
  let _svgFormGroupID = 0;
965
965
  let _svgFormDomID = 0;
966
+ let _svgFormOffscreenWarned = false;
966
967
 
967
968
  // rendering-context style keys and their legacy inline-style names
968
969
  const _legacyStyleKeys: Record<string, string> = {
@@ -1081,10 +1082,9 @@ export class SVGForm extends CanvasForm<SVGSpace> {
1081
1082
  SVGForm._warnOffscreen();
1082
1083
  }
1083
1084
 
1084
- private static _offscreenWarned = false;
1085
1085
  private static _warnOffscreen() {
1086
- if (SVGForm._offscreenWarned) return;
1087
- SVGForm._offscreenWarned = true;
1086
+ if (_svgFormOffscreenWarned) return;
1087
+ _svgFormOffscreenWarned = true;
1088
1088
  Util.warn(
1089
1089
  "offscreen canvases are not supported in SVG output; use CanvasSpace",
1090
1090
  );
package/src/UI.ts CHANGED
@@ -98,6 +98,11 @@ export const UIPointerActions = {
98
98
  export type UIPointerAction =
99
99
  (typeof UIPointerActions)[keyof typeof UIPointerActions];
100
100
 
101
+ // module state behind the `_counter` accessor, so no static field assignment
102
+ // follows the class (which would defeat tree-shaking); internal code still
103
+ // uses `UI._counter` (see Util.ts)
104
+ let _uiCounter = 0;
105
+
101
106
  /**
102
107
  * **[Experimental]** An abstract class that represents an UI element. It wraps a [`Group`](#link) and supports UI event handling.
103
108
  * Extend this class to create custom UI elements.
@@ -107,7 +112,22 @@ export class UI {
107
112
  _group: Group;
108
113
  _shape: string;
109
114
 
110
- protected static _counter: number = 0;
115
+ protected static get _counter(): number {
116
+ return _uiCounter;
117
+ }
118
+ protected static set _counter(n: number) {
119
+ // as with a static field, an assignment on a subclass stays on the subclass
120
+ if (this === UI) {
121
+ _uiCounter = n;
122
+ } else {
123
+ Object.defineProperty(this, "_counter", {
124
+ value: n,
125
+ writable: true,
126
+ enumerable: true,
127
+ configurable: true,
128
+ });
129
+ }
130
+ }
111
131
  protected _id: string;
112
132
  protected _actions: { [type: string]: (UIHandler | null)[] };
113
133
  // built-in machinery (UIButton hover, UIDragger drag) registers here, so
package/src/Util.ts CHANGED
@@ -94,11 +94,33 @@ export const Const = {
94
94
  gaussian: 0.3989422804014327,
95
95
  };
96
96
 
97
+ // Mutable statics live in module state behind accessors: a static field would
98
+ // be emitted as an assignment after the class, which bundlers keep, and with
99
+ // it the class and everything it references (see Svg.ts). Internal code still
100
+ // goes through the public static, so redefining it (Object.defineProperty, an
101
+ // assignment through a Proxy) takes effect as it did with a field.
102
+ let _utilWarnLevel: WarningType = "mute";
103
+
97
104
  /**
98
105
  * Util class provides static helper functions.
99
106
  */
100
107
  export class Util {
101
- static _warnLevel: WarningType = "mute";
108
+ static get _warnLevel(): WarningType {
109
+ return _utilWarnLevel;
110
+ }
111
+ static set _warnLevel(lv: WarningType) {
112
+ // as with a static field, an assignment on a subclass stays on the subclass
113
+ if (this === Util) {
114
+ _utilWarnLevel = lv;
115
+ } else {
116
+ Object.defineProperty(this, "_warnLevel", {
117
+ value: lv,
118
+ writable: true,
119
+ enumerable: true,
120
+ configurable: true,
121
+ });
122
+ }
123
+ }
102
124
 
103
125
  /**
104
126
  * Set a global warning level setting. If no parameter is passed, this will return the current warn-level. See [`Util.warn`](#link).
@@ -43,9 +43,12 @@ const EMPTY: Triangulation = {
43
43
  const EPS = 1.1102230246251565e-16;
44
44
  // Bounds on the rounding error of the determinants below, relative to the sum
45
45
  // of the absolute values of their terms (the classic forward error analysis
46
- // of the orientation and in-circle determinants).
47
- const ORIENT_BOUND = (3 + 16 * EPS) * EPS;
48
- const INCIRCLE_BOUND = (10 + 96 * EPS) * EPS;
46
+ // of the orientation and in-circle determinants). Computed through a function
47
+ // marked pure so bundles that never triangulate drop them: a bundler that
48
+ // turns these consts into vars cannot prove the bare products side-effect free.
49
+ const errorBound = (a: number, b: number) => (a + b * EPS) * EPS;
50
+ const ORIENT_BOUND = /* @__PURE__ */ errorBound(3, 16);
51
+ const INCIRCLE_BOUND = /* @__PURE__ */ errorBound(10, 96);
49
52
  // Coordinates that become integers below 2^31 when scaled by one power of two
50
53
  // (pixel grids, halves, quarters, every Float32 value in a sane range) keep
51
54
  // their differences exact in a double; the products those differences form are
@@ -322,15 +325,28 @@ function _incircleInt(
322
325
  // Exact evaluation: each double is sign * mantissa * 2^exponent with an
323
326
  // integer mantissa, so scaling every input to the smallest exponent gives
324
327
  // exact integers whose determinant has the same sign as the real one.
325
- const _bits = new Float64Array(1);
326
- const _words = new Uint32Array(_bits.buffer);
327
- _bits[0] = 1;
328
- const HI = _words[1] === 0x3ff00000 ? 1 : 0;
329
- const LO = HI ^ 1;
328
+ // Views over one buffer, and the indices of the words holding a double's
329
+ // high bits (sign, exponent, top of the mantissa) and low bits. Every
330
+ // initializer is a call marked pure, so a bundle that never triangulates
331
+ // drops all of it (a bundler cannot prove `HI ^ 1` free of side effects).
332
+ const _buffer = /* @__PURE__ */ new ArrayBuffer(8);
333
+ const _bits = /* @__PURE__ */ new Float64Array(_buffer);
334
+ const _words = /* @__PURE__ */ new Uint32Array(_buffer);
335
+ const HI = /* @__PURE__ */ wordIndex(true);
336
+ const LO = /* @__PURE__ */ wordIndex(false);
337
+
338
+ function wordIndex(high: boolean): number {
339
+ // little-endian hosts store the high word of 1.0 (0x3ff00000) second
340
+ const probe = new Float64Array([1]);
341
+ const highSecond = new Uint32Array(probe.buffer)[1] === 0x3ff00000;
342
+ return high === highSecond ? 1 : 0;
343
+ }
344
+
330
345
  const _mant = new Float64Array(8);
331
346
  const _expo = new Int32Array(8);
332
- // BigInt is only touched inside the exact fallback, so loading this module
333
- // never requires it
347
+ // BigInt is used only when called: by `crossingParameter` (every proper
348
+ // crossing in the polygon overlay) and by the exact predicates below, so
349
+ // loading this module never requires it
334
350
 
335
351
  function _scaled(values: Float64Array, count: number): bigint[] {
336
352
  let minExpo = 0x7fffffff;
@@ -360,8 +376,9 @@ function _scaled(values: Float64Array, count: number): bigint[] {
360
376
  }
361
377
 
362
378
  // `Number(bigint)` is finite below 2^1024; shift in whole limbs until then.
363
- const FINITE_LIMIT = BigInt(1) << BigInt(1023);
364
- const SHIFT_STEP = BigInt(64);
379
+ // Created on first use, so loading this module never evaluates BigInt.
380
+ let _finiteLimit: bigint | undefined;
381
+ let _shiftStep: bigint;
365
382
 
366
383
  /**
367
384
  * Parameter of a known proper crossing along a→b. Evaluate the determinants
@@ -391,11 +408,15 @@ export function crossingParameter(
391
408
  // Keep the conversion finite even when the input doubles span hundreds of
392
409
  // binary exponents. The numerator is between zero and the denominator, and
393
410
  // a denominator of ordinary size needs no shift at all.
411
+ if (_finiteLimit === undefined) {
412
+ _finiteLimit = BigInt(1) << BigInt(1023);
413
+ _shiftStep = BigInt(64);
414
+ }
394
415
  let shift = BigInt(0);
395
416
  let scaled = denominator;
396
- while (scaled >= FINITE_LIMIT) {
397
- scaled >>= SHIFT_STEP;
398
- shift += SHIFT_STEP;
417
+ while (scaled >= _finiteLimit) {
418
+ scaled >>= _shiftStep;
419
+ shift += _shiftStep;
399
420
  }
400
421
  return Number(numerator >> shift) / Number(scaled);
401
422
  }