wave-roll
Version:
JavaScript Library for Comparative MIDI Piano-Roll Visualization
1,478 lines • 1.2 MB
JavaScript
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^