UNPKG

wave-roll

Version:

JavaScript Library for Comparative MIDI Piano-Roll Visualization

1,478 lines 1.2 MB
var et = /* @__PURE__ */ ((n) => (n.Application = "application", n.WebGLPipes = "webgl-pipes", n.WebGLPipesAdaptor = "webgl-pipes-adaptor", n.WebGLSystem = "webgl-system", n.WebGPUPipes = "webgpu-pipes", n.WebGPUPipesAdaptor = "webgpu-pipes-adaptor", n.WebGPUSystem = "webgpu-system", n.CanvasSystem = "canvas-system", n.CanvasPipesAdaptor = "canvas-pipes-adaptor", n.CanvasPipes = "canvas-pipes", n.Asset = "asset", n.LoadParser = "load-parser", n.ResolveParser = "resolve-parser", n.CacheParser = "cache-parser", n.DetectionParser = "detection-parser", n.MaskEffect = "mask-effect", n.BlendMode = "blend-mode", n.TextureSource = "texture-source", n.Environment = "environment", n.ShapeBuilder = "shape-builder", n.Batcher = "batcher", n))(et || {}); const ho = (n) => { if (typeof n == "function" || typeof n == "object" && n.extension) { if (!n.extension) throw new Error("Extension class must have an extension object"); n = { ...typeof n.extension != "object" ? { type: n.extension } : n.extension, ref: n }; } if (typeof n == "object") n = { ...n }; else throw new Error("Invalid extension type"); return typeof n.type == "string" && (n.type = [n.type]), n; }, si = (n, t) => ho(n).priority ?? t, ce = { /** @ignore */ _addHandlers: {}, /** @ignore */ _removeHandlers: {}, /** @ignore */ _queue: {}, /** * Remove extensions from PixiJS. * @param extensions - Extensions to be removed. Can be: * - Extension class with static `extension` property * - Extension format object with `type` and `ref` * - Multiple extensions as separate arguments * @returns {extensions} this for chaining * @example * ```ts * // Remove a single extension * extensions.remove(MyRendererPlugin); * * // Remove multiple extensions * extensions.remove( * MyRendererPlugin, * MySystemPlugin * ); * ``` * @see {@link ExtensionType} For available extension types * @see {@link ExtensionFormat} For extension format details */ remove(...n) { return n.map(ho).forEach((t) => { t.type.forEach((e) => this._removeHandlers[e]?.(t)); }), this; }, /** * Register new extensions with PixiJS. Extensions can be registered in multiple formats: * - As a class with a static `extension` property * - As an extension format object * - As multiple extensions passed as separate arguments * @param extensions - Extensions to add to PixiJS. Each can be: * - A class with static `extension` property * - An extension format object with `type` and `ref` * - Multiple extensions as separate arguments * @returns This extensions instance for chaining * @example * ```ts * // Register a simple extension * extensions.add(MyRendererPlugin); * * // Register multiple extensions * extensions.add( * MyRendererPlugin, * MySystemPlugin, * }); * ``` * @see {@link ExtensionType} For available extension types * @see {@link ExtensionFormat} For extension format details * @see {@link extensions.remove} For removing registered extensions */ add(...n) { return n.map(ho).forEach((t) => { t.type.forEach((e) => { const s = this._addHandlers, i = this._queue; s[e] ? s[e]?.(t) : (i[e] = i[e] || [], i[e]?.push(t)); }); }), this; }, /** * Internal method to handle extensions by name. * @param type - The extension type. * @param onAdd - Function handler when extensions are added/registered {@link StrictExtensionFormat}. * @param onRemove - Function handler when extensions are removed/unregistered {@link StrictExtensionFormat}. * @returns this for chaining. * @internal * @ignore */ handle(n, t, e) { const s = this._addHandlers, i = this._removeHandlers; if (s[n] || i[n]) throw new Error(`Extension type ${n} already has a handler`); s[n] = t, i[n] = e; const r = this._queue; return r[n] && (r[n]?.forEach((o) => t(o)), delete r[n]), this; }, /** * Handle a type, but using a map by `name` property. * @param type - Type of extension to handle. * @param map - The object map of named extensions. * @returns this for chaining. * @ignore */ handleByMap(n, t) { return this.handle( n, (e) => { e.name && (t[e.name] = e.ref); }, (e) => { e.name && delete t[e.name]; } ); }, /** * Handle a type, but using a list of extensions with a `name` property. * @param type - Type of extension to handle. * @param map - The array of named extensions. * @param defaultPriority - Fallback priority if none is defined. * @returns this for chaining. * @ignore */ handleByNamedList(n, t, e = -1) { return this.handle( n, (s) => { t.findIndex((r) => r.name === s.name) >= 0 || (t.push({ name: s.name, value: s.ref }), t.sort((r, o) => si(o.value, e) - si(r.value, e))); }, (s) => { const i = t.findIndex((r) => r.name === s.name); i !== -1 && t.splice(i, 1); } ); }, /** * Handle a type, but using a list of extensions. * @param type - Type of extension to handle. * @param list - The list of extensions. * @param defaultPriority - The default priority to use if none is specified. * @returns this for chaining. * @ignore */ handleByList(n, t, e = -1) { return this.handle( n, (s) => { t.includes(s.ref) || (t.push(s.ref), t.sort((i, r) => si(r, e) - si(i, e))); }, (s) => { const i = t.indexOf(s.ref); i !== -1 && t.splice(i, 1); } ); }, /** * Mixin the source object(s) properties into the target class's prototype. * Copies all property descriptors from source objects to the target's prototype. * @param Target - The target class to mix properties into * @param sources - One or more source objects containing properties to mix in * @example * ```ts * // Create a mixin with shared properties * const moveable = { * x: 0, * y: 0, * move(x: number, y: number) { * this.x += x; * this.y += y; * } * }; * * // Create a mixin with computed properties * const scalable = { * scale: 1, * get scaled() { * return this.scale > 1; * } * }; * * // Apply mixins to a class * extensions.mixin(Sprite, moveable, scalable); * * // Use mixed-in properties * const sprite = new Sprite(); * sprite.move(10, 20); * console.log(sprite.x, sprite.y); // 10, 20 * ``` * @remarks * - Copies all properties including getters/setters * - Does not modify source objects * - Preserves property descriptors * @see {@link Object.defineProperties} For details on property descriptors * @see {@link Object.getOwnPropertyDescriptors} For details on property copying */ mixin(n, ...t) { for (const e of t) Object.defineProperties(n.prototype, Object.getOwnPropertyDescriptors(e)); } }, lf = { extension: { type: et.Environment, name: "browser", priority: -1 }, test: () => !0, load: async () => { await import("./browserAll-D4zRKxad.js"); } }, cf = { extension: { type: et.Environment, name: "webworker", priority: 0 }, test: () => typeof self < "u" && self.WorkerGlobalScope !== void 0, load: async () => { await import("./webworkerAll-CL0K5VmR.js"); } }; class Nt { /** * Creates a new `ObservablePoint` * @param observer - Observer to pass to listen for change events. * @param {number} [x=0] - position of the point on the x axis * @param {number} [y=0] - position of the point on the y axis */ constructor(t, e, s) { this._x = e || 0, this._y = s || 0, this._observer = t; } /** * Creates a clone of this point. * @example * ```ts * // Basic cloning * const point = new ObservablePoint(observer, 100, 200); * const copy = point.clone(); * * // Clone with new observer * const newObserver = { * _onUpdate: (p) => console.log(`Clone updated: (${p.x}, ${p.y})`) * }; * const watched = point.clone(newObserver); * * // Verify independence * watched.set(300, 400); // Only triggers new observer * ``` * @param observer - Optional observer to pass to the new observable point * @returns A copy of this observable point * @see {@link ObservablePoint.copyFrom} For copying into existing point * @see {@link Observer} For observer interface details */ clone(t) { return new Nt(t ?? this._observer, this._x, this._y); } /** * Sets the point to a new x and y position. * * If y is omitted, both x and y will be set to x. * @example * ```ts * // Basic position setting * const point = new ObservablePoint(observer); * point.set(100, 200); * * // Set both x and y to same value * point.set(50); // x=50, y=50 * ``` * @param x - Position on the x axis * @param y - Position on the y axis, defaults to x * @returns The point instance itself * @see {@link ObservablePoint.copyFrom} For copying from another point * @see {@link ObservablePoint.equals} For comparing positions */ set(t = 0, e = t) { return (this._x !== t || this._y !== e) && (this._x = t, this._y = e, this._observer._onUpdate(this)), this; } /** * Copies x and y from the given point into this point. * @example * ```ts * // Basic copying * const source = new ObservablePoint(observer, 100, 200); * const target = new ObservablePoint(); * target.copyFrom(source); * * // Copy and chain operations * const point = new ObservablePoint() * .copyFrom(source) * .set(x + 50, y + 50); * * // Copy from any PointData * const data = { x: 10, y: 20 }; * point.copyFrom(data); * ``` * @param p - The point to copy from * @returns The point instance itself * @see {@link ObservablePoint.copyTo} For copying to another point * @see {@link ObservablePoint.clone} For creating new point copy */ copyFrom(t) { return (this._x !== t.x || this._y !== t.y) && (this._x = t.x, this._y = t.y, this._observer._onUpdate(this)), this; } /** * Copies this point's x and y into the given point. * @example * ```ts * // Basic copying * const source = new ObservablePoint(100, 200); * const target = new ObservablePoint(); * source.copyTo(target); * ``` * @param p - The point to copy to. Can be any type that is or extends `PointLike` * @returns The point (`p`) with values updated * @see {@link ObservablePoint.copyFrom} For copying from another point * @see {@link ObservablePoint.clone} For creating new point copy */ copyTo(t) { return t.set(this._x, this._y), t; } /** * Checks if another point is equal to this point. * * Compares x and y values using strict equality. * @example * ```ts * // Basic equality check * const p1 = new ObservablePoint(100, 200); * const p2 = new ObservablePoint(100, 200); * console.log(p1.equals(p2)); // true * * // Compare with PointData * const data = { x: 100, y: 200 }; * console.log(p1.equals(data)); // true * * // Check different points * const p3 = new ObservablePoint(200, 300); * console.log(p1.equals(p3)); // false * ``` * @param p - The point to check * @returns `true` if both `x` and `y` are equal * @see {@link ObservablePoint.copyFrom} For making points equal * @see {@link PointData} For point data interface */ equals(t) { return t.x === this._x && t.y === this._y; } toString() { return `[pixi.js/math:ObservablePoint x=${this._x} y=${this._y} scope=${this._observer}]`; } /** * Position of the observable point on the x axis. * Triggers observer callback when value changes. * @example * ```ts * // Basic x position * const point = new ObservablePoint(observer); * point.x = 100; // Triggers observer * * // Use in calculations * const width = rightPoint.x - leftPoint.x; * ``` * @default 0 */ get x() { return this._x; } set x(t) { this._x !== t && (this._x = t, this._observer._onUpdate(this)); } /** * Position of the observable point on the y axis. * Triggers observer callback when value changes. * @example * ```ts * // Basic y position * const point = new ObservablePoint(observer); * point.y = 200; // Triggers observer * * // Use in calculations * const height = bottomPoint.y - topPoint.y; * ``` * @default 0 */ get y() { return this._y; } set y(t) { this._y !== t && (this._y = t, this._observer._onUpdate(this)); } } function uh(n) { return n && n.__esModule && Object.prototype.hasOwnProperty.call(n, "default") ? n.default : n; } function hf(n) { if (Object.prototype.hasOwnProperty.call(n, "__esModule")) return n; var t = n.default; if (typeof t == "function") { var e = function s() { var i = !1; try { i = this instanceof s; } catch { } return i ? Reflect.construct(t, arguments, this.constructor) : t.apply(this, arguments); }; e.prototype = t.prototype; } else e = {}; return Object.defineProperty(e, "__esModule", { value: !0 }), Object.keys(n).forEach(function(s) { var i = Object.getOwnPropertyDescriptor(n, s); Object.defineProperty(e, s, i.get ? i : { enumerable: !0, get: function() { return n[s]; } }); }), e; } var Pr = { exports: {} }, Ya; function uf() { return Ya || (Ya = 1, function(n) { var t = Object.prototype.hasOwnProperty, e = "~"; function s() { } Object.create && (s.prototype = /* @__PURE__ */ Object.create(null), new s().__proto__ || (e = !1)); function i(l, c, h) { this.fn = l, this.context = c, this.once = h || !1; } function r(l, c, h, u, f) { if (typeof h != "function") throw new TypeError("The listener must be a function"); var d = new i(h, u || l, f), p = e ? e + c : c; return l._events[p] ? l._events[p].fn ? l._events[p] = [l._events[p], d] : l._events[p].push(d) : (l._events[p] = d, l._eventsCount++), l; } function o(l, c) { --l._eventsCount === 0 ? l._events = new s() : delete l._events[c]; } function a() { this._events = new s(), this._eventsCount = 0; } a.prototype.eventNames = function() { var c = [], h, u; if (this._eventsCount === 0) return c; for (u in h = this._events) t.call(h, u) && c.push(e ? u.slice(1) : u); return Object.getOwnPropertySymbols ? c.concat(Object.getOwnPropertySymbols(h)) : c; }, a.prototype.listeners = function(c) { var h = e ? e + c : c, u = this._events[h]; if (!u) return []; if (u.fn) return [u.fn]; for (var f = 0, d = u.length, p = new Array(d); f < d; f++) p[f] = u[f].fn; return p; }, a.prototype.listenerCount = function(c) { var h = e ? e + c : c, u = this._events[h]; return u ? u.fn ? 1 : u.length : 0; }, a.prototype.emit = function(c, h, u, f, d, p) { var g = e ? e + c : c; if (!this._events[g]) return !1; var m = this._events[g], y = arguments.length, x, v; if (m.fn) { switch (m.once && this.removeListener(c, m.fn, void 0, !0), y) { case 1: return m.fn.call(m.context), !0; case 2: return m.fn.call(m.context, h), !0; case 3: return m.fn.call(m.context, h, u), !0; case 4: return m.fn.call(m.context, h, u, f), !0; case 5: return m.fn.call(m.context, h, u, f, d), !0; case 6: return m.fn.call(m.context, h, u, f, d, p), !0; } for (v = 1, x = new Array(y - 1); v < y; v++) x[v - 1] = arguments[v]; m.fn.apply(m.context, x); } else { var _ = m.length, b; for (v = 0; v < _; v++) switch (m[v].once && this.removeListener(c, m[v].fn, void 0, !0), y) { case 1: m[v].fn.call(m[v].context); break; case 2: m[v].fn.call(m[v].context, h); break; case 3: m[v].fn.call(m[v].context, h, u); break; case 4: m[v].fn.call(m[v].context, h, u, f); break; default: if (!x) for (b = 1, x = new Array(y - 1); b < y; b++) x[b - 1] = arguments[b]; m[v].fn.apply(m[v].context, x); } } return !0; }, a.prototype.on = function(c, h, u) { return r(this, c, h, u, !1); }, a.prototype.once = function(c, h, u) { return r(this, c, h, u, !0); }, a.prototype.removeListener = function(c, h, u, f) { var d = e ? e + c : c; if (!this._events[d]) return this; if (!h) return o(this, d), this; var p = this._events[d]; if (p.fn) p.fn === h && (!f || p.once) && (!u || p.context === u) && o(this, d); else { for (var g = 0, m = [], y = p.length; g < y; g++) (p[g].fn !== h || f && !p[g].once || u && p[g].context !== u) && m.push(p[g]); m.length ? this._events[d] = m.length === 1 ? m[0] : m : o(this, d); } return this; }, a.prototype.removeAllListeners = function(c) { var h; return c ? (h = e ? e + c : c, this._events[h] && o(this, h)) : (this._events = new s(), this._eventsCount = 0), this; }, a.prototype.off = a.prototype.removeListener, a.prototype.addListener = a.prototype.on, a.prefixed = e, a.EventEmitter = a, n.exports = a; }(Pr)), Pr.exports; } var df = uf(); const Ae = /* @__PURE__ */ uh(df), ff = Math.PI * 2, pf = 180 / Math.PI, mf = Math.PI / 180; class Ft { /** * Creates a new `Point` * @param {number} [x=0] - position of the point on the x axis * @param {number} [y=0] - position of the point on the y axis */ constructor(t = 0, e = 0) { this.x = 0, this.y = 0, this.x = t, this.y = e; } /** * Creates a clone of this point, which is a new instance with the same `x` and `y` values. * @example * ```ts * // Basic point cloning * const original = new Point(100, 200); * const copy = original.clone(); * * // Clone and modify * const modified = original.clone(); * modified.set(300, 400); * * // Verify independence * console.log(original); // Point(100, 200) * console.log(modified); // Point(300, 400) * ``` * @remarks * - Creates new Point instance * - Deep copies x and y values * - Independent from original * - Useful for preserving values * @returns A clone of this point * @see {@link Point.copyFrom} For copying into existing point * @see {@link Point.copyTo} For copying to existing point */ clone() { return new Ft(this.x, this.y); } /** * Copies x and y from the given point into this point. * @example * ```ts * // Basic copying * const source = new Point(100, 200); * const target = new Point(); * target.copyFrom(source); * * // Copy and chain operations * const point = new Point() * .copyFrom(source) * .set(x + 50, y + 50); * * // Copy from any PointData * const data = { x: 10, y: 20 }; * point.copyFrom(data); * ``` * @param p - The point to copy from * @returns The point instance itself * @see {@link Point.copyTo} For copying to another point * @see {@link Point.clone} For creating new point copy */ copyFrom(t) { return this.set(t.x, t.y), this; } /** * Copies this point's x and y into the given point. * @example * ```ts * // Basic copying * const source = new Point(100, 200); * const target = new Point(); * source.copyTo(target); * ``` * @param p - The point to copy to. Can be any type that is or extends `PointLike` * @returns The point (`p`) with values updated * @see {@link Point.copyFrom} For copying from another point * @see {@link Point.clone} For creating new point copy */ copyTo(t) { return t.set(this.x, this.y), t; } /** * Checks if another point is equal to this point. * * Compares x and y values using strict equality. * @example * ```ts * // Basic equality check * const p1 = new Point(100, 200); * const p2 = new Point(100, 200); * console.log(p1.equals(p2)); // true * * // Compare with PointData * const data = { x: 100, y: 200 }; * console.log(p1.equals(data)); // true * * // Check different points * const p3 = new Point(200, 300); * console.log(p1.equals(p3)); // false * ``` * @param p - The point to check * @returns `true` if both `x` and `y` are equal * @see {@link Point.copyFrom} For making points equal * @see {@link PointData} For point data interface */ equals(t) { return t.x === this.x && t.y === this.y; } /** * Sets the point to a new x and y position. * * If y is omitted, both x and y will be set to x. * @example * ```ts * // Basic position setting * const point = new Point(); * point.set(100, 200); * * // Set both x and y to same value * point.set(50); // x=50, y=50 * * // Chain with other operations * point * .set(10, 20) * .copyTo(otherPoint); * ``` * @param x - Position on the x axis * @param y - Position on the y axis, defaults to x * @returns The point instance itself * @see {@link Point.copyFrom} For copying from another point * @see {@link Point.equals} For comparing positions */ set(t = 0, e = t) { return this.x = t, this.y = e, this; } toString() { return `[pixi.js/math:Point x=${this.x} y=${this.y}]`; } /** * A static Point object with `x` and `y` values of `0`. * * This shared instance is reset to zero values when accessed. * * > [!IMPORTANT] This point is shared and temporary. Do not store references to it. * @example * ```ts * // Use for temporary calculations * const tempPoint = Point.shared; * tempPoint.set(100, 200); * matrix.apply(tempPoint); * * // Will be reset to (0,0) on next access * const fresh = Point.shared; // x=0, y=0 * ``` * @readonly * @returns A fresh zeroed point for temporary use * @see {@link Point.constructor} For creating new points * @see {@link PointData} For basic point interface */ static get shared() { return Ir.x = 0, Ir.y = 0, Ir; } } const Ir = new Ft(); class X { /** * @param a - x scale * @param b - y skew * @param c - x skew * @param d - y scale * @param tx - x translation * @param ty - y translation */ constructor(t = 1, e = 0, s = 0, i = 1, r = 0, o = 0) { this.array = null, this.a = t, this.b = e, this.c = s, this.d = i, this.tx = r, this.ty = o; } /** * Creates a Matrix object based on the given array. * Populates matrix components from a flat array in column-major order. * * > [!NOTE] Array mapping order: * > ``` * > array[0] = a (x scale) * > array[1] = b (y skew) * > array[2] = tx (x translation) * > array[3] = c (x skew) * > array[4] = d (y scale) * > array[5] = ty (y translation) * > ``` * @example * ```ts * // Create matrix from array * const matrix = new Matrix(); * matrix.fromArray([ * 2, 0, 100, // a, b, tx * 0, 2, 100 // c, d, ty * ]); * * // Create matrix from typed array * const float32Array = new Float32Array([ * 1, 0, 0, // Scale x1, no skew * 0, 1, 0 // No skew, scale x1 * ]); * matrix.fromArray(float32Array); * ``` * @param array - The array to populate the matrix from * @see {@link Matrix.toArray} For converting matrix to array * @see {@link Matrix.set} For setting values directly */ fromArray(t) { this.a = t[0], this.b = t[1], this.c = t[3], this.d = t[4], this.tx = t[2], this.ty = t[5]; } /** * Sets the matrix properties directly. * All matrix components can be set in one call. * @example * ```ts * // Set to identity matrix * matrix.set(1, 0, 0, 1, 0, 0); * * // Set to scale matrix * matrix.set(2, 0, 0, 2, 0, 0); // Scale 2x * * // Set to translation matrix * matrix.set(1, 0, 0, 1, 100, 50); // Move 100,50 * ``` * @param a - Scale on x axis * @param b - Shear on y axis * @param c - Shear on x axis * @param d - Scale on y axis * @param tx - Translation on x axis * @param ty - Translation on y axis * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.identity} For resetting to identity * @see {@link Matrix.fromArray} For setting from array */ set(t, e, s, i, r, o) { return this.a = t, this.b = e, this.c = s, this.d = i, this.tx = r, this.ty = o, this; } /** * Creates an array from the current Matrix object. * * > [!NOTE] The array format is: * > ``` * > Non-transposed: * > [a, c, tx, * > b, d, ty, * > 0, 0, 1] * > * > Transposed: * > [a, b, 0, * > c, d, 0, * > tx,ty,1] * > ``` * @example * ```ts * // Basic array conversion * const matrix = new Matrix(2, 0, 0, 2, 100, 100); * const array = matrix.toArray(); * * // Using existing array * const float32Array = new Float32Array(9); * matrix.toArray(false, float32Array); * * // Get transposed array * const transposed = matrix.toArray(true); * ``` * @param transpose - Whether to transpose the matrix * @param out - Optional Float32Array to store the result * @returns The array containing the matrix values * @see {@link Matrix.fromArray} For creating matrix from array * @see {@link Matrix.array} For cached array storage */ toArray(t, e) { this.array || (this.array = new Float32Array(9)); const s = e || this.array; return t ? (s[0] = this.a, s[1] = this.b, s[2] = 0, s[3] = this.c, s[4] = this.d, s[5] = 0, s[6] = this.tx, s[7] = this.ty, s[8] = 1) : (s[0] = this.a, s[1] = this.c, s[2] = this.tx, s[3] = this.b, s[4] = this.d, s[5] = this.ty, s[6] = 0, s[7] = 0, s[8] = 1), s; } /** * Get a new position with the current transformation applied. * * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering) * @example * ```ts * // Basic point transformation * const matrix = new Matrix().translate(100, 50).rotate(Math.PI / 4); * const point = new Point(10, 20); * const transformed = matrix.apply(point); * * // Reuse existing point * const output = new Point(); * matrix.apply(point, output); * ``` * @param pos - The origin point to transform * @param newPos - Optional point to store the result * @returns The transformed point * @see {@link Matrix.applyInverse} For inverse transformation * @see {@link Point} For point operations */ apply(t, e) { e = e || new Ft(); const s = t.x, i = t.y; return e.x = this.a * s + this.c * i + this.tx, e.y = this.b * s + this.d * i + this.ty, e; } /** * Get a new position with the inverse of the current transformation applied. * * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input) * @example * ```ts * // Basic inverse transformation * const matrix = new Matrix().translate(100, 50).rotate(Math.PI / 4); * const worldPoint = new Point(150, 100); * const localPoint = matrix.applyInverse(worldPoint); * * // Reuse existing point * const output = new Point(); * matrix.applyInverse(worldPoint, output); * * // Convert mouse position to local space * const mousePoint = new Point(mouseX, mouseY); * const localMouse = matrix.applyInverse(mousePoint); * ``` * @param pos - The origin point to inverse-transform * @param newPos - Optional point to store the result * @returns The inverse-transformed point * @see {@link Matrix.apply} For forward transformation * @see {@link Matrix.invert} For getting inverse matrix */ applyInverse(t, e) { e = e || new Ft(); const s = this.a, i = this.b, r = this.c, o = this.d, a = this.tx, l = this.ty, c = 1 / (s * o + r * -i), h = t.x, u = t.y; return e.x = o * c * h + -r * c * u + (l * r - a * o) * c, e.y = s * c * u + -i * c * h + (-l * s + a * i) * c, e; } /** * Translates the matrix on the x and y axes. * Adds to the position values while preserving scale, rotation and skew. * @example * ```ts * // Basic translation * const matrix = new Matrix(); * matrix.translate(100, 50); // Move right 100, down 50 * * // Chain with other transformations * matrix * .scale(2, 2) * .translate(100, 0) * .rotate(Math.PI / 4); * ``` * @param x - How much to translate on the x axis * @param y - How much to translate on the y axis * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.set} For setting position directly * @see {@link Matrix.setTransform} For complete transform setup */ translate(t, e) { return this.tx += t, this.ty += e, this; } /** * Applies a scale transformation to the matrix. * Multiplies the scale values with existing matrix components. * @example * ```ts * // Basic scaling * const matrix = new Matrix(); * matrix.scale(2, 3); // Scale 2x horizontally, 3x vertically * * // Chain with other transformations * matrix * .translate(100, 100) * .scale(2, 2) // Scales after translation * .rotate(Math.PI / 4); * ``` * @param x - The amount to scale horizontally * @param y - The amount to scale vertically * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.setTransform} For setting scale directly * @see {@link Matrix.append} For combining transformations */ scale(t, e) { return this.a *= t, this.d *= e, this.c *= t, this.b *= e, this.tx *= t, this.ty *= e, this; } /** * Applies a rotation transformation to the matrix. * * Rotates around the origin (0,0) by the given angle in radians. * @example * ```ts * // Basic rotation * const matrix = new Matrix(); * matrix.rotate(Math.PI / 4); // Rotate 45 degrees * * // Chain with other transformations * matrix * .translate(100, 100) // Move to rotation center * .rotate(Math.PI) // Rotate 180 degrees * .scale(2, 2); // Scale after rotation * * // Common angles * matrix.rotate(Math.PI / 2); // 90 degrees * matrix.rotate(Math.PI); // 180 degrees * matrix.rotate(Math.PI * 2); // 360 degrees * ``` * @remarks * - Rotates around origin point (0,0) * - Affects position if translation was set * - Uses counter-clockwise rotation * - Order of operations matters when chaining * @param angle - The angle in radians * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.setTransform} For setting rotation directly * @see {@link Matrix.append} For combining transformations */ rotate(t) { const e = Math.cos(t), s = Math.sin(t), i = this.a, r = this.c, o = this.tx; return this.a = i * e - this.b * s, this.b = i * s + this.b * e, this.c = r * e - this.d * s, this.d = r * s + this.d * e, this.tx = o * e - this.ty * s, this.ty = o * s + this.ty * e, this; } /** * Appends the given Matrix to this Matrix. * Combines two matrices by multiplying them together: this = this * matrix * @example * ```ts * // Basic matrix combination * const matrix = new Matrix(); * const other = new Matrix().translate(100, 0).rotate(Math.PI / 4); * matrix.append(other); * ``` * @remarks * - Order matters: A.append(B) !== B.append(A) * - Modifies current matrix * - Preserves transformation order * - Commonly used for combining transforms * @param matrix - The matrix to append * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.prepend} For prepending transformations * @see {@link Matrix.appendFrom} For appending two external matrices */ append(t) { const e = this.a, s = this.b, i = this.c, r = this.d; return this.a = t.a * e + t.b * i, this.b = t.a * s + t.b * r, this.c = t.c * e + t.d * i, this.d = t.c * s + t.d * r, this.tx = t.tx * e + t.ty * i + this.tx, this.ty = t.tx * s + t.ty * r + this.ty, this; } /** * Appends two matrices and sets the result to this matrix. * Performs matrix multiplication: this = A * B * @example * ```ts * // Basic matrix multiplication * const result = new Matrix(); * const matrixA = new Matrix().scale(2, 2); * const matrixB = new Matrix().rotate(Math.PI / 4); * result.appendFrom(matrixA, matrixB); * ``` * @remarks * - Order matters: A * B !== B * A * - Creates a new transformation from two others * - More efficient than append() for multiple operations * - Does not modify input matrices * @param a - The first matrix to multiply * @param b - The second matrix to multiply * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.append} For single matrix combination * @see {@link Matrix.prepend} For reverse order multiplication */ appendFrom(t, e) { const s = t.a, i = t.b, r = t.c, o = t.d, a = t.tx, l = t.ty, c = e.a, h = e.b, u = e.c, f = e.d; return this.a = s * c + i * u, this.b = s * h + i * f, this.c = r * c + o * u, this.d = r * h + o * f, this.tx = a * c + l * u + e.tx, this.ty = a * h + l * f + e.ty, this; } /** * Sets the matrix based on all the available properties. * Combines position, scale, rotation, skew and pivot in a single operation. * @example * ```ts * // Basic transform setup * const matrix = new Matrix(); * matrix.setTransform( * 100, 100, // position * 0, 0, // pivot * 2, 2, // scale * Math.PI / 4, // rotation (45 degrees) * 0, 0 // skew * ); * ``` * @remarks * - Updates all matrix components at once * - More efficient than separate transform calls * - Uses radians for rotation and skew * - Pivot affects rotation center * @param x - Position on the x axis * @param y - Position on the y axis * @param pivotX - Pivot on the x axis * @param pivotY - Pivot on the y axis * @param scaleX - Scale on the x axis * @param scaleY - Scale on the y axis * @param rotation - Rotation in radians * @param skewX - Skew on the x axis * @param skewY - Skew on the y axis * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.decompose} For extracting transform properties * @see {@link TransformableObject} For transform data structure */ setTransform(t, e, s, i, r, o, a, l, c) { return this.a = Math.cos(a + c) * r, this.b = Math.sin(a + c) * r, this.c = -Math.sin(a - l) * o, this.d = Math.cos(a - l) * o, this.tx = t - (s * this.a + i * this.c), this.ty = e - (s * this.b + i * this.d), this; } /** * Prepends the given Matrix to this Matrix. * Combines two matrices by multiplying them together: this = matrix * this * @example * ```ts * // Basic matrix prepend * const matrix = new Matrix().scale(2, 2); * const other = new Matrix().translate(100, 0); * matrix.prepend(other); // Translation happens before scaling * ``` * @remarks * - Order matters: A.prepend(B) !== B.prepend(A) * - Modifies current matrix * - Reverses transformation order compared to append() * @param matrix - The matrix to prepend * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.append} For appending transformations * @see {@link Matrix.appendFrom} For combining external matrices */ prepend(t) { const e = this.tx; if (t.a !== 1 || t.b !== 0 || t.c !== 0 || t.d !== 1) { const s = this.a, i = this.c; this.a = s * t.a + this.b * t.c, this.b = s * t.b + this.b * t.d, this.c = i * t.a + this.d * t.c, this.d = i * t.b + this.d * t.d; } return this.tx = e * t.a + this.ty * t.c + t.tx, this.ty = e * t.b + this.ty * t.d + t.ty, this; } /** * Decomposes the matrix into its individual transform components. * Extracts position, scale, rotation and skew values from the matrix. * @example * ```ts * // Basic decomposition * const matrix = new Matrix() * .translate(100, 100) * .rotate(Math.PI / 4) * .scale(2, 2); * * const transform = { * position: new Point(), * scale: new Point(), * pivot: new Point(), * skew: new Point(), * rotation: 0 * }; * * matrix.decompose(transform); * console.log(transform.position); // Point(100, 100) * console.log(transform.rotation); // ~0.785 (PI/4) * console.log(transform.scale); // Point(2, 2) * ``` * @remarks * - Handles combined transformations * - Accounts for pivot points * - Chooses between rotation/skew based on transform type * - Uses radians for rotation and skew * @param transform - The transform object to store the decomposed values * @returns The transform with the newly applied properties * @see {@link Matrix.setTransform} For composing from components * @see {@link TransformableObject} For transform structure */ decompose(t) { const e = this.a, s = this.b, i = this.c, r = this.d, o = t.pivot, a = -Math.atan2(-i, r), l = Math.atan2(s, e), c = Math.abs(a + l); return c < 1e-5 || Math.abs(ff - c) < 1e-5 ? (t.rotation = l, t.skew.x = t.skew.y = 0) : (t.rotation = 0, t.skew.x = a, t.skew.y = l), t.scale.x = Math.sqrt(e * e + s * s), t.scale.y = Math.sqrt(i * i + r * r), t.position.x = this.tx + (o.x * e + o.y * i), t.position.y = this.ty + (o.x * s + o.y * r), t; } /** * Inverts this matrix. * Creates the matrix that when multiplied with this matrix results in an identity matrix. * @example * ```ts * // Basic matrix inversion * const matrix = new Matrix() * .translate(100, 50) * .scale(2, 2); * * matrix.invert(); // Now transforms in opposite direction * * // Verify inversion * const point = new Point(50, 50); * const transformed = matrix.apply(point); * const original = matrix.invert().apply(transformed); * // original ≈ point * ``` * @remarks * - Modifies the current matrix * - Useful for reversing transformations * - Cannot invert matrices with zero determinant * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.identity} For resetting to identity * @see {@link Matrix.applyInverse} For inverse transformations */ invert() { const t = this.a, e = this.b, s = this.c, i = this.d, r = this.tx, o = t * i - e * s; return this.a = i / o, this.b = -e / o, this.c = -s / o, this.d = t / o, this.tx = (s * this.ty - i * r) / o, this.ty = -(t * this.ty - e * r) / o, this; } /** * Checks if this matrix is an identity matrix. * * An identity matrix has no transformations applied (default state). * @example * ```ts * // Check if matrix is identity * const matrix = new Matrix(); * console.log(matrix.isIdentity()); // true * * // Check after transformations * matrix.translate(100, 0); * console.log(matrix.isIdentity()); // false * * // Reset and verify * matrix.identity(); * console.log(matrix.isIdentity()); // true * ``` * @remarks * - Verifies a = 1, d = 1 (no scale) * - Verifies b = 0, c = 0 (no skew) * - Verifies tx = 0, ty = 0 (no translation) * @returns True if matrix has no transformations * @see {@link Matrix.identity} For resetting to identity * @see {@link Matrix.IDENTITY} For constant identity matrix */ isIdentity() { return this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1 && this.tx === 0 && this.ty === 0; } /** * Resets this Matrix to an identity (default) matrix. * Sets all components to their default values: scale=1, no skew, no translation. * @example * ```ts * // Reset transformed matrix * const matrix = new Matrix() * .scale(2, 2) * .rotate(Math.PI / 4); * matrix.identity(); // Back to default state * * // Chain after reset * matrix * .identity() * .translate(100, 100) * .scale(2, 2); * * // Compare with identity constant * const isDefault = matrix.equals(Matrix.IDENTITY); * ``` * @remarks * - Sets a=1, d=1 (default scale) * - Sets b=0, c=0 (no skew) * - Sets tx=0, ty=0 (no translation) * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.IDENTITY} For constant identity matrix * @see {@link Matrix.isIdentity} For checking identity state */ identity() { return this.a = 1, this.b = 0, this.c = 0, this.d = 1, this.tx = 0, this.ty = 0, this; } /** * Creates a new Matrix object with the same values as this one. * @returns A copy of this matrix. Good for chaining method calls. */ clone() { const t = new X(); return t.a = this.a, t.b = this.b, t.c = this.c, t.d = this.d, t.tx = this.tx, t.ty = this.ty, t; } /** * Creates a new Matrix object with the same values as this one. * @param matrix * @example * ```ts * // Basic matrix cloning * const matrix = new Matrix() * .translate(100, 100) * .rotate(Math.PI / 4); * const copy = matrix.clone(); * * // Clone and modify * const modified = matrix.clone() * .scale(2, 2); * * // Compare matrices * console.log(matrix.equals(copy)); // true * console.log(matrix.equals(modified)); // false * ``` * @returns A copy of this matrix. Good for chaining method calls. * @see {@link Matrix.copyTo} For copying to existing matrix * @see {@link Matrix.copyFrom} For copying from another matrix */ copyTo(t) { return t.a = this.a, t.b = this.b, t.c = this.c, t.d = this.d, t.tx = this.tx, t.ty = this.ty, t; } /** * Changes the values of the matrix to be the same as the ones in given matrix. * @example * ```ts * // Basic matrix copying * const source = new Matrix() * .translate(100, 100) * .rotate(Math.PI / 4); * const target = new Matrix(); * target.copyFrom(source); * ``` * @param matrix - The matrix to copy from * @returns This matrix. Good for chaining method calls. * @see {@link Matrix.clone} For creating new matrix copy * @see {@link Matrix.copyTo} For copying to another matrix */ copyFrom(t) { return this.a = t.a, this.b = t.b, this.c = t.c, this.d = t.d, this.tx = t.tx, this.ty = t.ty, this; } /** * Checks if this matrix equals another matrix. * Compares all components for exact equality. * @example * ```ts * // Basic equality check * const m1 = new Matrix(); * const m2 = new Matrix(); * console.log(m1.equals(m2)); // true * * // Compare transformed matrices * const transform = new Matrix() * .translate(100, 100) * const clone = new Matrix() * .scale(2, 2); * console.log(transform.equals(clone)); // false * ``` * @param matrix - The matrix to compare to * @returns True if matrices are identical * @see {@link Matrix.copyFrom} For copying matrix values * @see {@link Matrix.isIdentity} For identity comparison */ equals(t) { return t.a === this.a && t.b === this.b && t.c === this.c && t.d === this.d && t.tx === this.tx && t.ty === this.ty; } toString() { return `[pixi.js:Matrix a=${this.a} b=${this.b} c=${this.c} d=${this.d} tx=${this.tx} ty=${this.ty}]`; } /** * A default (identity) matrix with no transformations applied. * * > [!IMPORTANT] This is a shared read-only object. Create a new Matrix if you need to modify it. * @example * ```ts * // Get identity matrix reference * const identity = Matrix.IDENTITY; * console.log(identity.isIdentity()); // true * * // Compare with identity * const matrix = new Matrix(); * console.log(matrix.equals(Matrix.IDENTITY)); // true * * // Create new matrix instead of modifying IDENTITY * const transform = new Matrix() * .copyFrom(Matrix.IDENTITY) * .translate(100, 100); * ``` * @readonly * @returns A read-only identity matrix * @see {@link Matrix.shared} For temporary calculations * @see {@link Matrix.identity} For resetting matrices */ static get IDENTITY() { return yf.identity(); } /** * A static Matrix that can be used to avoid creating new objects. * Will always ensure the matrix is reset to identity when requested. * * > [!IMPORTANT] This matrix is shared and temporary. Do not store references to it. * @example * ```ts * // Use for temporary calculations * const tempMatrix = Matrix.shared; * tempMatrix.translate(100, 100).rotate(Math.PI / 4); * const point = tempMatrix.apply({ x: 10, y: 20 }); * * // Will be reset to identity on next access * const fresh = Matrix.shared; // Back to identity * ``` * @remarks * - Always returns identity matrix * - Safe to modify temporarily * - Not safe to store references * - Useful for one-off calculations * @readonly * @returns A fresh identity matrix for temporary use * @see {@link Matrix.IDENTITY} For immutable identity matrix * @see {@link Matrix.identity} For resetting matrices */ static get shared() { return gf.identity(); } } const gf = new X(), yf = new X(), fn = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1], pn = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1], mn = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1], gn = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1], uo = [], dh = [], ii = Math.sign; function _f() { for (let n = 0; n < 16; n++) { const t = []; uo.push(t); for (let e = 0; e < 16; e++) { const s = ii(fn[n] * fn[e] + mn[n] * pn[e]), i = ii(pn[n] * fn[e] + gn[n] * pn[e]), r = ii(fn[n] * mn[e] + mn[n] * gn[e]), o = ii(pn[n] * mn[e] + gn[n] * gn[e]); for (let a = 0; a < 16; a++) if (fn[a] === s && pn[a] === i && mn[a] === r && gn[a] === o) { t.push(a); break; } } } for (let n = 0; n < 16; n++) { const t = new X(); t.set(fn[n], pn[n], mn[n], gn[n], 0, 0), dh.push(t); } } _f(); const st = { /** * | Rotation | Direction | * |----------|-----------| * | 0° | East | * @group groupD8 * @type {GD8Symmetry} */ E: 0, /** * | Rotation | Direction | * |----------|-----------| * | 45°↻ | Southeast | * @group groupD8 * @type {GD8Symmetry} */ SE: 1, /** * | Rotation | Direction | * |----------|-----------| * | 90°↻ | South | * @group groupD8 * @type {GD8Symmetry} */ S: 2, /** * | Rotation | Direction | * |----------|-----------| * | 135°↻ | Southwest | * @group groupD8 * @type {GD8Symmetry} */ SW: 3, /** * | Rotation | Direction | * |----------|-----------| * | 180° | West | * @group groupD8 * @type {GD8Symmetry} */ W: 4, /** * | Rotation | Direction | * |-------------|--------------| * | -135°/225°↻ | Northwest | * @group groupD8 * @type {GD8Symmetry} */ NW: 5, /** * | Rotation | Direction | * |-------------|--------------| * | -90°/270°↻ | North | * @group groupD8 * @type {GD8Symmetry} */ N: 6, /** * | Rotation | Direction | * |-------------|--------------| * | -45°/315°↻ | Northeast | * @group groupD8 * @type {GD8Symmetry} */ NE: 7, /** * Reflection about Y-axis. * @group groupD8 * @type {GD8Symmetry} */ MIRROR_VERTICAL: 8, /** * Reflection about the main diagonal. * @group groupD8 * @type {GD8Symmetry} */ MAIN_DIAGONAL: 10, /** * Reflection about X-axis. * @group groupD8 * @type {GD8Symmetry} */ MIRROR_HORIZONTAL: 12, /** * Reflection about reverse diagonal. * @group groupD8 * @type {GD8Symmetry} */ REVERSE_DIAGONAL: 14, /** * @group groupD8 * @param {GD8Symmetry} ind - sprite rotation angle. * @returns {GD8Symmetry} The X-component of the U-axis * after rotating the axes. */ uX: (n) => fn[n], /** * @group groupD8 * @param {GD8Symmetry} ind - sprite rotation angle. * @returns {GD8Symmetry} The Y-component of the U-axis * after rotating the axes. */ uY: (n) => pn[n], /** * @group groupD8 * @param {GD8Symmetry} ind - sprite rotation angle. * @returns {GD8Symmetry} The X-component of the V-axis * after rotating the axes. */ vX: (n) => mn[n], /** * @group groupD8 * @param {GD8Symmetry} ind - sprite rotation angle. * @returns {GD8Symmetry} The Y-component of the V-axis * after rotating the axes. */ vY: (n) => gn[n], /** * @group groupD8 * @param {GD8Symmetry} rotation - symmetry whose opposite * is needed. Only rotations have opposite symmetries while * reflections don't. * @returns {GD8Symmetry} The opposite symmetry of `rotation` */ inv: (n) => n & 8 ? n & 15 : -n & 7, /** * Composes the two D8 operations. * * Taking `^` as reflection: * * | | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 | * |-------|-----|-----|-----|-----|------|-------|-------|-------| * | E=0 | E | S | W | N | E^ | S^ | W^ | N^ | * | S=2 | S | W | N | E | S^ | W^ | N^ | E^ | * | W=4 | W | N | E | S | W^ | N^ | E^ | S^ | * | N=6 | N | E | S | W | N^ | E^ | S^ | W^ | * | E^=8 | E^ | N^ | W^ | S^ | E | N | W | S | * | S^=10 | S^ | E^ | N^ | W^ | S | E | N | W | * | W^=12 | W^ | S^ | E^ | N^